5

私はHaskellで次のようにネストされたヘルパー関数(ところで、外部関数のパラメータを時折使用し、再帰的である)を書いていました(loop):

sum a b = let
  loop s i = if i > b then s else loop (s + i) (i + 1)
  in loop 0 a

Common Lisp におけるこれの最も明確な類似物は何ですか?

このあたりを検索したところ、関数から関数を返すことに焦点を当てた議論 (およびそのような「返された」関数を呼び出そうとしたときに発生する可能性がある問題) を見つけましたが、これは私が見る限り、まったく同じ状況ではありません。

4

2 に答える 2

2

あなたのLispがテールコールを最適化する場合のラベルと名前付きLets

「ばかげた Lisp のトリック」をするのと同じように、Scheme では、

sum a b = let
  loop s i = if i > b then sum else loop (s + i) (i + 1)
  in loop 0 a

letrec または名前付き let:

(define (sum a b)
  (letrec ((loop (lambda (s i)
                   (if (> i b)
                       s
                       (loop (+ s i) (+ i 1))))))
    (loop 0 a)))

(define (sum a b)
  (let loop ((s 0) (i a))
    (if (> i b)
        s
        (loop (+ s i) (+ i 1)))))

Letrec は、Scheme が Lisp-1 であるため、Baggers が説明した の機能を提供labelsます。名前付き let は、Common Lisp でマクロを使用して実行できますlabels

(defmacro named-let (name bindings &body body)
  `(labels ((,name ,(mapcar 'first bindings)
              ,@body))
     (,name ,@(mapcar 'second bindings))))

(pprint (macroexpand
 '(named-let loop ((s 0) (i a))
   (if (> i b)
       s
       (loop (+ s i) (+ i 1))))))

;; (LABELS ((LOOP (S I)
;;            (IF (> I B)
;;                S
;;                (LOOP (+ S I)
;;                      (+ I 1)))))
;;   (LOOP 0 A))

Do と Do* はどこでも効率的であるべき

しかし、末尾呼び出しは Common Lisp では必ずしも最適化されていないため、反復のためのこの種の再帰はそれほど一般的ではありません。反復アナログはdo次のとおりです。

(defun sum (a b)
  (do ((s 0 (+ s i))
       (i a (+ i 1)))
      ((> i b) s)))

ループも機能します

あなたも使うことができますloopが、もう少し冗長です(しかし、あなたが精通しているなら、おそらくもっと読みやすいでしょうdo

(defun sum (a b)
  (loop
     for s = 0 then (+ s i)
     for i = a then (+ i 1)
     when (> i b) return s))
于 2013-10-23T13:18:10.987 に答える