找到原子的位置 - 如果不存在则返回nil

时间:2017-02-05 01:30:51

标签: recursion position lisp common-lisp

我试图在列表中找到原子的位置。

预期结果:

(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)))) ) )

3 个答案:

答案 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是可能的,因为从本地函数可以看到返回的函数。

递归函数返回另一个函数

还可以使用CATCHTHROW将控件返回到另一个函数:

(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)))