如果元素不在列表中,如何获取列表的最后一个元素并返回nil

时间:2014-05-27 15:54:55

标签: clisp

我想获取列表中任何元素的位置,如果元素不在列表中,则获取nil。我做了:

(defun myposition (letter list)
  (cond
    ((atom list) nil)
    ((equal (car list) letter) 0)
    ((null (car list)) (myposition letter))
    (t (1+ (myposition letter (cdr list)))) ) )

(myposition 'k '(g h i j k l)
4
(myposition 'p '(g h i j k l)
nil is not a number

当我替换((atom list) nil) par ((atom list) 0)时,我得到6而不是

(myposition 'p '(g h i j k l)
6

1 个答案:

答案 0 :(得分:0)

在第一个示例中,您的函数将递归计算1+1+1+1+0 = 4以查找正确的结果。

在第二个示例中,它将遍历整个列表,每个(非匹配)元素添加1,最后添加 nil。所以它实际上计算1+1+1+1+1+1+nil,这是不正确的,因为nil不是数字,因此是错误消息。如果您将nil替换为零,则会计算1+1+1+1+1+1+0这是错误的。

所以你的基本问题是你递归地添加1,并且到达列表的末尾,你想要扔掉你在那之前计算的内容。但是你有一个额外的待处理,你无法逃脱。

最简单的方法是从递归到尾递归解决方案,这在技术上是简单的goto。这里的添加是通过递增一个变量来完成的,而不是通过展开调用堆栈来完成,这样可以很容易地丢弃之前添加的结果,只返回nil,因为没有待添加的。{/ p>

(尾部)递归解决方案可能是:

(defun myposition (letter lst)
  (labels ((sub (lst pos)
             (cond
              ((null lst) nil)
              ((equal (car lst) letter) pos)
              (t (sub (cdr lst) (1+ pos))))))
    (if (atom lst) nil (sub lst 0))))

这可以在Common Lisp中使用,但从技术上讲,如果你的实现没有tail call optimisation,它可能仍会为大型列表打击堆栈。这就是为什么Common Lisp更喜欢迭代解决方案,例如使用loop宏:

(defun myposition (letter lst)
  (when (consp lst)
    (loop for c in lst for i from 0
      when (equal c letter) return i)))