はじめに
この連載ではプログラミング言語Mokkosuを基礎から順番に説明していきます。「プログラミング言語Mokkosuって何?」という方は以下の記事をご覧ください。
ここではMokkosuの最新版をインストール済みで、プログラムの実行方法をすでに知っていることを前提に解説を行います。
式の計算
Mokkosuではプログラムは式の形で表現し、式を計算して値を求めることがプログラムの実行に相当します。
最初はMokkosuに簡単な数式の計算をさせてみましょう。以下は $1 + 2 \times 3$ を計算して表示するMokkosuのプログラムです。
println (1 + 2 * 3);
プログラムをコンパイルして実行すると出力ウインドウに
7
と表示されます。
Mokkosuでは数式を以下の演算子を使って表現します。
| 数学 | Mokkosu |
|---|---|
| $x + y$ | x + y |
| $x - y$ | x - y |
| $x \times y$ | x * y |
| $x \div y$ | x / y |
| $x \bmod y$ | x % y |
| $x ^ y$ | x ** y |
| $-x$ | ~-x |
上に挙げた演算子は整数に関する演算子です。整数に関する演算子の結果は整数になります。例えば、5 / 2の計算結果は2.5ではなく2になります。数学と同じように、掛け算や割り算は足し算や引き算よりも先に計算されます。優先度を変えたい場合は部分式を以下のように括弧で囲みます。
println ((1 + 2) * 3);
上のプログラムを実行すると出力ウインドウに、
9
と表示されます。
Mokkosuではプログラムを式の形で表現すると書きました。例えば1 + 2 * 3は式ですが、実はprintln (1 + 2 * 3)も式です。ここでprintlnは関数です。printlnは引数を受け取って、その値を表示して、ユニットとよばれる値を返します。ユニットは()のように表記し、結果に特に意味がないことを表します。
トップレベルの式は必ずユニットを返す必要があります。例えば以下の式はコンパイルエラーになります。
1 + 2 * 3; # 計算結果が()にならないのでコンパイルエラー
Mokkosuでは関数を呼び出す際に引数に括弧は必要ありません。例えば以下は正しいMokkosuのプログラムです。
println 42;
しかし以下のプログラムはエラーです。
println 1 + 2;
Mokkosuの処理系はこのプログラムを見ると、println (1 + 2)の意味ではなく、(println 1) + 2の意味であると解釈します。ユニット値に整数の2を足すことはできないのでコンパイル時にエラーになります。
関数の定義
式の計算と関数の呼び出し方が理解できたところで、次は関数の定義の仕方を学びましょう。もっとも簡単な関数の定義の仕方は、入力と出力の対応を記述することです。以下は引数の値を2倍する関数fの定義例です。
fun f = { 1 -> 2; 2 -> 4; 3 -> 6; 4 -> 8; 5 -> 10 };
この関数を使うには以下のプログラムを実行します。
fun f = { 1 -> 2; 2 -> 4; 3 -> 6; 4 -> 8; 5 -> 10 };
println (f 3);
上のプログラムを実行すると出力ウインドウに
6
と表示されます。
上の関数の定義では、引数が1から5の範囲の場合は定義されていますが、それ以外の場合は定義されていません。上のやり方だと整数全体に対して定義するのは現実的ではありません。引数の値を2倍する関数は変数を使うともっと簡潔に表現できます。
以下は変数を使った関数fの定義です。
fun f = { x -> x * 2 };
これでいちいち値を列挙する必要がなくなりました。値の列挙と変数を使った定義は組み合わせることができます。以下の関数gは引数の値が0の場合は0を返し、それ以外の値の場合は1を返します。
fun g { 0 -> 0; x -> 1 };
上の0 -> 0とx -> 1の順番を入れ替えることはできません。Mokkosuの処理系は左側に書かれたものから順番にマッチするか試していきます。変数はあらゆる値とマッチするので入れ替えた場合はどんな値をあたえても1を返す関数になります。
上の関数では引数にxと名前を付けていますが、この変数xは->の右側で使われることはないのでわざわざ名前を付けるまでもありません。特定の名前が必要ない場合は、変数の代わりに_と書くことができます。以下は上の関数定義を_を使うように書き換えた例です。
fun g = { 0 -> 0; _ -> 1 };
特定の値の時に返す値を切り替える方法は分かりましたが、例えば引数の値が0以上の場合など条件に応じて切り替えるにはどうすれ良いのでしょうか。その場合は、? 条件という記述を追加します。以下の関数hは引数が正の数の場合は1を負の数の場合は~-1をゼロの場合は0を返す関数の定義例です。
fun h = {
x ? x > 0 -> 1;
x ? x < 0 -> ~-1;
0 -> 0
};
条件は以下の演算子を使って記述します。
| 意味 | Mokkosu |
|---|---|
| より小さい | x < y |
| より大きい | x > y |
| 以下 | x <= y |
| 以上 | x >= y |
| 等しくない | x <> y |
| 等しい | x == y |
| かつ | x && y |
| または | x || y |
| でない | not x |
例えばxは3以上かつ6未満という条件は以下のようになります。
x >= 3 && x < 6
これまで関数にはfやgといった名前を付けてきましたが、関数に名前は必ずしも必要ではありません。以下のプログラムでは引数の値を2倍する関数を名前を付けずに使っています。
printfn ({x -> x * 2} 3);
実行すると出力ウインドウに
6
と表示されます。
変数の利用
let文を使うと、値に名前を付けることができます。以下は2 + 3の計算結果にxと名前を付けています。このようなxのことを変数とよびます。
x = 2 + 3;
変数xはそれ以降のプログラムで利用できます。
let x = 2 + 3;
println x;
上のプログラムを実行すると出力ウインドウに、
5
と表示されます。
同じ名前の変数に値を割り当てると前の変数が隠ぺいされます。
let x = 10;
let x = 20;
println x;
上のプログラムを実行すると出力ウインドウに、
20
と表示されます。
再帰
関数の定義の中で自分自身を呼び出すことがができます。このような呼び出しのことを再帰呼び出しとよびます。以下の関数factは自分自身を呼び出すことで階乗 $n!$ の計算をします。
fun fact = { 0 -> 1; n ? n > 0 -> n * fact (n - 1) };
階乗は引数の値が0のときは1、0以上の整数nのときはnに自分自身をn - 1で呼び出したものを掛け合わせた値になります。上の定義ではこの事実をそのまま式として表現しています。
再帰を使うとMokkosuでできることの幅が広がります。再帰は慣れるまで使い方が難しいかもしれませんが、いろいろな例を試してぜひ習得してください。
他の再帰の例をみてみましょう。以下はフィボナッチ数列のn項目を計算する関数fibの定義して、fib 10の値を求めるプログラム例です。
fun fib = {
0 -> 0;
1 -> 1;
n -> fib (n - 1) + fib (n - 2)
};
println (fib 10);
実行すると出力ウインドウに、
55
と表示されます。
タプル
タプルは複数の値をまとめたデータ構造です。例えば、整数の2と整数の3をまとめてタプルにすると、
(2, 3)
という風になります。
タプルを使うと、関数に複数の値を同時に渡すことができるようになります。
以下は2つの引数を受け取って、それらの和を返す関数addを定義して利用する例です。
fun add = { (x, y) -> x + y };
println (add (3, 4));
実行すると出力ウインドウに、
7
と表示されます。
タプルを使うと複数の値を返す関数も定義できます。
以下の関数addmulは2つの引数を受け取って、それらの和と積を同時に返します。
fun addmul = { (x, y) -> (x + y, x * y) };
この関数を実際に呼び出すには以下のようにします。
let (a, b) = addmul (3, 4);
println a;
println b;
実行すると出力ウインドウに、
7
12
と表示されます。
3引数以上の関数も、2引数の場合と同様に定義できます。
練習問題
問題1
$(10 + 2) \div -3$ を計算して結果を出力ウインドウに表示するプログラムを書いてください。
問題2
引数として税抜き価格を与えると税込み価格を返す関数を定義してください。
問題3
$n$を引数としたとき、$2^n$を計算する関数を定義してください。
問題4
$m^n$を計算する関数を定義してください。
問題5
2要素のタプルを受け取って、値が大きい方の要素を返す関数を定義してください。
問題6
3要素のタプルを受け取って、値が最大の要素を返す関数を定義してください。
問題7
2つの整数の最大公約数を求める関数を定義してください。
問題8
2つの整数の最小公倍数を求める関数を定義してください。
問題9
$n$番目の素数を返す関数を定義してください。
問題10
$n$番目の双子素数をタプルで返す関数を定義してください。