ログイン
編集不可のページディスカッション情報添付ファイル
alstamber/Scala

MMA

目次

  1. なにこれ
  2. とりあえず触ってみる
  3. 変数の定義
  4. varとval
  5. 関数定義
  6. 単独で動くプログラムをつくってみる
  7. Scalaらしいループ
  8. 配列
    1. 配列の生成
    2. 配列への代入と参照
    3. 便利な配列の生成方法
  9. リスト
    1. リストの生成
    2. リストの結合
    3. リストの先頭に要素を追加
  10. タプル
    1. タプルを作る
    2. タプルを使う
  11. クラス
  12. メソッド
  13. シングルトンオブジェクト
    1. シングルトンオブジェクトの定義
    2. シングルトンオブジェクトの性質
  14. コンパニオンクラスとコンパニオンオブジェクト
    1. コンパニオンオブジェクトの例
  15. 基本型
  16. 基本リテラル
    1. 整数リテラル
    2. 浮動小数点リテラル
    3. 文字リテラル
    4. 文字列リテラル
    5. シンボルリテラル
    6. Booleanリテラル
  17. 演算子
    1. 比較演算子について
    2. 演算子の優先順位について
    3. リッチラッパー
  18. 制御構造
    1. if式
    2. while式, do-while式
    3. for式
      1. for式の基本的な使い方
      2. Range
      3. フィルタリング
      4. 入れ子のfor式
      5. yield
    4. try-catch-finally
    5. match式
  19. ローカル関数
  20. 関数リテラル
  21. 第一級オブジェクトとしての関数リテラル
  22. プレースホルダー
  23. 部分適用
    1. 部分適用の内部的動作
    2. アンダースコアの省略
  24. クロージャ
    1. 束縛変数と自由変数
    2. 開いた項と閉じた項
    3. クロージャの定義
    4. ローカル関数としてクロージャを定義する
  25. 連続パラメータ
  26. 末尾再帰
  27. 高階関数
    1. 高階関数の例
    2. 高階関数もう一例
  28. カリー化
    1. カリー化された関数にプレースホルダーを使ってみる
    2. 3引数以上で試してみる
    3. カリー化するメリット
  29. 制御構造の生成
  30. 名前渡しパラメータ
    1. こんなことしなくても関数を渡すようにすればいいんじゃね
  31. Scala的オブジェクト指向
    1. ライブラリの仕様
    2. 抽象クラス
    3. 引数なしメソッドの定義
    4. クラスの継承
    5. メソッドとフィールドのオーバーライド
    6. コンストラクタ
    7. 補助コンストラクタ
    8. パラメータでフィールドを初期化してしまう
    9. スーパークラスのコンストラクタ呼び出し
    10. override修飾子
    11. ポリモーフィズム
    12. final
    13. 合成と継承
    14. 引数をとるメソッドを定義
    15. ファクトリーメソッド
  32. Scalaのクラス構造
    1. Anyクラス
    2. AnyValクラス
    3. AnyRefクラス
    4. Javaのプリミティブ型との関連
    5. Nullクラス
    6. Nothingクラス
  33. トレイト
    1. トレイトを定義する
    2. トレイトを使う
    3. スーパークラスを持っているクラスにミックスイン
    4. 複数トレイトのミックスイン
    5. トレイトのメソッドのオーバーライド
    6. トレイトとは結局何か
    7. トレイトを実際に使ってみる
    8. 積み重ね可能な変更
    9. 多重継承ではなくトレイトによるミックスインを採用した理由
    10. ミックスインの線形化
  34. case class
    1. ケースクラスの利点
  35. パターンマッチ
    1. 基本的なパターンマッチ
    2. 型によるパターンマッチ
    3. 変数を使ったパターンマッチ
    4. case文とmatch式の違い
    5. ワイルドカードによるマッチ
    6. 定数パターンマッチ
    7. 定数パターンマッチの注意点
    8. シーケンスパターン
    9. タプルパターン
    10. パターンの変数への束縛
    11. ケースクラスによるマッチング
    12. パターンガード
    13. sealed class
  36. Option型
    1. Option型を返す関数を作る
    2. Option型のメリット
    3. Someから値を取り出す
  37. 応用的なパターンマッチ
    1. 変数定義の際のパターンマッチ
    2. 関数リテラルの中でパターンマッチする
    3. for式でのパターンマッチ
  38. Listの実装
    1. List型
    2. Listの生成
    3. Listの基本操作
    4. Listの様々な操作
    5. リストに適用する高階メソッド
    6. 畳み込み
    7. Listコンパニオンオブジェクトに実装されているメソッド

