我使用local-time编写了以下循环:

(defun count-dates (stop-date k)
  (loop for step = (local-time:today)
        then (local-time:timestamp- step 1 :day)
        while (local-time:timestamp>= step stop-date)
        collect (funcall k step)))

它可以像这样运行:
(count-dates (local-time:encode-timestamp 0 0 0 0 1 1 2019) #'princ)

虽然这很简单明了,但我想知道如何在不使用全能loop结构的情况下编写它,并提出:
(defun count-dates2 (stop-date k)
  (reverse (labels ((f (acc step)
                      (if (local-time:timestamp>= step stop-date)
                          (f (cons (funcall k step) acc)
                             (local-time:timestamp- step 1 :day))
                          acc)))
             (f '() (local-time:today)))))

这似乎过于复杂,使用reverse和累加器有没有一种更简单的方法来实现与循环相同的效果,而不必进行变异,也不可能溢出堆栈?

最佳答案

不在常见的Lisp中,不:如果您想要一个迭代构造,您需要使用显式迭代构造:CL不保证语法递归构造实际上是迭代的loop不是唯一的迭代构造,当然您可以编写自己的迭代和结果集合构造。
实际上,并不能保证您的第二个版本不会在cl中溢出堆栈:大多数当前实现将尾调用编译为迭代,尽管在解释的代码中可能无法处理,但有些实现受其目标(例如jvm)的约束而不能这样做。也有一些主要的历史原生代码实现没有(例如符号CL)。
有一些Lisp族语言确实在语言中指定尾部调用是迭代的,特别是Scheme,在这种语言中,第二个版本就可以了。
至于需要向后建立列表然后反转它们的问题:我认为这是LISPS中列表方式的必然结果:如果你不乐意改变现有的列表或者每一步都采取批量复制的方式,那么你只需要增加列表来增加列表。
当然,你可以隐藏你在幕后构建的列表的变异,这样你就永远不需要知道发生了什么,但这并不意味着它既不会变异结构,也不会反向构建结构,然后反向构建例如,我有一个结构,它看起来像:

(collecting
  ...
  (collect ...)
  ...)

它向前构建列表,但它通过保持尾部指针并改变正在构建的列表来实现。

08-17 15:10