このパートでは、fluorite-7言語のもっとも象徴的な機構であるパイプについて取り扱います。
レッスン1 パイプ
中置|
はパイプ演算子です。
パイプ演算子は、左辺のストリーマの各要素に対して、右辺を適用する演算子です。
次の例は、1から4までの数値を、それぞれ10倍にして表示するコードです。
$ fl7 '1, 2, 3, 4 | _ * 10'
10
20
30
40
この機構は、シェルのパイプや、jq言語の同様の機構と似ています。
|
の右では、与えられたそれぞれの要素を_
で参照できます。
この例では、_
には1から4までの数値が順番に代入された状態で、_ * 10
という式が評価されます。
すなわち、以下のコードと概ね同じです。
$ fl7 '1 * 10, 2 * 10, 3 * 10, 4 * 10'
10
20
30
40
レッスン2 パイプの連結
パイプは複数連結できます。
$ fl7 '1 .. 4 | _ * 10 | _ + 5'
15
25
35
45
このコードでは、1 .. 4
によって生み出された4個の数値が、1個ずつ10倍にされて5を加算されます。
結果、1から4までのそれぞれの数値に10倍と+5を適用したストリーマが得られました。
レッスン3 ストリーマを返すパイプ
パイプは、左辺のストリーマを受け取って、その要素それぞれに対して右辺を適用したものを、再びストリーマにまとめるものでした。
では、右辺にストリーマを生成する式を置いたらどうなるでしょうか。
$ fl7 '1 .. 4 | 1 .. _'
1
1
2
1
2
3
1
2
3
4
得られたストリーマがすべて平坦化された状態で出力されました。
パイプをどれだけ連結しても、ストリーマは必ず1次元的です。
ストリーマではなく配列を返した場合はどうなるでしょうか。
$ fl7 '1 .. 4 | [1 .. _]'
1
1,2
1,2,3
1,2,3,4
その場合、階段状のものが出来ました。
これは、次の式と概ね等価です。
$ fl7 '[1 .. 1], [1 .. 2], [1 .. 3], [1 .. 4]'
1
1,2
1,2,3
1,2,3,4
レッスン4 変数名の変更
次のように書くと、_
ではなく別の名前で値を受け取ることができます。
$ fl7 'i: 1 .. 4 | i * 10'
10
20
30
40
変数宣言と演算子の形が被っていることをに気を付けてください。|
の左辺に現れた:
は、パイプ処理で使用する変数名を表します。
パイプ演算子全体を変数に格納する場合は、必ずパイプ演算子全体を括弧で囲みます。
$ fl7 'i: (1 .. 4 | _ * 10); i'
10
20
30
40
レッスン5 非ストリーマのパイプ
これまで、|
の左辺にはストリーマを与えると言ってきました。
しかし、数値などの普通の値を与えた場合はどうなるでしょうか。
$ fl7 '7 | _ * 10'
70
その場合は、単に右辺が1回だけ呼び出されます。
ストリーマを受け取る演算子や関数は、非ストリーマを与えても正しく動くという性質があります。
次の式を実行するとどうなるでしょうか。
$ fl7 '(5 | [_ .. 0])[1]'
パイプ演算子はストリーマを返すものという説明だったので、ストリーマに対して後置[1]
をしているので、今までに説明されていない挙動になりそうです。
しかし、実際には4
が返されます。
$ fl7 '(5 | [_ .. 0])[1]'
4
これは[5 .. 0][1]
の結果です。
ストリーマは、左辺が非ストリーマのとき、右辺を即時適用して、そのままストリーマで包み直すことなく返す性質があります。
この性質はパイプ演算子をストリーマへのマッピング処理ではなく、即席の変数代入として使う場合に役立つでしょう。
レッスン6 パイプ変数への代入
前項では|
の左辺が非ストリーマの場合、左辺の値がそのまま右辺に渡されると説明しました。
では、左辺がストリーマであってもそのまま右辺に渡したい場合はどうすればよいでしょうか。
その場合は、|
の左辺で:
の代わりに=
を使った割り当てを行うことでできます。
$ fl7 's = 1 .. 5 | [s]'
1,2,3,4,5
この場合も、右辺を計算した後にストリーマで包み直すことなく、そのまま右辺の結果が得られます。
レッスン7 九九表の出力
これまでに得た知識を使って以下の文字列を出力してみましょう。
1,2,3,4,5,6,7,8,9
2,4,6,8,10,12,14,16,18
3,6,9,12,15,18,21,24,27
4,8,12,16,20,24,28,32,36
5,10,15,20,25,30,35,40,45
6,12,18,24,30,36,42,48,54
7,14,21,28,35,42,49,56,63
8,16,24,32,40,48,56,64,72
9,18,27,36,45,54,63,72,81
全体は9要素のストリーマになっています。
まずこれを満たすために、次のように書いてみます。
$ fl7 '1 .. 9'
1
2
3
4
5
6
7
8
9
見事、9行の出力が得られました。
しかし、ストリーマの要素は数値そのものではなく、要素が9個の配列です。
そこで、数値ではなく9要素の配列を出力するように変更してみます。
$ fl7 '1 .. 9 | [1 .. 9]'
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
1,2,3,4,5,6,7,8,9
9×9の大きさのテーブルになりました。
あとは各セルの値が正しくないといけません。
各セルの値は今内側の範囲演算子がもたらす整数になっていますが、これは外側の値とかけられなければなりません。
そこで、各セルの値を変更するようにパイプを書いてみます。
$ fl7 '1 .. 9 | [1 .. 9 | _ * _]'
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
1,4,9,16,25,36,49,64,81
変なものが得られました。
_
は内側の1 .. 9
のそれぞれの値、つまり横の位置です。
各セルの値は横の位置である_
の自乗になりました。
横の位置と縦の位置の積になるように、片方は縦の位置を参照するように変更してみます。
$ fl7 'a: 1 .. 9 | [1 .. 9 | a * _]'
1,2,3,4,5,6,7,8,9
2,4,6,8,10,12,14,16,18
3,6,9,12,15,18,21,24,27
4,8,12,16,20,24,28,32,36
5,10,15,20,25,30,35,40,45
6,12,18,24,30,36,42,48,54
7,14,21,28,35,42,49,56,63
8,16,24,32,40,48,56,64,72
9,18,27,36,45,54,63,72,81
見事、九九表のようなものが出来ました。
空白を除くとfl7 'a:1..9|[1..9|a*_]'
という23文字で書けます。
fluorite-7は、このように短い記述で反復処理を記述することができます。
まとめ
- パイプ演算子
|
は左辺のストリーマの各要素を右辺に適用したストリーマを返す。 - パイプを連結すると左から順に処理を適用していく。
- パイプの右辺でストリーマを返すと平坦なストリーマになる。
-
var: streamer | formula
で変数名をvar
に変更できる。 - 左辺が非ストリーマの場合、返される値は右辺の結果そのもの。
-
var = value | formula
で変数var
に値を代入して右辺を評価する。 - 九九表を
a:1..9|[1..9|a*_]
で出力できるワンライナーでの反復処理に特化した言語機能。