Mapのメソッド

基本操作

val map = Map(1->"one", 2->"two", 3->"three") // 生成

map + (4->"four") // キー・値の追加
map + (4->"four", 5->"five") // キー・値の追加
map - 2 // キーを指定してキー・値の削除

map.size // 長さを得る 3

map(1) // 値取得 one
map.apply(1) // 上と同じ
map.get(1) match { // 値取得
  case Some(v) => v
  case None => "Unknown"
}
map.getOrElse(1, "Unknown") // 上に同じ

map.contains(5) // 指定されたキーがあるかどうか false

map.keySet // キーのSetが返る Set(1,2,3)
for(k <- map.keys) println(map(k)) // キーの繰り返し
for(v <- map.values) println(v) // 値の繰り返し
for(kv <- map) println("%d:%s".format(kv._1, kv._2)) // キー・値の繰り返し

より高度な操作

// key-valueのうち、keyの値でフィルタ
map.filter((kv) => kv._1 >= 2) // キー1がフィルタされたMapが返る
map.filterKeys(_ >= 2) // 上と同じ

// key-valueに対して操作し、結果をリスト or マップで返す
map.map((kv) => "%d:%s".format(kv._1, kv._2))  // List(1:one, 2:two, 3:three)
map.map { case (k,v) => (k,v*k) } // Map(1->"one", 2->"twotwo", 3->"threethreethree")

// 全valueに対して関数を実行した結果をMapで返す
val jap = Map("one"->"ICHI", "two"->"NI", "three"->"SAN")
map.mapValues((v) => jap(v)) // Map(1->"ICHI", 2->"NI", 3->"SAN")

// キーが奇数の時のみ"キー:値"という形式のListを返す
map.collect {
  case(k, v) if (k % 2 == 1) => "%d:%s".format(k,v) // List(1,3)
}

Listのメソッド

基本操作

val list = List(1,2,3) // 生成
val list = List.range(1,4) // 生成 List(1,2,3)
val list = List(1 to 3: _*) // 生成 List(1,2,3)

val list = List.empty // 空リストの生成

0 +: list  // 先頭に要素を追加 List(0,1,2,3)
0 :: list // 上と同じ
list :+ 4 // 最後に要素を追加 List(1,2,3,4)
list ++ List(4,5,6) // リスト同士の結合 List(1,2,3,4,5,6)
list ::: List(4,5,6) // 上と同じ

list.length // 要素の長さを得る 3
list.size // 上と同じ

list(2) // 要素の取得 3
list.apply(2) // 上と同じ

list.head // 先頭要素の取得 1
list.last // 最終要素の取得 3
list.init // 最終要素を除くリスト List(1,2)
list.tail // 先頭要素を除くリスト List(2,3)

list.max // 最大要素の取得 3
list.min // 最小要素の取得 1

List(1,2,3,4,5).sum // 要素の合計 15 (1+2+3+4+5)
List(1,2,3,4,5).product // 要素の積 120 (1*2*3*4*5)

list.isEmpty // 要素が空かどうか false
list.nonEmpty // 要素が空でないかどうか true

list.contains(2) // 含むかどうか true

list.indexOf(2) // 要素2が何番目にあるか 1
List(1,2,3,2,1).lastIndexOf(2) // 要素2が最後から数えて何番目にあるか 3

List(2,1,5,4,3).sorted // 並び替え List(1,2,3,4,5)
list.reverse // 逆順で並び替え List(3,2,1)

List(1,2,2,5,4,5).distinct // 重複を除く List(1,2,5,4)

List(1,2,3,4,5,6).slice(2,4) // 部分リストの取得 List(3,4)
List(1,2,3,4).splitAt(2) // 添字2で分割しタプルを返す (List(1,2), List(3,4)

List(1,2,3,4,5).mkString(":") // 連結 "1:2:3:4:5"

より高度な操作

// 関数実行結果がtrueな最初の要素を得る Some(3)
List(1,2,3,4,5).find(_ > 2) 

