私はLISPの初心者です。
リストの現在の合計を取得するために、私は次のように書いています-
(setf sum 0.0)
(mapcar #'(lambda(x)
(setf sum (+ sum x)) sum) values))
たとえば、'(1 2 3 4)
入力として指定すると、上記のコードは'(1 3 6 10)
出力として返されます。
グローバル変数を使用せずに(よりエレガントな方法で)同じことを行うことは可能sum
ですか?
私はLISPの初心者です。
リストの現在の合計を取得するために、私は次のように書いています-
(setf sum 0.0)
(mapcar #'(lambda(x)
(setf sum (+ sum x)) sum) values))
たとえば、'(1 2 3 4)
入力として指定すると、上記のコードは'(1 3 6 10)
出力として返されます。
グローバル変数を使用せずに(よりエレガントな方法で)同じことを行うことは可能sum
ですか?
(loop for x in '(1 2 3 4) sum x into y collect y)
scanl
ワンライナーです:
(defun scanl (f init xs)
(loop for x in xs collect (setf init (funcall f init x))))
loop
次のように使用できます。
(defun running-sum (xs)
(loop with sum = 0
for x in xs
collect (setf sum (+ sum x))))
(running-sum '(1 2 3 4))
基本的には同じですが、グローバル変数ではなくローカル変数を使用しているため、より明確になる可能性があります。
または、再帰関数とラッパー関数を定義することもできます。
(defun running-sum-recursive (xs)
(running-sum-recursive2 0 xs))
(defun running-sum-recursive2 (sum xs)
(if (eq xs nil)
nil
(let ((new-sum (+ sum (car xs))))
(cons new-sum (running-sum-recursive2 new-sum (cdr xs))))))
(running-sum-recursive '(1 2 3 4))
ただし、利用可能な場合、これは私には不必要に複雑に思えloop
ます。
Haskellでは、次のようなランニングサムを実行できることに注意してください。
runningSum xs = scanl1 (+) xs
runningSum [1, 2, 3, 4]
ここで重要なのはscanl1
関数です。Lispにも似たようなものが存在する可能性がありますが(そして今ではほぼ2回書いています)、私はしばらくLispを使用していません。
編集:いくつか検索した後、Common Lispにはまたはのようなものが含まれているとは思わないscanl
のでscanl1
、ここにあります:
(defun scanl (f val xs)
(loop for x in xs
collect (setf val (funcall f val x))))
(defun scanl1 (f xs)
(cons (car xs)
(scanl f (car xs) (cdr xs))))
(scanl1 #'+ '(1 2 3 4))
編集:ループを短縮する方法についての提案に対するhuaiyuanの回答に感謝します。
または、高階関数を使用することもできます
(define (running-sum ls)
(cdr (reverse (foldl (lambda (y xs) (cons (+ (car xs) y) xs)) '(0) ls))))
Haskellにはリスト再帰用の関数の豊富なインベントリがありますがreduce
、少なくともあります。これが基本的な(つまりloop
魔法のない)機能的な解決策です:
(defun running-sum (lst)
(reverse (reduce (lambda (acc x)
(cons (+ (first acc) x) acc))
(rest lst)
:initial-value (list (first lst)))))
元のリストの先頭を初期値として使用し、リストの残りの部分をウォークスルーして、先頭に合計を追加し(先頭に追加するのが自然なため)、最終的に取得したリストを逆にします。
reduce
ほとんどの場合、値を累積するシーケンスをトラバースする必要がある場合に使用できます。
push
--nreverse
イディオムを使用した基本的な反復ソリューションは次のとおりです。
(defun running-sum (lst)
(let ((sums (list (first lst))))
(dolist (x (rest lst))
(push (+ x (first sums)) sums))
(nreverse sums)))
スキームでは、アキュムレータを使用してリストの合計を再帰的に計算します。そのようです:
; Computes a list of intermediary results of list summation
(define list-sum
(lambda (l)
(letrec ((recsum (lambda (lst acc acclst)
(if (pair? lst)
(recsum (cdr lst) (+ acc (car lst)) (cons acc acclst))
(cons acc acclst)))))
(recsum (cdr l) (car l) '()))))
出力:
> (list-sum '(1 2 3 4))
(10 6 3 1)
> (list-sum '(2 4 6 8 10))
(30 20 12 6 2)
>
リストを再帰する秘訣は、毎回最初の要素/車を外し、残り/cdrを渡すことです。追加のパラメーター(アキュムレーターと呼ばれる)を使用して中間結果を保持し、その合計を渡すことができます。上記の2つのアキュムレータを使用しました。1つは最後の合計用で、もう1つは以前のすべての合計のリスト用です。
私はLISPで何もしたことがないので、これがあなたの方言(?)に直接変換されるかどうかはわかりませんが、概念的には単純で、LISPでも実行できると確信しています。
何かがすぐにはっきりしないかどうか尋ねてください。私がこの言語族を使ってからしばらく経ちました:)