Scala Sandbox TOP

Scala-sandbox-docs

Scala-sandboxの文書ページです.

技術情報やライブラリの使い方,FAQ(Q&A)などがここに書かれます.

なお,このページとは別にScala-sandboxのトップページがあります.

技術情報

文法

  • val
    • valで定義した変数は、一度値を設定した後は更新することができません (Javaのfinalのようなものです)。あくまで、更新できないのは変数の値で であって、変数が指している先のオブジェクトを変更することは可能であることに 注意してください(この点もJavaのfinalと同じです)。
      val a :Int = 10
      a = 20 // エラー。valで定義した変数の値を更新することはできない
      
      val a = 10 // 変数の型は省略可能
      
      val b:Array[Int] = Array(1, 2, 3)
      b(0) = 2 // OK。変数が指しているオブジェクト(配列)の内容は更新できる
      
  • var
    • varで定義した変数は、値を更新することができる変数になります (Javaの通常の変数と同じ)。
      var a :Int = 10
      a = 20 // OK。
      
      var a = 10 // 変数の型は省略可能
      
  • for
    • for文は、Javaの拡張for文と同じように使えます
      // 1から10まで1つずつ加算しながらループ
      for (i <- 1 to 10) {
        printf("{0} ", i)
      }
      
      > 1 2 3 4 5 6 7 8 9 10 
      
      // 0から9まで1つずつ加算しながらループ
      for (i <- 0 until 10) {
        printf("{0} ", i)
      }
      
      > 0 1 2 3 4 5 6 7 8 9 
      
      // Int型の配列でループ
      for (value <- Array(1, 2, 3, 4)) {
        printf("{0} ", value)
      }
      
      > 1 2 3 4 
      
    • 関数型言語でよく使われるmapやfilterにも対応しています
      // map
      val values = for (value <- Array(1, 2, 3, 4)) yield {
        value.toString
      }
      
      > Array[java.lang.String] = Array(1, 2, 3, 4)
      
      // filter
      val values = for {value <- Array(1, 2, 3, 4); if (value % 2) == 0} yield {
        value.toString
      }
      
      > Array[java.lang.String] = Array(2, 4)
      
  • ブロック
    • {式1; 式2; ... 式N}のように書きます。複数の式をまとめて一つの 式として扱いたいときに使います。最後の式以外では、変数を宣言することも できます。
      class Foo {
        val message = {
          val str1 = "Hello"
          val str2 = ", world!"
          str1 + str2
        }
      }
      
  • 無名関数
    • {引数名:型 => 処理; 返値}のように書くことで、無名の関数を生成できます。
    • 引数の型は、処理系が推論可能な場合は省略できます。
    • 配列やリストで使われるmapやfilterに渡す引数を手軽に書きたい場合などに便利です。
      // IntからStringへ変換
      Array(1, 2, 3, 4).map{value =>
        value.toString
      }
      // 2の倍数だけ抜き出す
      Array(1, 2, 3, 4).filter{value =>
        (value % 2) == 0
      }
      
  • class
    • Javaのclassとほぼ同じ意味として使え、インスタンス変数やメソッドを定義できます
      class Hello {
        def greeting(msg: String): String = {
          msg + ", world!"
        }
      }
      
      class Person {
        var _name = ""
        def name: String = {
          _name
        }
      }
      
    • クラス定義とコンストラクタを同時に書けます
      class Person(_name: String) {
        def name: String = _name
      }
      
    • コンストラクタの派生を書くことができます(注意: 元となるコンストラクタは1つしか書くことができません)
      class Person(_name: String) {
        def this() = {
          this("")
        }
        ...
      }
      
    • クラスなどを継承できます
      class Foo(foo: String) {
        ...
      }
      
      class Boo(foo: String, boo: Integer) extends Foo(foo) {
        ...
      }
      
    • getterを定義したい
      class Person(val name:String, val age:Int)
      
      val p = new Person("Ichiro", 34)
      println(p.name) // => "Ichiro"
      println(p.age) // => 34
      
    • setterを定義したい
      class Person(val name:String, var hits:Int)
      
      val p = new Person("Ichiro", 0)
      println(p.hits) // => 0
      p.hits = 100
      println(p.hits) // => 100
      
  • trait
  • object
    • シングルトンオブジェクトを定義します。
      object Hello {
        def greeting(msg: String): String = {
          msg + ", world!"
        }
      }
      
    • オブジェクト名.メソッド名(...)で定義したメソッドを呼び出すことができます。シングルトンオブジェクトを利用することで、Javaのstaticメソッドに相当することができます。
      println(Hello.greeting("Hello")) // Hello
      
  • case class
  • パターンマッチ(via http://rainyday.blog.so-net.ne.jp/2007-01-01
    • 基本系
      def matchtest(value:Any) = value match {
       case 1 => "One"
       case (a,b) => "Tuple"
       case (x :: xs) => "List"
       case Some(x) => x
       case <elem>{x}</elem> => "XML element"
       case _ => "others"
      }
      
      matchtest(1) // => "One"
      matchtest((2,3)) // => "Tuple"
      matchtest(List(4,5)) // => "List"
      matchtest(new Some(6))  // => 6
      matchtest(<elem>value</elem>) // "XML element"
      matchtest(None) // "others"
      
    • ガード
      value match {
       case (a,b) if (a > 10) => ...
      }
      
    • 無名関数として書く
      List(4,8,12,16).zipWithIndex.foreach {
       case (col, i) => ...
      }
      
  • apply
    • applyメソッドを持ったオブジェクトは、オブジェクト(引数...)という形で呼び出すことができます。
      object Add {
        def apply(lhs :Int, rhs :Int) :Int = lhs + rhs
      }
      println(Add(1, 2)) // 3
      
      class PropertiesWrapper(val p : java.util.Properties) {
         def apply(key :String) :Option[String] = p.getProperty(key)
         def apply(key :String, defaultValue :String) :String = p.getProperty(key, defaultValue)
      }
      val properties = new PropertiesWrapper(System.getProperties)
      println(properties("java.version"))
      println(prpperties("hoge", "bar"))
      
  • unapply
  • implicit
  • 演算子を定義したい
  • 単項演算子を定義したい
    • 単項の前置演算子としてユーザが定義できるのは、+,-,!,~の4種類です。
      class P {
        def unary_+ :Int = 1 // unary_`operator_name`の形で定義
      }
      println(+new P) // 1
      
      のような形でメソッドを定義および使用することができます。
    • 単項の後置演算子はScalaでは通常のメソッドと同じように定義します。
      class P {
        def + :Int = 1
      }
      println(new P+) // 1
      

def

  • 可変長引数を定義したい
    • 可変長引数を取るメソッドは以下のようにして定義することができます。最後の引数 の型名の後に*を付けることで、その引数が可変長引数になります。メソッド内部 での可変長引数の扱いは配列と似ていますが微妙に違うので、配列を受け取るメソッド などに可変長引数を渡したい場合、toArrayメソッドを使用しましょう。
      def printAll(xs :Int*) {
        for(x <- xs) print(x)
        println()
      }
      ...
      printAll(1, 2, 3) // => 123
      
  • 可変長引数を取るメソッドに配列などのオブジェクトを展開した要素を渡したい
    • 可変長引数を取るメソッドの実引数の後ろに:_*を付けることで可能です。
      def printAll(xs :Int*) {
        for(x <- xs) print(x)
        println()
      }
      ...
      printAll(Array(1, 2, 3):_*) // => 123
      
  • 「可変長引数を引数に取る関数」の型を記述したい
    • val f :Int* => Unit = ...だと構文エラーになります。 val f :(Int*) => Unit = ...のように、可変長引数の型を丸括弧でくくってください。
  • 注意:可変長引数を持つ関数のオーバロードには注意が必要
    • 可変長引数は、内部的にscala.Seqに変換される。よって、例えば以下のように引数の型が違う様に見えても、内部的には同じtest(scala.Seq)になってしまうため、コンパイル出来ない。
      def test(i:Int*   ) = "hoge"
      def test(s:String*) = "piyo"
      

FAQ(Q&A)

Javaからの移行

  • Scalaのfor文はJavaのfor文と違うようですが
    • 確かに意味はかなり違いますが,ほぼ同じ事ができます
      Java:  for(int i = 0; i < 10; i++) { ... }
      Scala: for(i <- 0 until 10) { ... }
      class Person(val name:String, var hits:Int)
      
      val p = new Person("Ichiro", 0)
      println(p.hits) // => 0
      p.hits = 100
      println(p.hits) // => 100
      
      Java:  for(int i = 0; i <= 10; i++) { ... }
      Scala: for(i <- 0 to 10) { ... }
      
      Java:  for(String str : new String[] {"aaa", "bbb", "ccc"}) { ... }
      Scala: for(str <- Array("aaa", "bbb", "ccc")) { ... }
      
  • Scalaのwhile文の中にロジックを書きたい
    • Scalaのwhile文はループの評価式しか書けませんのでブロックを使って書きます
      Java:
        int len;
        while((len = in.read(b)) > 0} { ... }
      Scala:
        val len: Int = 0
        while({len = in.read(b); len > 0}) { ... }
      
  • Javaのコレクションを使いたい
    • scala.collection.jcl._を使いましょう.Scalaによって機能が拡張されますしGenericsも扱う事ができます
  • メソッドをsynchronizedしたい
    Java:  synchronized void foo() { ... }
    Scala: def foo() = synchronized { ... }
    
  • オブジェクトをsynchornizedしたい
    Java:  synchornized(foo) { ... }
    Scala: foo.synchronized { ... }
    
  • Javaのequals相当のことをするには?
    • Anyクラスのequalsまたは==メソッドを使いましょう。逆にJavaの==相当のことをしたいときは、 AnyRefクラスのeqメソッドを使います。
  • returnしたい
    • Javaのものとほぼ同様にして使える、returnがあるので、それを利用しましょう。ただし、Scalaの場合、 最後に評価した式がメソッドの返り値になるため、returnを書かなくても済む場合が多く、returnを使う機会は Javaと比べるとそれほど多く無いでしょう。
  • break/continueしたい
    • Scalaにはbreak/continueに相当する構文はありません。例外を利用して、似た機能を持ったライブラリを作るか、標準ライブラリの高階関数をうまく 使うなどして、break/continueを使わずに済ますしか無いでしょう。

困った

  • パッケージ名に「scala」という名前があると,importできません
    • Scalaでは相対的にimportする事ができますが,パッケージ名に「scala」という名前があるとScala本来のパッケージをimportできなくなります.importするときに_root_.を先頭に 付加することで回避できますが、パッケージ名に「scala」という名前を含めない方が無難 でしょう。
      import _root_.scala.collection.mutable._
      
  • 予約語と同じ名前のメソッド(ex. Thread.yieldなど)を呼び出しできない
    • Thread.yieldの場合は,yieldが予約語で登録されているため,そのままでは呼び出しできません.この場合は,`名前`と書きます
      Thread.`yield`()
      
  • i1 & i2 != 0がコンパイルエラーになる
    • Javaと演算子の優先順位が違っているためです。括弧を使って(i1 & i2) != 0 とすることでコンパイルが通るようになります。

ソース(src)の説明

リンク