【问题标题】:Cannot do sum in lisp with do loop不能用do循环在lisp中求和
【发布时间】:2017-04-25 07:49:12
【问题描述】:
(defun suma (L)
  (setq var 0)
  (do 
      ((i 0 (+ i 1)))
      ((= i (length L)))
    (+ var (nth i L)))
  var)

为什么总是返回0?

它不应该返回列表 L 的总和吗?

【问题讨论】:

  • 请先使用一个好的编辑器,它会为您缩进代码。
  • 您应该将varlet 绑定,而不是使用setq 使其成为全局对象
  • 我用 let 得到了同样的结果...而且我使用 notepad++ 但发布的代码没有缩进 sry
  • setq 的问题在于您正在创建一个全局变量。你不应该在函数中这样做。

标签: lisp common-lisp


【解决方案1】:

+ 不修改其参数,因此,由于您从未修改 var,因此返回其初始值 0。

您需要将(+ var (nth i L)) 替换为(incf var (nth i L)),相当于(setq var (+ var (nth i L)))

incf

请注意,您应该将varlet 绑定,而不是使用setq 将其设为全局。

最重要的是,请注意您的算法在列表参数的长度上是二次的(因为nth 每次从一开始都会扫描您的列表)。

这里有一些更好的实现:

(defun sum-1 (l)
  (reduce #'+ l))

(defun sum-2 (l)
  (loop for x in l sum x))

(defun sum-3 (l)
  (let ((sum 0))
    (dolist (x l sum)
      (incf sum x))))

这是一个不好的实现:

(defun sum-4 (l)
  (apply #'+ l))

sum-4 的问题在于,如果提供的列表长度大于call-arguments-limit,它将失败。

【讨论】:

  • 我确实将 var 与 let 绑定并放入 incf,但得到相同的结果,即 0。我检查了循环,它完成了它的工作,但是当我添加到 var 时,它输入 0,我不明白为什么。但是当我打印( nth i L )时,它会在索引 i 处打印列表元素。
  • 请将您的新代码添加到问题的末尾。请确保它的格式正确,因为我们 Lispers 通过缩进解析代码,而不是通过括号计数(应该由编辑而不是人类来完成)。
【解决方案2】:

我认为这是对完整学习体验的评论,但我无法在评论中添加代码。

有一种方法可以在不修改任何参数的情况下进行求和,那就是递归地进行:

(defun recsum (list)
  (if list
      (+ (first list) (recsum (rest list)))
      0))

这个版本可以通过编译器进行尾调用优化,和循环一样快:

(defun recsum2 (list &optional (accumulator 0))
  (if list
      (recsum2 (rest list) (+ accumulator (first list)))
      accumulator))

您可以使用do 来完成您想要做的事情,如下所示:

(defun suma (l)
  (do 
   ((var 0)
    (i 0 (+ i 1)))
   ((= i (length l)) var)
    (incf var (nth i l))))

但是我们通常不会在dos 的正文中做任何事情,所以它是这样的:

(defun suma (l)
  (do
   ((i 0 (+ i 1))
    (var 0 (+ var (nth i l))))
   ((= i (length l)) var)))

但是nthlength 很慢,所以最好这样做:

(defun suma (l)
  (do* 
   ((var (first l) (+ var (first list)))
    (list (rest l) (rest list)))
   ((null list) var)))

这个在do中没有*,在空列表上返回0:

(defun suma (l)
  (do 
   ((acc 0 (+ acc (first list)))
    (list l (rest list)))
   ((null list) acc)))

但我最喜欢的是来自@sds 的reduce 版本,它也可以在空列表中返回 0,并带有 :initial-value 0

编辑:recsum2 没有返回任何内容,因此需要修复。

【讨论】:

  • 如果我想做的“全部”是对一个列表求和,在 Common Lisp 中,我可能会使用 (loop for v in the-list sum v)
猜你喜欢
  • 2015-07-03
  • 1970-01-01
  • 2020-11-26
  • 1970-01-01
  • 1970-01-01
  • 2022-12-12
  • 2013-05-06
  • 2019-01-11
  • 2016-09-23
相关资源
最近更新 更多