思考錯誤

すべてはどうでもよくなる

「プログラミングの基礎」読みほぐし④

sotus.hatenablog.com

上記記事の続きです。

4.3 関数の型

Ocaml は強く型付けされており、プログラム中で出てくるデータのすべてにデータ型がついています。

この性質は関数にも適用されます。関数にもすべて型がついているのです。
例えば、次の関数を定義した際のインタプリタの返答を見ていきましょう。

# let f x = 3 * x ;;
val f : int -> int = <fun>
// f というint -> int 型の関数を定義しました。その値は関数です。
//(という意味)

この例で言うと、関数の型は「int -> int 型」となります。

関数の型は、A -> B のように書き、これで「A型の値を受け取ったらB型の値を返すような関数の型」を表します。

引数が2つ以上ある次のような場合はどうでしょう。

# let g x y = x * x + y * y - 4 ;;
val g : int -> int -> int = <fun>

引数が x と y と、2つあります。
この場合、関数 g は 「int -> int -> int 型」の関数となり、A -> B -> C すなわち「ひとつ目の引数としてA型の値を受け取り、ふたつ目の引数としてB型の値を受け取る。結果、C型の値を返す関数。」となります。

ちなみに、 = <fun> は定義された変数が関数であることを示しています。通常の変数を定義した場合には値がそのまま表示されますが、関数の場合は中身が概して大きくなりがちなためです。

4.4 型推論と型チェック

Ocaml インタプリタは、ぼくらの見えないところで以下の2つのことを行ってくれています。

  1. 型推論
  2. 型チェック
型推論

関数を定義した際に、式で使用されている値をもとに関数の型を決めることをいいます。

型チェック

期待される正しい型のデータが使われているかチェックすることをいいます。

強く型付けされた言語では、この型チェックによってプログラムの信頼性を大きく向上させることができるようですね。
現レベルではそのようなメリットを享受できないかもしれませんが、自分でプログラムを作るようになると、そのありがたさを痛感するようになるようです。

4.5 関数の実行方法

変数を使ったプログラムがどのように実行されるかは、前々回くらいに見ていきましたが、関数は引数があるため多少複雑になります。

さて、実際に例を見ていきましょう。

# let jikyu = 950 ;;
val jikyu : int = 950
# let kihonkyu = 100 ;;
val kihonkyu : int = 100
# let kyuyo x = kihonkyu + x * jikyu ;;
val kyuyo : int -> int = <fun>
#

上記の宣言をしたあとに、次の計算を実行したとします。

# kyuyo 25 + kyuyo 28 + kyuyo 31

まず、Ocaml インタプリタkyuyo 25 を実行しようとします。ここで、一発目の関数呼び出しが行われます。

関数が呼び出されたときは次の2つのことが行われます。

  • 関数呼び出しを呼びだされた関数の中身で置き換える
  • 置き換えられた関数の中身のうち、引数を実引数で置き換える

ここでいう、実引数とは、関数呼び出しをしたときに渡された値のことです。 たとえば kyuyo 25 という関数呼び出しでは実引数は 25 となります。

その後は次のように実行されます。

(kihonkyu + x * jikyu) + kyuyo 28 + kyuyo 31 // 関数が置き換わる
(kihonkyu + 25 * jikyu) + kyuyo 28 + kyuyo 31 // x が実引数に置き換わる
(100 + 25 * jikyu) + kyuyo 28 + kyuyo 31 // 変数の実行過程と同様
(100 + 25 * 950) + kyuyo 28 + kyuyo 31
(100 + 23750) + kyuyo 28 + kyuyo 31
23850 + kyuyo 28 + kyuyo 31

// 2度目の関数呼び出し

23850 + (kihonkyu + 28 * jikyu) + kyuyo 31
23850 + (100 + 28 * jikyu) + kyuyo 31
23850 + (100 + 28 * 950) + kyuyo 31
23850 + (100 + 26600) + kyuyo 31
23850 + 26700 + kyuyo 31
50550 + kyuyo 31

// 3度目の関数呼び出し

50550 + (kihonkyu + 31 * jikyu)
50550 + (100 + 31 * jikyu)
50550 + (100 + 31 * 950)
50550 + (100 + 29450)
50550 + 29550
80100 // 解

このように、Ocaml の関数は非常に数学の関数と近しいものとなっています。 数学の関数ってなんでしたっけ?

ちなみに、C などの命令形プログラミング言語にも関数という概念がありますが、それと Ocaml の関数ではまったくの別物となります。

続く

続きます。ハァ、ハァ・・・

次回は 4.6 関数定義に対するデザインレシピ をじっくり見ていきたいと思います。

それでは。