【问题标题】:Is it correct to use the backtick / comma idiom inside a (loop ...)?在(循环...)中使用反引号/逗号成语是否正确?
【发布时间】:2010-09-11 05:13:12
【问题描述】:

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

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

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

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

【问题讨论】:

  • 仅供参考,只需省略 DO,它就符合 ANSI CL。 :) (DO 用于执行 Lisp 表达式,而不是用于引入 LOOP 子句。)
  • 你确定是这样做的?我用从 0 到 5 的两个循环运行它并得到 ((0 . 0) (1 . 1) (2 . 2) (3 . 3) (4 . 4) (5 . 5)),我没有认为是预期的结果。
  • 我不是要生成一个表,而是要说明从具有两个变量的循环中收集值。删除 do... 部分删除了警告消息“警告:循环:DO 后缺少表单:CLtL2 允许,ANSI CL 禁止。”
  • dsm:啊,好的,我现在明白你的意图了。原始“do”部分的问题是循环需要一个表单。在任何情况下,您最好避免使用反引号方法,因为 (cons x y) 使意图更清晰。

标签: lisp iteration


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    我认为这里的答案是资源利用率(来自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】:

      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 here 显示的函数“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)))
      

      希望对您有所帮助!

      【讨论】:

      • 你是对的,有更有效的方法来完成我正在做的事情。我试图尽快复制我认为脚本的预期输出。我不是 CL 用户(我是 Schemer)所以我不知道 CL 是否有任何数字列表创建功能,所以感谢提示
      • 这确实是一个很好的答案,但正如我所指出的,损坏的循环只是一个说明。我现在编辑了问题以更好地反映循环的意图(consing variables,而不是生成表)。感谢您的回答。
      • 好的,我在上面评论了你的初衷。这个答案中的所有内容都概括了,所以这不是损失......
      【解决方案4】:

      为什么不直接

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

      【讨论】:

      • 领先我 9 秒,哦,我为什么要详细说明? ;o)
      • 代码只是一个例子,我有“更聪明”的代码来确定循环正在寻找什么,而不是制作表格。
      • 哦,好吧,我不知道你问是不是因为你的代码被破坏了。
      猜你喜欢
      • 1970-01-01
      • 2017-09-22
      • 1970-01-01
      • 2011-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-23
      • 1970-01-01
      相关资源
      最近更新 更多