作为某些Eulerian travails的一部分,我尝试使用分解轮代码Sieve of Eratosthenes。到目前为止我的代码是:
(defun ring (&rest content)
"Returns a circular list containing the elements in content.
The returned list starts with the first element of content."
(setf (cdr (last content)) content))
(defun factorization-wheel (lst)
"Returns a circular list containing a factorization
wheel using the list of prime numbers in lst"
(let ((circumference (apply #'* lst)))
(loop for i from 1 to circumference
unless (some #'(lambda (x) (zerop (mod i x))) lst)
collect i into wheel
finally (return (apply #'ring
(maplist
#'(lambda (x) ; Takes exception to long lists (?!)
(if (cdr x)
(- (cadr x) (car x))
(- circumference (car x) -1)))
wheel))))))
(defun eratosthenes (n &optional (wheel (ring 4 2)))
"Returns primes up to n calculated using
a Sieve of Eratosthenes and a factorization wheel"
(let* ((candidates (loop with s = 1
for i in wheel
collect (setf s (+ i s))
until (> s n))))
(maplist #'(lambda (x)
(if (> (expt (car x) 2) n)
(return-from eratosthenes candidates))
(delete-if
#'(lambda (y) (zerop (mod y (car x))))
(cdr x)))
candidates)))
对于超过6个元素的轮子,我得到以下结果。我真的不明白为什么:
21 > (factorization-wheel '(2 3 5 7 11 13))
(16 2 4 6 2 6 4 2 4 6 6 2 6 4 2 6 4 6 8 4 ...)
21 > (factorization-wheel '(2 3 5 7 11 13 17))
> Error: Too many arguments.
> While executing: FACTORIZATION-WHEEL, in process listener(1).
该算法似乎正常工作,否则会产生具有6个或更少元素的轮子的素数。
当长名单传递给他们时,显然apply
或ring
会嗤之以鼻。
但不应该将列表计为单个参数吗?我承认我彻底搞砸了。任何意见都表示赞赏。
答案 0 :(得分:8)
ANSI Common Lisp允许实现约束可以传递给函数的最大参数数量。这个限制由call-arguments-limit给出,可以小到50。
对于行为类似于代数群操作符的函数
关联属性(+
,list
和其他),我们可以通过使用reduce
来抽取输入列表同时将函数视为二进制来绕过极限。
例如,要添加大量数字:(reduce #'+ list)
而不是(apply #'+ list)
。
reduce
在Common Lisp中,即使列表为空,reduce
也会起作用。很少有其他语言可以提供这一点,而且它实际上并非来自reduce
:它不适用于所有功能。但是使用+
,我们可以编写(reduce #'+ nil)
并计算零,就像(apply #'+ nil)
一样。
为什么?因为可以使用零参数调用+
函数,并且在使用零参数调用时,它会为添加剂组生成标识元素:0
。这与reduce
函数相吻合。
在其他一些语言中,必须为fold
或reduce
函数指定初始种子值(如0
),否则为非空列表。如果两者都没有,那就是错误。
Common Lisp reduce
,如果给出一个空列表而没有:initial-value
,将调用没有参数的内核函数,并使用返回值作为初始值。由于该值是唯一的值(列表为空),因此返回该值。
注意具有最左边参数的特殊规则的函数。例如:
(apply #'- '(1)) -> -1 ;; same as (- 1), unary minus semantics.
(reduce #'- '(1)) -> 1 ;; what?
正在进行的是当reduce
被赋予一个单元素列表时,它只返回元素而不调用该函数。
基本上它建立在上面提到的数学假设之上,如果没有提供:initial-value
,那么f
应该支持(f) -> i
,其中i
是一个标识元素与f
的关系,以便(f i x) -> x
。这在减少单例列表(reduce #'f (list x)) -> (f (f) x) -> (f i x) -> x
时用作初始值。
-
功能并未遵守这些规则。 (- a 0)
表示"从a
"中减去零。产生a
,而(- a)
是a
的加法逆,可能纯粹是出于语用,符号的原因(也就是说,没有让Lisp程序员写(- 0 a)
只是为了翻转签名,只是为了让-
在reduce
和apply
下表现得更加一致。也可能不会使用零参数调用-
函数。
如果我们想要取一个数字列表并从某个值x
中减去它们,那么它的模式是:
(reduce #'- list-of-numbers :initial-value x)