【问题标题】:list as function argument in Lisp列表作为 Lisp 中的函数参数
【发布时间】:2018-10-04 06:07:42
【问题描述】:

我有以下代码:

(defun TREE-CONTAINS (N TREE)
  (cond (( = (car TREE) nil) nil)
        (( = (car TREE) N) t)
         (t TREE-CONTAINS (N (cdr TREE)))
  )
)

它接受一个数字 N 和一个列表 TREE 并检查 N 是否存在于列表 TREE 中。很简单,但由于某种原因,当我调用我的函数时,我不断收到此错误

(TREE-CONTAINS 3 '((1 2 3) 7 8))
*** - +: (1 2 3) is not a number

代码有问题吗?我对 Lisp 很陌生,所以也许我只是没有看到非常明显的东西.. 在此先感谢!

【问题讨论】:

    标签: lisp common-lisp


    【解决方案1】:

    语法错误

    您的代码包含多个标记为编译器警告的语法错误:

    CL-USER> (defun TREE-CONTAINS (N TREE)
               (cond (( = (car TREE) nil) nil)
                     (( = (car TREE) N) t)
                     (t TREE-CONTAINS (N (cdr TREE)))
                )
              )
    ;Compiler warnings :
    ;   In TREE-CONTAINS: Undeclared free variable TREE-CONTAINS
    ;   In TREE-CONTAINS: Undefined function N
    TREE-CONTAINS
    

    原因是 Common Lisp 中的括号与其他编程语言的含义不同:它们不用于指定运算符的应用顺序(如 3 * (2 + 4)3 * 2 + 4 不同),但是是语法的组成部分,用于指定“语句”的不同部分,如cond 或函数应用程序(如(function-name arg1 arg2 ... argn))。所以这种情况下的语法错误在最后一行,你应该在其中调用函数TREE-CONTAINS,参数N(cdr TREE)为:

    CL-USER> (defun TREE-CONTAINS (N TREE)
               (cond (( = (car TREE) nil) nil)
                     (( = (car TREE) N) t)
                     (t (TREE-CONTAINS N (cdr TREE)))
                )
              )
    TREE-CONTAINS
    

    语义错误

    但是,如果你尝试这个功能,你会发现一个错误:

     CL-USER> (TREE-CONTAINS 2 '(1 2 3))
    
     The value NIL is not of the expected type NUMBER.
    

    原因是您使用= 将数字((car TREE))与值nil 进行比较,而= 只能用于比较数字。对于一般情况,请改用eqeql

    CL-USER> (defun TREE-CONTAINS (N TREE)
               (cond (( eql (car TREE) nil) nil)
                     (( = (car TREE) N) t)
                     (t (TREE-CONTAINS N (cdr TREE)))
                )
              )
    TREE-CONTAINS
    
    CL-USER> (TREE-CONTAINS 2 '(1 2 3))
    T
    

    还有另一个问题:你应该检查列表是否为空,而不是第一个元素是否为零。也就是说,第一个条件应该是:

    (cond ((eq TREE nil) nil)
    

    或更好:

    (cond ((null TREE) nil)
    

    风格说明

    1. 列表是树的一种特殊情况:如果使用术语树,程序应该更复杂,考虑到元素可以是子列表的情况。

    2. 使用小写标识符,因为所有内容都被转换为大写

    3. 将右括号放在表达式的末尾,而不是换行。

    所以你的函数可能是这样的:

    (defun list-contains (n list)
      (cond ((null list) nil)
            ((= (car list) n) t)
            (t (list-contains n (cdr list)))))
    

    检查树而不是列表的成员身份

    另一方面,如果您想检查通用树,即可以包含子列表的列表,如(tree-contains 3 '((1 2 3) 7 8)),在您的递归中,您应该考虑列表元素本身就是这种情况一个列表,然后执行双重递归。这是一个可能的解决方案:

    CL-USER> (list-contains 2 '(1 (2 3) 4))
    
    The value (2 3) is not of the expected type NUMBER.
    
    CL-USER> (defun tree-contains (n tree)
               (cond ((null tree) nil)
                     ((listp (car tree)) (or (tree-contains n (car tree))
                                             (tree-contains n (cdr tree))))
                     ((= (car tree) n) t)
                     (t (tree-contains n (cdr tree)))))
     TREE-CONTAINS
     CL-USER> (tree-contains 2 '(1 (2 3) 4))
     T
    

    【讨论】:

      【解决方案2】:

      除了接受的答案之外,这里还有另一种编写相同谓词的方法,没有cond

      (defun list-contains-p (number list)
        (and (consp list)
             (or (= number (first list))
                 (list-contains-p number (rest list)))))
      

      【讨论】:

        最近更新 更多