我有一些代码从循环中收集点(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)
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top