なにこれ

とりあえず触ってみる

   1 scala> 1+1
   2 res0: Int = 2

   1 scala> res0 * 5
   2 res1: Int = 10

   1 scala> println("Hello, world!")
   2 Hello, world!

変数の定義

   1 scala> val msg = "Hello, world!"
   2 msg: String = Hello, world!

   1 scala> val msg2:String = "Hello, world!"
   2 msg2: String = Hello, world!

varとval

   1 scala> val msg = "Hello, world!!"
   2 <console>:8: error: reassignment to val
   3        msg = "Hello, World!!"

関数定義

   1 def 関数名(引数名:引数型, 引数名:引数型, ...):戻り値型 = 関数の式

   1 def hello(name:String):String = {
   2   return "Hello! " + name
   3 }

   1 def hello(name:String):String = {
   2   "Hello! " + name
   3 }

   1 def hello(name:String) = {
   2   "Hello! " + name
   3 }

   1 def hello(name:String) = "Hello! " + name

単独で動くプログラムをつくってみる

   1 object hoge
   2   def main(args:Array[String]) = {
   3     //メインの処理
   4   }
   5 }

scalac hoge.scala

scala hoge

Scalaらしいループ

   1 var i = 0
   2 while(i < args.length) {
   3   println(args(i))
   4   i += 1
   5 }

   1 args.foreach((arg:String) => println(arg))

   1 args.foreach(arg => println(arg))

配列

配列の生成

   1 val hoge = Array[String](3)

配列への代入と参照

   1 hoge(2) = "kyoko"
   2 println(hoge(2))

便利な配列の生成方法

   1 val hoge = Array("Akari", "Kyoko", "Yui", "Chinatsu")

リスト

リストの生成

   1 val hoge = List(2, 3, 5, 7)

リストの結合

   1 val hoge = List(2, 3)
   2 val fuga = List(5, 7)
   3 val piyo = hoge ::: fuga

リストの先頭に要素を追加

   1 val hoge = List(3, 5, 7)
   2 val fuga = 2 :: hoge

   1 hoge.::(2)

タプル

タプルを作る

   1 val hoge = (1, "Kyoko", 2, "Akari")

タプルを使う

   1 println(hoge._2)  // Kyoko と表示される
   2 

クラス

   1 class Hoge {
   2   var hoge = 1 //フィールド
   3   var fuga = 3 //フィールド
   4 
   5   def myMultiple() = hoge * fuga //メソッド
   6 }

   1 val a = new Hoge()

   1 a.hoge = 2  // OK
   2 

メソッド

   1 private def hello() = "Hello, World!"

シングルトンオブジェクト

