次の方法で共有


Functions

関数は、任意のプログラミング言語でのプログラム実行の基本的な単位です。 他の言語と同様に、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

注釈

関数名は、関数を表す識別子です。 パラメーター リストは、スペースで区切られた連続するパラメーターで構成されます。 「パラメーター」セクションの説明に従って、各パラメーターに明示的な型を指定できます。 特定の引数型を指定しない場合、コンパイラは関数本体から型を推論しようとします。 関数本体は式で構成されます。 通常、関数本体を構成する式は、戻り値である最終的な式で最高値を示す多数の式で構成される複合式です。 戻り値の型はコロンの後に型が続き、省略可能です。 戻り値の型を明示的に指定しない場合、コンパイラは最終的な式から戻り値の型を決定します。

単純な関数定義は次のようになります。

let f x = x + 1

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

関数は inlineマークできます。 inlineの詳細については、「インライン関数」を参照してください。

Scope

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

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

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

let f x = x + 1

ただし、コンパイラは関数を可能な限りジェネリックにしようとします。 たとえば、次のコードに注意してください。

let f x = (x, x)

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

関数本体

関数本体には、ローカル変数と関数の定義を含めることができます。 このような変数と関数は、現在の関数の本体のスコープ内にありますが、外部には含まれません。 次の例に示すように、インデントを使用して、定義が関数本体にあることを示す必要があります。

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

詳細については、「 コードの書式設定ガイドライン 」および 「詳細構文」を参照してください。

戻り値

コンパイラは、関数本体の最後の式を使用して戻り値と型を決定します。 コンパイラは、前の式から最終的な式の型を推測する場合があります。 前のセクションで示した関数cylinderVolumeでは、piの型は、floatするリテラル 3.14159の型から決定されます。 コンパイラは、piの型を使用して、floatする式length * pi * radius * radiusの型を決定します。 したがって、関数の全体的な戻り値の型は 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

関数の呼び出し

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

let vol = cylinderVolume 2.0 3.0

引数の部分的な適用

指定した数より少ない数の引数を指定した場合は、残りの引数を受け取る新しい関数を作成します。 この引数の処理方法は カレー と呼ばれ、F# のような関数型プログラミング言語の特徴です。 たとえば、2 つのサイズのパイプを使用するとします。1 つは半径 が 2.0 で 、もう 1 つは半径 が 3.0 です。 パイプのボリュームを決定する関数を次のように作成できます。

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 です。

ラムダ式

ラムダ式は、名前のない関数です。 前の例では、名前付き関数の インクリメントmul を定義する代わりに、ラムダ式を次のように使用できます。

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

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

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

Pipelines

パイプ演算子 |> は、F# でデータを処理するときに広く使用されます。 この演算子を使用すると、柔軟な方法で関数の "パイプライン" を確立できます。 パイプライン処理を使用すると、関数呼び出しを連続する操作として連結できます。

let result = 100 |> function1 |> function2

次のサンプルでは、これらの演算子を使用して単純な機能パイプラインを構築する方法について説明します。

/// Square the odd values of the input and add one, using F# pipe operators.
let squareAndAddOdd values =
    values |> List.filter (fun x -> x % 2 <> 0) |> List.map (fun x -> x * x + 1)

let numbers = [ 1; 2; 3; 4; 5 ]

let result = squareAndAddOdd numbers

結果は [2; 10; 26]です。 前のサンプルでは、リスト処理関数を使用して、パイプラインを構築するときに関数を使用してデータを処理する方法を示しています。 パイプライン 演算子自体は、F# コア ライブラリで次のように定義されています。

let (|>) x f = f x

関数の構成

F# の関数は、他の関数から構成できます。 2 つの関数 function1 と function2 の構成は、function1 の適用とその後の function2 の適用を表す別の関数です

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

結果は 202 です。

コンポジション演算子 >> は 2 つの関数を受け取り、関数を返します。これに対し、パイプライン演算子 |> は値と関数を受け取り、値を返します。 次のコード例は、関数シグネチャと使用法の違いを示すことによって、パイプライン演算子とコンポジション演算子の違いを示しています。

// Function composition and pipeline operators compared.
let addOne x = x + 1
let timesTwo x = 2 * x

// Composition operator
// ( >> ) : ('T1 -> 'T2) -> ('T2 -> 'T3) -> 'T1 -> 'T3
let Compose2 = addOne >> timesTwo

// Backward composition operator
// ( << ) : ('T2 -> 'T3) -> ('T1 -> 'T2) -> 'T1 -> 'T3
let Compose1 = addOne << timesTwo

// Result is 5
let result1 = Compose1 2

// Result is 6
let result2 = Compose2 2

// Pipelining
// Pipeline operator
// ( |> ) : 'T1 -> ('T1 -> 'U) -> 'U
let Pipeline2 x = addOne x |> timesTwo

// Backward pipeline operator
// ( <| ) : ('T -> 'U) -> 'T -> 'U
let Pipeline1 x = addOne <| timesTwo x

// Result is 5
let result3 = Pipeline1 2

// Result is 6
let result4 = Pipeline2 2

関数のオーバーロード

型のメソッドはオーバーロードできますが、関数はオーバーロードできません。 詳細については、「 メソッド」を参照してください。

こちらも参照ください