思考錯誤

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

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

今回は 4.6 関数定義に対するデザインレシピについて見ていきます。
難しい内容(少なくともぼくにとっては)のため、今回はこの節のみ見ていきます。

では始めましょう。

関数定義に対するデザインレシピ

デザインレシピとはなんでしょうか。ぼくはこの言葉を初めて聞きました。
デザインレシピとは、プログラム作成時に従うべき指針となるもののことのようです。

関数定義に対するデザインレシピは下記です。

目的

  • 作成する関数が何をするものかを考えます
  • 何を受け取り、何を返すのかを考え、関数の型を決定します
  • これをもとに関数の出だしを作成します

  • 関数の動きを具体的にします
  • そのためには、関数に望まれる入力と出力の例を作成します
  • それを実行可能なプログラムにします

本体

  • 関数の本体を作成します
  • 目的の段階では関数が「何を」するのか考えましたが、ここではそれを「どうやって」実現するかを考えます

テスト

  • 望む動作をしているか確認します
  • で作ったテストプログラムを使って確認します
  • 望む動作をしていなかったら、このデザインレシピに沿って原因を考え、誤りがあれば修正します

準備

関数を作り始める前に、kyuyo.ml というファイルを用意します。
そうです、今回から外部ファイルを使ってプログラムを書いていくのです。

ファイルが用意できたら、適当なエディタで kyuyo.ml を開きます。

デザインレシピをに従って関数を作るときには、必ずファイルを開いてそこに関数を作ります。そして、実行するときにはそのファイルを読み込むようにします。
こうしておくと、プログラムに間違いがあったときなどにも簡単に訂正して読み込み直すことができます。

やってみる

関数のヘッダを作る

最初に考えることは、作成する関数の目的です。
これから作ろうとしている関数は「働いた時間に応じたアルバイト代を計算すること」とします。

まず関数について、下記のようなルールを定めます。

  • 関数名は kyuyo とする
  • 働いた時間もアルバイト代も整数を使う(よって、関数の型は int -> int となる)
  • 働いた時間を x という変数で表す

これらの情報はヘッダにまとめます。
次のように書きます。

(* 目的: 働いた時間 x に応じたアルバイト代を計算する *)
(* kyuyo: int -> int *)
let kyuyo x = 0

このカッコ内 (* 米 *) はコメントです。
また、OCaml インタプリタに直接プログラムを書いていたときは ;; が必要でしたが、ファイルに関数を書く場合は不要です。

さて、これで関数のヘッダができました。

例(テスト)を作る

OCaml インタプリタ#kyuyo 25 ;; などといちいちテストを実行して行くのでは面倒です。
なので、これらを実行可能なテストプラグラムとしてまとめて書きます。具体的には以下のように書きます。

(* テスト *)
let test1 = kyuyo 25 = 23850
let test2 = kyuyo 28 = 26700
let test3 = kyuyo 31 = 29550

ここで1 行目はカッコを使うと let test1 = (kyuyo25 = 23850) となります。
まぎらわしいですが、まず最初の = は変数 test1 を定義するもので、二つ目の = は値が正しいかどうか確かめるための演算子となっています。

関数が正しく動作している場合は、これらのテストはすべて true となります。

実行(1)

kyuyo.ml を保存し、OCaml インタプリタから読み込んでみましょう。
読み込みには、次のように #use という命令を使用します。

// kyuyo.ml の存在するディレクトリに移動する
# #use "kyuyo.ml" ;;

// 実行結果
val kyuyo : 'a -> int = <fun>
val test1 : bool = false
val test2 : bool = false
val test3 : bool = false

テスト結果がすべて false となってしまいました。   これは、まだ本体を作っておらず、いつも 0 を返すようにしているからです。

ちなみに、先に関数の本体として型の合う適当な値を入れたのは、ここでテストを実行できるようにするためです。

さて、この先は目標ができます。
テストの結果をすべて true にすれば良いのです。

テストはプログラムの正当性を完全に保証するものではありませんが、テストを行っていないプログラムよりは信頼性は格段に高くなります。ですから入出力の例を作ったら必ずそれをテストプログラムとして書いておきましょう。

関数を完成させる

それでは関数を完成させ、プログラムを完成させましょう。

最終的なプログラム( kyuyo.ml) の内容は下記です。

// ヘッダここから
// プログラムの中身を見なくてもこのヘッダを見れば、何をするプログラムかわかります。

(* 目的: 働いた時間 x に応じたアルバイト代を計算する *)
(* kyuyo: int -> int *)


// 本体ここから

(* 時給(円)*)
let jikyu = 950

(* 基本給(円)*)
let kihonkyu = 100

(* 計算 *)
let kyuyo x = kihonkyu + x * jikyu


// テストここから

(* テスト*)
let test1 = kyuyo 25 = 23850
let test2 = kyuyo 28 = 26700
let test3 = kyuyo 31 = 29550

実行(2)

プログラムが出来上がったらいよいよテストです。

# #use "kyuyo.ml" ;;
val jikyu : int = 950
val kihonkyu : int = 100
val kyuyo : int -> int = <fun>
val test1 : bool = true
val test2 : bool = true
val test3 : bool = true

まとめ

以上がデザインレシピを使ったプログラムの書き方となります。
この考え方は、作っているプログラムの種類やプログラミング言語にかかわらず使用することができます。

これで第4章は終了です。 次回は第5章 条件分岐 に進みます。