Groovyのクロージャー

  • link:/category/programming/ [プログラミング]

  • Groovy

1. 概要

この入門チュートリアルでは、この動的で強力なJVM言語の重要な機能であるlink:/groovy-language[Groovy]でクロージャーの概念を探ります。
JavascriptやPythonを含む他の多くの言語は、クロージャーの概念をサポートしています。 ただし、クロージャーの特性と機能は言語によって異なります。
Groovyクロージャーの重要な側面に触れ、途中でどのように使用されるかの例を示します。

2. 閉鎖とは何ですか?

クロージャーは匿名のコードブロックです。 Groovyでは、これは* http://docs.groovy-lang.org/latest/html/api/groovy/lang/Closure.html [_Closure_]クラスのインスタンス*です。 クロージャは0個以上のパラメータを取り、常に値を返します。
さらに、クロージャーは、そのスコープ外の周囲の変数にアクセスし、実行中にそれらをローカル変数とともに使用する場合があります。
さらに、クロージャーを変数に割り当てることも、メソッドにパラメーターとして渡すこともできます。 したがって、クロージャは遅延実行の機能を提供します。

3. 閉鎖宣言

  • Groovy Closureには、パラメーター、矢印→、および実行するコードが含まれます。*パラメーターはオプションであり、指定された場合はコンマで区切られます。

3.1. 基本宣言

def printWelcome = {
    println "Welcome to Closures!"
}
ここでは、クロージャー_printWelcome_が呼び出されたときにステートメントを出力します。 それでは、単項閉包の簡単な例を書いてみましょう。
def print = { name ->
    println name
}
ここで、クロージャー_print_は1つのパラメーター_name_âを取り、呼び出されたときにそれを出力します。
*クロージャの定義はメソッドに似ているので、それらを比較しましょう:
def formatToLowerCase(name) {
    return name.toLowerCase()
}
def formatToLowerCaseClosure = { name ->
    return name.toLowerCase()
}
ここでは、メソッドと対応するクロージャーは同様に動作します。 ただし、クロージャとメソッドには微妙な違いがあります。これについては、後でクロージャとメソッドのセクションで説明します。

3.2. 実行

*クロージャーは2つの方法で実行できます。他のメソッドと同じように呼び出すことも、_call_メソッドを使用することもできます。*
たとえば、通常のメソッドとして:
print("Hello! Closure")
formatToLowerCaseClosure("Hello! Closure")
そして_call_メソッドで実行します:
print.call("Hello! Closure")
formatToLowerCaseClosure.call("Hello! Closure")

4. パラメーター

Groovyクロージャーのパラメーターは、通常のメソッドのパラメーターに似ています。

4.1. 暗黙のパラメーター

パラメーターが定義されていない場合、Groovyは「_it」__という名前の暗黙的なパラメーターを想定するため、パラメーターなしで単項閉包を定義できます。
def greet = {
    return "Hello! ${it}"
}
assert greet("Alex") == "Hello! Alex"

4.2. 複数のパラメータ

2つのパラメーターを取り、それらを乗算した結果を返すクロージャーを次に示します。
def multiply = { x, y ->
    return x*y
}
assert multiply(2, 4) == 8

4.3. パラメータタイプ

これまでの例では、パラメーターに型が提供されていませんでした。 クロージャパラメータのタイプを設定することもできます。 たとえば、_multiply_メソッドを書き換えて、他の操作を検討しましょう。
def calculate = {int x, int y, String operation ->
    def result = 0
    switch(operation) {
        case "ADD":
            result = x+y
            break
        case "SUB":
            result = x-y
            break
        case "MUL":
            result = x*y
            break
        case "DIV":
            result = x/y
            break
    }
    return result
}
assert calculate(12, 4, "ADD") == 16
assert calculate(43, 8, "DIV") == 5.375

4.4. バラールス

通常のメソッドと同様に、クロージャーで可変数の引数を宣言できます。 例えば:
def addAll = { int... args ->
    return args.sum()
}
assert addAll(12, 10, 14) == 36

5. 引数としてのクロージャー

