それが僕には楽しかったんです。

僕と MySQL と時々 MariaDB

全てのプログラマに捧げるScala入門 ラムダと高階関数、カリー化

はじめに

Scalaをああだこうだ使っているけど、いまいちココらへんが理解できていないので
なんとなくまとめて整理してみる。

ラムダ式

上の参考サイトを元に値を引数で一つ受け取って、それに1加算して返す関数を考える。
通常の書き方で書くと次のようになる。

def inc(x: Int): Int = x + 1

inc(1) // return => 2

これをラムダで書き換えてみる。

すると次のようになる。

val inc: Int => Int = x => x + 1
inc(1) // return => 2

ただこちらは関数型でない言語を使ったことがない人には直感で理解がしにくいと思う。
上記のコードを次の用にするとわかりやすいかもしれない。

val inc: (Int => Int) = (x :Int) => x + 1

(Int => Int)が変数incの型であり、Int型の引数をとりInt型の戻り値を返すという意味になっている
(Int => Double)ならInt型の引数をとり、Double型の戻り値を返すという意味合いになる。
引数を2つとり、それらを加算して返す関数は次のように書ける。

val plus: ((Int,Int) => Int) = (x: Int, y: Int) => x + y

変数の型(今回ならInt => Int)を省略することができて、その場合は次のようになる。

val inc = (x: Int) => x + 1

この場合だと引数を二個以上もつ関数もラムダで書きやすい。
上記と同様に引数を2つ受け取り、それを加算する関数を書くと次のようになる。

val plus = (x: Int, y: Int) => x + y
plus(5,6) // return => 11

ここまで書いた右辺のことをラムダ式という。

無名関数

ラムダ式は名前のない関数、つまり無名関数として定義でき
無名関数は変数に束縛されていると言える。

高階関数

一言で言うなら、関数を引数にとる関数のことで次のように書ける。
ラムダ式のことがわかっていればさほど難しい話ではない。

def calc(plus: (Int,Int) => Int, minus: Int) = plus(2,3) - minus
val plus_func: (Int, Int) => Int = (x, y) => x + y

calc(plus_func, 5) //return => 0

引数だけでなく、戻り値として関数を返すこともできるのが高階関数である。

def add(x: Int): (Int => Int) = (y : Int) => x + y
add(2)(3) // return => 5

この様な高階関数はあまり意識しなくても使っていることが多く
よく使う場面ではScalaのコレクションにおいて、mapやforeach,filterなどのメソッドを使うときである

(1 to 100).filter(x => x % 2 == 0)

カリー化

カリー化とは(Int, Int) => Intのように複数の引数をとるラムダにおいて Int => Int => Intのように引数を分割し、
ひとつ引数を取ったあとに、その引数をメソッドチェインのようにチェインさせて書く方法のことを指す。

関数型言語ではよく使われており、Haskellでは複数の引数をとると自動でカリー化されるらしい。

さっきのplus関数で例を挙げてみる。

先に書いたカリー化していない場合は

val plus: (Int, Int) => Int = (x: Int, y: Int) => x + y

となるが

これをカリー化すると次のようになる

val plus: Int => Int => Int = (x: Int) => (y: Int) => x + y
plus(2)(3) // return => 5

これが使えるとなにが嬉しいかというと、filterなどの高階関数を扱うあれこれにおいて
片方の値を定数にして、もう片方を動的に処理した場合などに非常に便利になる。

val myFilter: Int => Int => Boolean = (x: Int) => (y: Int) => x < y 
(1 to 100).filter(myFilter(50))
res10: scala.collection.immutable.IndexedSeq[Int] = Vector(51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100)