LoginSignup
0
0

More than 3 years have passed since last update.

fluorite-7 日本語チュートリアル Part 13 制御構文

Posted at

チュートリアルトップ

←前回 [次回→](

レッスン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 ?| ba | _ !== 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

まだない。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0