所以我有一个程序:
(defun add (L)
(cond((endp L) nil)
(t(cons(1+(first L)))(add(rest L)))))
将为列表的每个成员添加1。我想检查列表是否都是数字,如果没有,则返回nil,并且不知道如何在defun中执行此操作。
我想过做
(defun add (L)
(cond((endp L) nil)
((not(numberp(first L))) nil)
(t(cons(1+(first L)))(add(rest L)))))
但如果非数字位于中间,那么仍将返回列表的开头。我如何预先检查并在开始时返回零?
答案 0 :(得分:4)
您可以将其打包在condition-case
(defun add (L)
(condition-case nil
(mapcar '1+ L)
(error nil)))
答案 1 :(得分:3)
另一种可能性是使用迭代:
(defun add (l)
(loop for x in l
if (numberp x)
collect (1+ x)
else do (return-from add nil)))
在第一个非数字元素上使用nil
立即退出该函数。
答案 2 :(得分:3)
您不会使用递归实现迭代,因为Lisp已经提供了迭代结构。示例:MAPCAR
。
Common Lisp还提供了像RETURN-FROM
这样的控制流结构,您可以从块返回。由DEFUN
定义的函数具有名称的块,BLOCK
也可以显式创建命名块。两者的例子:
CL-USER 62 > (block mapping
(mapcar (lambda (item)
(if (numberp item)
(1+ item)
(return-from mapping nil)))
'(1 2 3 nil 5 6)))
NIL
CL-USER 63 > (block mapping
(mapcar (lambda (item)
(if (numberp item)
(1+ item)
(return-from mapping nil)))
'(1 2 3 4 5 6)))
(2 3 4 5 6 7)
作为功能:
CL-USER 64 > (defun increment-list (list)
(mapcar (lambda (item)
(if (numberp item)
(1+ item)
(return-from increment-list nil)))
list))
INCREMENT-LIST
CL-USER 65 > (increment-list '(1 2 3 4 5 6))
(2 3 4 5 6 7)
CL-USER 66 > (increment-list '(1 2 3 nil 5 6))
NIL
答案 3 :(得分:2)
我会说,在Common Lisp中,检查列表中的所有元素都是数字的惯用方法是(every #'numberp the-list)
,所以我可能会把它写成:
(defun add-1 (list)
(when (every #'numberp list)
(mapcar #'1+ list)))
可以使用(if ...)
或(and ...)
,但在这种情况下,我认为(when ...)
可以提供最清晰的代码。
答案 4 :(得分:1)
难点在于传播nil
导致列表末尾的nil
导致所有内容都为nil
。一种解决方案是检查add
是否返回nil
,但(rest xs)
不是nil
。但是,IMO可以更简单地迭代列表两次,第一次检查数字,然后在第二次迭代时进行加法。
试试这个:
(defun add (xs)
(cond ((endp xs) nil)
((not (numberp (car xs))) nil)
(t (let ((r (add (rest xs))))
(cond ((and (not r) (rest xs)) nil)
(t (cons (1+ (first xs)) r)))))))
除非出现错误,否则会导致:
(add '()) => nil
(add '(1 2)) => '(2 3)
(add '(x y)) => nil
(add '(1 2 y)) => nil
编辑:没有let
。这会导致2^(n+1)-1
调用add
来获取长度为n
的列表。
(defun add (xs)
(cond ((endp xs) nil)
((not (numberp (car xs))) nil)
(t (cond ((and (not (add (rest xs))) (rest xs)) nil)
(t (cons (1+ (first xs)) (add (rest xs)))))))))