题
我有一些代码从循环中收集点(consed 整数),如下所示:
(loop
for x from 1 to 100
for y from 100 downto 1
collect `(,x . ,y))
我的问题是,使用是否正确 `(,x . ,y)
在这个情况下?
编辑:此示例不是要生成 100x100 项的表,这里的代码只是说明两个循环变量的使用及其值的构造。我已经编辑了循环以澄清这一点。我使用的实际循环取决于其他几个函数(并且是函数本身的一部分),因此用文字整数替换调用并将循环从函数中拉出更有意义。
解决方案
这样做会“更好”得多 (cons x y)。
但要 回答问题, ,这样做没有什么问题:)(除了让它慢一点)。
其他提示
我认为这里的答案是资源利用率(如下 这个帖子)
例如在剪辑中:
[1]> (time
(progn
(loop
for x from 1 to 100000
for y from 1 to 100000 do
collect (cons x y))
()))
WARNING: LOOP: missing forms after DO: permitted by CLtL2, forbidden by ANSI
CL.
Real time: 0.469 sec.
Run time: 0.468 sec.
Space: 1609084 Bytes
GC: 1, GC time: 0.015 sec.
NIL
[2]> (time
(progn
(loop
for x from 1 to 100000
for y from 1 to 100000 do
collect `(,x . ,y)) ;`
()))
WARNING: LOOP: missing forms after DO: permitted by CLtL2, forbidden by ANSI
CL.
Real time: 0.969 sec.
Run time: 0.969 sec.
Space: 10409084 Bytes
GC: 15, GC time: 0.172 sec.
NIL
[3]>
需求侧管理:你的代码有一些奇怪的事情 这里. 。注意
(loop for x from 1 to 100000
for y from 1 to 100000 do
collect `(,x . ,y))
相当于:
(loop for x from 1 to 100
collecting (cons x x))
这可能不完全是你想要的。注意三件事:首先,按照您的编写方式,x 和 y 具有相同的作用。您可能想嵌套循环。其次,你在 y 之后所做的事情是不正确的,因为它后面没有 lisp 形式。第三,你是对的,你可以在这里使用反引号方法,但它会使你的代码更难阅读,而且不符合惯用语,没有任何好处,所以最好避免。
猜测你的实际意图,你可能会这样做(使用循环):
(loop for x from 1 to 100 appending
(loop for y from 1 to 100 collecting (cons x y)))
如果您不喜欢循环宏(例如 Kyle),您可以使用另一个迭代构造,例如
(let ((list nil))
(dotimes (n 100) ;; 0 based count, you will have to add 1 to get 1 .. 100
(dotimes (m 100)
(push (cons n m) list)))
(nreverse list))
如果您发现自己经常做这种事情,您可能应该编写一个更通用的函数来交叉列表,然后将这些整数列表传递给它
如果您真的遇到迭代问题,而不仅仅是循环问题,您可以递归地执行此类操作(但请注意,这不是方案,您的实现可能无法保证 TCO)。Kyle 展示的函数“genint” 这里 是常见(但非标准)函数 iota 的变体。然而,添加到列表中并不是一个好主意。像这样的等效实现:
(defun iota (n &optional (start 0))
(let ((end (+ n start)))
(labels ((next (n)
(when (< n end)
(cons n (next (1+ n))))))
(next start))))
应该更有效,但仍然不是尾部调用。请注意,我已将其设置为更常见的基于 0 的值,但为您提供了一个从 1 或任何其他整数开始的可选参数。当然上面的内容可以写成这样:
(defun iota (n &optional (start 0))
(loop repeat n
for i from start collecting i))
其优点是不会因大型参数而破坏堆栈。如果您的实现支持尾部调用消除,您还可以通过执行以下操作来避免递归运行不当:
(defun iota (n &optional (start 0))
(labels ((next (i list)
(if (>= i (+ n start))
nil
(next (1+ i) (cons i list)))))
(next start nil)))
希望有帮助!
为什么不只是
(cons x y)
顺便说一句,我尝试在 CLISP 中运行您的代码,但它没有按预期工作。由于我不太喜欢循环宏,因此您可以通过以下方式递归地完成相同的事情:
(defun genint (stop)
(if (= stop 1) '(1)
(append (genint (- stop 1)) (list stop))))
(defun genpairs (x y)
(let ((row (mapcar #'(lambda (y)
(cons x y))
(genint y))))
(if (= x 0) row
(append (genpairs (- x 1) y)
row))))
(genpairs 100 100)