Scala By Example を読みます。
資料: http://www.scala-lang.org/docu/files/ScalaByExample.pdf
1. Introduction
いんとろだくしょん
2. A First Example
imperative に書いたクイックソートの例。
def sort(xs: Array[Int]) { def swap(i: Int, j: Int) { val t = xs(i); xs(i) = xs(j); xs(j) = t } def sort1(l: Int, r: Int) { val pivot = xs((l + r) / 2) var i = l; var j = r while (i <= j) { while (xs(i) < pivot) i += 1 while (xs(j) > pivot) j -= 1 if (i <= j) { swap(i, j) i += 1 j -= 1 } } if (l < j) sort1(l, j) if (j < r) sort1(i, r) } sort1(0, xs.length - 1) }
だいたい見たまま。ポイントは
- var は mutable な変数の宣言、val は immutable な変数の宣言。
- 局所関数を定義できる。自由変数も OK 。これはいいな。
functional に書いたクイックソートの例。
def sort(xs: Array[Int]): Array[Int] = { if (xs.length <= 1) xs else { val pivot = xs(xs.length / 2) Array.concat( sort(xs filter (pivot >)), xs filter (pivot ==), sort(xs filter (pivot <))) } }
(pivot >) は x => pivot > x の略記。Haskell と同じ。ただし (< pivot) とは書けない。例によって、記号を特別扱いしているわけじゃない。なので
val a = Array(1, 2, 3) (a concat)(a) // ArrayBufferRO(1, 2, 3, 1, 2, 3)
ということもできる。
Scala は while を自分で定義できるぜ!の例。
def While (p: => Boolean) (s: => Unit) { if (p) { s; While(p)(s) } }
(p: => Boolean, s: => Unit) でなく (p: => Boolean) (s: => Unit) なのは、カリー化した関数を定義するためであろう。
引数の型が Boolean でなく => Boolean なところがポイントなんだろうけど、わかったようなわからないような。
あと、return キーワードと、メソッド本体の前に = をつけるとかつけないとか書いてあるけど、よくわからない。
def foo { 1 } // foo: Unit def foo: Unit { 1 } // error: illegal start of declaration def foo: Int { 1 } // error: illegal start of declaration def foo = { 1 } // foo: Int def foo: Unit = { 1 } // foo: Unit def foo: Int = { 1 } // foo: Int def foo { return 1 } // foo: Unit def foo: Unit { return 1 } // error: illegal start of declaration def foo: Int { return 1 } // error: illegal start of declaration def foo = { return 1 } // error: method foo has return statement; needs result type def foo: Unit = { return 1 } // foo: Unit def foo: Int = { return 1 } // foo: Int
うーん、難しい。return 1 しても () が帰ることがあるとか不気味。
3. Programming with Actors and Messages
Actor を使ってオークションをシミュレーションするような感じの例。
- メッセージを種別ごとにケースクラスで設計している。
- メッセージ送信は client ! Status(maxBid, closing) とか。例によって ! はただの識別子。この文法は使い出があるなあ。
他に面白い話はなかった。
4. Expressions and Simple Functions
ここから文法ごとにフォーカスを当ててこまごまと説明。
4.1. Expressions And Simple Functions
定義と値定義の違い。
- 定義 (def x = e) のときは e を評価しない。x が使われるたびに e が評価される。
- 値定義 (val x = e) のときはすぐに e を評価する。x は e の評価結果に置き換わる (e を再評価しない) 。
んー。
式から値への段階的な簡単化を簡約 (reduction) という。うん。
4.2. Parameters
関数の仮引数には型指定が必須。うーん、まあしょうがないのかな。
call-by-value と call-by-name の話。Scala ではデフォルトで call-by-value だけど、仮引数の型を => ではじめれば call-by-name になるよ。
def loop: Int = loop def first(x: Int, y: Int) = x first(1, loop) // 無限ループ def first(x: Int, y: => Int) = x first(1, loop) // 1 が返る
うっひょー。関数を使う人 (= 呼び出す人) には、実引数が評価されるかどうかわからんところが熱いですね。
4.3. Conditional Expressions
if-else は値を返すよ。普通のこと。
Scala の真偽値は true と false 。! 、&& 、|| は、まあ普通に動く。ということは ! だけは識別子じゃないのかな。
4.4. Example: Square Roots by Newton's Method
スルー。
4.5. Nested Functions
さっきも出てきた関数のネスト定義。というか局所関数。
関数型プログラミングのスタイルでは小さいヘルパ関数をよく作るんだけど、名前空間を汚さずに関数を作れるし使える局所関数はとても便利。Ruby でできないのが歯がゆい。Ruby は呼び出し形式によってメソッドを関数と見立てるパラダイムなのでしょうがないんだけど、Scala がこのあたりをどうしてるかはまだわからない。どうもしてないのかな。
{ ... } はブロックで、ブロックは式の一種。ブロックの中に入るのが文か式かよくわからないけど、式っぽいことが書いてある。けど実際には文みたいのが入ってることが多いことからみて、Ruby 同様に文と式の区別はたぶん曖昧なんだろう。要するに、Ruby の ( ... ) と同じと思えばよさそうか。{ ... } の中の補助定義はこの中だけで見える。
ブロックの中の定義の後にはセミコロンが必須。ただし以下のいずれか場合には、行の最後に勝手にセミコロンが補完される。
- その行が、式の終わりにならない語で終わっている場合 (ピリオドとか中置演算子とか)
- 次の行が、式の始めにならない語で始まってる場合
- () か [] の中の場合 (これらの中には複数の文が入り得ないのでわかるとのこと)
以下は全部 leagl:
def f(x: Int) = x + 1; f(1) + f(2) def g1(x: Int) = x + 1 g(1) + g(2) def g2(x: Int) = {x + 1}; /* `;' 必須 */ g2(1) + g2(2) def h1(x) = x + y h1(1) * h1(2) def h2(x: Int) = ( x // 括弧がないとダメ + y // でないと x の後にセミコロンが挿入されちゃう ) h2(1) / h2(2)
ということで、定義の扱いがおぼろげながら見えてきた。
- def f = { 1 } の { ... } は式をまとめてるだけ。実際、def f = 1 と書いてもいい。def が右辺値をすぐに評価しないので、関数定義になる。
- val f = { 1 } だとすぐに評価されるので、val f = { println("foo!"); 1 }; f; f とかやっても foo! は一回しか出力されない。
- call-by-name な仮引数を使うことで、Ruby のブロックみたいな記述ができる?
def foo(f: => Unit) { f; f; f } foo({ println("foo!") }) // foo! を 3 回表示 foo { println("foo!") } // 同じ、括弧が省略できる条件はわかってない
まあ、Ruby のブロックみたいな記述ができるのは狙ってるわけではないかな。このブロックは仮引数が取れないし。
以上、長々と書いたけど大したことは言ってない。