←前回 [次回→](
レッスン1 分岐
fluorite-7には予約語が存在しないため、if文もありません。
その代わり、論理系の演算子は無意味になる項の評価を行わないことが保証されており、これを条件分岐に使います。
形 | 名称 | 効果 |
---|---|---|
a? b: c |
三項演算子 | aが真ならb、偽ならc |
a&& c |
論理積演算子 | aが真ならb |
a|| c |
論理和演算子 | aが偽ならb |
a?: c |
エルビス演算子 | aがNULLならb |
a!: c |
逆エルビス演算子 | aが非NULLならb |
論理系演算子の左辺の値によって、評価すると出力を行うOUT
関数が働かないことに注目してください。
$ fl7 'TRUE ? OUT(1) : OUT(10); FALSE ? OUT(2) : OUT(20);'
1
20
$ fl7 'TRUE && OUT(1); FALSE && OUT(2);'
1
$ fl7 'TRUE || OUT(1); FALSE || OUT(2);'
2
$ fl7 'TRUE ?: OUT(1); FALSE ?: OUT(2); NULL ?: OUT(3);'
3
$ fl7 'TRUE !: OUT(1); FALSE !: OUT(2); NULL !: OUT(3);'
1
2
条件分岐は、適宜次のように括弧やスペース等で整形するのが見やすいかもしれません。
TRUE ? (
OUT(1);
) : (
OUT(10);
);
TRUE && (
OUT(1);
);
これは、一般的なプログラミングにおける次のような記述に相当します。
if (true) {
OUT(1);
} else {
OUT(10);
}
if (true) {
OUT(1);
}
レッスン2 範囲ループ
fluorite-7にはif分岐同様、ループ用のwhile・forなどの文がありません。
その代わり、パイプ演算子|
で同様のループを行います。
範囲演算子と組み合わせると、一定の範囲のループを簡潔に記述できます。
$ fl7 '1 .. 5 | OUT(_ * 100);'
100
200
300
400
500
これも、ループ本文の内容によっては次のように整形すると見やすいでしょう。
1 .. 5 | (
OUT(_ * 100);
);
レッスン3 無限ループ
無限ループを行うには、LOOP
定数を用いると良いでしょう。
LOOP
定数は、NULLを無限個生成するストリーマです。
次の例では、途中で強制終了しない限り無限にNULLという出力を行い続けます。
$ fl7 'LOOP | OUT(_);'
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
NULL
^CNULL
また、範囲ループの終端をINFINITY
や-INFINITY
にすると、無限にカウントするループを構築できます。
$ fl7 '1 .. -INFINITY | _'
1
0
-1
-2
-3
-4
-5
-6
^C-7
レッスン4 whileループ
WHILE
関数を使用すると、ループごとに判定される条件によってループの継続および中断を制御できます。
$ fl7 'a: 0; WHILE(() -> a < 10) | (OUT(a * 100); a = a + 1;);'
0
100
200
300
400
500
600
700
800
900
a: 0;
WHILE(() -> a < 10) | (
OUT(a * 100);
a = a + 1;
);
これはJavaScriptでいう次のコードに相当します。
var a = 0;
while (a < 10) {
OUT(a * 100);
a = a + 1;
}
上のコードは、第2引数を取るWHILE
関数を使うバージョンに書き換えることができます。
$ fl7 'a: 0; WHILE(() -> a < 10; () -> a = a + 1) | (OUT(a * 100););'
0
100
200
300
400
500
600
700
800
900
a: 0;
WHILE(() -> a < 10; () -> a = a + 1) | (
OUT(a * 100);
);
WHILE
関数に対して条件式を逆に解釈するUNTIL
関数もあります。
$ fl7 'a: 0; UNTIL(() -> a >= 10; () -> a = a + 1) | (OUT(a * 100););'
0
100
200
300
400
500
600
700
800
900
レッスン5 forループ
FOR
関数は、初期値を返す関数、変数を受け取るとループを継続するか否かを判定する関数、変数を受け取ると更新後の値を返す関数を与えてループ用のストリーマを生成する関数です。
$ fl7 'i: FOR(() -> 10; i -> i < 20; i -> i + 1) | (OUT("[ $i ]"););'
[ 10 ]
[ 11 ]
[ 12 ]
[ 13 ]
[ 14 ]
[ 15 ]
[ 16 ]
[ 17 ]
[ 18 ]
[ 19 ]
i: FOR(() -> 10; i -> i < 20; i -> i + 1) | (
OUT("[ $i ]");
);
多くのプログラミング言語に搭載されているfor文に近い書き方でループを作ることができます。
単純なカウントアップであれば、多くの場合はFOR
関数ではなく範囲演算子を使った方がより簡潔に書けるでしょう。
例えば、上記のFOR(() -> 10; i -> i < 20; i -> i + 1)
は10 ~ 20
と書けます。
レッスン6 エルビスパイプ演算子
中置?|
は、非NULL値のみを通過させる逆エルビスパイプ演算子というパイプ演算子の一種です。
中置!|
はその逆で、NULL値のみを通過させるエルビスパイプ演算子です。
エルビス系パイプは、次のようなパイプと三項演算子の組み合わせと概ね同じ動きをします。
サンプル | 同等の記述 | |
---|---|---|
逆エルビス | a ?| b |
a | _ !== NULL ? b : () |
エルビス | a !| b |
a | _ === NULL ? b : () |
エルビス系パイプはエルビス演算子に近く、非NULLであれば偽の値も真値と同じ扱いをします。
$ fl7 '1 ?| _ * 10'
10
$ fl7 '0 ?| _ * 10'
0
$ fl7 'NULL ?| _ * 10'
$ fl7 '1 | _ !== NULL ? _ * 10 : ()'
10
$ fl7 '0 | _ !== NULL ? _ * 10 : ()'
0
$ fl7 'NULL | _ !== NULL ? _ * 10 : ()'
なぜ?:
がエルビス演算子であるのに対して、?|
の方が逆エルビスパイプ演算子と呼ばれるのでしょうか。
それは、右辺を評価する条件がエルビス系演算子と逆であるためです。
$ fl7 '(1 ?: 10), (NULL ?: 20)'
1
20
$ fl7 '(1 !: 10), (NULL !: 20)'
10
NULL
$ fl7 '(1 ?| 10), (NULL ?| 20)'
10
$ fl7 '(1 !| 10), (NULL !| 20)'
20
この違いは、エルビス演算子?:
を構成する:
の起源に由来します。
エルビス演算子a ?: b
は元々a !== NULL ? a : b
をまとめたものでした。
逆エルビスパイプ演算子a ?| b
はa | _ !== NULL ? b : ()
をまとめたものです。
エルビス演算子?:
には:
があるため、右辺は三項演算子の偽の節に対応しますが、逆エルビスパイプ演算子?|
には:
がないため、右辺は三項演算子の真の節に対応するのです。
レッスン7 switch文
fluorite-7には一般的なプログラミング言語におけるswitch文に相当する文法がないため、同様な処理を行うにはパイプと三項演算子を組み合わせて使用します。
3 | (
_ === 1 ? (
"one"
) : _ === 2 ? (
"two"
) : _ === 3 ? (
"three"
) : (
"default"
)
)
レッスン8 break文
中置:=
はbreak演算子です。
break演算子は、ラベル演算子:
と組み合わせて使います。
$ fl7 'a: (OUT(1); a := 10; OUT(2);)'
1
10
a: (
OUT(1);
a := 10;
OUT(2);
)
ラベル演算子:
は、右辺の式を左辺のラベルとして定義し、break演算子:=
では左辺に示すラベルに対して、右辺の値を強制的に置換します。
ラベル演算子やbreak演算子は式であるため、次のように式の途中の項を表現するのに丸括弧で囲んで注入することができます。
$ fl7 '1 + (a: (a := 10; 1000)) + 100'
111
1 + (
a: (
a := 10;
1000
)
) + 100
複式a := 10; 1000
では、a := 10
がなければ末尾の式1000
の値を返そうとしますが、break演算子a := 10
があるため、複式を途中で抜けています。
この10
は、a: (a := 10; 1000)
の値として使われます。
この挙動は例外処理に使われるthrow-catchと似ていて、break演算子でラベルに対して値を投げて、ラベル演算子でそれをキャッチして返しているようなものです。
break演算子は使用可能な宣言済みのラベルに対してしか使用できません。
また、ラムダ演算子などの一部の演算子の項の中から外に向けてbreak演算子を使うことはできません。
しかし、文レベルのパイプ演算子|
を含む多くの演算子はラベルの使用を阻害しません。
$ fl7 '1 + (a: (1 .. INFINITY | (_ === 50 && (a := 10;););)) + 100'
111
1 + (
a: (
1 .. INFINITY | (
_ === 50 && (
a := 10;
);
);
)
) + 100
この例では、break演算子は分岐&&
およびループ|
を抜けています。
ループの終端が無限大になっていることに注意してください。
break演算子を使うと、このような文レベルのパイプ演算子を使った無限ループを抜け出すことができます。
ラベル演算子と変数宣言演算子
ラベル演算子:
と変数宣言演算子:
は非常に見た目が似通っていますが、それらは混同してください。
実際、両者はまったく同一のものです。
次のように、同じ:
記号をラベルかつ変数宣言として兼用することができます。
$ fl7 'a: (a := 10; 1000); a + 1'
11
a: (
a := 10;
1000
);
a + 1
レッスン9 return文
break演算子:=
から左辺を除いたものはreturn演算子:=
です。
break演算子との違いは、ラベルを指定する必要がない代わりに、自動的に最も内側の関数を抜け出すことです。
$ fl7 'f: () -> (:= 10; 1000); f()'
10
f: () -> (
:= 10;
1000
);
f()
まとめ
- 論理系演算子を条件分岐に使える。
- パイプ系演算子をループに使える。
- 中置
:=
で:
でラベルを付けた式を脱出できる。 - 前置
:=
で関数を脱出できる。
Part 14
まだない。