🎄 19/25. 在 Raku 中使用 map 和 Seq 计算 π 的值
— 焉知非鱼欢迎来到Raku One-Liner Advent Calendar的第19天!今天,我们将使用两种不同的方法计算π的值。这篇博文的目的是使用不同的方法来生成数字序列。
Pre-party #
当然,在 Raku 中你不需要自己计算 π 的值,因为 Raku 给出了一些π
和pi
形式的预定义常数,以及双倍的值τ
和tau
。
但为了演示 map 和序列的用法,让我们实现一种最简单的算法来计算 π:
这是检查答案的草案代码:
my $pi = 1;
my $sign = 1;
for 1..10000 -> $k {
$sign *= -1;
$pi += $sign / ($k * 2 + 1);
}
say 4 * $pi;
第1部分 #
现在,让我们使用map
来使答案紧凑一点。最好使公式更通用:
这是我们的第一个单行程序:
say 4 * [+] (^1000).map({(-1) ** $_ / (2 * $_ + 1)})
我希望你能理解这段代码里的所有东西。我们在今年的 Advent Calendar 的前几天介绍了该答案的不同部分,例如,在第11天的帖子中。
但是,我仍然想强调你需要-1
周围的圆括号。如果输入-1 ** $_
,则总是得到 -1,因为减号前缀应用于取幂的结果。所以正确的代码是(-1) ** $_
。
第2部分 #
尝试使用序列运算符...
根据上述公式生成行也很有趣。此外,我们将使用有理数(见第12天)来创建分数⅓,⅕等。
say 4 * [+] <1/1>,
{-Rat.new($^n.numerator, $^n.denominator + 2)} ...
*.abs < 1E-5;
这个序列以有理数<1/1>
开始,这很方便,因为我们可以立即取其分子和分母。序列的生成器块使用此数字来创建新的有理数,其分母在每次迭代时加2。
你可能会问为什么我引用 $^n.numerator
,它总是 1。这是必需的,因为要改变符号,我们需要知道当前值的符号,并且符号保存在有理数值的分子部分中。
该占位符变量 $^n
自动取生成器代码块的第一个(并且是仅有的)参数。
生成序列,直到当前值的绝对值变得足够小。用 * ≅ 0
替换最终条件可能很诱人,但该程序运行时间太长而无法看到结果,因为近似相等运算符的默认容差为 10−15,而行不会快速收敛。
此外,你不能使用<... / ...>
语法在生成器中创建Rat
数字:
{ <$^n.numerator / $^n.denominator + 2> }
在这种情况下,Raku 会将其视为引号构造,例如<red green blue>
,而不是代码块,你将获得字符串列表。
这就是今天的一切。明天见!