基礎を終えた人のScalaミニtips

どう書く?orgでScalaを書いてくれる人もでてきたので、超一部の方向けにScalaのことでも書いてみようと思います。言うなれば、基礎を終えた人のScalaミニtips。知っている人は知っている、でもあまり知られていないことを並べていきます。

下に行くほどマニア度あがります、たぶん。FPよりの話が多いかもしれません。はやりのYコンビネータの話とか。たぶん。

ではいってみましょう。

Predefされているものは把握しておきましょう

Scalaにはscala.Predefというオブジェクトがあります。この中で定義されているものは常にインポートされていて使える状態になっているので把握しておきましょう。

例:

 1def exit(status: Int): Nothing = {
 2  java.lang.System.exit(status)
 3  throw new Throwable()
 4}
 5
 6def assert(assertion: Boolean) {
 7  if (!assertion)
 8    throw new java.lang.AssertionError("assertion failed")
 9}
10
11def print(x: Any) = Console.print(x)
12def println() = Console.println()
13def println(x: Any) = Console.println(x)

C言語ライクなforはどうかくの?

こうです。

1val a = Array(1,2,3,4,5)
2var i= -1;while({i += 1; i < a.size;}) {
3  println(a(i))
4}

setterを使おう

setHogeみたいなのは、カッコわるいです。RubyのアクセッサーしかりPythonのpropertyしかり、 instance.name = value 形式で扱えるのがモダンな言語というものです。

1class Test {
2  var _name = "default"
3  def name_=(newValue:String) { _name = newValue }
4  def name = _name
5}
6
7val a = new Test
8a.name = "new"
9println(a.name)

単項演算子も定義できます。

できます。

 1class Test {
 2  var _name = "default"
 3  def name_=(newValue:String) { _name = newValue }
 4  def name = _name
 5  def unary_- = "unary:" + name
 6}
 7
 8val a = new Test
 9a.name = "new"
10println(-a)

applyでオブジェクトをメソッドのように呼び出せるよね? hoge(index) = value はオーバーライドできないの?

できますよ。なんとupdateというすげーふつーの名前のメソッドを定義するんです。

 1object dictionary {
 2val data = Array(null, "A","B","C")
 3
 4def apply(x:String) = x match {
 5  case "one" => data(1)
 6  case "two" => data(2)
 7  case "three" => data(3)
 8}
 9
10def update(x:String,y:String) = x match {
11  case "one" => data(1) = y 
12  case "two" => data(2) = y
13  case "three" => data(3) = y
14}
15
16}
17dictionary("one") = "X"   
18dictionary("two") = "Y"
19dictionary("three") = "Z"
20println(dictionary("one")+","+dictionary("two")+","+dictionary("three"))

可変長引数は取れますか。また、リストや配列を展開してメソッドにわたせますか。

もちろん。

1def sumPlus(plus: Int, n: Int*) = plus + sum(n :_*)

JAVAのObject型可変長変数をとるメソッドはどう呼びますか?

ちょっとめんどくさいですが、こうです。

1String.format("%d %s", List(1, "hoge").map(_.asInstanceOf[AnyRef]).toArray)

インスタンスのメソッドを束縛できますか?そのとき、メソッドがオーバーロードされている場合はどうしますか?

こうします。オーバーロードされている場合は _ の後ろに型をつけます。

1val format = (new SimpleDateFormat("dd")).format _:Date => String

ここからFPよりです。

カリー化はできますか?

Function.curriedを使います。uncurriedもあります。

1def test(i:int, j:int) = {
2  printf("i:{0}, j:{1}", i, j)
3}
4val f = Function.curried(test _)(1)
5f(1)

遅延評価は?

lazyを使います。Streamも使いこなせるとハッピーです。

たとえば、無限フィボナッチ数列は以下のように定義します。

1lazy val fib: Stream[Int] = Stream.cons(0,
2     Stream.cons(1, fib.zip(fib.tail).map(p => p._1 + p._2)))

先生、関数合成がしたいです・・・

Haskellでは短く直感的にかけるからいいですよね。

Scalaでは正直、逆に長ったらしくなりますが、どうしてもというなら・・・

1({ x:int => x + 1 } andThen { x:int => x * 2 })(3) // => 8
2({ x:int => x + 1 } compose { x:int => x * 2 })(3) // => 7

forはモナドに使える構文です。リスト内包表現用ではありません。

リストはモナドですし、HaskellのMaybeに相当するOptionというモナドもあります。map, flatMap, filterメソッドを実装すればforで書けるオブジェクトを定義できます。

Yコンビネータとかいうものの話が(一部で)盛り上がってますが、そういうのってScalaでできるんですか。型があるから複雑だと思うんだけど。

えーと、無名関数で再帰したいんでしょうか?こんな感じでどうでしょう?ふつーはこんなの使いませんけどね。

1def Y[A,B](f:((A=>B),A)=>B,x:A):B = f((x1:A)=>Y(f,x1),x)
2
3println(
4  Y(
5    (f:((int,int)) => int, arg:(int,int)) => arg match { case (x, y) => x match {
6      case 0 => y
7      case _ => f((x - 1, y + x))
8    }} , (10, 0))
9)

さて、数少ない日本人Scala好きの皆さん。どれくらいご存知でしたでしょうか。まぁ、基礎からちょっとScalaやればこれくらいは皆さんご存知だと思うんですが、やはりScalaはカオスな言語なので、なかなか見つけられない機能も多いんで、最近Scalaやりはじめた人は参考にしてもらえるとうれしいです。

comments powered by Disqus