我尝试了Rosettacode中的一些示例,并遇到了所提供的Ackermann示例的问题:在“未修改”运行它时(我将utf-8变量名替换为latin-1变量名),得到了(类似,但现在可以复制):
Caught: groovy.lang.GroovyRuntimeException: Cannot read write-only property: credentialsProvider
groovy.lang.GroovyRuntimeException: Cannot read write-only property: credentialsProvider
删除第3行中的原始声明(通过注释掉):
$ perl6 t/ackermann.p6
65533
19729 digits starting with 20035299304068464649790723515602557504478254755697...
Cannot unbox 65536 bit wide bigint into native integer
in sub A at t/ackermann.p6 line 3
in sub A at t/ackermann.p6 line 11
in sub A at t/ackermann.p6 line 3
in block <unit> at t/ackermann.p6 line 17
出了什么问题?该程序不会分配太多内存。自然整数种类有限吗?
我将Ackermann function中的代码替换为m和the与n,以实现更好的终端交互以复制错误,并尝试注释掉原型声明。我也问丽兹;)
$ perl6 t/ackermann.p6
65533
19729 digits starting with 20035299304068464649790723515602557504478254755697...
Numeric overflow
in sub A at t/ackermann.p6 line 8
in sub A at t/ackermann.p6 line 11
in block <unit> at t/ackermann.p6 line 17
答案 0 :(得分:8)
请先阅读JJ的答案。轻而易举,并得出了这个答案,实际上是对它的阐述。
TL; DR A(4,3)
是一个非常大数字,无法在此Universe中进行计算。但是乐堂会尝试的。这样一来,如果使用缓存版本,就会超出与内存分配和索引相关的合理限制,而如果不使用缓存,则会超出与数值计算有关的限制。
我尝试了Rosettacode的一些示例,并遇到了the provided Ackermann example
的问题
用一些强调的重点引用任务说明:
任意精度是首选(因为该功能增长如此快)
P6的标准整数类型Int
是arbitrary precision。 P6解决方案使用它们来计算可能的最高级答案。只有当您尝试做不可能的事情时,它才会失败。
“未修改”运行时(我将utf-8变量名替换为拉丁1变量名)
替换变量名并不重要。
但是添加A(4,3)
行将代码从现实中的可计算转换为现实中的不可计算。
您修改的示例只有一个解释性注释:
这是该版本的缓存版本... 使 A(4,2)成为可能
请注意,A(4,2)
解决方案的长度接近20,000位。
如果您查看该页面上的其他解决方案,则大多数甚至都不会尝试访问A(4,2)
。在Phix版本上有这样的评论:
已优化。仍然没有bignum库,因此ack(4,2),即power(2,65536)-3,显然是19729位及以上,超出了(CPU / FPU硬件)和此[代码]。 / p>
A(4,2)
的解决方案是最先进的。
A(4,3)
在实践中不可计算引用Academic Kids: Ackermann function:
即使对于较小的输入(例如4,3),阿克曼函数的值也会变得很大,以至于无法进行计算,实际上它们的十进制扩展甚至不能存储在整个物理宇宙中。
因此(在此宇宙中)计算A(4,3).say
是不可能的。
不可避免地会导致甚至任意精度整数运算的溢出。只是时间和方式的问题。
Cannot unbox 65536 bit wide bigint into native integer
第一条错误消息提到了以下代码行:
proto A(Int \m, Int \n) { (state @)[m][n] //= {*} }
state @
是anonymous state array variable。
默认情况下,@
变量对P6的the default concrete type使用abstract array type。这种默认的数组类型可在实现复杂性和出色性能之间取得平衡。
尽管计算A(4,2)
的索引(m
和n
)足够小,以至于计算可以完成而不会超出默认数组的索引限制。
此限制是“本地”整数(注意:不是 a "natural" integer)。 P6称为“本地”整数,由其运行的硬件支持固定宽度整数,通常为long long,而后者通常为64位。
64位宽的索引最多可以处理9,223,372,036,854,775,807个索引。
但是在尝试计算A(4,3)
时,该算法会生成65536位(8192字节)宽的整数索引。这样的整数可能最大为2 65536 ,即20,032 decimal digit number。但是允许的最大索引是64位本机整数。因此,除非您注释掉使用数组的缓存行,否则对于A(4,3)
,程序最终将引发异常:
无法将65536位宽的bigint拆箱为本地整数
正如已经说明的,没有足够大的数组可以帮助完全计算A(4,3)
。另外,一个64位整数已经是一个很大的索引(9,223,372,036,854,775,807
)。就是说,P6可以容纳更大的数组,因此我将在下面简要讨论,因为理论上的可能性可能会引起其他问题的兴趣。
但是在讨论更大的数组之前,running the code below on tio.run在该平台上显示了默认数组类型的实际限制 :
my @array;
@array[2**29]++; # works
@array[2**30]++; # could not allocate 8589967360 bytes
@array[2**60]++; # Unable to allocate ... 1152921504606846977 elements
@array[2**63]++; # Cannot unbox 64 bit wide bigint into native integer
(注释掉错误行以查看以后的/更大的错误。)
“无法分配8589967360字节”错误是MoarVM紧急情况。这是tio.run拒绝内存分配请求的结果。
我认为“无法分配...元素”错误是由于超出一些内部Rakudo实现限制而引发的P6级异常。
即使为程序提供了大量内存,最后一条错误消息也会显示默认数组类型的索引限制。
可以创建/使用其他支持稀疏数组等功能的@
(does Positional
)数据类型。
而且,使用这种机制,可能有人可以编写一个数组实现,该数组支持比默认数组类型(可能是在底层平台指令之上分层逻辑)所支持的整数索引更大。
如果创建了这样的替代方案并将其命名为BigArray
,则可以将高速缓存行替换为:
my @array is BigArray;
sub A(Int \, Int \) { @BigArray[][] //= {*} }
同样,这个 still 不足以存储用于完全计算A(4,3)
的临时结果,但我的意思是展示自定义数组类型的使用。
Numeric overflow
注释掉缓存后,您将得到:
数字溢出
P6 / Rakudo执行任意精度运算。尽管有时将其称为无限精度,但实际上并不是(不可能)无限,而是“任意”,实际上,在计算中,“任意”对于“理智”。
从传统上讲,这意味着用尽内存来存储数字。但是在Rakudo的情况下,我认为有一种尝试,可以在完全用尽RAM之前从真正的Int
切换到Num
(浮点数)来保持正常。但是随后,计算A(4,3)
甚至会溢出两次浮点数。
因此,虽然缓存很快就崩溃了,但无论如何,代码肯定会在以后崩溃,然后您将得到一个数值溢出,该溢出可能表现为内存不足错误或数值溢出错误。情况..
答案 1 :(得分:5)
数组下标使用native ints;这就是为什么在将大整数用作数组下标时在第3行出现错误的原因。您可能需要定义一个新的BigArray,该整数将Ints用作数组下标。
第二个问题出现在**运算符中:结果为Real,并且当低级运算返回Num时,它将引发异常。 https://github.com/rakudo/rakudo/blob/master/src/core/Int.pm6#L391-L401
因此创建BigArray可能无济于事。您还必须创建自己的**,该**始终可以与Int一起使用,但是您似乎已经达到了无限精度Ints的极限(不是那么无限)。