我试图在列表中找到原子的位置。
预期结果:
(position-in-list 'a '(a b c d e))
给出0
(position-in-list 'b '(a b c d e))
给出1
(position-in-list 'Z '(a b c d e))
给出了无。
我有一个功能,当项目在列表中时正确给出位置:
(defun position-in-list (letter list)
(cond
((atom list) nil)
((eq (car list) letter) 0)
(t (+ 1 (position-in-list letter (cdr list))))))
问题是当项目不存在时它不会返回nil,好像它达到(atom list)
nil它会给出这个错误:*** - 1+: nil is not a number
就像它解开时一样,它会尝试将值添加到nil
。
有没有办法调整此函数(保持相同的结构),以便在项目不在列表中时正确返回nil
?
注意:
我知道图书馆中有position
个功能,但我不想使用它。
我知道我的问题类似于this one,但我上面提到的问题没有得到解决。
*编辑* 感谢大家的回答。虽然我没有必要的知识来理解你提到的所有建议,但这很有帮助。
我找到了另一个解决问题的方法:
(defun position-in-list (letter liste)
(cond
((atom liste) nil)
((equal letter (car liste)) 0)
((position-in-list letter (cdr liste)) (+ 1 (position-in-list letter (cdr liste)))) ) )
答案 0 :(得分:5)
一种可能的解决方案是使递归函数成为另一个函数的局部函数。最后,然后从返回周围的函数 - 因此您不需要从每个递归调用返回NIL
结果。
本地递归函数从函数返回
可以使用LABELS
定义本地递归函数。
(defun position-in-list (letter list)
(labels ((position-in-list-aux (letter list)
(cond
((atom list) (return-from position-in-list nil))
((eql (first list) letter) 0)
(t (+ 1 (position-in-list-aux
letter (cdr list)))))))
(position-in-list-aux letter list)))
这个RETURN-FROM
是可能的,因为从本地函数可以看到返回的函数。
递归函数返回另一个函数
还可以使用CATCH
和THROW
将控件返回到另一个函数:
(defun position-in-list (letter list)
(catch 'position-in-list-catch-tag
(position-in-list-aux letter list)))
(defun position-in-list-aux (letter list)
(cond
((atom list) (throw 'position-in-list-catch-tag nil))
((eql (first list) letter) 0)
(t (+ 1 (position-in-list-aux
letter (cdr list))))))
测试功能EQL
另请注意,按惯例,默认测试功能为EQL
,而非EQ
。这也允许使用数字和字符。
答案 1 :(得分:4)
您需要检查递归调用返回的值:
(defun position-in-list (letter list)
(cond
((atom list) nil)
((eq (car list) letter) 0)
(t
(let ((found (position-in-list letter (cdr list))))
(and found
(1+ found))))))
请注意,此实现不是尾递归。
答案 2 :(得分:3)
通常,提供:test
关键字参数来选择我们应该使用的相等函数是有用的,所以我们这样做。为编译器提供尾部调用优化的能力也很方便(注意,Common Lisp中不需要TCO,但大多数编译器都会使用正确的优化设置,参考编译器手册),因此我们使用另一个关键字参数为了那个原因。这也意味着我们从最内层调用返回的内容完全按原样返回,因此如果我们返回一个数字或nil
并不重要。
(defun position-in-list (element list &key (test #'eql) (position 0))
(cond ((null list) nil)
((funcall test element (car list)) position)
(t (position-in-list element
(cdr list)
:test test :position (1+ position)))))
当然,最好将TCO友好的递归包装在内部函数中,因此我们(正如Rainer Joswig正确指出的那样)不公开内部实现细节。
(defun position-in-list (element list &key (test #'eql)
(labels ((internal (list position)
(cond ((null list) nil)
((eql element (car list)) position)
(t (internal (cdr list) (1+ position))))))
(internals list 0)))