有没有办法在 Raku 中自动使用 FatRats?
Is there a way to automatically use FatRats in Raku?
Raku 的一个巧妙之处在于它会在适当的时候自动使用有理数而不是浮点数(例如,当两个整数相除时)。
不幸的是,一旦分母变得太大,无论如何都会使用浮点数。
有 FatRat
类型不这样做,但据我所知,使用它们的唯一方法是明确地这样做。
例如,此脚本计算 π 的位数:
#!/usr/bin/env raku
unit sub MAIN(Int $decimals = 1_000);
sub atan_repr(Int $n, Int :$decimals)
{
my $x = $n;
my $n2 = $n²;
my $sign = 1;
my $limit = 10**($decimals+2);
my $result = FatRat.new(1, $x);
for 3,5...* -> $i {
$x ×= $n2;
$sign ×= -1;
$result += FatRat.new($sign, $i × $x);
last if $x ≥ $limit;
}
return $result;
}
my $π = 4 × (4 × atan_repr(5, :$decimals) - atan_repr(239, :$decimals));
my $π-str = ~$π; # Make sure we don't do string conversion over and over
print '3. ';
for 0,5...^$decimals -> $d {
print " # $d\n " if $d && $d %% 50;
print $π-str.substr(2+$d,5), ' ';
}
print " # $decimals" if $decimals && $decimals %% 50;
say '';
它非常优雅,除了 FatRat.new(1, $x)
之类的东西。我更愿意只使用 1/$x
并以某种方式声明应该自动使用 FatRat
s 而不是 Rat
s。也许类似于 use bigrat
,类似于 Perl 的 use bigint
和 use bignum
?
有没有我没找到的方法?
单独使用 Rat
s 没问题,直到 运行 不准确(这种情况很少见)。我认为是 2022.02 版本引入了 $*RAT-OVERFLOW
动态变量。它指定当 Rat
超出其精度时应执行的行为。默认情况下,它设置为 Num
,这意味着它将恢复使用(有损)浮点数。
您也可以将其设置为 FatRat
,这将导致在 Rat
运行 秒失准时自动升级到 FatRat
。您还可以指定 Failure
(让它 fail
)、Exception
(让它抛出异常)或 CX::Warn
(让它在降级到 [=15 时发出警告) =]).
这是如何运作的? $*RAT-OVERFLOW
变量应该是一个 class 或实例,当 Rat
溢出时将在其上调用 UPGRADE-RAT
方法。因此,您 可以 通过创建一个带有 UPGRADE-RAT
方法的 class 来引入您自己的行为。
因此,要在全球范围内激活它以升级到 FatRat
,您需要:
INIT $*RAT-OVERFLOW = FatRat;
要仅为词法范围激活它:
my $*RAT-OVERFLOW = FatRat;
我看到这仍然需要记录:(
Raku 的一个巧妙之处在于它会在适当的时候自动使用有理数而不是浮点数(例如,当两个整数相除时)。 不幸的是,一旦分母变得太大,无论如何都会使用浮点数。
有 FatRat
类型不这样做,但据我所知,使用它们的唯一方法是明确地这样做。
例如,此脚本计算 π 的位数:
#!/usr/bin/env raku
unit sub MAIN(Int $decimals = 1_000);
sub atan_repr(Int $n, Int :$decimals)
{
my $x = $n;
my $n2 = $n²;
my $sign = 1;
my $limit = 10**($decimals+2);
my $result = FatRat.new(1, $x);
for 3,5...* -> $i {
$x ×= $n2;
$sign ×= -1;
$result += FatRat.new($sign, $i × $x);
last if $x ≥ $limit;
}
return $result;
}
my $π = 4 × (4 × atan_repr(5, :$decimals) - atan_repr(239, :$decimals));
my $π-str = ~$π; # Make sure we don't do string conversion over and over
print '3. ';
for 0,5...^$decimals -> $d {
print " # $d\n " if $d && $d %% 50;
print $π-str.substr(2+$d,5), ' ';
}
print " # $decimals" if $decimals && $decimals %% 50;
say '';
它非常优雅,除了 FatRat.new(1, $x)
之类的东西。我更愿意只使用 1/$x
并以某种方式声明应该自动使用 FatRat
s 而不是 Rat
s。也许类似于 use bigrat
,类似于 Perl 的 use bigint
和 use bignum
?
有没有我没找到的方法?
单独使用 Rat
s 没问题,直到 运行 不准确(这种情况很少见)。我认为是 2022.02 版本引入了 $*RAT-OVERFLOW
动态变量。它指定当 Rat
超出其精度时应执行的行为。默认情况下,它设置为 Num
,这意味着它将恢复使用(有损)浮点数。
您也可以将其设置为 FatRat
,这将导致在 Rat
运行 秒失准时自动升级到 FatRat
。您还可以指定 Failure
(让它 fail
)、Exception
(让它抛出异常)或 CX::Warn
(让它在降级到 [=15 时发出警告) =]).
这是如何运作的? $*RAT-OVERFLOW
变量应该是一个 class 或实例,当 Rat
溢出时将在其上调用 UPGRADE-RAT
方法。因此,您 可以 通过创建一个带有 UPGRADE-RAT
方法的 class 来引入您自己的行为。
因此,要在全球范围内激活它以升级到 FatRat
,您需要:
INIT $*RAT-OVERFLOW = FatRat;
要仅为词法范围激活它:
my $*RAT-OVERFLOW = FatRat;
我看到这仍然需要记录:(