LoginSignup
22
21

More than 5 years have passed since last update.

MapにFunction1/PartialFunctionがmix-inされている理由(美しい・・)

Last updated at Posted at 2014-03-31

はじめに

Mapのscaladocを見ていた時、継承しているもの一覧中に異質なものを見つけました。
他のはなんとなくわかると思うのですが、Function1? PartialFunction?? んんん??
scala.png

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されていました。
scala.png

但しこちらは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)
22
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
21