Skip to content

step6 関数の定義

ここまでのステップで、数当てゲームの基本的なロジックは完成した。 しかし、現状のコードではProgram.fs のトップレベルにすべての処理が記述されており、再利用性や可読性が低い。 そこで、このステップでは F# における関数の定義方法を学び、判定ロジックを関数として切り出すことで、コードをよりモジュール化された、読みやすいものに改善する。

関数の定義方法

F#で関数を定義するには、let キーワードを使用する。 関数も変数と同様に let で束縛される値の一種であり、関数を定義することは、関数値を変数に束縛することに相当する。

基本的な構文は以下の通りだ。

let 関数名 パラメータ1 パラメータ2 ... =
関数本体

例えば、2 つの整数の和を返す add 関数は以下のように定義できる。

let add x y =
x + y

この例では、add という名前の関数を定義している。 add 関数は xy という 2 つのパラメータを受け取り、x + y の結果を返す。 F#では、関数の戻り値の型は、型推論によって自動的に決定される。 もちろん、明示的に型を指定することも可能だ。

let add (x: int) (y: int): int =
x + y

TypeScript との比較

ここで、TypeScript で同様の add 関数を定義してみよう。

function add(x: number, y: number): number {
return x + y;
}
//or
const add = (x: number, y: number): number => x + y;

F# と TypeScript の関数定義にはいくつかの違いがある。

  1. F# では function キーワードではなく、let キーワードを使用する。
  2. F# では通常、型推論が使われるため、パラメータや戻り値の型を明示的に指定する必要がない (もちろん、指定することもできる)。
  3. F# では関数が値を返すため、明示的な return ステートメントは不要 (末尾の式が関数の戻り値となる)。

どちらの言語も、関数を定義するための簡潔な構文を提供しているが、型推論や暗黙の戻り値など、F# にはより関数型プログラミングに適した特徴が見られる。

関数の使用

F#では、関数は値を返す。 関数を使用するには、単に関数名に続けて引数を記述する。TypeScript のように () で囲む必要はない。

let result = add 10 5
printfn "%d" result // 出力: 15

この例では、add 関数に 105 を引数として与え、結果を result 変数に代入している。

判定ロジックを関数に切り出す

それでは、Step 5 で作成した Program.fs の判定ロジックを関数として切り出してみよう。 まず、Result 型の定義はそのまま残す。

type GameResult =
| TooBig
| TooSmall
| Correct

次に、判定ロジックを judge という名前の関数として定義する。 judge 関数は、正解の数 answer とユーザーの入力 input を受け取り、Result 型の値を返す。

let judge answer input =
match compare input answer with
| 1 -> TooBig
| -1 -> TooSmall
| _ -> Correct

関数を定義したら、型を見てみよう。 judge: int -> int -> Result と表示されるはずだ。

let judge: int -> int -> GameResult =
match compare input answer with
| 1 -> TooBig
| -1 -> TooSmall
| _ -> Correct

このように、-> を関数に適応すると、新しい関数に部分適応される。 たとえば、judge 42 は、int -> Result 型の関数になる。 これにより、関数の再利用性が高くなる。

Program.fs の全体像

ここまでの変更を適用した Program.fs の全体像は以下のようになる。

type GameResult =
| TooBig
| TooSmall
| Correct
let judge answer input =
match compare input answer with
| 1 -> TooBig
| -1 -> TooSmall
| _ -> Correct
let answer = 42
printfn "正解は%dです" answer
printfn "1から100までの数字を入力してください。"
let inputNumber = stdin.ReadLine() |> int
let result = judge answer inputNumber
let message =
match result with
| TooBig -> "大きすぎます"
| TooSmall -> "小さすぎます"
| Correct -> "正解です"
printfn "%s" message

書き換えたら dotnet run で実行してみよう。 実行結果はこれまでと同じになるはずだ。

まとめ

このステップでは、以下の内容を学んだ。

  • let キーワードを使った関数の定義方法
  • 型推論による関数の戻り値の型の決定
  • 関数の呼び出し方法
  • 判定ロジックの関数への切り出し
  • 関数の部分適用
  • F# と TypeScript における関数定義の比較

関数を適切に定義し、使用することで、コードの再利用性と可読性を向上させることができる。 また、F#では関数が第一級の値であり、変数に束縛したり、引数として渡したり、戻り値として返したりすることができる。

次のステップでは、ユーザーが繰り返し入力できるようにループを実装する。