1. 概要

このチュートリアルでは、Scalaの高階関数について学習します。

2. 高階関数

簡単に言えば、次の条件のいずれかまたは両方を満たす場合、関数は高階であると言えます。

  • パラメータとして1つ以上の関数を取ります
  • 関数を返します

関数がScalaの第一級市民であるという事実は、これを可能にします。 Java 8+を知っている人にとって、それはおそらくなじみのない主題ではありません。

最初に、Scalaの標準ライブラリによって提供される最も人気のある高階関数を使用したいくつかの例を見てみましょう。 次に、独自の高階関数を作成します。

3. パラメータとして機能

Scalaで最も人気のある高階関数のいくつかを調べることから始めましょう。 これから説明するように、それぞれがパラメータとして関数を取ります。

3.1. マップ

map は、あるコレクションを別のコレクションに変換する関数です。 各要素を変換する方法を説明するのが私たちの仕事です。

いくつかの名前を含むシーケンスが与えられ、各名前の前に“ sir”を付けることが目標であるとします。 map 関数を使用すると、これを非常に簡単に実現できます。

val expected = Seq("sir Alex Ferguson", "sir Bobby Charlton", "sir Frank Lampard")
val names = Seq("Alex Ferguson", "Bobby Charlton", "Frank Lampard")
 
val sirNames = names.map(name => "sir " + name)

assertEquals(expected, sirNames)

この場合、必要な変換を説明する無名関数を作成しました。 ただし、適切に定義された関数を使用することもできます。

def prefixWithSir(name: String) = "sir " + name
val sirNames = names.map(prefixWithSir)

関数パラメーターを渡すこの方法は、関数がより複雑な場合に特に役立ちます。

3.2. フィルター

filter を使用すると、関心のない要素からコレクションをクリアできます。

「John」で始まる名前のみを保持する方法を見てみましょう。

val expected = Seq("John O'Shea", "John Hartson")
val names = Seq("John O'Shea", "Aiden McGeady", "John Hartson")

val johns = names.filter(name => name.matches("^John .*"))

assertEquals(expected, johns)

高階関数を呼び出し、適切な述語を提供した後、目的の要素のみを含む新しいシーケンスを取得しました。

3.3. 削減

reduce は、以前の機能とは少し異なる機能です。 は、タイプ Tの複数の要素をタイプTの単一の要素に結合します。

reduceを使用して収益のリストを追加する方法を見てみましょう。

val expected = 2750
val earnings = Seq(1000, 1300, 450)

val sumEarnings = earnings.reduce((acc, x) => acc + x)

assertEquals(expected, sumEarnings)

ここでは、一連の収益があり、それらの合計を計算します。 そのためには、より複雑な関数を提供する必要があります。 この関数は、アキュムレータaccと現在の要素xの2つの要素で構成されています。

アキュムレータは、コレクションで実行された計算の状態を維持する責任があります。

デフォルトでは、reducereduceLeftを呼び出し、コレクションを左から右にトラバースします。 逆順が必要な場合は、reduceRight。を使用できます。

3.4. 折り畳み

foldreduceと非常によく似ています。 違いは、アキュムレータの初期値を設定できることです。 これは、返すオブジェクトのタイプを制御できることも意味します。

いくつかの文字列があり、それらのすべての単語を数えたいとしましょう。

val expected = 6
val strings = Seq("bunch of words", "just me", "it")

val numberOfWords = strings.foldLeft(0)((acc, x) => acc + x.split(" ").size)

assertEquals(expected, numberOfWords)

foldLeftメソッドを使用していることに注意してください。 同様に、 reduceLeft で見たように、計算はコレクションの最初から開始されます。 関数の最初のパラメーターである独自のアキュムレーター0を提供しました。

残りはreduceと同じです。

4. 関数を返す関数

これまで、他の関数をパラメーターとして受け取る高階関数について説明してきました。 それでは、関数を返す関数を見つけましょう。 これが実際に動作することを確認するために、ScalaのパターンマッチングAPIを使用して関数を返す独自の高階関数を記述してみましょう。

def mathOperation(name: String): (Int, Int) => Int = (x: Int, y: Int) => {
  name match {
    case "addition" => x + y
    case "multiplication" => x * y
    case "division" => x/y
    case "subtraction" => x - y
  }
}

def add: (Int, Int) => Int = mathOperation("addition")
def mul: (Int, Int) => Int = mathOperation("multiplication")
def div: (Int, Int) => Int = mathOperation("division")
def sub: (Int, Int) => Int = mathOperation("subtraction")

assertEquals(15, add(10, 5))
assertEquals(50, mul(10, 5))
assertEquals(2, div(10, 5))
assertEquals(5, sub(10, 5))

mathOperation 関数を定義しました。この関数は、操作の名前をパラメーターとして受け取ります。 戻り値のタイプ(Int、Int)=> Int は、関数が返されることを示します— nameパラメーターで指定された操作を実行する関数。

次に、関数型変数( add mul div 、および sub )を作成し、それらが次のように機能することを確認しました。期待される。

これは、オブジェクト指向プログラミングでわかっているファクトリデザインパターンと非常によく似ているかもしれません。

5. 結論

このチュートリアルでは、Scalaの高階関数について学びました。 これらは、関数型プログラミングで使用できる基本的なツールです。

高階関数は非常に強力であることがわかります。 それらは、単純で定型文のない、読みやすいコードを書くことを可能にします。

いつものように、GitHubでコードスニペットを見つけることができます。