はじめに
Mapのscaladocを見ていた時、継承しているもの一覧中に異質なものを見つけました。
他のはなんとなくわかると思うのですが、Function1? PartialFunction?? んんん??
Function1とPartialFunctionおさらい
Function1は引数一個を取る関数を表します。
以下、数字を文字列に変換するFunction1の宣言例です。
val int2String: Function1[Int, String] = _.toString
次にPartialFunctionですが、Function1を継承しています。
trait PartialFunction[-A, +B] extends (A) ⇒ B
scaladocに書かれている説明では「総称型Aのすべての値を満たすわけではない単項の関数」とあります。
以下、PartialFunctionの宣言例です。
val pf: PartialFunction[Int, String] = { case 1 => "one" }
orElse(やandThen/compose)関数を使って関数合成が行えます。
val pf1: PartialFunction[Int, String] = { case 1 => "one" }
val pf2: PartialFunction[Int, String] = { case 2 => "two" }
val pf: PartialFunction[Int, String] = pf1 orElse pf2
pf(1) // one
pf(2) // two
pf(3) // MatchError発生
なお、上記はお馴染みのこれと同義ですね。
n match {
case 1 => "one"
case 2 => "two"
}
MapにFunction1/PartialFunctionがmix-inされている理由
もうお気づきだと思いますが、要はMapからキーを指定して値を取り出す処理というのが、引数一つを与えて結果を返すFunction1[K,V]/PartialFunction[K,V]だよねってことですね。
val map = Map(1->"one", 2->"two")
map(1) // one
ということは・・・
やはり!Listにも同様にmix-inされていました。
但しこちらはFunction1[Int, A]/PartialFunction[Int, A]で、入力は常にInt型で固定されている、というのがMapとの違いです。
興味深いですね。
val list = List("zero", "one", "two")
list(0) // zero
何が嬉しいのか?
お馴染みの関数達の中にはFunction1やPartialFunctionを引数に取るものが多いため、うまくいけばMap/Listだけで代用出来るようになり、より簡潔に記述できるようになります。
map関数
map関数は引数にFunction1を取ります。
before
戻り値はList(one, two, three, unknown)となります。
List(1,2,3,4) map { n =>
if(n == 1) "one"
else if(n == 2) "two"
else if(n == 3) "three"
else "unknown"
}
※なお、PartialFunctionはFunction1の子なので当然PartialFunctionを渡しても同様の結果を得ることが出来ます。
List(1,2,3,4) map {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "unknown"
}
after
mapはすべての要素について写像処理を行うため、マッチしないものがないようorElseで関数合成してMapを補足してやります。
List(1,2,3,4) map { Map(1->"one", 2->"two", 3->"three") orElse { case _ => "unknown" } }
collect関数
collect関数は引数にPartialFunctionを取ります。
この関数の振る舞いはfilter + mapを1度に行うもので、PartialFunctionを満たさないものは処理されません。
before
用意していない4は処理されず、戻り値はList(one, two, three)となります。
List(1,2,3,4) collect {
case 1 => "one"
case 2 => "two"
case 3 => "three"
}
after
List(1,2,3,4) collect { Map(1->"one", 2->"two", 3->"three") }
Scalaの構造の美しさに大変感動しました。
追記
@gakuzzzz さんに、SetはA=>Booleanをmixinしているという情報をtwitterで教えていただきました。
有難うございます。
val acceptable = Set(1,2)
val o = Option(1)
o.exists(acceptable)