代码之家  ›  专栏  ›  技术社区  ›  dsm

在(循环…)中使用回溯/逗号习语是否正确?

  •  1
  • dsm  · 技术社区  · 17 年前

    我有一些代码,它从循环中收集点(连续整数),看起来像这样:

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

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

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

    4 回复  |  直到 17 年前
        1
  •  7
  •   leppie    17 年前

    这样做会“更好”(缺点x y)。

    但是为了 回答问题 ,这样做没什么错:)(只是稍微慢一点)。

        2
  •  5
  •   Community Mohan Dere    9 年前

    我认为这里的答案是资源利用率(如下 This post )

    例如在clisp中:

    [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]>
    
        3
  •  3
  •   Community Mohan Dere    9 年前

    dsm:你的代码有一些奇怪的地方 here 请注意

    (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后面的do是不正确的,因为后面没有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”函数 here 是常见(但不是标准)功能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)))
    

    希望这能有所帮助!

        4
  •  1
  •   Kyle Cronin    17 年前

    为什么不只是

    (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)
    
    推荐文章