简体   繁体   English

在Lisp函数中使用带有缺点的嵌套汽车

[英]Using nested car with cons in a lisp function

I'm making a recursive lisp function that takes two lists and makes a sublist of index pairs 我正在做一个递归的Lisp函数,该函数需要两个列表并创建索引对的子列表

ex: put in (ABCD) and (1 2 3 4) and get ((1 A) (2 B) (3 C) (4 D)) 例如:放入(ABCD)和(1 2 3 4)并得到((1 A)(2 B)(3 C)(4 D))

However, I'm having trouble using car along with cons to make said sublist. 但是,我在使用汽车和缺点时难以进入所说的子列表。 Here's my code: 这是我的代码:

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

I played around for a little and it appears using car to create lists just doesn't work most of the time. 我玩了一会儿,似乎大部分时间都无法用汽车创建列表。 Additionally, I'm using CLISP. 另外,我正在使用CLISP。 Any ideas? 有任何想法吗?

Thanks! 谢谢!

The general idea goes into the right direction. 总体思路朝着正确的方向发展。 There are a bunch of problems, though. 但是,还有很多问题。 Let's have a look. 我们来看一下。

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

First indentation: 第一个缩进:

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

Next dangling parentheses: 下一个悬挂的括号:

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

Return the empty list in IF for the true case: 对于真实情况,请在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))))

Now cons to the result in the false case: 现在以错误的情况考虑结果:

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

What remains to be done? 还有什么要做?

  • see the comment by 'rsm': replace the quote with a call to list . 请参阅'rsm'的注释:用对list的调用替换引号。
  • don't use list-length . 不要使用list-length Use null instead. 请改用null It checks if a list is empty. 它检查列表是否为空。 list-length would traverse the input whole lists on each call -> inefficient. list-length将遍历每次调用时输入的整个列表->低效率。

If you call list-length at each step of recursion, you are going to traverse both lists entirely each time, which gives your zipper function a quadratic time complexity with respect to the sum of your lists' sizes: 如果在递归的每个步骤调用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))
   ...

...

This is inefficient and not necessary here. 这是低效率的,在这里不是必需的。 Since you are already visiting both lists, you can directly check if any of them is empty using null or endp , which take constant time. 由于您已经在访问两个列表,因此可以使用nullendp直接检查它们是否为空,这需要花费固定的时间。 You return NIL as soon as one of the list is empty, which is by the way the default behaviour of mapcar . 列表之一为空时,您将立即返回NIL,这是mapcar的默认行为。 Notice also that mapcar can work on multiple lists at the same time, for example: 还要注意, mapcar可以同时处理多个列表,例如:

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

If you know a function that takes (at least) two arguments and return a list, then you could mapcar that function over your lists and have a list of lists. 如果你知道一个函数,(至少)两个参数,并返回一个列表,那么你可以mapcar在你列出了功能,并有一个列表的列表。

The zip function from other languages such as Python and Haskell is a special case of mapcar . mapcar是特例,其他语言(例如Python和Haskell)的zip函数。

The traditional zip function can be achieved via: 传统的zip功能可以通过以下方式实现:

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

Such that for this case: 这样在这种情况下:

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

To swap the order of the pairs as you indicated, simply change the function passed to mapcar accordingly: 要按照您的指示交换对的顺序,只需相应地更改传递给mapcar的函数:

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

Checkout the documentation for mapcar for more details. 请查看mapcar文档以获取更多详细信息。

zip function with arbitrary number of lists 带有任意数量列表的zip函数

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

Shortest list determines depth of zipping. 最短列表确定拉链的深度。

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

You could do something such as this: 您可以执行以下操作:

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

We can check if either of the lists is null, so that we could stop when one of the lists runs out (similar to how mapcar will stop applying a function to lists when one of the lists runs out). 我们可以检查其中一个列表是否为空,以便当其中一个列表用完时可以停止(类似于当一个列表用完时mapcar将停止对列表应用函数的方式)。 We can then cons a nested list with the car's of the lists in each recursive call. 然后,我们可以在每个递归调用中将嵌套列表与列表中的汽车进行比较。 Your output would be: 您的输出将是:

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

We could also use mapcar as it will iterate over both lists and returns the result of applying the function to both lists (unlike mapc). 我们还可以使用mapcar,因为它将遍历两个列表并返回将函数应用于两个列表的结果(与mapc不同)。 This is fewer lines of code, and since it will return when some list runs out, there's no need for a conditional: 这是更少的代码行,并且由于它将在某些列表用完时返回,因此不需要有条件的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM