このパートでは、関数を生成して名前を付けることを扱います。
レッスン1 変数
fluorite-7では、変数名: 値; 式
という形で変数を宣言し、使用します。
$ fl7 'x: 10; x * 7'
70
var: val
は変数を宣言しつつ代入を行います。
fluorite-7では、変数は宣言時に必ず代入を行う必要があります。
変数はいくつでも宣言できます。
$ fl7 'x: 10; y: x * 7; y * 10'
700
レッスン2 変数のスコープ
変数は、宣言された部分より後の箇所からのみ利用できます。
また、括弧で囲われた内部で宣言された変数は、外部からは見えません。
次の式では括弧の中で宣言されたx
を括弧の外側から参照して、ストリーマを生成しようとしていますが、括弧の外ではx
は未定義の識別子になるため、同内容の文字列がそのまま現れています。
$ fl7 '(x: 10; 5), x'
5
x
この場合、式を変形してx
の宣言を括弧から取り外すことでx
が見えるようになります。
$ fl7 'x: 10; 5, x'
5
10
レッスン3 変数に代入できる値
変数には、式の結果であれば何でも代入できます。
fluorite-7では変数に型が存在しません。
$ fl7 'x: [123, 456, 789]; x[2], x[1], x[0]'
789
456
123
同様にストリーマを変数に入れることもできます。
$ fl7 'x: 1 .. 5; [x], x'
1,2,3,4,5
1
2
3
4
5
変数に入れられたストリーマは、使われるたびに同じ順序で値を返却します。
つまり、ストリーマは何度でも使いまわすことができます。
これが、ストリーマが単に「ストリーム」と呼ばれていない理由です。
レッスン4 関数の生成
fluorite-7では、「引数名->
式」という形で関数を生成できます。
この演算子をラムダ演算子と呼びます。
$ fl7 'x -> x * 10'
[FluoriteFunction]
x
でも10
でもxxxxxxxxxx
とかでもなく、[FluoriteFunction]
という何かよくわからない文字が出ました。
関数を出力しようとすると、[FluoriteFunction]
と出力されます。
これだけでは何が生成されたのか分からないため、次のレッスンに進みましょう。
レッスン5 関数の適用
関数は、「関数(
引数)
」という形で使用します。
$ fl7 '(x -> x * 10)(7)'
70
「xを与えたらxを10倍して返す関数」に対して、7を与えました。
関数は7をxとして受け取り、関数の本文ではxで表される7を10倍にして返しました。
関数から帰ってきた70がソースコードの値となり、70が出力されました。
レッスン6 関数への命名
fluorite-7の関数は、「値」の一種です。
そのため、変数に入れることができます。
$ fl7 'f: x -> x * 10; f(7)'
70
変数f
に対してx -> x * 10
で表される関数を割り当てて、fに格納された関数をf(7)
という形で呼び出しています。
fluorite-7では、変数に入れられて名前で呼ぶことができるようになった関数が名前付き関数です。
逆に言うと、関数自体には名前はなく、変数に入れることで初めて名前で呼べるようになります。
レッスン7 高階関数
関数は引数として値を受け取れます。
関数は値の一種です。
そのため、関数に関数を渡すこともできます。
$ fl7 'doubleFunc: f -> x -> f(f(x)); doubleFunc(x -> x * 10)(7)'
700
いきなり複雑になりました。
整形してみます。
doubleFunc: f -> (
x -> f(f(x))
);
doubleFunc(x -> x * 10)(7)
関数doubleFunc
は、関数f
を受け取る関数で、戻り値はx -> f(f(x))
で表される関数です。
x -> f(f(x))
は何かというと、値x
を受け取ったら、関数f
を2回適用して返す関数です。
ということは、doubleFunc
は「値を入れたら値を加工して返す関数」で呼び出すことができ、それを更に「値」で呼び出すことができます。
それがdoubleFunc(x -> x * 10)(7)
です。
x -> x * 10
は「値を入れたら値を10倍して返す関数」で、doubleFunc(x -> x * 10)
は「値を入れたら値を10倍してまた10倍して返す関数」です。
それに7を入力しているので、7を2回10倍した700が得られます。
レッスン8 複数の引数
関数は引数を0個や複数個取ることもできます。
$ fl7 'f: () -> 10; f, f()'
[FluoriteFunction]
10
$ fl7 'f: (x; y) -> x * y; f, f(4; 5)'
[FluoriteFunction]
20
関数の引数は、カンマ,
ではなくセミコロン;
で区切ります。
列挙演算子,
で区切って並べたものは一つの値であるストリーマです。
ストリーマを受け取る関数に対しては、ストリーマの要素を,
で区切って与えます。
$ fl7 'sum: _ -> +_; sum(4, 6)'
10
レッスン9 関数の引数の書き方
さて、ラムダ演算子の左辺の書き方は少々特殊です。
以下のものはすべて同じ意味です。
(x; y) -> x * y
(x, y) -> x * y
x, y -> x * y
まず、ラムダ演算子の左辺は,
区切りでもかまいません。
また、引数が0個の場合を除いて、それを取り囲む括弧は省略できます。
これにより、関数の宣言部分では括弧をすべて省略することができます。
$ fl7 'mul: a, b -> a * b; mul(4; 6)'
24
関数に関連する演算子の結合優先度は、以下の通りです。
- ↑高優先度
- その他の算術演算子
+
-
等 - 列挙演算子
,
- ラムダ演算子
->
- 変数宣言演算子
:
- 文の区切り
;
- ↓低優先度
これにより、まず関数の本文の列挙要素が一番先に結合し、その次に列挙部分が結合し、そのあとで関数になり、そして変数になり、そのあとで;
によって変数宣言が完了します。
実はラムダ演算子の左辺にもこの優先度が適用されています。
そのため、,
で区切られた引数の羅列のあとに->
が適用され、括弧が不要となります。
レッスン10 クロージャ
記述された場所から参照できる変数を本文の中から参照することができる関数をクロージャと呼びます。
fluorite-7の関数はすべてクロージャです。
$ fl7 '(x: 10; () -> x)()'
10
関数を使うと、変数をスコープ外から参照できます。
レッスン11 再帰関数
実は変数宣言演算子:
で宣言した変数は、:
の右辺からもアクセスすることができます。
これを使うと、再帰関数を作ることができます。
$ fl7 'f: n -> n === 0 ? 1 : n * f(n - 1); f(6)'
720
f: n -> n === 0
? 1
: n * f(n - 1);
f(6)
これは階乗を計算する再帰関数です。
a === 0 ? b : c
の意味は、a
が0であればb
を使用し、そうでなければc
を使用するという意味です。
まとめ
- 変数を宣言するには
var: val;
と書く。 - 括弧の中でも変数を宣言できる。
- 変数はそれを取り囲むもっとも内側の括弧の以降の部分から利用できる。
- ストリーマを何度も使うことができる。
- 関数は
args -> formula
と書く。 - 変数に関数を代入すると名前付き関数として使える。
- 関数に関数を渡せる。
- 関数呼び出しの引数の区切りは
;
、関数生成の引数の区切りは;
または,
。 - 関数内から関数外の変数を呼べる。
- 宣言された関数内からその関数自身を呼べる。