【问题标题】:Understanding loop macro expansion了解循环宏扩展
【发布时间】:2014-11-17 10:00:19
【问题描述】:

我扩展了下面的宏,看看它是如何工作的,发现自己有点困惑。

(loop for i below 4 collect i)

扩展为(为了便于阅读,我已经对其进行了一些清理)

(block nil
  (let ((i 0))
    (declare (type (and number real) i))
    (let* ((list-head (list nil))
           (list-tail list-head))
      (tagbody
       sb-loop::next-loop
         (when (>= i 4) (go sb-loop::end-loop))
         (rplacd list-tail (setq list-tail (list i)))
         (setq i (1+ i))

         (print "-------") ;; added so I could see the lists grow
         (print list-head)
         (print list-tail)
         (print "-------")

         (go sb-loop::next-loop)
       sb-loop::end-loop
         (return-from nil (cdr list-head))))))

..这是运行上面的输出..

;; "-------" 
;; (NIL 0) 
;; (0) 
;; "-------" 
;; "-------" 
;; (NIL 0 1) 
;; (1) 
;; "-------" 
;; "-------" 
;; (NIL 0 1 2) 
;; (2) 
;; "-------" 
;; "-------" 
;; (NIL 0 1 2 3) 
;; (3) 
;; "-------"

我只是看不到 list-head 的修改位置,我必须假设 head 和 tail 是 eq ,因此修改一个正在修改另一个,但有人可以分解rplacd 行上发生的事情?

【问题讨论】:

  • 为什么要修改列表头?将其视为单个链表:头部不会改变,只有尾部会在添加新元素时改变。但是,应用于列表的 print 函数会打印从当前元素到最后一个元素的列表,这就是为什么您会看到列表头在增长而列表尾发生变化的原因。
  • 我不想修改它。我想了解实际发生的情况。如果打印输出使情况混乱,那么这可能就是我不理解的原因。您应该在答案中发表评论,以便我可以按您的方式发送业力!
  • +1 用于挖掘源代码、使用宏扩展、显示有问题的明确行为等的好问题。很好!
  • 又名“头哨把戏”。

标签: loops macros common-lisp


【解决方案1】:

list-headlist-tail 最初是 same(在 eq 意义上)。 list-head 是一个缺点,其中 cdr 是正在收集的列表。 list-tail 指向列表中的最后一个缺点(最初除外,见下文)。

要在列表末尾添加一个元素,replacd 修改 list-tail 的 cdr 以添加新的 cons,并更新 list-tail 以指向新的 cons。

当循环终止时,结果是list-head的cdr。

为什么这个复杂的业务带有额外的缺点?因为当list-tail 始终是指向列表最后一个缺点的指针时,列表附加算法变得更容易。但一开始,空列表没有任何缺点。所以诀窍是让列表变得更长。

【讨论】:

  • 就这样,一切都说得通了!谢谢,原来我搞乱了我脑海中的评估顺序。这完美地清除了一切。干杯!
  • 应归功于 Richard C Waters。请参阅“一些有用的 Lisp 算法:第 1 部分”中的“在 Lisp 中实现队列”merl.com/publications/docs/TR91-04.pdf(另请查看“第 2 部分”。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-10
  • 1970-01-01
  • 2020-03-31
相关资源
最近更新 更多