不使用中间列表过滤范围

Filter a range without using an intermediate list

我写了一个函数 is-prime 来验证给定的数字是否是质数,并相应地 returns tnil

(is-prime 2) ; => T
(is-prime 3) ; => T
(is-prime 4) ; => NIL

到目前为止,还不错。现在我想生成一个介于 minmax 之间的素数列表,所以我想想出一个将这两个值作为参数和 returns 列表的函数所有素数:

(defun get-primes (min max)
  ...)

现在这是我目前卡住的地方。当然,我 可以 做的是创建一个列表,其中包含从 minmax 和 运行 [=20= 的所有数字范围] 就可以了。

无论如何,这意味着我首先必须创建一个可能 巨大的 列表,其中包含我无论如何都会丢弃的大量数字。所以我想反过来做,建立一个列表,只包含 minmax 之间的数字,根据 is-prime 谓词,这些数字实际上是质数。

如何以 功能性 方式执行此操作,即不使用 loop?我当前的方法(使用 loop)如下所示:

(defun get-primes (min max)
  (loop
    for guess from min to max
    when (is-prime guess)
    collect guess))

也许这是一个完全愚蠢的问题,但我想我只见树木不见森林。

有什么提示吗?

Common Lisp 不支持 函数式编程 方法。该语言基于对底层机器更务实的看法:没有TCOstacks是有限的,各种资源是有限的(参数个数这是允许的,等等),突变是可能的。对于任何 Haskell 爱好者来说,这听起来都不是很有动力。但是 Common Lisp 是为了编写 Lisp 应用程序而开发的,而不是为了推进 FP 研发。 Common Lisp 中的求值(和 Lisp 中一样)是 strict 而不是 lazy。默认数据结构也不是 lazy。尽管有 lazy 个库。 Common Lisp 也没有提供标准功能,例如 continuationscoroutines - 这在这种情况下可能很有用。

默认的 Common Lisp 方法是 non-functional 并使用某种 迭代 构造:DOLOOPITERATE 图书馆。就像你的例子一样。 Common Lisp 用户发现它非常好。有些人,比如我,认为 Iterate 是有史以来最好的循环结构。 ;-) 优点是创建高效代码相对容易,即使宏的实现量很大。

有多种不同的方法:

  • 惰性流。阅读 SICP 有帮助,请参阅 streams 上的部分。也可以在 Common Lisp 中轻松完成。请注意,Common Lisp 使用单词 stream 表示 I/O 流 - 这是不同的东西。
  • 使用 生成器函数 next-prime。从初始范围生成这个函数并调用它,直到你得到所有你感兴趣的素数。

这里是生成器函数的一个简单示例,从某个起始值生成偶数:

(defun make-next-even-fn (start)
  (let ((current (- start
                    (if (evenp start) 2 1))))
    (lambda ()
      (incf current 2))))


CL-USER 14 > (let ((next-even-fn (make-next-even-fn 13))) 
               (flet ((get-next-even ()
                        (funcall next-even-fn)))
                 (print (get-next-even))
                 (print (get-next-even))
                 (print (get-next-even))
                 (print (get-next-even))
                 (list (get-next-even)
                       (get-next-even)
                       (get-next-even))))

14 
16 
18 
20 
(22 24 26)

定义 next-prime 生成器留作练习。 ;-)

  • 使用某种更复杂的惰性数据结构。对于 Common Lisp,有 Series,这是一个早期的迭代建议 - 也在 CLtL2 中发布:Edi Weitz(汉堡的数学教授)的 Series. The new book Common Lisp Recipes 有一个用于计算素数的系列示例.请参阅第 7.15 章。您可以下载本书的源代码并在那里找到示例。
  • Series 的更简单变体,例如 pipes