2013-03-12 18 views
5

私はLISP初心者です。 LISP:リストの実行合計を取得する方法は? (グローバル変数なし)

は、リストの実行中の合計が、私は次のように書いています取得するには - あなたが入力として '(1 2 3 4)を与える場合、例えば

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

、上記のコードは'(1 3 6 10)出力としてなどを返します。

グローバル変数sumを使用せずに同じことを(より洗練された方法で)行うことはできますか?

答えて

6
(loop for x in '(1 2 3 4) sum x into y collect y) 

scanlはonelinerです:

(defun scanl (f init xs) 
    (loop for x in xs collect (setf init (funcall f init x)))) 
+0

ああ、そうですループを少し締め付けることができます。 +1投票。しかし、initは単に初期値としてだけでなく、現在の値としても使われていないので、それを反映するために名前を変更することをお勧めします。 'value'、' val'、または 'v'おそらく? –

+0

これはクールです!ありがとう。 – ramgorur

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 functionです。 Lispに似たようなものが存在する可能性がありますが(今は2回書いていますが)しばらくLispを使用していません。

編集:

(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)) 

編集:いくつか検索した後、私はので、ここで彼らは、Common Lispのはかなりscanlまたはscanl1ようなものを含んでいるとは思いませんかループについての提案のためのhuaiyuanの答えのおかげで短縮することができます。

+0

高階関数を使用することができますが、おかげで、 'scanl1'は非常に面白そう、私がいました同様のものがLISPでも利用可能かどうか疑問に思っています。 – ramgorur

1

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

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

すぐにわからないことがあるかどうか尋ねます。私は、言語のこのファミリを使用していたので、それはしばらくしている:)

+1

戻る前にリストを元に戻すことができます。 – Ravi

+0

@Ravi正直言って私はそれを残して、例のコードに行を保存するのに十分だと思いました:)しかし、結果は逆になります –

+1

申し訳ありません、それは自然なプログラマーのぺティシズムです:) – Ravi

2

Haskellはリストの再帰のための機能の豊富な在庫を持っているが、我々はしました少なくともreduceを得ました。

(defun running-sum (lst) 
    (reverse (reduce (lambda (acc x) 
        (cons (+ (first acc) x) acc)) 
        (rest lst) 
        :initial-value (list (first lst))))) 

私は初期値として、元のリストの先頭を使用し、先頭に合計を追加し、リストの残りの部分を歩いています(理由:ここでは基本的には(すなわちloop魔法なし)機能ソリューションです頭に追加するのは当然です)、最後に得られたリストを逆にします。

ほとんどの場合、値を累積するシーケンスをトラバースする必要がある場合、reduceを使用できます。ここで

push使用して基本反復解法である - nreverseイディオム:

(defun running-sum (lst) 
    (let ((sums (list (first lst)))) 
    (dolist (x (rest lst)) 
     (push (+ x (first sums)) sums)) 
    (nreverse sums))) 
2

それとも、

(define (running-sum ls) 
    (cdr (reverse (foldl (lambda (y xs) (cons (+ (car xs) y) xs)) '(0) ls)))) 
関連する問題