[HeadFirst]Kotlin Ramdaと高次関数

23297 ワード

Lambdaについて


Lambda式は、コードブロックを持つオブジェクトです.オブジェクトなので、変数にランダを割り当てることもできます.ramdaを関数に渡して、コードブロックを実行する関数にすることもできます.
Collectionや使用時にランダを関数に渡す機能が特に役立ちます.Collectionの可変オブジェクトにはsortBy関数があり、ランダを渡して関数をどのようにソートするかを指定できます.

Lambdaはどのように作成されたのか

{ x: Int -> x + 5 }
上のコードは簡単なランダコードです.xを渡したら、5を加えて返事します.->を基準とし、左側をパラメータ、右側を主体とする.ランダはパラメータを持たなくてもいいし、パラメータを1つだけ持ってもいいです.
このランダコードには名前がありません.だから匿名のランデです.下記のように書くとPow!は、文字列のラムダコードを返します.
{ "Pow!" }

変数にlambdaを割り当てる


ランダを変数に割り当てる方法は特にありません.次の例では、val変数にランダがあるため、他のランダを割り当てることはできません.別のランダを割り当てたい場合はvarと宣言する必要があります.
val addFive = {x: Int -> x + 5}
変数にランダを割り当てるのは、ランダの戻り値ではなく、コードブロックを割り当てることです.このように割り当てられたコード(ramda)を実行するには、ramdaを明示的に呼び出す必要があります.

ラムダを呼ぶ


ランダはinvoke関数で呼び出すことができます.もちろん、この場合は必要なパラメータを渡す必要があります.
fun main() {
    val addInt = {x: Int, y: Int -> x + y}
    println(addInt.invoke(5, 6))
}

ラムダ式データ型


他のすべての対象のように、ランダにも資料型のものがあります.他のオブジェクトとの違いがある場合、ランダはオブジェクトの名前を指定しません.しかしランダはパラメータと戻り値の資料型を指定した.このランダの資料型は関数資料型とも呼ばれる.
したがって,ラムダ型を表現する方法は特別であり,(parameters)->return typeはこのように表現される.すなわち,パラメータとしてIntを受け入れてStringを返すRamdaをInt->Stringと表す.
val msg = {x: Int -> "The value is $x"}
変数にランダを割り当てると、コンパイラはこれらの変数を推定し、変数のデータ型に代入します.このプロセスを減らすには、以下の資料型を直接入力します.ランダに割り当てられた変数にデータ型を指定した場合、ランダコード部分でパラメータ型の宣言を行う必要はありません.この部分はコンパイラによって推定されるからです.
fun main() {
    val addInt: (Int, Int) -> Int = {x: Int, y: Int -> x + y}
    // val addInt: (Int, Int) -> Int = {x, y -> x + y} 이것역시 가능
    println(addInt.invoke(5, 6))
}
RamdaのReturnTypeの代わりにUnitを使用すると、ReturnのないRamdaを作成できます.
val myLam: () -> Unit = {println("Hi")}

itで置き換える


パラメータを持つramdiaコンパイラがパラメータのタイプを推定できる場合(変数のデータ型に宣言されている場合)、パラメータを省略できます.またramdabody部分ではitキーワードで代用できます.
// 이 코드가
var addFive: (Int) -> Int = {x: Int -> x + 5}
// 아래처럼 바뀐다.
addFive: (Int) -> Int = {it + 5}
itキーワードについて
itキーワードは、パラメータを正しく認識できる場合にそのパラメータに代わるキーワードである.たとえば、次のように使用できます.
collection.forEach { println(it)}
val strings = someArray.map { it.toString() }
itキーワードに関する質問

関数にramdaを割り当てる


関数のパラメータには、1つ以上のramdaを割り当てることができます.このように関数を設計すると、より一般的な関数を作成することができます.このようにランダをパラメータとして受け入れるか、ランダが返す関数を高次関数(higher-orderfunction)と呼ぶ.
ランダをパラメータとする関数の定義と使用を以下に示す.
fun convert(x: Double, converter: (Double) -> Double): Double {
    val result = converter(x) // 왜 invoke 를 안써도 되는걸까?
    println("$x is converted to $result")
    return result
}

fun main() {
    convert(20.0,{ it * 1.8 + 32 })
    convert(20.0) { it * 1.8 + 32 } // 함수의 마지막 파라미터가 람다라면 이렇게 가능
}
関数のパラメータがramdaのみの場合、()呼び出しを行わなくてもよい.
fun hello(word: ()-> String) {
    println(word.invoke())
}

fun main() {
    hello { "Hello!" }
}
ランダフォーマットについて
ラムダの体の部分には線が1本しかないわけにはいかない.数行でドランダを構成できます.複数行の場合は、最後の行を戻り値とします.
val lam = {c: Double -> println(c)
                        c * 1.8 + 32 }

ランダの関数を返します

fun convert(x: Double, converter: (Double) -> Double): Double {
    val result = converter(x)
    println("$x is converted to $result")
    return result
}

fun getConversionLambda(str: String) : (Double) -> Double {
    when (str) {
        "CentigradeToFahrenheit" -> { return {it * 1.8 + 32} }
        "KgsToPounds" -> { return  { it * 2.204623 } }
        "PoundsToUSTons" -> { return { it / 2000.0} }
        else -> { return { it } }
    }
}

fun main() {

    val pounds = getConversionLambda("KgsToPounds")(2.5)
    // 람다를 반환하는 함수를 람다를 인자로 받는 함수의 파라미터 자리에 넣을 수 있다.
    convert(20.0, getConversionLambda("CentigradeToFahrenheit"))
    
}

パラメータとしてランダを受け入れ、ランダを返します。

fun combine(lam1: (Double) -> Double, lam2: (Double) -> Double) : (Double) -> Double {
    return {x: Double -> lam2(lam1(x))}
}

fun main() {
    val kgsToPounds = { x: Double -> x * 2.204623 }
    val poundsToUSTons = { x: Double -> x / 2000.0 }
    val kgsToUSTons = combine(kgsToPounds, poundsToUSTons)
    val usTons = kgsToUSTons.invoke(1000.0)
    println(usTons)
}

type alias:Ramdaの可読性向上


関数のタイプ(資料型)を指定すると、コードがより複雑になります.上で作成したcombine関数について、(Double)->Doubleが繰り返しています.これらの部分をtype aliasで置き換えると可読性が向上します.
type aliasでは、既存のタイプの名前を変更できます.
typealias DoubleConversion = (Double) -> Double
fun combine(lam1: DoubleConversion, lam2: DoubleConversion) : DoubleConversion {
    return {x: Double -> lam2(lam1(x))}
}
これらのtype aliasが既に存在するタイプであれば、いずれも適用できます.arrayやcollectionにも適用できます.
class Animal(val name: String)
typealias AnimalArr = Array<Animal>
typealias AnimalMap = Map<String, Animal>

fun main() {
    val arr: AnimalArr = arrayOf(Animal("Cat"),Animal("Dog"),Animal("Hippo"))
    val map: AnimalMap = mapOf("Cat" to Animal("Cat"),"Dog" to Animal("Dog"), "Hippo" to Animal("Hippo"))
}