Common lisp 为什么apply在一个大列表上抛出一个CONTROL-STACK-expensed-ERROR?
如果Common lisp 为什么apply在一个大列表上抛出一个CONTROL-STACK-expensed-ERROR?,common-lisp,sbcl,Common Lisp,Sbcl,如果x具有值253391,但在253392*上出现(SB-KERNEL::CONTROL-STACK-expeated-ERROR)时失败,则可以工作。这是小于调用参数限制的数量级 递归正在耗尽堆栈吗?如果是,是否在应用中?为什么还没有优化呢 *同样有趣的是,(apply#max(i的循环从1到253391 collect 1))抛出错误,但是253390没有问题 **调用参数限制计算为4611686018427387903(借助于格式的~R,结果是这是四个五分之六百一十一万一千六百八十六万亿
x
具有值253391
,但在253392
*上出现(SB-KERNEL::CONTROL-STACK-expeated-ERROR)
时失败,则可以工作。这是小于调用参数限制的数量级
递归正在耗尽堆栈吗?如果是,是否在应用中?为什么还没有优化呢
*同样有趣的是,(apply#max(i的循环从1到253391 collect 1))
抛出错误,但是253390
没有问题
**调用参数限制
计算为4611686018427387903(借助于格式
的~R
,结果是这是四个五分之六百一十一万一千六百八十六万亿一百八十八亿一千八百四十二亿七千七百三十八万七千九百零三)
可以传递给SBCL中函数的参数
您不能传递参数。你传递论点
x
和y
是函数foo
的参数
(defun foo (x y) (list x y))
20
和22
是函数foo
调用中的参数
(defun foo (x y) (list x y))
请参阅变量调用参数限制
和lambda参数限制
SBCL和调用参数限制
如果函数几乎不能处理所声明的参数数量,那么这看起来像是SBCL
中的错误。您可能需要报告此错误。可能他们需要更改调用参数限制的值
测试
APPLY
是测试它的一种方法
另一个:
(foo 20 22)
还可以将FUNCALL与多个参数一起使用
为什么存在限制?
编写公共Lisp标准是为了在各种不同的计算机上实现高效。人们认为一些机器级函数调用实现只支持有限数量的参数。该标准规定支持的参数数量可以低至50
。实际上,有些实现支持的参数数量相对较少
因此,Common Lisp中的apply
不是列表处理工具,而是使用计算的arglist调用函数
对于列表和向量处理,使用REDUCE,而不是APPLY
如果要对列表中的所有数字求和,请替换
(eval (append '(drop-params)
(loop for i from 1 to 2533911 collect 1)))
与
递归
apply是一个非优化递归函数
我不明白为什么函数APPLY
应该使用递归
例如,如果你想到
(reduce #'+ list) ; can handle arbitrary long lists
参数的重复求和由函数+
完成,而不是由apply
完成
这与
(apply #'+ '(1 2 3 4 5))
如果使用两个参数重复调用函数+
是通过reduce
完成的,是什么导致堆栈耗尽?
尽管递归通常是堆栈耗尽的罪魁祸首,但在本例中并非如此。根据报告:
在完全调用中,参数被传递,在堆栈顶部创建一个部分框架,并将堆栈参数存储到该框架中
生成的每个列表元素都存储在新的堆栈框架中,从而快速耗尽堆栈。大概,通过–control stack size
传递SBCL一个更大的值会增加函数调用中可以传递的参数数量的实际限制
为什么调用参数限制
比实际限制大得多?
向有类似问题的人发出的警告解释了为什么堆栈大小的实际限制没有反映在调用参数限制中
:
您在这里看到的情况不是由于基本的实现限制,而是因为有人选择了特定的堆栈大小选择——如果堆栈大小更大,那么您的调用不会出错。考虑到我们在堆栈上传递多余的参数的策略,在任何给定时间可传递的参数的实际最大数量取决于程序状态,实际上不是常数。
规格说明是这样的,所以SBCL似乎有
有人在讨论这个问题,有人在消息来源中建议至少有一位撰稿人认为应该将其降低到一个不那么荒谬的值:
(reduce #'+ '(1 2 3 4 5))
SBCL实现调用参数限制的特殊方法可能有改进的余地,并可能导致意外行为,但它确实遵循ANSI规范。
实际限制取决于堆栈上剩余的空间,因此根据此值定义调用参数限制
将不符合规范对常量值的要求。感谢您指出我将参数称为“参数”的事实(两次,不少于两次!我编辑了这个问题,因为正确的术语对于清晰性很重要)。我熟悉reduce
,但我唯一关心的是超出参数计数的限制,而不是实际处理列表。我将尝试eval
,看看是否有不同的结果。说明:我确实在寻找导致堆栈耗尽错误的可能实现原因。stac上有什么为什么?我实际上对执行列表处理的更好的方法不感兴趣。你希望应用什么样的递归?我不希望有。我也不希望有,这就是为什么我感到困惑的原因。请看下面我的答案,了解堆栈上的实际结果,这在事后看来是非常明显的。我只是太习惯于思考“堆栈溢出==错误的递归”,我猜
(reduce #'+ '(1 2 3 4 5))
;; TODO: Reducing CALL-ARGUMENTS-LIMIT to something reasonable to
;; allow DWORD ops without it looking like a bug would make sense.
;; With a stack size of about 2MB, the limit is absurd anyway.