1

我有一些代码从一个看起来像这样的循环中收集点(consed 整数):

(loop
    for x from 1   to     100
    for y from 100 downto 1
        collect `(,x . ,y))

我的问题是,`(,x . ,y)在这种情况下使用是否正确?

编辑:这个示例不是关于生成一个 100x100 项目的表,这里的代码只是说明了两个循环变量的使用和它们的值的组合。我已经编辑了循环以明确这一点。我使用的实际循环取决于其他几个函数(并且是其自身的一部分),因此用文字整数替换调用并将循环从函数中拉出更有意义。

4

4 回答 4

7

这样做会更好(缺点 xy)。

但是要回答这个问题,这样做没有错:)(除了让它慢一点)。

于 2008-09-19T12:39:30.473 回答
5

我认为这里的答案是资源利用(来自这篇文章

例如在剪辑中:

[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]>
于 2008-09-19T13:00:00.003 回答
3

dsm:这里的代码有一些奇怪的地方。注意

(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)))

希望有帮助!

于 2008-09-19T15:56:26.740 回答
1

为什么不只是

(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)
于 2008-09-19T12:39:22.127 回答