在 Lisp 的 mapcon 上应用附加

Applying append on mapcon in Lisp

The Lisp function G is defined by:

(defun g (l)
   (mapcon #'list l)) 

What is the result of evaluating the form (apply #'append (mapcon #'g '(1 2)))?
Justify the answer.

我看到 mapconnconccdr 一起使用,但最终答案将是 (1 2 2 2),我不知道如何正确解释它。请帮忙。

首先,让我们调用:

(mapcon (lambda (list) (print list) nil) '(1 2))

因为匿名函数returns一直为NIL,结果列表为NIL;调用打印如下:

(1 2) 
(2) 

因此在您的示例中,当您调用 (mapcon #'g '(1 2)) 时,g 将首先用 (1 2) 调用,然后用 (2) 调用。函数 g returns 一个列表,mapcon 连接它们。

可以通过显式计算每个部分在 REPL 中复制发生的事情:

USER> (mapcon #'list '(1 2))
((1 2) (2))

USER> (mapcon #'list '(2))
((2))

USER> (nconc ** *)
((1 2) (2) (2))

最后,(apply #'append list-of-lists) 使用参数列表调用 append

append的签名是:

append &rest lists => result 

也就是说,如果l1l2l3是列表,则包含它们所有元素的列表是:

(append l1 l2 l3)

此处 append 的参数存储在列表中,因此将任意参数列表传递给函数的方法是使用 apply。所以这意味着 (apply #'append lists) 连接列表中的所有列表,这就是为什么在你的情况下结果是 (1 2 2 2).

请注意,当参数的数量是任意的(可能很大)时,不建议使用 apply,因为 apply 受到 CALL-ARGUMENTS-LIMIT 的限制。另一种可能的方法是:

(loop for list in lists append list)

这里要理解的主要是

(mapcon #'f xs)  =  (apply #'nconc  (maplist #'f xs))
                 =  (loop  for ys on xs  nconc (f ys))

所以

(g xs)              =
(mapcon #'list xs)  =  (apply #'nconc  (maplist #'list xs))
                    =  (loop  for ys on xs  nconc   (list ys))
                    =  (loop  for ys on xs  append  (list ys))
                    =  (loop  for ys on xs  collect       ys )
                ;;  =  (maplist  #'identity  xs)

这只是 #'cdr 对参数列表的迭代应用,收集其非空后缀:

[4]> (loop  for ys on '(1 2)  collect ys)    ; (g '(1 2))
((1 2) (2))

[5]> (loop  for ys on '(  2)  collect ys)    ; (g '(  2))
(      (2))

因此我们有

(mapcon #'g '(1 2)) 
=
(loop  for ys on '(1 2)  nconc (g ys))
=
(loop  for ys in '((1 2) (2))  nconc (g ys))     ; ----- 'in' NB
=
(nconc  (g '(1 2))  (g '(2)))
=
(append  '((1 2) (2))  '((2)))
=
'(         (1 2) (2)     (2) )

因此

(apply #'append (mapcon #'g '(1 2)))
=
(apply #'append (nconc (g '(1 2)) (g '(2))))
=
(apply #'append (append '((1 2) (2)) '((2))))
=
(apply #'append '(        (1 2) (2)    (2) ))
=
(        append          '(1 2)'(2)   '(2)  )
=
'(                         1 2   2      2   )