【问题标题】:Why does function apply complain about long lists?为什么函数应用抱怨长列表?
【发布时间】:2015-12-10 05:29:43
【问题描述】:

作为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 个或更少元素的轮子的素数。

显然applyring 会在向他们传递长列表时嗤之以鼻。

但是列表不应该算作单个参数吗?我承认我完全糊涂了。任何意见表示赞赏。

【问题讨论】:

  • 错过这个算法的要点(和美丽)非常受欢迎。埃拉托色尼筛法基于重复添加;它不使用mod 或任何其他方法测试可分性。 (参见例如this paper。)
  • 感谢伟大的链接!我的factorization-wheel 也是垃圾,所以我想我会重新开始。

标签: lisp common-lisp apply sieve-of-eratosthenes wheel-factorization


【解决方案1】:

ANSI Common Lisp 允许实现限制可以传递给函数的参数的最大数量。此限制由call-arguments-limit 给出,可以小至 50。

对于行为类似于代数群运算符的函数,遵循 关联属性(+list 和其他),我们可以通过使用 reduce 抽取输入列表同时将函数视为二进制来绕过限制。

例如添加大量数字:(reduce #'+ list) 而不是(apply #'+ list)

关于reduce的注释

在 Common Lisp 中,即使列表为空,reduce 似乎也可以工作。很少有其他语言可以为您提供此功能,而且它实际上并非来自reduce:它不适用于所有功能。但是对于+,我们可以写成(reduce #'+ nil),它计算为零,就像(apply #'+ nil)一样。

这是为什么呢?因为+ 函数可以用零参数调用,当用零参数调用时,它会产生加法组的标识元素:0。这与reduce 函数相吻合。

在其他一些语言中,foldreduce 函数必须被赋予一个初始种子值(如 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)只是为了翻转一个标志,只是为了让-reduceapply 下表现得更加一致)。 - 函数也不能以零参数调用。

如果我们想要获取一个数字列表并从某个值x 中减去它们,那么模式是:

(reduce #'- list-of-numbers :initial-value x)

【讨论】:

  • 确实,我通过创建一个将整个列表作为参数的ring-list 函数设法解决了这个问题。不过,我可能会从头开始重做。
  • idempotence 虽然意味着别的东西,即ƒ(ƒ(x)) ≡ ƒ(x)。使用x 表示f/[x] 来自折叠的定义(/),即f/[x,...ys...] == x `f` (f/ys) 因此f/[x] == f/[x,...[]...] = x `f` (f/[]) == x 因为f/[] 应该是f 的标识元素(例如,0 表示@987654372 @,而 1 代表 *),正如您正确指出的那样。
  • @WillNess 谢谢;我修正了“幂等”的误用。
  • 我调整了它;随时恢复。
猜你喜欢
  • 1970-01-01
  • 2015-06-09
  • 2020-08-12
  • 2016-02-27
  • 1970-01-01
  • 2019-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多