我正在尝试定义一个以列表作为参数的和函数。问题是我的列表不仅包含数字,还包含字母。所以我的问题是,我如何避免这些字母并继续验证列表的其余部分?
例子:(总和'(a 2 4 b d))= 6
(defun sum (l)
( if (numberp (CAR l)) (sum (CDR l)) (+ (CAR l) (sum (CDR l))) )
)
我所拥有的只是一个"堆栈溢出"错误。
提前致谢。
答案 0 :(得分:1)
您的代码尝试应用递归方法。格式正确,名称较长:
(defun sum (list)
(if (numberp (car list))
(sum (cdr list))
(+ (car list) (sum (cdr list)))))
您有堆栈溢出,因为当列表为空时您没有提供基本情况。 如果你将NIL列表传递给你的函数,会发生什么?
(car list)
返回NIL,这不是数字。(car list)
(仍然为NIL)添加到递归调用SUM ,其中(cdr list)
,也是 NIL。 所以,你必须定义给出空列表时会发生什么。
这是典型的家庭作业问题。我最喜欢的模板涉及etypecase
,因为它提供了一种防御性编码方法,可以尽早拒绝错误并且对读者非常友好:
(defun foo (list)
(etypecase list
(null <base-case>)
(cons <general-case>)))
但是,您经常会发现这个或类似于cond
的内容:
(defun foo (list)
(if (null list)
<base-case>
<general-case>))
这可能是学生的期望。更好的方法是使用endp
而不是null
,因为前者检查参数是否实际上是一个列表。
在您的问题中:
car
(零,如果不是数字)与递归获得的cdr
之和的总和。但是,您将拥有一个需要在调用堆栈上存储中间结果的递归函数,这很浪费。为了得到一个尾递归函数,你需要将得到的和作为函数的一个参数传递:首先传递0,然后每个递归调用首先计算总和,然后递归调用自身。基本案例返回总和。这可以保证在递归调用之间不需要存储中间结果,在适当的情况下(这取决于您的实现)可以作为循环进行优化。但是由于Common Lisp已经提供了迭代结构,你应该在实践中使用它们:
(loop for e in list when (numberp e) sum e)
如果您对高阶函数感兴趣,以下工作也可以,但是分配一个中间列表(这是否可接受取决于列表的预期大小):
(reduce #'+ (remove-if-not #'numberp list))
答案 1 :(得分:0)
不是最有效的方法,但检查函数remove-if(或delete-if)和numberp。