我是一名LISP新手。
为了获得列表的运行总和,我写的是 -
(setf sum 0.0)
(mapcar #'(lambda(x)
(setf sum (+ sum x)) sum) values))
例如,如果您输入'(1 2 3 4)
作为输入,则上面的代码会返回'(1 3 6 10)
作为输出,等等。
是否可以在不使用全局变量sum
的情况下(以更优雅的方式)执行相同的操作?
答案 0 :(得分: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))))
答案 1 :(得分: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中可能存在类似的东西(现在我们已经差不多写了两次),但我有一段时间没有使用过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))
编辑:感谢怀远回答有关如何缩短循环的建议。
答案 2 :(得分:3)
或者您可以使用高阶函数
(define (running-sum ls)
(cdr (reverse (foldl (lambda (y xs) (cons (+ (car xs) y) xs)) '(0) ls))))
答案 3 :(得分: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)))
答案 4 :(得分:1)
在Scheme 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)
>
递归列表的技巧是每次关闭第一个元素/ car并传递其余的/ cdr。您可以使用额外的参数(称为累加器)来保留中间结果,并传递总和。我上面使用了两个累加器:一个用于最后一个和一个用于所有先前总和的列表。
我从来没有在LISP中做过任何事情,所以我不知道这是否会直接翻译成你的方言(?),但它在概念上很简单,我相信它在LISP中也是可行的。
请问是否有些事情没有立即清楚。自从我使用这一系列语言已经有一段时间了:))