6

私はLISPの初心者です。

リストの現在の合計を取得するために、私は次のように書いています-

(setf sum 0.0)
(mapcar #'(lambda(x)
    (setf sum (+ sum x)) sum) values))

たとえば、'(1 2 3 4)入力として指定すると、上記のコードは'(1 3 6 10)出力として返されます。

グローバル変数を使用せずに(よりエレガントな方法で)同じことを行うことは可能sumですか?

4

5 に答える 5

8
(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))))
于 2013-03-13T01:09:43.300 に答える
4

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の回答に感謝します。

于 2013-03-12T23:41:31.123 に答える
3

または、高階関数を使用することもできます

(define (running-sum ls)
     (cdr (reverse (foldl (lambda (y xs) (cons (+ (car xs) y) xs)) '(0) ls))))
于 2013-03-16T16:18:17.260 に答える
2

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)))
于 2013-03-13T09:56:25.457 に答える
1

スキームでは、アキュムレータを使用してリストの合計を再帰的に計算します。そのようです:

; 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でも実行できると確信しています。

何かがすぐにはっきりしないかどうか尋ねてください。私がこの言語族を使ってからしばらく経ちました:)

于 2013-03-13T00:22:07.463 に答える