A bit more on Rat vs FatRat in Raku

昨天,我们正在深入研究 Rakudo Raku,以了解Rat值成为Num值的时间。事实证明,如果该值变得太小,这意味着它的分母变得越来越大,Rakudo开始使用Num值而不是Rat。

我们找到了它发生的地方。今天,让我们进行一个练习,看看Raku的行为是否可能不同,即扩展数据类型而不是将其切换为浮点数并且失去准确性。

改变很简单。所有你需要的是更新DIVIDE_N例程中的ifs:

--- a/src/core/Rat.pm
+++ b/src/core/Rat.pm
@@ -48,16 +48,14 @@ sub DIVIDE_NUMBERS(Int:D \nu, Int:D \de, \t1, \t2) {
           ($numerator   := -$numerator),
           ($denominator := -$denominator))),
       nqp::if(
-        nqp::istype(t1, FatRat) || nqp::istype(t2, FatRat),
+        nqp::istype(t1, FatRat) || nqp::istype(t2, FatRat) || $denominator >= UINT64_UPPER,
         nqp::p6bindattrinvres(
           nqp::p6bindattrinvres(nqp::create(FatRat),FatRat,'$!numerator',$numerator),
           FatRat,'$!denominator',$denominator),
-        nqp::if(
-          $denominator < UINT64_UPPER,
           nqp::p6bindattrinvres(
             nqp::p6bindattrinvres(nqp::create(Rat),Rat,'$!numerator',$numerator),
-            Rat,'$!denominator',$denominator),
-          nqp::p6box_n(nqp::div_In($numerator, $denominator)))))
+            Rat,'$!denominator',$denominator)
+        ))
 }

现在有两种结果:例程产生一个Rat值或一个FatRat。当子参数已经是FatRats或当前Rat太接近于零时,后者发生。

从昨天的帖子中用牛顿算法编译并测试我们修改过的raku可执行文件:

my $N = 25;
my @x = 
    Rat.new(1, 1), 
    -> $x { 
        $x - ($x ** 2 - $N) / (2 * $x)
    } ... *;

.WHAT.say for @x[0..10];
.say for @x[1..10];

正如预期的那样,序列的第一个元素是大鼠,而尾部是由FatRats组成的:

(Rat)
(Rat)
(Rat)
(Rat)
(Rat)
(Rat)
(FatRat)
(FatRat)
(FatRat)
(FatRat)
(FatRat)

另外,如果您打印这些值,则可以轻松看到它:

13
7.461538
5.406027
5.01524760
5.0000231782539490
5.0000000000537228965718724535111
5.00000000000000000000028861496160410945540567902983713732806515
5.000000000000000000000000000000000000000000008329859606174157518822601061625174583303232554885171687075417887439374231515823
5.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000693865610585718905982734693675309615913812411108046914931948226816763601320201386971350204028084660605790650314446568089428143916887535905115787146371799888
5.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004814494855534925123195523522159753005055993378092336823010386671077751892080269126953923957066141452855241262256569975702944214065988292758274535222239622977104185030432093986146346015004230914044314506580063758070896734658461687838556535528402765772220596451598003813021305355635793333485373058987453787504731

* * *

我不知道什么是更好的 - 对于有理数(不包括理性角色)或者可以同时拥有’窄’和’宽’值的两种不同类型,或者一种切换到更宽数据的机制当没有足够的容量时键入。我觉得最好的是最后一种选择(当然FatRat和Rat使用不同的类型来存储分子和分母)。

据我所知,这正是最初的想法:

对于尚未执行数字作用的值,将返回Int,Rat,Num或Complex的最窄适合类型;然而,包含由/分隔的两个整数的字符串将作为Rat返回(如果分母溢出int64,则返回FatRat)。

同样感觉更自然的是默默地为更多数字增加更多空间,而不是打破鼠类型的想法。无论如何,对此有不同的看法,但这不应阻止Raku的普及。

Raku 

comments powered by Disqus