我正在制作一个递归lisp函数,该函数接受两个列表并创建一个索引对的子列表
ex:放入(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。有什么想法吗?
谢谢!
答案 0 :(得分:5)
总的思路朝着正确的方向发展。但是,还有很多问题。让我们看看。
countries = forms.MultipleChoiceField(choices=COUNTRIES, widget=forms.CheckboxSelectMultiple)
第一个缩进:
(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)
(setq c 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
(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)
nil
(cons '((car a) (car b))
(zipper (cdr a) (cdr b)))))
的调用代替引号。list
。请改用list-length
。它检查列表是否为空。 null
将遍历每次呼叫的输入整个列表->低效。答案 1 :(得分:3)
如果您在递归的每一步调用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))
...
...
这是低效率的,在这里没有必要。由于您已经在访问这两个列表,因此可以使用null
或endp
直接检查它们是否为空,这需要花费固定的时间。列表之一为空时,您将立即返回NIL,这是mapcar
的默认行为。还请注意,mapcar
可以同时处理多个列表,例如:
(mapcar #'+ '(1 2) '(5 8))
=> (6 10)
如果您知道一个函数(至少需要两个参数)并返回一个列表,则可以在列表上mapcar
使用该函数,并获得一个列表列表。
答案 2 :(得分: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以获得更多详细信息。
答案 3 :(得分:0)
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"))
答案 4 :(得分:0)
您可以执行以下操作:
(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))