はじめに
コップ本の勉強中で、理解が難しかったので実際に実装して理解していく。
今回やりたいことは、高階関数と、型パラメータを用いて、重複する処理を簡略化していく。
結論→事前知識→リファクタリングの過程という順に書いていく。
結論
素直に書いた場合
例えば、第一引数の値と第二引数の値を掛け算して、結果を返却する例を用いる。
object hoge {
def main(args: Array[String]): Unit = {
computeSomethingS("hoge")(2)
computeSomethingI(3)(2)
computeSomethingD(3.0)(2)
def computeSomethingS(a: String)(b: Int): String = a * b
def computeSomethingI(a: Int)(b: Int): Int = a * b
def computeSomethingD(a: Double)(b: Int): Double = a * b
}
}
複数の型を扱う場合は、上記のように書く必要があるかと思う。
このコードの問題としては、Long型や、char型が増えた時などに関数を新しく定義してあげる必要がある。
高階関数と型パラメータを用いた場合
object hoge {
def main(args: Array[String]): Unit = {
computeSomething("hoge")(_ * 2)
computeSomething(3)(_ * 2)
computeSomething(3.0)(_ * 2)
def computeSomething[A](a: A)(f:A => A): A = f(a)
}
}
事前知識
関数リテラルと、関数オブジェクト
scala以外の言語でもこんな感じの処理を見たことがあると思う。
args.foreach(arg => println(arg))
これを分解していくと・・・
foreachの中のarg => println(arg)
は関数リテラルと呼ぶ。
argパラメータをprintlnに渡している。
関数リテラルの中身のみを取り出してみると以下のように書くことができる。
(str: String) => println(str)
この関数リテラルは実行時にオブジェクトとして存在するので、変数に代入することができる。
つまり、以下のように表現することができる。
var doPrint = (str: String) => println(str)
値としての関数(var ・・・)は、関数でもあるので以下のように呼び出すことができる。
doPrint("hello scala")
// output: hello scala
関数リテラルを引数として受け取る。
文字リテラルと同様に関数リテラルも引数に渡すことができる。
- 文字リテラルなら
def returnArg(str: String):String = str
returnArg("hello")
- 関数リテラルなら
def computeSomething(f:Int => Int):Int = f(2)
computeSomething((x: Int) => x + 3)
- advance
全ての引数が、1回のみ使われる場合は、引数の宣言を省略し、_ で表現することができるので、
簡略化した場合は、以下のように記述することができる。
def computeSomething(f:Int => Int):Int = f(2)
computeSomething(_ + 3)
リファクタリングの過程
最初の例
object hoge {
def main(args: Array[String]): Unit = {
computeSomethingS("hoge")(2)
computeSomethingI(3)(2)
computeSomethingD(3.0)(2)
def computeSomethingS(a: String)(b: Int): String = a * b
def computeSomethingI(a: Int)(b: Int): Int = a * b
def computeSomethingD(a: Double)(b: Int): Double = a * b
}
}
高階関数を適用した
object hoge {
def main(args: Array[String]): Unit = {
computeSomethingS("hoge")(_ * 2)
computeSomethingI(3)(_ * 2)
computeSomethingD(3.0)(_ * 2)
def computeSomethingS(a: String)(f:String => String): String = f(a)
def computeSomethingI(a: Int)(f:Int => Int): Int = f(a)
def computeSomethingD(a: Double)(f:Double => Double): Double = f(a)
}
}
型パラメータを適用した
object hoge {
def main(args: Array[String]): Unit = {
computeSomething("hoge")(_ * 2)
computeSomething(3)(_ * 2)
computeSomething(3.0)(_ * 2)
def computeSomething[A](a: A)(f:A => A): A = f(a)
}
}
最後に
scala難しいけど、楽しい!!!!