mutsuで「ループなしで1〜100を印字」を動かす実験
1000/999²の小数展開を使って1〜100を連番出力するトリックを、自作Rakuインタプリタmutsu(mutsu)で再現できるか試した記録。
#raku #mutsu #experiment #math
目的
Gaucheの多倍長有理数演算で成立するこのトリックが、Rakuの数値タワー(Int/Rat/FatRat)でも同様に動くかを確認する。
環境
- mutsu 0.21.0(miseでグローバルインストール:
~/.local/share/mise/installs/github-tokuhirom-mutsu/0.21.0/bin/mutsu)
試行錯誤
最初はGaucheの有理数演算に倣ってFatRat(Rakuの任意精度有理数型)で組んだ。
my $x = FatRat.new(1000, 999**2) * FatRat.new(10**303, 1);
my $n = $x.floor.Str;
$n = ('0' x (3 - $n.chars % 3) % 3) ~ $n;
for $n.comb(3) -> $g { print "$g "; }
これは動いたが、FatRatが実は不要と気づいた。RakuのIntはデフォルトで多倍長整数なので、1000 * 10^303をそのまま999²で整数除算(div、floor除算)すればよい。
my $digits = ((1000 * 10**303) div 999**2).Str;
$digits = '0' x (-$digits.chars % 3) ~ $digits;
say $digits.comb(3).head(100).join(' ');
さらにゼロパディングを手計算せずsprintfの幅指定に任せれば1行に収まる。
say sprintf("%0303d", (1000 * 10**303) div 999**2).comb(3).head(100).join(' ');
実行結果
$ mutsu print100.raku
001 002 003 004 005 006 007 008 009 010 ... 097 098 099 100
3桁ずつのグループが001から100まで途切れなく並んだ(さらに先の101まで正しく続くことも別途確認済み)。
読み取れること
- mutsu上でRakuの
Intは最初から多倍長(bignum)であり、FatRatを持ち出さなくても整数のdivだけで元のトリックを再現できる。Gauche版が有理数演算(/)を使っていたのに対し、Rakuでは10^303倍を先にIntの乗算に織り込めるため整数除算1回で済む。 divは正の被除数・除数に対してはfloorと同じ結果になるため、明示的な.floor呼び出しは不要。sprintfの幅指定(%0303d)でゼロ埋めを任せられるので、桁数を手動計算するコードが消せる。
躓いた点
substrで先頭から30文字を単純に切り出すと、ゼロパディングをしていない生の数字列は3の倍数境界からズレており、001,002,...ではなく100,200,300,...のようにズレたグループ化に見えてしまった。ゼロパディングして総桁数を3の倍数に揃えることで解決した。