【问题标题】:Cannot take CAR of 2 (Common Lisp)不能接受 2 的 CAR(Common Lisp)
【发布时间】:2020-04-04 06:24:46
【问题描述】:

我是 Lisp 新手,我正在尝试使用 CAR/CDR 递归来迭代不同深度的列表。我见过类似的问题,但没有一个能解决我遇到的确切问题或如何解决它。在此示例中,我尝试对列表中的每个数字求平方:

(defun square-list (x)
  (cond ((and (atom x) (not (equal x nil)))
         (cons (* x x) (or (square-list (car x)) (square-list (cdr x)))))
        (t (or (square-list (car x)) (square-list (cdr x))))))

从阅读中,我的印象是一个原子的 CAR 和 CDR 要么是 nil,要么是另一个原子。我在这里做错了什么?感谢您的宝贵时间。

编辑: 我已经解决了在任意深度列表中对数字进行平方的基本问题(例如 '(1 ((2) 3) (4 5))),但是当我将字母扔进混合。这是新代码:

(defun square-list (x)
          (cond ((null x) nil) 
                ((and (atom x) (numberp x)) (* x x))
                (t (cons (square-list (car x)) (square-list (cdr x))))))

错误依旧:"Cannot take CAR of A" with list '(a ((2) 3) (4 5))。

【问题讨论】:

    标签: recursion lisp common-lisp


    【解决方案1】:

    如果您需要对列表进行递归操作,那么您应该考虑两件事:终止情况,例如当列表为空时会发生什么,以及递归情况,例如当列表至少有一个元素。

    在你的情况下,当列表为空时,没有数字必须是平方的,所以返回空列表。否则,您必须构建(即cons)一个新列表,其第一个元素是列表的第一个元素(即(car x))的平方,列表的其余部分是通过将列表的其余部分平方获得(即(cdr x))。

    以下是您的问题的可能解决方案:

    CL-USER>  (defun square-list (x)
                (if (null x)
                    nil
                    (cons (* (car x) (car x)) (square-list (cdr x)))))
    
    SQUARE-LIST
    CL-USER> (square-list '(1 2 3 4 5))
    (1 4 9 16 25)
    

    顺便说一句,您可以只使用列表的carcdr,而不是原子(这会导致运行时错误)。

    CL-USER> (car (list 1 2 3 4 5))
    1
    CL-USER> (cdr (list 1 2 3 4 5))
    (2 3 4 5)
    CL-USER> (car 1)
    The value 1 is not of the expected type LIST.
    [Condition of type TYPE-ERROR]
    

    如果您需要将树的所有元素(即包含任意嵌套子列表的列表)平方,则需要更改函数以管理三种不同的情况,而不是两种:

    1. 如果列表为空,则返回空列表,
    2. 如果列表的第一个元素本身就是一个列表,则递归地将 square-list 应用到它,并将通过应用 square-list 获得的结果“反”到列表的其余部分,
    3. 1234563

    例如:

    CL-USER> (defun square-list (x)
               (cond ((null x) nil)
                     ((listp (car x)) (cons (square-list (car x))
                                            (square-list (cdr x))))
                     (t (cons (* (car x) (car x)) (square-list (cdr x))))))
    SQUARE-LIST
    CL-USER> (square-list '(1 ((2 3) (4))))
    (1 ((4 9) (16)))
    

    作为最后的评论,请注意上面的函数不是尾递归的。使尾递归成为第一个的简单方法是使用经典的“累加器”策略。

    CL-USER> (defun square-list (x &optional accumulator)
               (if (null x)
                   (reverse accumulator)
                   (square-list (cdr x) (cons (* (car x) (car x)) accumulator))))
    SQUARE-LIST
    CL-USER> (square-list '(1 2 3 4 5))
    (1 4 9 16 25)
    

    【讨论】:

    • 谢谢。我很了解尾递归函数,但我的目标是生成一个适用于任意深度列表的函数,例如 (1 ((2) 3) (4))。我认为这称为 CAR/CDR 递归。
    • @stymies_camel,好的,我已经改变了答案。
    猜你喜欢
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-06
    • 1970-01-01
    相关资源
    最近更新 更多