【问题标题】:Using nested car with cons in a lisp function在 lisp 函数中使用带有 cons 的嵌套汽车
【发布时间】:2019-02-11 03:53:35
【问题描述】:

我正在制作一个递归 lisp 函数,它接受两个列表并创建一个索引对的子列表

例如:输入 (A B C D) 和 (1 2 3 4) 并得到 ((1 A) (2 B) (3 C) (4 D))

但是,我在使用 car 和 cons 来制作所述子列表时遇到了麻烦。这是我的代码:

(DEFUN zipper (a b)
    (if (= (OR (list-length a) (list-length b)) 0)
        (setq c NIL)
        (progn (zipper (cdr a) (cdr b))
        (cons '((car a) (car b)) c))
    )
)

我玩了一会儿,似乎使用汽车创建列表在大多数情况下都不起作用。此外,我正在使用 CLISP。有什么想法吗?

谢谢!

【问题讨论】:

  • quote ' 停止评估。使用list 创建列表:(cons (list (car a) (car b)) c)

标签: list lisp common-lisp clisp cons


【解决方案1】:

总体思路朝着正确的方向发展。但是,有很多问题。让我们看看。

(DEFUN zipper (a b)
    (if (= (OR (list-length a) (list-length b)) 0)
        (setq c NIL)
        (progn (zipper (cdr a) (cdr b))
        (cons '((car a) (car b)) c))
    )
)

第一个缩进:

(DEFUN zipper (a b)
  (if (= (OR (list-length a) (list-length b)) 0)
      (setq c NIL)
      (progn (zipper (cdr a) (cdr b))
        (cons '((car a) (car b)) c))                 ; <--
    )
  )

下一个悬空括号:

(DEFUN zipper (a b)
  (if (= (OR (list-length a) (list-length b)) 0)
      (setq c NIL)
      (progn (zipper (cdr a) (cdr b))
        (cons '((car a) (car b)) c))))

为真情况返回 IF 中的空列表:

(defun zipper (a b)
  (if (= (OR (list-length a) (list-length b)) 0)
      nil
      (progn (zipper (cdr a) (cdr b))
        (cons '((car a) (car b)) c))))

现在反对 false 情况下的结果:

(defun zipper (a b)
  (if (= (OR (list-length a) (list-length b)) 0)
      nil
      (cons '((car a) (car b))
            (zipper (cdr a) (cdr b)))))

还有什么要做的?

  • 查看 'rsm' 的评论:将引用替换为对 list 的调用。
  • 不要使用list-length。请改用null。它检查列表是否为空。 list-length 会在每次调用时遍历输入的整个列表 -> 效率低下。

【讨论】:

    【解决方案2】:

    如果您在递归的每一步调用 list-length,您每次都将完全遍历两个列表,这使您的 zipper 函数相对于列表大小的总和具有二次时间复杂度:

    (zipper '(1 2 3) '(4 5 6))
    => (list-length (1 2 3))
    => (list-length (2 3))
    => (list-length (3))
    => (list-length ())
    
    => (list-length (4 5 6))
    => (list-length (4 5))
    => (list-length (5))
    => (list-length ())
    
    (zipper '(2 3) '(5 6))
    => (list-length (2 3))
       ...
    => (list-length (5 6))
       ...
    
    ...
    

    这是低效的,在这里没有必要。由于您已经访问了这两个列表,您可以使用nullendp 直接检查其中是否为空,这需要恒定的时间。一旦列表之一为空,您就会返回 NIL,这是mapcar 的默认行为。另请注意,mapcar 可以同时处理多个列表,例如:

    (mapcar #'+ '(1 2) '(5 8))
    => (6 10)
    

    如果您知道一个函数需要(至少)两个参数并返回一个列表,那么您可以mapcar 该函数覆盖您的列表并拥有一个列表列表。

    【讨论】:

      【解决方案3】:

      来自其他语言(如 Python 和 Haskell)的 zip 函数是 mapcar 的特例。

      传统的zip功能可以通过以下方式实现:

      > (mapcar #'list list1 list2 ... listn)
      

      对于这种情况:

      > (mapcar #'list '(A B C D) '(1 2 3 4))
      ((A 1) (B 2) (C 3) (D 4))
      

      要按照您的指示交换对的顺序,只需相应地更改传递给 mapcar 的函数:

      > (mapcar #'(lambda (x y) (list y x)) '(A B C D) '(1 2 3 4))
      ((1 A) (2 B) (3 C) (4 D))
      

      查看documentation for mapcar了解更多详情。

      【讨论】:

        【解决方案4】:

        zip 具有任意数量列表的函数

        (defun zip (&rest lists)
          (apply #'mapcar #'list lists))
        

        最短的列表决定了压缩的深度。

        CL-USER> (zip '(1 2 3) '(a b c) '("a" "b" "c"))
        ((1 A "a") (2 B "b") (3 C "c"))
        CL-USER> (zip '(1 2 3) '(a b c) '("a" "b" "c" "d"))
        ((1 A "a") (2 B "b") (3 C "c"))
        

        【讨论】:

          【解决方案5】:

          你可以这样做:

          (defun zipper (a b)
             (if (or (null a) (null b))
                 nil
                 (cons (list (car b) (car a)) (our-combiner (cdr a) (cdr b)))))
          

          我们可以检查其中一个列表是否为空,以便在其中一个列表用完时停止(类似于 mapcar 在其中一个列表用完时停止将函数应用于列表的方式)。然后,我们可以使用每个递归调用中列表的汽车来构建一个嵌套列表。您的输出将是:

          CL-USER> (zipper '(a b c d) '(1 2 3 4))
          ((1 A) (2 B) (3 C) (4 D))
          CL-USER> 
          

          我们也可以使用 mapcar,因为它会遍历两个列表并返回将函数应用于两个列表的结果(与 mapc 不同)。这是更少的代码行,并且由于它会在某些列表用完时返回,因此不需要条件:

          (defun zipper (a b)
            (mapcar #'list b a))
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-11-27
            • 1970-01-01
            相关资源
            最近更新 更多