関数のパターン

完了

これまでに、反復的な式を取り上げ、それらの式を関数に入れました。 これでコードの外観が向上し、読みやすくなります。 関数の使用に慣れてきたら、この分野に存在するいくつかの強力なパターンに目を向けてみることをお勧めします。 これらのパターンを使用することで、読みやすく、保守しやすいコードが得られます。

宣言型と命令型

コーディングを開始するときは、式を記述することが多いでしょう。 次に、別の式を記述し、さらに別の式を記述していきます。 あなたの目的は問題の解決であり、その解決方法について考えています。 このアプローチは、"命令型" アプローチと呼ばれます。 これによって手近な問題が解決されるため、これには何の問題もありません。 しかし、"宣言型" アプローチと呼ばれる、別のルートを取ることもできます。 宣言型アプローチの例としては、SQL を使用してデータベースに対してクエリを行う場合が挙げられます。

式の例を次に示します。

SELECT * 
FROM Students s
WHERE s.Location = "Ohio" 

このコードが宣言型である理由は、必要な "内容" は指定されているが、問題を解決する "方法" は指定されていないからです。 "方法" は SQL に任されています。

このアプローチは F# にも適用できます。 次のコードは、宣言型のアプローチを使用します。

let studentsFromOhio = 
    allStudents  
    |> filterLocation "Ohio"

前のコードでは、操作方法を具体的に示さずに、データを操作し、必要な内容を求めています。 前の例のようなコードであれば、読みやすく、推論するのも容易です。 これについて説明するため、F# でサポートされているいくつかの便利なパターンを見てみましょう。

関数型パターン

F# には、より "関数型" のアプローチを可能にする便利なパターンがあります。 次のパターンについて説明します。

  • 合成: 合成は、複数の関数を 1 つの関数に組み合わせます。
  • パイプライン: パイプラインは値から始まり、ある関数からの出力を次の関数の入力として使用して、複数の関数を順番に呼び出します。

コンポジション

合成とは、関数を結合し、それらを特定の順序で順番に適用することです。 この合成演算子は 2 つの関数を受け取って、1 つの新しい関数を返します。

コードを作成しているとき、ある関数を呼び出し、その直後に別の関数を呼び出すことがよくあります。 たとえば、リストを並べ替え、割引対象のすべての商品をフィルター処理して除外したい場合があります。

次の例では、関数 add2() が呼び出され、その結果が multiply3() 関数に提供されています。

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply a =
    let sum = add2 a
    let product = multiply3 sum
    product

printfn "%i" (addAndMultiply 2) // 12

このパターンはよくあり、F# に演算子が含まれています。 >> 演算子を使用すると、2 つ以上の関数を 1 つの大きな関数に結合できます。 >> 演算子を使用すると、次のように、前のコードを簡略化できます。

let add2 a = a + 2
let multiply3 a = a * 3 
let addAndMultiply = add2 >> multiply3

printfn "%i" (addAndMultiply 2) // 12

結合された関数 addAndMultiply() は、左から右に構成される関数を適用します。 この例では、add2() が最初に発生し、最後に multiply3() が発生します。

パイプライン

パイプライン演算子 |> は、関数と引数を受け取り、値を返します。 この例を使って、パイプラインと合成の違いを見てみましょう。

let list = [4; 3; 1]
let sort (list: int list) = List.sort list
let print (list: int list)= List.iter(fun x-> printfn "item %i" x) list

list |> sort |> print // item 1 item 3 item 4

最後のコード行は、最初の関数 list への入力として機能する整数のリスト sort() から開始します。 その操作の結果は print() に送られます。 パイプラインと合成との大きな違いは、パイプラインではあるデータ (この場合は整数のリスト) から開始し、それを一連の関数で順番に処理していく点です。