_Closure_を引数として通常のGroovyメソッドに渡すことができます。 これにより、メソッドはクロージャーを呼び出してタスクを完了し、動作をカスタマイズできます。
簡単なユースケースについて説明しましょう:通常の数字のボリュームの計算。
この例では、体積は面積と高さの積として定義されます。 ただし、面積の計算は形状によって異なる場合があります。
したがって、クロージャ_areaCalculator_を引数として取る_volume_メソッドを記述し、呼び出し中に面積計算の実装を渡します。
def volume(Closure areaCalculator, int... dimensions) {
    if(dimensions.size() == 3) {
        return areaCalculator(dimensions[0], dimensions[1]) * dimensions[2]
    } else if(dimensions.size() == 2) {
        return areaCalculator(dimensions[0]) * dimensions[1]
    } else if(dimensions.size() == 1) {
        return areaCalculator(dimensions[0]) * dimensions[0]
    }
}
assert volume({ l, b -> return l*b }, 12, 6, 10) == 720
同じ方法を使用して円錐の体積を見つけましょう:
assert volume({ radius -> return Math.PI*radius*radius/3 }, 5, 10) == Math.PI * 250

6. ネストされたクロージャー

*クロージャ内でクロージャを宣言して呼び出すことができます。*
たとえば、すでに説明した_calculate_クロージャーにロギング機能を追加しましょう。
def calculate = {int x, int y, String operation ->

    def log = {
        println "Performing $it"
    }

    def result = 0
    switch(operation) {
        case "ADD":
            log("Addition")
            result = x+y
            break
        case "SUB":
            log("Subtraction")
            result = x-y
            break
        case "MUL":
            log("Multiplication")
            result = x*y
            break
        case "DIV":
            log("Division")
            result = x/y
            break
    }
    return result
}

7. 文字列の遅延評価

Groovy __String__sは通常、作成時に評価および補間されます。 例えば:
def name = "Samwell"
def welcomeMsg = "Welcome! $name"

assert welcomeMsg == "Welcome! Samwell"
_name_変数の値を変更しても、_welcomeMsg_は変更されません。
name = "Tarly"
assert welcomeMsg != "Welcome! Tarly"
*クロージャー補間により、__ String__s *の遅延評価を提供し、それらの周囲の現在の値から再計算できます。 例えば:
def fullName = "Tarly Samson"
def greetStr = "Hello! ${-> fullName}"

assert greetStr == "Hello! Tarly Samson"
今回だけ、変数を変更すると、補間された文字列の値にも影響します。
fullName = "Jon Smith"
assert greetStr == "Hello! Jon Smith"

8. コレクションの閉鎖

Groovyコレクションは、多くのAPIでクロージャーを使用します。 たとえば、アイテムのリストを定義し、暗黙的なパラメータを持つ単項クロージャ_each_を使用して印刷します。
def list = [10, 11, 12, 13, 14, true, false, "BUNTHER"]

list.each {
    println it
}

assert [13, 14] == list.findAll{ it instanceof Integer && it >= 13 }
多くの場合、何らかの基準に基づいて、マップからリストを作成する必要があります。 例えば:
def map = [1:10, 2:30, 4:5]

assert [10, 60, 20] == map.collect{it.key * it.value}

9. クロージャーvsメソッド

これまで、クロージャーの構文、実行、およびパラメーターを見てきましたが、これらはメソッドにかなり似ています。 クロージャーとメソッドを比較しましょう。
通常のGroovyメソッドとは異なり:
  • メソッドへの引数として_Closure_を渡すことができます

  • 単項クロージャは、暗黙の_it_パラメータを使用できます

  • _Closure_を変数に割り当てて、後で実行できます。
    メソッドとして、または_call_を使用して

  • Groovyは実行時にクロージャーの戻り値の型を決定します

  • クロージャー内でクロージャーを宣言して呼び出すことができます

  • クロージャは常に値を返します

    したがって、クロージャーは通常のメソッドよりも利点があり、Groovyの強力な機能です。

10. 結論

この記事では、Groovyでクロージャーを作成する方法を見て、それらがどのように使用されるかを調べました。
クロージャーは、遅延実行のためにオブジェクトとメソッドに機能を注入する効果的な方法を提供します。
いつものように、この記事のコードと単体テストはhttps://github.com/eugenp/tutorials/tree/master/core-groovy[over on GitHub]で入手できます。