[Raku] 如何生成各種無限長的數列

作者:   發佈於: ,更新於:   #rakulang #sequence

Raku 中的 ... 算符可用來定義各種無限長的數列,比方說,要定義出費波那契數列,能用以下這簡短的算式:

1, 1, -> $a, $b { $a + $b } ... Inf

前兩個 1 是兩項初始參數,第三個部份 -> $a, $b { $a + $b } 是個無名函式,接兩個參數傳回兩參數之和,而第四個部份是個 ... Inf 則表示:「直到無限大為止」。(Inf 這個字是用來表示「無限大」,就是「比所有你寫得出來的數值都還要大的那個東西」。)

這算式可被「放」在一個陣列 @fib 裡:

my @fib = 1, 1, -> $a, $b { $a + $b } ... Inf;

雖然看來這就像是把一個無限長的數列存在 @fib 這個變數當中,但在資源有限計算機裡這是不可能的。@fib 的內容實際上只有 @fib[0]@fib[1] 這兩項,後面所有的項目都是還不存在的,或者說是還沒計算出來的,只有計算公式是存在的。

這整行算式,其實是一個計算過程,只是在語法上看起來或許不太像而已。而 @fib 雖然仍是一個陣列,但也同時帶有隋性求值這項特性。

Raku 語中的 ... 算符,就是能把一個「計算過程」定義成一個有點像是有限序列的工具。

比如說,這麼寫,就似乎定義了一個能裝下所有正整數的陣列:

my @all-positive-integers = 1 ... Inf;

比如說,接著這麼寫,就似乎定義了一個裝了所有質數的陣列:

my @all-primes = @all-positive-integers.grep(&is-prime);

嗯... 這在計算上是不可能的。目前人類所算得的最大質數只到 2⁸²⁵⁸⁹⁹³³ - 1 而已,但質數有無限多個,更大的質數必定是有,只是還沒被 GIMPS 計算出來而已。喔,在那之前,正整數有無限多個,一個陣列也是無法裝下所有正整數的。

顯然,@all-positive-integers 內所裝的並非是「所有正整數」,而是一個能「逐一產生出所有正整數的迭代器」,而 @all-primes 內所裝的亦非「所有質數」,而是一個能「逐一產生出所有質數的迭代器」。它們都是個內帶了一點狀態的函式,也就是所謂的物件。

但在使用上,與一般陣列無異。比方說可以用 @all-positive-integers[42] 來取得第 43 個正整數 43:

say @all-positive-integers[42];

#=> 43

或是可以用 @all-primes[42] 來取得第 43 個質數 191

say @all-primes[42];

#=> 191

那麼,如果有朝一日要來找「費波那契數列中的所有質數」這個數列中的第 43 項的話,就可這麼寫:

my @fib-primes = @fib.grep(&is-prime);

say @fib-primes[42];

嗯,這是個十分難算得的數字,得算很久很久很久很久很久很久,而且它有 194676 位數這麼長吧。

但至少這程式是很簡短的。