シングルトンオブジェクトの定義

   1 object Hoge {
   2   // あーだこーだ
   3 

シングルトンオブジェクトの性質

コンパニオンクラスとコンパニオンオブジェクト

コンパニオンオブジェクトの例

基本型

基本リテラル

整数リテラル

浮動小数点リテラル

文字リテラル

文字列リテラル

シンボルリテラル

   1 `hoge //hogeのシンボルリテラル
   2 

   1 val hoge = "hoge"
   2 val hoge2 = "hoge" //hogeとhoge2は違うオブジェクトになっている
   3 
   4 val fuga = `fuga
   5 val fuga2 = `fuga //fugaとfuga2は同じオブジェクト
   6 

Booleanリテラル

演算子

   1 1 + 2
   2 (1).+(2)  // この2つは同じ意味
   3 

比較演算子について

   1 "hoge" == "hoge" //左と右の文字列は同じオブジェクトかも知れないし違うオブジェクトかもしれないから、TrueかもしれないしFalseかもしれない
   2 

   1 "hoge" == "hoge" //かならずTrue
   2 

演算子の優先順位について

リッチラッパー

   1 0 min 5 // min演算子で計算されて結果が0になる
   2 

制御構造

if式

   1 if (条件式) {
   2   //真の時の式
   3 }else{
   4   //偽の時の式
   5 }

   1 val hoge = "hoge"
   2 println(if (!hoge.isempty) hoge else "default")

while式, do-while式

   1 var a = x
   2 var b = y
   3 while(a != 0){
   4   val temp = a
   5   a = b % a
   6   b = temp
   7 }

for式

for式の基本的な使い方

   1 val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu")
   2 for(member <- yuruyuri){
   3   println(member)
   4 }

Range

   1 for(i <- 1 to 10) println(i) //(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)という数列が生成される
   2 for(i <- 1 until 10) println(i) //(1, 2, 3, 4, 5, 6, 7, 8, 9)という数列が生成される
   3 

フィルタリング

   1 val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu")
   2 for(member <- yuruyuri if member.endsWith("i")){
   3   println(member)
   4 }

入れ子のfor式

   1 for(i <- 1 to 9; j <- 1 to 9) println(i*j)

yield

   1 val hoge = for (i <- 0 to 5) yield i+1

try-catch-finally

   1 try{
   2   //例外が発生しそうな処理を書く
   3 }catch{
   4     case e: 例外名  => 例外処理
   5 }

match式

   1 val yuruyuri = "kyoko"
   2 yuruyuri match {
   3   case "kyoko" => println("so cute")
   4   case "akari" => println("who is she?")
   5   case _ => println("so so") // _ はdefaultに相当
   6 }

   1 val yuruyuri = "kyoko"
   2 val result = yuruyuri match {
   3   case "kyoko" => "so cute"
   4   case "akari" => "who is she?"
   5   case _ => "so so"
   6 }
   7 println(result)

ローカル関数

   1 def calc(a:Int, b:Int) = {
   2   def add() = a + b
   3   def sub() = a - b
   4   (add, sub)
   5 }

関数リテラル

   1 arg => println(arg)

第一級オブジェクトとしての関数リテラル

   1 val hoge = (x:Int) => x+1
   2 println(hoge(10))

プレースホルダー

   1 val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu")
   2 yuruyuri.foreach(println(_))
   3 // yuruyuri.foreach(x => println(x))と同じ
   4 

   1 val myadd = _ + _ //エラーが出る
   2 

   1 val myadd = (_:Int) + (_:Int)
   2 println(myadd(2, 3))

部分適用

   1 def sum(a:Int, b:Int, c:Int) = a + b + c
   2 val sum2 = sum(_:Int, 3, _:Int)

   1 println(sum2(2, 4)) //9が出力される
   2 

   1 def sum(a:Int, b:Int, c:Int) = a + b + c
   2 val sum2 = sum _

部分適用の内部的動作

   1 def sum(a:Int, b:Int) = a + b
   2 val sum2 = sum _

   1 sum2(2, 3)

アンダースコアの省略

   1 val yuruyuri = List("Kyoko", "Akari", "Yui", "Chinatsu")
   2 yuruyuri.foreach(println)
   3 // foreachメソッドの引数には呼び出す関数を必ず記述しなければならないため、println(_)のアンダースコアは省略できる
   4 

クロージャ

束縛変数と自由変数

   1 (x:Int) => x + more

   1 var more = 1
   2 (x:Int) => x + more

開いた項と閉じた項

クロージャの定義

   1 var more = 1
   2 val f = (x:Int) => x + more //これがクロージャ
   3 

   1 var more = 1
   2 var f = (x:Int) => x + more
   3 
   4 println(f(3)) //4が出力される
   5 
   6 more = 10
   7 
   8 println(f(3)) //13が出力される
   9 

   1 val list = List(1,2,3)
   2 var sum = 0
   3 list.foreach(sum += _)
   4 println(sum) //6が出力される
   5 

ローカル関数としてクロージャを定義する

   1 def myadd(more:Int) = (x:Int) => x + more
   2 
   3 val add10 = myadd(10)
   4 val add100 = myadd(100)
   5 
   6 println(add10(10)) //20が出力される
   7 println(add100(10)) //110が出力される
   8 

連続パラメータ

   1 def echo(args:String*) = args.foreach(println)

末尾再帰

高階関数

高階関数の例

   1 def listEnds(query:String) = {
   2   for(str <- list; if str.endsWith(query)) yield str
   3 }
   4 def listContains(query:String) = {
   5   for(str <- list; if str.contains(query)) yield str
   6 }
   7 def fileRegex(query:String) = {
   8   for(str <- list; if str.matches(query)) yield str
   9 }

   1 def listMatching(query:String, matcher:(String, String) => Boolean) = {
   2   for(str <- list; if matcher(str, query)) yield str
   3 }

   1 def listEnding(query:String) = {
   2     listMatching(query, (str:String, query:String) => str.endsWith(query)
   3 }

   1 def listEnding(query:String) = listMatching(query, _.endsWith(_))

   1 def listMatching(matcher:String => Boolean) = {
   2   for(str <- list; if matcher(str)) yield str
   3 }
   4 def listEnding(query:String) = listMatching(_.endsWith(query))

高階関数もう一例

   1 list.exists(_ < 0)

   1 list.exists(x => x < 0)

カリー化

   1 def plainSum(x:Int, y:Int) = x + y //カリー化されてない
   2 plainSum(2, 3)
   3 
   4 def curriedSum(x:Int)(y:Int) = x + y //カリー化されてる
   5 curriedSum(2)(3)

   1 def curriedSum2(x:Int) = (y:Int) => x + y

カリー化された関数にプレースホルダーを使ってみる

   1 val onePlus = curriedSum(1)_ //一つ目の引数に1を与えて固定してる
   2 onePlus(2) // curriedSum(1)(2)と同じ意味になる
   3 

3引数以上で試してみる

   1 def hoge(x:Int)(y:Int)(z:Int) = x + y + z
   2 val fuga = hoge(1)_
   3 fuga(2, 3) // hoge(1, 2, 3)と同じ意味
   4 

カリー化するメリット

制御構造の生成

   1 def withPrintWriter(file:File, op:PrintWriter => Unit){
   2   val writer = new PrintWriter(file)
   3   try{
   4     op(writer)
   5   } finally {
   6     writer.close()
   7   }
   8 }
   9 
  10 withPrintWriter(new File("date.txt"), writer => writer.println(new java.util.Date))

   1 def withPrintWriter(file:File)(op:PrintWriter => Unit){
   2   val writer = new PrintWriter(file)
   3   try{
   4     op(writer)
   5   } finally {
   6     writer.close()
   7   }
   8 }
   9 
  10 withPrintWriter(new File("date2.txt")){
  11   writer => writer.println(new java.util.Date)
  12 }

名前渡しパラメータ

   1 object MyLog {
   2   val isDebug = true
   3 
   4   def main(args: Array[String]) {
   5     log(heavyFunc)
   6   }
   7 
   8   def log(s: String) = { //heavyFuncのデバッグログを取るための関数
   9     if(isDebug) {
  10       println("call log")
  11       println(s)
  12     }
  13   }
  14 
  15   def heavyFunc() = {
  16     println("call heavyFunc") //実際には重い処理が用意されていると考える
  17     "hoge"
  18   }
  19 }

   1 call heavyFunc
   2 call log
   3 hoge

   1 object MyLog {
   2   val isDebug = true
   3 
   4   def main(args: Array[String]) {
   5     log(heavyFunc)
   6   }
   7 
   8   def log(s: => String) = { //ここが書き換わっている
   9     if(isDebug) {
  10       println("call log")
  11       println(s)
  12     }
  13   }
  14 
  15   def heavyFunc() = {
  16     println("call heavyFunc")
  17     "hoge"
  18   }
  19 }

こんなことしなくても関数を渡すようにすればいいんじゃね

   1 def log(s: () => String) = { //ここが書き換わっている
   2   if(isDebug) {
   3     println("call log")
   4     println(s)
   5   }
   6 }

   1 log("hoge") //エラー
   2 log(() => "hoge")) //関数リテラルとして書かないといけない
   3 

Scala的オブジェクト指向

ライブラリの仕様

抽象クラス

   1 abstract class Element {
   2   def contents:Array[String]
   3 }

引数なしメソッドの定義

   1 abstract class Element {
   2   def contents:Array[String]
   3   def height:Int = contents.length
   4   def width:Int = if(height == 0) 0 else contents(0).length
   5 }

   1 abstract class Element {
   2   def contents:Array[String]
   3   val height:Int = contents.length
   4   val width:Int = if(height == 0) 0 else contents(0).length
   5 }

クラスの継承

   1 class ArrayElement(conts:Array[String]) extends Element {
   2   def contents:Array[String] = conts
   3 }

メソッドとフィールドのオーバーライド

コンストラクタ

   1 class Hoge {
   2   println("hoge") //この部分はHogeを生成した時に実行される
   3 }

   1 class Hoge(msg : String) {
   2   println(msg)
   3 }

補助コンストラクタ

   1 class Hoge(msg : String) {
   2   def this() = this("hoge") 引数を与えなかった時のコンストラクタを定義
   3   println(msg)
   4 }

パラメータでフィールドを初期化してしまう

   1 class ArrayElement(val contents:Array[String]) extends Element //valをつけることでフィールドと同じ名前のパラメータを定義できて、直接フィールド初期化ができる
   2 

   1 class ArrayElement(private val contents:Array[String]) extends Element

スーパークラスのコンストラクタ呼び出し

   1 class LineElement(s:String) extends ArrayElement(Array(s)){ //extends ArrayElement(Array(s))と書くことで親クラスのコンストラクタにArray(s)を与えることができる
   2   override def width = s.length //親クラスをオーバーライドするのでoverrideをつけます
   3   override def height = 1
   4 }

override修飾子

ポリモーフィズム

   1 class UniformElement(
   2   // 範囲を埋め尽くす文字を指定
   3   ch:Char
   4   override val width:Int,
   5   override val height:Int
   6 ) extends Element {
   7   private val line = ch.toString * width
   8   def contents = Array.make(height, line)
   9 }

   1 val e1:Element = new ArrayElement(Array("hoge", "huga"))
   2 val e2:Element = new LineElement("hello")
   3 val e3:Element = new UniformElement('x', 3, 4) 

   1 abstract class Element {
   2   def demo() {println("Element's implementation invoked")}
   3 }
   4 
   5 class ArrayElement extends Element {
   6   override def demo() {println("Array Element's implementation invoked")}
   7 }
   8 
   9 class LineElement extends ArrayElement {
  10   override def demo() {println("Line Element's implementation invoked")}
  11 }
  12 
  13 class UniformElement extends Element

   1 val e = new ArrayElement
   2 e.demo() //Array Element's implementation invokedが出力される
   3 
   4 val e = new LineElement
   5 e.demo() //Line Element's implementation invokedが出力される
   6 
   7 val e = new UniformElement
   8 e.demo() //Element's implementation invokedが出力される
   9 

final

合成と継承

   1 class lineElement(s:String) extends Element {
   2   val contents = Array(s)
   3   override def width = s.length
   4   override def height = 1
   5 }

引数をとるメソッドを定義

   1 def above(that: Element): Element =  {
   2   new ArrayElement(this.contents ++ that.contents) // ++ は配列を結合するための演算子
   3 }
   4 
   5 def beside(that: Element):Element = {
   6   new ArrayElement(
   7     for ((line1, line2) <- this.contents zip that.contents) yield line1 + line2
   8   )
   9 }

   1 override def toString = contents mkString "\n" //mkStringは引数にとった文字列を糊にしてcontentsの要素を結合するメソッド
   2 

ファクトリーメソッド

   1 object Element {
   2   def elem(contents:Array[String]):Element = {
   3     new ArrayElement(contents)
   4   }
   5 
   6   def elem(char:Char, width:Int, height:Int):Element = {
   7     new UniformElement(char, width, height)
   8   }
   9 
  10   def elem(line:String):Element = {
  11     new LineElement(line)
  12   }
  13 }

   1 object Element {
   2   private class ArrayElement(val contents:Array[String]) extends Element
   3 
   4   private class LineElement(s:String) extends Element{
   5     val contents = Array(s)
   6     override def width = s.length
   7     override def height = 1
   8   }
   9 
  10   private class UniformElement(
  11     ch:Char,
  12     override val width:Int,
  13     override val height:Int
  14    ) extends Element {
  15     private val line = ch.toString * width
  16     def contents = Array.make(height, line)
  17   }
  18 
  19   def elem(contents:Array[String]):Element = {
  20     new ArrayElement(contents)
  21   }
  22 
  23   def elem(char:Char, width:Int, height:Int):Element = {
  24     new UniformElement(char, width, height)
  25   }
  26 
  27   def elem(line:String):Element = {
  28     new LineElement(line)
  29   }
  30 }

Scalaのクラス構造

Anyクラス

AnyValクラス

AnyRefクラス

Javaのプリミティブ型との関連

Nullクラス

Nothingクラス

   1 def error(message:String):Nothing = throw new RuntimeException(message)

トレイト

トレイトを定義する

   1 trait Philosophical {
   2   def philosophize(){
   3     println("I consume memory, therefore I am!")
   4   }
   5 }

トレイトを使う

   1 class Kyoko extends Philosophical
   2 
   3 val kyoko = new Kyoko
   4 kyoko.philosophize()
   5 I consume memory, therefore I am!

スーパークラスを持っているクラスにミックスイン

   1 class Human
   2 
   3 class Kyoko extends Human with Philosophical
   4 
   5 val kyoko = new Kyoko
   6 kyoko.philosophize()
   7 I consume memory, therefore I am!

複数トレイトのミックスイン

   1 class Human
   2 
   3 trait hasLeg
   4 
   5 class Kyoko extends Human with Philosophical with hasLeg
   6 
   7 val kyoko = new Kyoko
   8 kyoko.philosophize()
   9 I consume memory, therefore I am!

トレイトのメソッドのオーバーライド

トレイトとは結局何か

トレイトを実際に使ってみる

   1 class Rational(n:Int, d:Int) extends Ordered[Rational]{
   2   def compare(that:Rational) = //compareの値が正、負、0のどれかになるかという結果をもとに比較演算子の結果を求めるような仕組みになっている
   3     (this.numer * that.denom) - (that.numer * this.denom) //numerは分子, denomは分母に対応するフィールド
   4 }

積み重ね可能な変更

   1 abstract class IntQueue {
   2   def get():Int
   3   def put(x:Int)
   4 }
   5 
   6 import scala.collection.mutable.ArrayBuffer
   7 class BasicIntQueue extends IntQueue {
   8   private val buf = new ArrayBuffer[Int] //mutableになっている
   9 
  10   def get() = buf.remove(0)
  11   def put(x:Int){ buf += x } 
  12 }

   1 trait Doubling extends IntQueue {
   2   abstract override def put(x:Int){super.put(2 * x)}
   3 }

   1 class MyQueue extends BasicIntQueue with Doubling
   2 
   3 val queue = new MyQueue
   4 queue.put(10)
   5 queue.get() //20が出てくる
   6 

   1 val queue = new BasicIntQueue with Doubling
   2 queue.put(10)
   3 queue.get() //20が出てくる
   4 

   1 // 待ち行列の値に1加えるトレイト
   2 trait Incrementing extends IntQueue {
   3   abstract override def put(x:int){super.put(x +1)}
   4 }
   5 
   6 // 待ち行列に負の数を加えないようにするトレイト
   7 trait Filtering extends IntQueue {
   8   abstract override def put(x:int){ if(x >= 0) super.put(x)}
   9 }

   1 val queue = new BasicIntQueue with Incrementing with Filtering
   2 
   3 queue.put(-2);queue.put(-1);queue.put(0);
   4 
   5 queue.get() //1が出てくる
   6 
   7 queue.get() //エラー
   8 

多重継承ではなくトレイトによるミックスインを採用した理由

ミックスインの線形化

   1 class Animal
   2 
   3 trait Furry extends Animal
   4 trait HasLegs extends Animal
   5 trait FourLegged extends HasLegs
   6 
   7 class Cat extends Animal with Furry with FourLegged

   1 AnimalAnyRefAny //Animalはこのような階層構造になっている
   2 FurryAnimalAnyRefAny //Furryトレイトは次のような構造
   3 CatFourLeggedHasLegsFurryAnimalAnyRefAny //Catクラスはこのような構造
   4 

case class

ケースクラスの利点

   1 case class Foo(name: String)
   2 val foo = Foo("foo")

   1 case class Foo(name: String)
   2 val foo = new Foo("bar")
   3 foo.name // "bar"
   4 

パターンマッチ

基本的なパターンマッチ

   1 def hoge(i:Int):String = i match {
   2   case 1 => "1です"
   3   case _ => "1以外です"
   4 }

型によるパターンマッチ

   1 def printKind(elem: Any) = elem match {
   2     case str: String => println("String") //String型の時
   3     case i: Int => println("Integer") //Int型の時
   4     case _ => println("Other") //その他の時
   5 }

変数を使ったパターンマッチ

   1 def printKind(elem: Any) = elem match {
   2     case str: String => println("String " + str) //String型の時"String (elemの中身)"
   3     case i: Int => println("Integer " + i) //Int型の時"Integer (elemの中身)"
   4     case _ => println("Other") //その他の時
   5 }

case文とmatch式の違い

ワイルドカードによるマッチ

   1 abstract class Expr
   2 
   3 // 変数定義クラス 
   4 case class Var(name:String) extends Expr
   5 // 数値定義クラス
   6 case class Number(num:Double) extends Expr
   7 // 単項演算クラス
   8 case class UnOp(opearator:String, arg:Expr) extends Expr
   9 // 二項演算クラス
  10 case class BinOp(opearator:String, left:Expr, right:Expr) extends Expr

   1 def test(expr:Expr) = expr match {
   2   case BinOp(op, left , right) => println(expr + " は2項演算")
   3   case _ => //何も書かないと何も起きない
   4 }

   1 test(BinOp("+", Number(1), Number(3))) //「BinOp(+,Number(1.0),Number(3.0)) は2項演算」と出力される
   2 
   3 test(UnOp("+", Number(3)))  // 何も出ない
   4 

   1 def test(expr:Expr) = expr match {
   2   case BinOp(_, _ , _) => println(expr + "は2項演算")
   3   case _ => println("2項演算以外")
   4 }

   1 test(BinOp("+", Number(1), Number(3))) // 「BinOp("+", Number(1), Number(3))は2項演算」と出力される
   2 

定数パターンマッチ

   1 def describe(x:Any) = x match {
   2   case 5 => "五"
   3   case true => "真"
   4   case "hello" => "はろう"
   5   // 空リストにマッチするシングルトンオブジェクト
   6   case Nil => "空のリスト"
   7   case _ => "Other"
   8 }

定数パターンマッチの注意点

   1 import Math.{E, Pi}
   2 
   3 E match {
   4   case Pi => "Pi"
   5   case _ => "Other"
   6 } // 式の結果は"Other"になる。EとPiはそれぞれ定数とみなされる。定数パターンマッチでは完全一致しないと行けないから別物としてみなされる。
   7 

   1 import Math.{E, Pi}
   2 val pi = Math.Pi
   3 
   4 E match {
   5   case pi => "Pi"
   6   case _ => "Other"
   7 } // コンパイルエラー。最初のcase piが変数のパターンマッチになりここにすべてのオブジェクトがマッチしてしまう。なのでcase _が余分なものと判断されエラー
   8 

シーケンスパターン

   1 def test(list:Any) = list match {
   2   case List(0, _, _) => println("hoge")
   3   case _ =>
   4 }

   1 def test(list:Any) = list match {
   2   case List(0, _*) => println("hoge")
   3   case _ =>
   4 }

タプルパターン

   1 def testTuple(tuple:Any) = tuple match {
   2   case (a, b, c) => println("triple tuple")
   3   case _ => 
   4 }

パターンの変数への束縛

   1 abstract class Expr
   2 / 数値定義クラス
   3 case class Number(num:Double) extends Expr
   4 // 単項演算クラス
   5 case class UnOp(opearator:String, arg:Expr) extends Expr
   6 
   7 // 絶対値演算2回を1回に置き換える処理
   8 // 絶対値演算は1回やっても2回やっても同じ結果になる
   9 def test(expr:Expr) = expr match{
  10   case UnOp("abs", e @ UnOp("abs", _)) => e
  11   case _ =>
  12 }

ケースクラスによるマッチング

   1 abstract class Name
   2 case class FirstName(name: String) extends Name
   3 case class LastName(name: String) extends Name
   4 def printName(name:Name): Unit = {
   5   name match {
   6     case FirstName(name) => println(name)
   7     case LastName(name) => println(name.toUpperCase)
   8     case _ =>
   9   }
  10 }
  11 printName(FirstName("Martin"))   // "Martin"
  12 printName(LastName("Odersky")) // "ODERSKY"
  13 

パターンガード

   1 def guardPattern(e:Expr) = e match {
   2   case BinOp("+", x ,x) => BinOp("*", x, Number(2)) //パターンにxが2回出てきているのでコンパイルエラーになる
   3   case _ => e
   4 }

   1 def guardPattern(e:Expr) = e match {
   2   case BinOp("+", x ,y) if x == y => BinOp("*", x, Number(2)) 
   3   case _ => e
   4 }

sealed class

   1 sealed class Hoge
   2 class Fuga extends Hoge
   3 class Piyo extends Hoge
   4 
   5 def describe(e:Hoge):String = e match {
   6   case Fuga => "Fuga"
   7 }

Option型

Option型を返す関数を作る

   1 def findFile( filename:String):Option[File] = {
   2   val file = new File(filename)
   3   if( file.exists ) Some(file)  // Fileが存在したらSomeに入れて返す
   4   else None                     // ないならNoneオブジェクトを返す
   5 }

Option型のメリット

Someから値を取り出す

   1 val file = findFile("hoge")
   2 file match {
   3   case Some(f) => "exists! " + f.getName
   4   case None => "Nothing!"
   5 }

応用的なパターンマッチ

変数定義の際のパターンマッチ

   1 val testTuple = (123, "abc")
   2 val (number, str) = testTuple //numberに123, strに"abc"が束縛される
   3 

関数リテラルの中でパターンマッチする

   1 val withDefault:Option[Int] => Int = { //Option[Int]を受け取ってIntを返すような関数リテラルを定義した!
   2   case Some(x) => x
   3   case None => 0
   4 }

for式でのパターンマッチ

   1 val greetings = List(("hello", "scala"), ("hi", "python"), ("bye" , "php"))
   2 for ((greeting, language) <- greetings) println(greeting + ' ' + language) //Listの要素のTupleを取り出して1つ目の要素をgreeting, 2爪の要素をlanguageに束縛している
   3 

Listの実装

List型

Listの生成

   1 val list = 1 :: 2 :: 3 :: Nil //終わりを明示するため最後はNilにしないといけない
   2 

Listの基本操作

Listの様々な操作

   1 List("a","b","c","d","e").indices // List(0, 1, 2, 3, 4)が返ってくる
   2 

   1 List(1,2,3,4,5).zip(List("a","b","c","d","e")) // List((1,"a"), (2,"b"), (3,"c"), (4,"d"), (5,"e"))が返ってくる
   2 

   1 val arr = Array(1,2,3,4,5,6,7,8,9)
   2 List(1,2,3,4,5).copyToArray(arr, 3) // Array(1, 2, 3, 1, 2, 3, 4, 5, 9)が返ってくる
   3 

リストに適用する高階メソッド

畳み込み

Listコンパニオンオブジェクトに実装されているメソッド

   1 val list = List.apply(1, 2, 3) //listにList(1, 2, 3)が束縛される
   2 

alstamber/Scala (最終更新日時 2013-03-09 01:51:21 更新者 alstamber)