如何在 Lisp Works 中对包含数字和字母的列表求和
How to sum a list which contain numbers and letters in Lisp Works
我正在尝试定义一个将列表作为参数的求和函数。问题是我的列表不仅可以包含数字,还可以包含字母。所以我的问题是,我怎样才能避开字母并继续验证列表的其余部分?
示例:(sum '(a 2 4 b d)) = 6
(defun sum (l)
( if (numberp (CAR l)) (sum (CDR l)) (+ (CAR l) (sum (CDR l))) )
)
我只有一个 "Stack overflow" 错误。
提前致谢。
不是最有效的方法,但请检查函数 remove-if(或 delete-if)和 numberp。
您的代码试图应用递归方法。在这里,格式正确,名称较长:
(defun sum (list)
(if (numberp (car list))
(sum (cdr list))
(+ (car list) (sum (cdr list)))))
你有堆栈溢出,因为当列表为空时你没有提供基本情况。
如果将 NIL 列表传递给函数,会发生什么?
(car list)
returns NIL,不是数字。
- 你去else分支。
- 您尝试将
(car list)
添加到 对 SUM 的递归调用 (cdr list)
, 是也 无。
- 你回到了递归调用的第一步:无休止的递归,最终会触发错误,因为你在每次调用中不断分配堆栈帧。
因此,您必须定义给出空列表时会发生什么。
这是一道典型的家庭作业题。我最喜欢的模板涉及 etypecase
,因为它提供了一种防御性编码方法,可以及早拒绝错误并且非常 reader 友好:
(defun foo (list)
(etypecase list
(null <base-case>)
(cons <general-case>)))
但是,您也经常会发现这个,或者与 cond
等价的东西:
(defun foo (list)
(if (null list)
<base-case>
<general-case>))
这大概是对学生的期待吧。更好的方法是使用 endp
而不是 null
,因为前者会检查参数是否实际上是一个列表。
你的问题:
- 基本情况显然 returns 0.
- 一般情况计算
car
(如果不是数字则为零)与 cdr
的总和,以递归方式获得。
但是,你将有一个递归函数需要将中间结果存储在调用堆栈上,这是一种浪费。为了拥有尾递归函数,您需要将结果总和作为函数的参数传递:首先传递 0,然后每个递归调用首先计算总和,然后再递归调用自身。基本情况 returns 总和。这保证了递归调用之间不需要存储中间结果,在适当的情况下(这取决于您的实现)可以作为循环进行优化。但是由于 Common Lisp 已经提供了迭代结构,你应该在实践中使用它们:
(loop for e in list when (numberp e) sum e)
如果您对高阶函数感兴趣,下面的方法也可以,但会分配一个中间列表(这是否可以接受取决于列表的预期大小):
(reduce #'+ (remove-if-not #'numberp list))
我正在尝试定义一个将列表作为参数的求和函数。问题是我的列表不仅可以包含数字,还可以包含字母。所以我的问题是,我怎样才能避开字母并继续验证列表的其余部分?
示例:(sum '(a 2 4 b d)) = 6
(defun sum (l)
( if (numberp (CAR l)) (sum (CDR l)) (+ (CAR l) (sum (CDR l))) )
)
我只有一个 "Stack overflow" 错误。
提前致谢。
不是最有效的方法,但请检查函数 remove-if(或 delete-if)和 numberp。
您的代码试图应用递归方法。在这里,格式正确,名称较长:
(defun sum (list)
(if (numberp (car list))
(sum (cdr list))
(+ (car list) (sum (cdr list)))))
你有堆栈溢出,因为当列表为空时你没有提供基本情况。 如果将 NIL 列表传递给函数,会发生什么?
(car list)
returns NIL,不是数字。- 你去else分支。
- 您尝试将
(car list)
添加到 对 SUM 的递归调用(cdr list)
, 是也 无。 - 你回到了递归调用的第一步:无休止的递归,最终会触发错误,因为你在每次调用中不断分配堆栈帧。
因此,您必须定义给出空列表时会发生什么。
这是一道典型的家庭作业题。我最喜欢的模板涉及 etypecase
,因为它提供了一种防御性编码方法,可以及早拒绝错误并且非常 reader 友好:
(defun foo (list)
(etypecase list
(null <base-case>)
(cons <general-case>)))
但是,您也经常会发现这个,或者与 cond
等价的东西:
(defun foo (list)
(if (null list)
<base-case>
<general-case>))
这大概是对学生的期待吧。更好的方法是使用 endp
而不是 null
,因为前者会检查参数是否实际上是一个列表。
你的问题:
- 基本情况显然 returns 0.
- 一般情况计算
car
(如果不是数字则为零)与cdr
的总和,以递归方式获得。
但是,你将有一个递归函数需要将中间结果存储在调用堆栈上,这是一种浪费。为了拥有尾递归函数,您需要将结果总和作为函数的参数传递:首先传递 0,然后每个递归调用首先计算总和,然后再递归调用自身。基本情况 returns 总和。这保证了递归调用之间不需要存储中间结果,在适当的情况下(这取决于您的实现)可以作为循环进行优化。但是由于 Common Lisp 已经提供了迭代结构,你应该在实践中使用它们:
(loop for e in list when (numberp e) sum e)
如果您对高阶函数感兴趣,下面的方法也可以,但会分配一个中间列表(这是否可以接受取决于列表的预期大小):
(reduce #'+ (remove-if-not #'numberp list))