// 関数実行結果がtrueなもののみフィルタ
List(1,2,3,4,5,6).filter(_ % 2 == 0) // List(2,4,6)
// 関数実行結果がfalseなもののみフィルタ
List(1,2,3,4,5,6).filterNot(_ % 2 == 0) // List(1,3,5)

// 全要素への関数適用(2倍)
List(1,2,3,4).map(_ * 2) // List(2,4,6,8)

// filterとmapの組み合わせ List(4,8)
List(1,2,3,4).collect {
  case x if (x % 2 == 0) => x * 2
}

// 要素に対して関数を適用し返ってきたコレクションを連結する
List(1,2,3,4).flatMap(1 to _) // List(1,1,2,1,2,3,1,2,3,4)

// 先頭2要素のみ取り出したリストを返す
List(1,2,3,4,5).take(2) // List(1,2)
// 後方2要素のみ取り出したリストを返す
List(1,2,3,4,5).takeRight(2) // List(4,5)
// 先頭2要素を除いたリストを返す
List(1,2,3,4,5).drop(2) // List(3,4,5)
// 後方2要素を除いたリストを返す
List(1,2,3,4,5).dropRight(2) // List(1,2,3)

// 要素ごとに関数を適用しresultに格納
List(1,2,3,4).reduceLeft((result,a) => result + a) // 10 (1+2+3+4)
// reduceLeftのresultに初期値を与える
List(1,2,3,4).foldLeft(10)((result,a) => result + a) // 20 (10+1+2+3+4)
// foldLeftの演算途中結果のコレクションを返す
List(1,2,3,4).scanLeft(10)((result,a) => result + a) // List(10, 11, 13, 16, 20)

// 関数適用してtrueなものがあるかどうか
List(1,2,3,4,5).exists(_ > 4) // true
// 全要素が関数実行結果がtrueかどうか
List(1,2,3,4,5).forall(_ > 0) // true

//  要素に対する関数実行のtrue/falseで分けたタプルを返す
List(1,2,3,4,5).partition(_ > 2) // (List(3,4,5), List(1,2))

// 2ずつ移動したListのIteratorを返す
for(x <- List(1,2,3,4,5).grouped(2)) println(x)
// List(1,2)
// List(3,4)
// List(5)

// 2ずつ移動したListのIteratorを返す
for(x <- List(1,2,3,4,5).sliding(2)) println(x)
// List(1,2)
// List(2,3)
// List(3,4)
// List(4,5)

// 戻り値をキーにMapを生成 Map(even->List(2,4,6), odd->List(1,3,5)
List(1,2,3,4,5,6).groupBy {
  case x if (x % 2 == 0) => "even"
  case _ => "odd"
}

Scalaの基本文法(忘れそうなもの)

for

1〜10まで3刻みで。

for(i 1<- 1 to 10 by 3)  println(i)

1〜20(20は含まない)のうち、2と3の倍数だけ。

for(i <- 1 until 20 if i % 2 == 0; i % 3 == 0)  prinltn(i)

2重ループ

for(i <- 1 to 2; j <- 1 to 4) println(i + "-" + j)

戻り値を返すfor。

val result = for(i <- 1 to 10) yield i*2

// for-yield文は以下と同じイメージ
val result = (1 to 10).map(_*2)

コレクション

1〜100までのリストの生成。

val list = List(1 to 100) // こうするとList内に長さ100のVectorが1つ格納されたリストが出来る
val list2 = List(1 to 100: _*) // 目的を達するのはこちら
val list3 = List.range(1, 100) // こっちのがいいか

関数について

関数定義

通常はメソッドとして宣言される。

def 関数名[型パラメータ](引数名:引数の型名,……):返り値 = 関数本体

たとえば、

def sum(x:Int, y:Int) = x + y

関数リテラル

変数に代入可能。メソッドの引数としても。

(変数名:型名,……) => 関数本体

たとえば、

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

高階関数

引数に関数をとる関数。
第一引数がIntで、第二引数は関数。第一引数の数字をなんらか加工して返す関数をとる。

