在 Lisp 中附加到 "loop-collect" 的结果
Appending to the result of a "loop-collect" in Lisp
假设我运行以下
(loop for i to 4 collect i)
然后我得到一个列表 (0 1 2 3 4)
。现在,如果我想在结果中附加一些东西,我可以在它的 last
元素上使用 rplacd
,但是由于 Lisp 列表是链表,它不是很有效。这里的列表少得离谱,但这只是一个例子。
然而,由于循环工具 returns 列表以递增顺序排列,它必须跟踪指向最后一个元素的指针,并用 rplacd
或类似的东西更新结果。 macroexpand-all
表明这是 CCL 所做的,可能还有其他 lisps。
问题:有没有办法在finally
子句中使用这个"pointer"?它允许在结果中附加一些东西,这有时很有用。
当然,编写指针机制很容易,但不是很好。例如,以下将列表 e
附加到列表 (0 1 ... n)
.
(defun foo (n e)
(let* ((a (list nil)) (tail a))
(loop for i to n
do (rplacd tail (setf tail (list i)))
finally (rplacd tail (setf tail e))
(return (cdr a)))))
Question: Is there a way to use this "pointer" in the finally clause? It would allow one to append something to the result, which is
sometimes useful.
我认为答案是"no"。 finally 子句的语法如下:
initial-final::= initially compound-form+ | finally compound-form+
当然,您可以将 收集到 一些特定变量,然后附加或 nconc :
CL-USER> (loop for i from 1 to 5
collect i into ns
finally (return (nconc ns (list 'a 'b 'c))))
;=> (1 2 3 4 5 A B C)
不过,这确实需要额外遍历结果,这可能是不希望的。 (我认为这是您要避免的。)
另一种选择是使用 nconc 而不是 collect 来构建列表。如果您正在使用 collect,那么您一次只是获取一个值,然后收集它。您可以将该值放入列表中,然后使用 nconc "collect" 它。如果将那个元素列表保存到循环中的一个变量中,就可以引用它,它几乎就是一个尾指针。例如:
CL-USER> (loop for i from 1 to 5
for ilist = (list i)
nconc ilist into ns
finally (progn
(nconc ilist '(a b c)) ; ***
(return ns)))
;=> (1 2 3 4 5 A B C)
在***标记的行中,调用nconc只需要遍历ilist的最终值即可,即(5)。这是一个非常快的 nconc 调用。
每次迭代的额外比较和额外的迭代给你这个:
CL-USER 2 > (defun foo (n e &aux (z (1+ n)))
(loop for i to z
unless (= i z)
collect i
else
nconc e))
FOO
CL-USER 3 > (foo 4 '(f o o))
(0 1 2 3 4 F O O)
假设我运行以下
(loop for i to 4 collect i)
然后我得到一个列表 (0 1 2 3 4)
。现在,如果我想在结果中附加一些东西,我可以在它的 last
元素上使用 rplacd
,但是由于 Lisp 列表是链表,它不是很有效。这里的列表少得离谱,但这只是一个例子。
然而,由于循环工具 returns 列表以递增顺序排列,它必须跟踪指向最后一个元素的指针,并用 rplacd
或类似的东西更新结果。 macroexpand-all
表明这是 CCL 所做的,可能还有其他 lisps。
问题:有没有办法在finally
子句中使用这个"pointer"?它允许在结果中附加一些东西,这有时很有用。
当然,编写指针机制很容易,但不是很好。例如,以下将列表 e
附加到列表 (0 1 ... n)
.
(defun foo (n e)
(let* ((a (list nil)) (tail a))
(loop for i to n
do (rplacd tail (setf tail (list i)))
finally (rplacd tail (setf tail e))
(return (cdr a)))))
Question: Is there a way to use this "pointer" in the finally clause? It would allow one to append something to the result, which is sometimes useful.
我认为答案是"no"。 finally 子句的语法如下:
initial-final::= initially compound-form+ | finally compound-form+
当然,您可以将 收集到 一些特定变量,然后附加或 nconc :
CL-USER> (loop for i from 1 to 5
collect i into ns
finally (return (nconc ns (list 'a 'b 'c))))
;=> (1 2 3 4 5 A B C)
不过,这确实需要额外遍历结果,这可能是不希望的。 (我认为这是您要避免的。)
另一种选择是使用 nconc 而不是 collect 来构建列表。如果您正在使用 collect,那么您一次只是获取一个值,然后收集它。您可以将该值放入列表中,然后使用 nconc "collect" 它。如果将那个元素列表保存到循环中的一个变量中,就可以引用它,它几乎就是一个尾指针。例如:
CL-USER> (loop for i from 1 to 5
for ilist = (list i)
nconc ilist into ns
finally (progn
(nconc ilist '(a b c)) ; ***
(return ns)))
;=> (1 2 3 4 5 A B C)
在***标记的行中,调用nconc只需要遍历ilist的最终值即可,即(5)。这是一个非常快的 nconc 调用。
每次迭代的额外比较和额外的迭代给你这个:
CL-USER 2 > (defun foo (n e &aux (z (1+ n)))
(loop for i to z
unless (= i z)
collect i
else
nconc e))
FOO
CL-USER 3 > (foo 4 '(f o o))
(0 1 2 3 4 F O O)