関数 (F#)

更新 : 2010 年 5 月

関数は、あらゆるプログラミング言語においてプログラムの実行の基本となる単位です。 他の言語の場合と同様に、F# の関数にもそれぞれ名前と本体があり、パラメーターや引数を受け取ることができます。 F# ではさらに、関数型プログラミング構成要素もサポートしています。たとえば、関数を値として処理したり、名前のない関数を式で使用したりできます。また、関数の合成による新しい関数の作成、カリー化関数、関数の引数の部分適用による関数の暗黙の定義などがサポートされます。

関数を定義するには let キーワードを使用します。再帰関数の場合は、let rec というキーワードの組み合わせを使用します。

// Non-recursive function definition.
let [inline] function-name parameter-list [ : return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body

解説

function-name は、関数を表す識別子です。 parameter-list は、スペースで区切られた一連のパラメーターで構成されます。 「パラメーター」で説明するように、各パラメーターの明示的な型を指定できます。 特定の引数型を指定しない場合は、型が関数本体から推測されます。 function-body は式で構成されます。 関数本体を構成する式は、通常、複数の式から成る複合式で、その最後の式が戻り値になります。 return-type では、コロンの後に戻り値の型を指定します。これは省略可能です。 戻り値の型を明示的に指定しない場合は、最後の式から特定されます。

単純な関数定義の例を以下に示します。

let f x = x + 1

この例の関数の名前は f、引数は x、引数の型は int、関数本体は x + 1、戻り値の型は int です。

inline 指定子は、コンパイラに対するヒントであり、関数のサイズが小さいためにコードを呼び出し元の本体に統合できることを表します。

スコープ

モジュール スコープ以外のレベルのスコープでは、値や関数名を再利用してもエラーになりません。 名前を再利用すると、後から宣言した名前が前に宣言した名前をシャドウします。 ただし、モジュールの最上位のスコープでは名前が一意である必要があります。 たとえば次のコードは、モジュール スコープではエラーになりますが、関数内ではエラーになりません。

let list1 = [ 1; 2; 3]
// Error: duplicate definition.
let list1 = []  
let function1 =
   let list1 = [1; 2; 3]
   let list1 = []
   list1

これに対して、次のコードは、どのレベルのスコープでも許容されます。

let list1 = [ 1; 2; 3]
let sumPlus x =
// OK: inner list1 hides the outer list1.
   let list1 = [1; 5; 10]  
   x + List.sum list1

パラメーター

パラメーターの名前は関数名の後に指定します。 次の例のように、パラメーターの型を指定できます。

let f (x : int) = x + 1

型を指定する場合は、パラメーターの名前の後にコロンで区切って指定します。 パラメーターの型を省力した場合は、コンパイラによって推論されます。 たとえば次の関数定義では、1 の型が int であるため、引数 x の型は int と推論されます。

let f x = x + 1

ただし、コンパイラは、可能な限り、関数を汎用的にしようとします。 たとえば次のようなコードがあるとします。

let f x = (x, x)

この関数は、任意の型の 1 つの引数から組を作成します。 この関数では型が指定されていないため、任意の型の引数を使用できます。 詳細については、「自動汎化 (F#)」を参照してください。

パラメーターの詳細については、「パラメーターと引数 (F#)」を参照してください。

関数本体

関数本体には、ローカル変数と関数の定義を含めることができます。 それらの変数と関数のスコープは、現在の関数の本体に限られます。 簡易構文オプションを有効にしている場合は、次のように、定義が関数本体に含まれていることをインデントによって示す必要があります。

let cylinderVolume radius length =
    // Define a local value pi.
    let pi = 3.14159
    length * pi * radius * radius

詳細については、「コードのフォーマットに関するガイドライン (F#)」および「詳細な構文 (F#)」を参照してください。

戻り値

コンパイラは、関数本体の最後の式を使用して戻り値とその型を特定します。 最後の式の型が前の式から推論される場合もあります。 前のセクションの関数 cylinderVolume の pi の型は、リテラル 3.14159 の型から float と特定されます。 さらに、pi の型を使用して、式 h * pi * r * r の型が float と特定されます。 このため、関数全体の戻り値の型が float になります。

戻り値を明示的に指定するには、次のようなコードを使用します。


let cylinderVolume radius length : float =
   // Define a local value pi.
   let pi = 3.14159
   length * pi * radius * radius

この場合、関数全体に float が適用されます。パラメーターの型にもこの型を適用するには、次のコードを使用します。

let cylinderVolume (radius : float) (length : float) : float

関数は、必ず 1 つの値を返します。 関数が実際の値を返さない場合は、unit の値が返される可能性があります。 複数のデータを返すには、いくつかの方法があります。 1 つは、組の値を返す方法です。 関数が組の値を返す場合は、let バインディングで組パターンを使用して、組の要素を複数の値に割り当てることができます。 次に例を示します。

let firstAndLast list1 =
    (List.head list1, List.head (List.rev list1))

let (first, last) = firstAndLast [ 1; 2; 3 ]
printfn "First: %d; Last: %d" first last

このコードの出力は、次のようになります。

  

複数のデータを返す別の方法には、参照セルを使用する方法、および byref パラメーターを使用する方法があります。 使用例を含む詳細については、「参照セル (F#)」および「パラメーターと引数 (F#)」を参照してください。

関数の呼び出し

関数を呼び出すには、関数名の後にスペースを入れ、その後に各引数を指定します。各引数はスペースで区切ります。 たとえば、関数 cylinderVolume を呼び出して結果を値 vol に割り当てるには、次のコードを使用します。

let vol = cylinderVolume 2.0 3.0

関数が組を単一のパラメーターとして受け取る場合、関数呼び出しの末尾は、他の言語での引数リストのようなかっこで囲んだリストになります。 関数名と左かっこの間の空白は省略できます。 たとえば、組をパラメーターとして受け取る cylinderVolume 関数を次に示します。

let cylinderVolume(radius, length) =
    let pi = 3.14159
    length * pi * radius * radius

組を引数とした関数の呼び出しは次のようになります。

let vol = cylinderVolume(2.0, 3.0)

関数がパラメーターを受け取らない場合は、次のコード行のように、unit の値 () を引数として指定します。

initializeApp()

関数の名前自体は単なる関数値なので、unit の値を示すかっこを省略すると、関数は参照されるだけで呼び出されません。

引数の部分適用

指定されている数より少ない数の引数を渡すと、残りの引数を使用する新しい関数が作成されます。 これは、カリー化と呼ばれる引数の処理方法で、F# のような関数型プログラミング言語の特徴の 1 つです。 たとえば、半径がそれぞれ 2.0 と 3.0 の 2 つのパイプがあるとします。 この場合、次のようにして、パイプの体積を特定する関数を作成することができます。

let smallPipeRadius = 2.0
let bigPipeRadius = 3.0

// These define functions that take the length as a remaining
// argument:

let smallPipeVolume = cylinderVolume smallPipeRadius
let bigPipeVolume = cylinderVolume bigPipeRadius

その後、必要に応じて、2 つのサイズのパイプのさまざまな長さを追加の引数として指定します。

let length1 = 30.0
let length2 = 40.0
let smallPipeVol1 = smallPipeVolume length1
let smallPipeVol2 = smallPipeVolume length2
let bigPipeVol1 = bigPipeVolume length1
let bigPipeVol2 = bigPipeVolume length2

再帰関数

再帰関数とは、自身を呼び出す関数です。 再帰関数を使用するには、let キーワードの後に rec キーワードを指定する必要があります。 関数の本体から再帰関数を呼び出す方法は、他の関数呼び出しの場合と変わりません。 次の再帰関数は、n 番目のフィボナッチ数を計算します。 フィボナッチ数列は、古代から知られている数列で、数例の各数値が、前の 2 つの数値の和になります。

let rec fib n = if n < 2 then 1 else fib (n - 1) + fib (n - 2)

再帰関数は、特殊な技巧 (アキュムレータや継続の使用など) を意識して慎重に使用しないと、スタック オーバーフローを引き起こしたり、プログラムの実行効率が低下したりする可能性があります。

関数値

F# では、すべての関数が値と見なされ、実際に、関数値と呼ばれています。 関数は値であるため、他の関数の引数として使用したり、値が使用されるその他のコンテキストで使用したりできます。 関数値を引数として受け取る関数の例を次に示します。

let apply1 (transform : int -> int ) y = transform y

関数値の型を指定するには、-> トークンを使用します。 このトークンの左側に引数の型を、右側に戻り値の型を指定します。 上の例の apply1 は、関数 transform を引数として受け取る関数で、transform は、整数を受け取る、別の整数を返す関数です。 apply1 の使用方法を次のコードに示します。

let increment x = x + 1

let result1 = apply1 increment 100

このコードを実行すると、result の値が 101 になります。

複数の引数を指定するには、次のように、連続する -> トークンで区切ります。

let apply2 ( f: int -> int -> int) x y = f x y

let mul x y = x * y

let result2 = apply2 mul 10 20

結果は 200 になります。

ラムダ式

ラムダ式とは、名前のない関数です。 前の例では、名前のある関数 increment と mul を定義しましたが、次のように、代わりにラムダ式を使用することもできます。

let result3 = apply1 (fun x -> x + 1) 100

let result4 = apply2 (fun x y -> x * y ) 10 20

ラムダ式を定義するには、fun キーワードを使用します。 ラムダ式は関数定義に似ていますが、引数リストと関数本体の区切りに = トークンではなく -> トークンを使用します。 引数の型は、通常の関数定義と同様に、推論されるようにすることも、明示的に指定することもできます。戻り値の型も、本体の最後の式の型から推論されます。 詳細については、「ラムダ式: fun キーワード (F#)」を参照してください。

関数合成とパイプライン処理

F# の関数は、他の関数から合成することができます。 function1 と function2 という 2 つの関数を合成すると、function1 に続いて function2 を適用する別の関数になります。

let function1 x = x + 1
let function2 x = x * 2
let h = function1 >> function2
let result5 = h 100

結果は 202 になります。

パイプライン処理を使用すると、複数の関数呼び出しを一連の操作として連結することができます。 以下に例を示します。

let result = 100 |> function1 |> function2

この場合も、結果は 202 になります。

参照

その他の技術情報

値 (F#)

F# 言語リファレンス

履歴の変更

日付

履歴

理由

2010 年 5 月

「パラメーター」のコード例を修正。

コンテンツ バグ修正

2011 年 4 月

パラメーターおよび戻り値について明確にするための情報を追加。