def exe(x:Int, func:(Int)=>Int) = {
  println(x + " -> " + func(x))
}

exe(5, (x:Int) => x * 9) // 5 -> 45

引数なし、戻り値なしの関数を引数に取る場合は、

def exe(func:() => Unit) = {
  println("start")
  func()
  println("end")
}

exe(() => println("hello"))  // hello

上と似ているけど、コードブロックを引数にとる場合は、

def exe(func: => Unit) = {
  func
}

exe {
  val list = List(1,4,100,55,2)
  println(list.max)
}  // 100

Scala流ダックタイピング

rubyの例。

def execute_with_log(f)
  puts "start"
  f.execute()
  puts "end"
end

って定義しておけば、引数にはexecuteメソッドを持つクラスのオブジェクトはなんでも渡せる。
Javaではこれは実現できなくて、引数で宣言した型(もしくはインタフェース)のオブジェクトか、そのサブクラスに限定される。
ScalaではStructural Typeという方法でコンパイラの恩恵を受けながらもダックタイピングが可能になる。

class X {
  def exe(callable: { def call(num:Int):Unit; def call2(msg:String):String }, num:Int) = {
    callable.call(num)
    println(callable.call2("duckTyping!"))
  }
}

exeメソッドはInt引数を取って戻り値無しのcallという名前のメソッドと、String引数を取って戻り値Stringのcall2という名前のメソッドを持つクラスならなんでも受け取ることが出来る。
では実行。

class A  {
  def call(n:Int) = { println("A" * n) }
  def call2(msg:String):String = "%s in A".format(msg)
}

class B extends A {
  override def call(n:Int) = println("B" * n)
  override def call2(msg:String):String = "%s in B".format(msg)
}

class C {
  def call(n:Int) = println("C" * n)
  def call2(msg:String):String = "%s in C".format(msg)
}

class D {}

def main(args: Array[String]):Unit = {
  val a = new A()
  val b = new B()
  val c = new C()
  val d = new D()

  val x = new X()

  x.exe(a, 1)
  x.exe(b, 2)
  x.exe(c, 3)
  //x.exe(d)  // コンパイルエラー。
}

exe引数のところ、持つべきメソッドが複数あると書くのが大変になってくるので別に定義しておくとスマートになります。

type Callable = {
  def call(num:Int):Unit
  def call2(msg:String):String
}

class X {
  def exe(callable: Callable, num: Int) = {
    callable.call(num)
    println(callable.call2("duckTyping!"))
  }
}

DuckTypingって動的型付け言語特有の機能かと思っていましたが、静的型付け言語の恩恵に預かりながら安全に実現できるって素敵です。

部分適用とカリー化

部分適用

まず普通の関数。

def sum(a:Int, b:Int, c:Int) = a + b + c
val result = sum(1,3,5) // 9

コレをこんなふうに出来る。第三引数を5に固定。

val sum2 = sum(_:Int, _:Int, 5)
val result = sum2(1,3)  // 9

カリー化

引数が元の関数の最初の引数で、残りの引数を取って結果を返す関数を返す関数のこと。
先ほど定義したsum関数をカリー化するには・・

val sumCurry = (sum _).curried
currySum: Int => (Int => (Int => Int)) = <function1>

実行するには・・

sumCurry(1)(2)(3) // 6

val sumCurry1  = sumCurry(1)  // sumCurry1: Int => (Int => Int) = <function1>
val sumCurry2 = sumCurry1(2)  // sumCurry2: Int => Int = <function1>

sumCurry2(3) // 6

定義としては次のようにすることも。

def sumCurry(a:Int)(b:Int)(c:Int) = a + b + c

実行は

val sumCurry1 = sumCurry(1)  // sumCurry1: Int => (Int => Int) = <function1>
val sumCurry2 = sumCurry1(2)  // sumCurry2: Int => Int = <function1>

sumCurry2(3) // 6

今のところ、こんなシーンで使ったら威力発揮しそう!というのは見えていませんw
とりあえず現時点では知識として整理しておくにとどめます。