レッスン1 文
fluorite-7では、「評価」して「値を返却」するものを式、「動作」だけを行うものを文と呼びます。
ソースコード全体などの一部の場所に現れる式は、;
で区切って複数の部分に分けることができます。
このような複数の部分を持つ式を複式と呼びます。
複式のうち、最後の;
より右側にある部分以外はすべて文です。
文は0個以上書くことができ、式は0個か1個書くことができます。
;
は無駄に多数書いた場合、単に無視されます。
$ fl7 'x: 1; y: x + 1; y + 1'
3
$ fl7 ';;;;; x: 1;;;;; y: x + 1;;;;; y + 1'
3
レッスン2 文だけで構成された複式
複式は、戻り値の式を省略することができます。
$ fl7 'x: 1; y: x + 1;'
$ fl7 ''
戻り値の省略された複式は、空ストリーマ()
が返ります。
レッスン3 丸括弧による複式の配置
複式が直接置けない場所でも、丸括弧(
)
で囲うことで複式が置けるようになります。
次に示すものは中置*
の左辺に複式を置く例です。
$ fl7 'x: 10; x + x * 100'
1010
$ fl7 '(x: 10; x + x) * 100'
2000
(
)
がなければx + x * 100
が一つの式としてくっついてしまいますが、(
)
によってそれを防いでいます。
次に示すものは関数呼び出しの引数に複式を置く例です。
$ fl7 'f: a, b, c -> a * b * c; f(2; (x: 3; x); 5)'
30
関数呼び出しの(
)
内では、;
は関数の引数の区切りという特別な役割を持っています。
そこで、一旦(
)
で囲うことで;
が引数の区切りとして認識されるのを防ぎ、複式を置けるようにしています。
レッスン4 変数宣言文
これまでにも何度か登場しましたが、「変数名:
値;
」で変数を宣言できます。
これは変数宣言文という文の一種です。
変数宣言文は、以降の部分でその変数を名前で呼べるようにし、かつ指定された値を変数に代入します。
$ fl7 'x: 10; x'
10
レッスン5 式文
一部の式は、そのまま文として埋め込むことができます。
文として埋め込まれた式を式文と呼びます。
$ fl7 '1; 2; 3'
3
1
や2
はそのままでは評価をし、値を返却する式です。
これを文の場所に置くと、評価だけをして返された値を無視するようになります。
レッスン6 OUT関数
OUT関数は、評価されると第1引数の値を標準出力(ブラウザ版ではF12で現れる開発者モード内のコンソール)にログとして出力する関数です。
$ fl7 '1; OUT(2); 3'
2
3
文は「評価」だけを行って戻り値を無視しますが、OUT関数は「評価」されただけで処理を行うため、式文にしても効果を発揮します。
このような、評価しただけで効果を発揮するような処理を副作用のある処理と呼びます。
レッスン7 代入
中置=
は、代入演算子です。
=
の左辺が表すものに、右辺を値を代入します。
=
の左辺は、セッターとして解釈されます。
セッターとして解釈できない式は、=
の左辺に置くことはできません。
セッターは、値を代入するための箱を表します。
対して、値そのものを生成する式はゲッターと呼びます。
1 + 2
のような値を返す式はすべてゲッターです。
変数宣言文で作った変数はセッターになることができます。
$ fl7 'x: 10; x = 20; x'
20
変数に代入を行うと、その変数の値が書き換わり、以降その変数を呼び出したときに、最後に代入された値が得られるようになります。
配列の要素はセッターになることができます。
$ fl7 'a: [1, 2]; a[0] = 20; a'
20,2
配列は変数が多数集まったようなものです。
配列の定義域を外れる添え字を指定した場合、その添え字が正であれば、配列が自動的に拡張されて代入されます。
$ fl7 'a: [1, 2]; a[4] = 20; a'
1,2,NULL,NULL,20
配列の長さを得る前置$#
と組み合わせて、「配列[$#
配列] =
値」で配列の末尾に要素を追加できます。
$ fl7 'a: [1, 2]; a[$#a] = 20; a'
1,2,20
オブジェクトのプロパティはセッターになることができます。
$ fl7 'p: {a: 1}; c: p{b: 2}; c.a = 20; p, c'
{a:1}
{b:2;a:20}
p: {
a: 1
};
c: p{
b: 2
};
c.a = 20;
p, c
変数p
は親オブジェクトで、プロパティa
が定義されています。
変数c
は子オブジェクトで、プロパティb
が定義されているとともに、p
のプロパティa
がメンバとして見えています。
このとき、c
のプロパティa
に代入を行うと、c
に新たにプロパティa
が定義され、p
は書き換わりません。
オブジェクトのプロパティへの代入は、常にそのオブジェクトに対して行われ、親オブジェクトはその影響をうけません。
また、定義されていないメンバに対して代入を行うと、プロパティが新たに定義されて代入されます。
レッスン8 クロージャ内での代入
fluorite-7では、クロージャ内からクロージャ外の変数に対して代入ができます。
以下のコードを見てみましょう。
$ fl7 'f: (a: 1; [(() -> a), (b -> a = b),]); out: []; out[$#out] = f[0](); f[1](50); out[$#out] = f[0](); out[]'
1
50
f: (
a: 1;
[
( () -> a ),
( b -> a = b ),
]
);
out: [];
out[$#out] = f[0]();
f[1](50);
out[$#out] = f[0]();
out[]
コードの前半部分を抜き出してみます。
f: (
a: 1;
[
( () -> a ),
( b -> a = b ),
]
);
f
は関数(クロージャ)が2個入った配列です。
変数a
は(
)
の中に入っているので(
)
の外部からはアクセスできませんが、a
にアクセスしている2個の関数が配列にまとめられてf
に代入されています。
これにより、f
を通じて変数a
を操作することができます。
() -> a
は変数a
の値を取得する関数で、f[0]
に格納されています。
b -> a = b
は第1引数の値を変数a
に代入する関数で、f[1]
に格納されています。
コードの後半部分を抜き出してみます。
out: [];
out[$#out] = f[0]();
f[1](50);
out[$#out] = f[0]();
out[]
配列変数out
に値を格納すると、最後にそれを表示します。
out[$#out] =
は配列変数out
の末尾に要素を追加するイディオムです。
out[$#out] = f[0]();
がやっていることは、out
の最後にf[0]()
を追加しています。
f[0]
例の変数a
の値を得る関数だったので、f[0]()
は変数a
の値を表します。
結果、out
の末尾に変数a
の現在の値が入ります。
f[1](50);
は同様に変数a
に50
を代入する動作を行います。
まとめると、a
の初期値である1
をout
に追加し、a
に50
を代入し、a
の代入後の値である50
をout
に追加しています。
最終的にout
の中身は1と50となり、それをストリーマ展開して出力すると
1
50
という出力が得られます。
まとめ
- 式を
;
で区切ると複式になる。 - 最後の
;
より左は全部文で右にあるのは式。 - 複式内で式を省略すると空ストリーマが返る。
- 直接複式を置けない場所には
(
)
で囲んで置く。 - 中置
:
で構成された文は変数宣言文。 - 式を文の場所に置くと式文。
- OUT関数は式文にしても効果がある。
- 中置
=
は代入。 - 変数には代入できる。
- 配列要素には代入できる。
- 定義域を超える添え字を指定して代入すると、配列が拡張される。
- オブジェクトのプロパティには代入できる。
- 関数内から関数の外部にある変数に対して直接代入ができる。