次回ハンズオン勉強会の告知です。
第4回は会議室1がとれなくてちょっと狭い部屋でしたが
次回は会議室1を確保したのでお間違えなきようご注意下さい。
-----
第5回 JavaエンジニアのためのScalaハンズオン勉強会
[日時]
2011年7月7日(木) 19:00~20:00
[場所]
エムスリー株式会社 12F来客用会議室1
http://corporate.m3.com/corporate/overview/map.html
[題材]
コレクションの扱い
「for式、ジェネレータ、foreachやmapなど」
-----
例のごとく、以下のブログ記事をネタに進めます。
http://d.hatena.ne.jp/seratch2/20110429/1304072372
ただ、この記事はボリュームが少ないし、色々もっと伝えるべき事があるはずなので
ちょっと考えてみます。
お疲れ様でした。
反省点として、会議室が暑かったので
(定時後はビル全体でエアコンがオフになるのです・・)
次回から始めるときに窓を開けるようにしましょう、ということで。
あと、今回も勉強会中に出た疑問点は宿題という事で
何か進展がありましたらぜひシェアをお願いします。
1.マッチ式が省略できるメカニズムについて
以下のような例で
マッチ式を省略できるメカニズムについて
知りたいという疑問があがりました。
List(0,1,2,3,4,"S") foreach {
each => each match {
case i: Int => println(i)
case _ => println("NaN")
}
}
List(0,1,2,3,4,"S") foreach {
case i: Int => println(i)
case _ => println("NaN")
}
2. Streamを使った実例が知りたい
collection.immutable.Streamを
うまく使ったコードが見たいという話が出ました。
よろしくお願いします。
Streamについて少し調べてみました。
ネタは「Scala By Example」のChapter 12です。
http://www.scala-lang.org/docu/files/ScalaByExample.pdf
p.101に
---
For instance, we can find the second prime number between 1000 and
10000 by applying methods filter and apply on an interval stream:
Stream.range(1000, 10000) filter isPrime at 1
The difference to the previous list-based implementation is that now
we do not needlessly construct and test for primality any numbers
beyond 1013.
---
とあります。
「at 1」は今のScalaのシンタックスだとNGなので、古いバージョンでの書き方?かな。
「find the second prime number」ということは「apply(1)」で同じことになるはずなので以下の通り、REPLで試しました。
def isPrime(n: Long): Boolean = Stream.range(2, n) forall (x => n % x != 0)
Stream.range(1000L,10000L) filter isPrime apply(1)
Stream.range(1000L,100000000000L) filter isPrime apply(1)
試していただければ分かる通り、両者とも同じパフォーマンスが得られました。
つまり、要素を指定した場合は、上記の記述の通り、その要素以降は評価されていないことがわかります。
一方、要素を指定しなかった場合はどうなるかというと・・
単にtailが遅延評価されるだけでデータは全部メモリ上にロードされてしまいます。
Stream.range(1000,10000) filter(_<1100) filter isPrime foreach { println }
Stream.range(1000,100000000000L) filter(_<1100) filter isPrime
foreach { println }
上記の後者の例だと、前者と同様にprintするところまではすぐに終わりますが
その後、何やら処理が返ってきません。我慢して待っているとそのうちOOMになります。
どうやら「Stream.range(1000,100000000000L).force」をメモリにロードしている様子です。
また、上で定義しているisPrimeについても
内部処理でListを使うかStreamを使うかで挙動は異なります。
def isPrimeWithStream(n: Long): Boolean = Stream.range(2, n) forall
(x => n % x != 0)
def isPrimeWithList(n: Long): Boolean = List.range(2, n) forall (x =>
n % x != 0)
たとえばこのように呼び出してみるとパフォーマンスの差が歴然です。
というか、後者はREPLで実行するとOOMになると思います。
isPrimeWithStream(12345678L)
isPrimeWithList(12345678L)
List.range(2,n)の時点で無条件に全件をメモリにロードするListに対して
Streamの場合はforallの判定条件にマッチしないものが見つかった時点までしか
要素をロードしないという最適化が効いている感じですね。
ということで、このような違いを踏まえた上で
Streamをうまく使うとよさそうです。
コメントや突込みなどありましたら
ぜひよろしくお願いします。
2011/7/7 Kazuhiro SERA <ser...@gmail.com>:
なるほど。Streamの遅延評価のメリットってことなんですね。
Streamの無限リストとして使いどころもなんとなく見えてきましたけど
きっと普通(って何かはあれですが)のWebアプリでは
必要になるような問題にぶちあたることはあまり無いんですかね。
--
Naoki Tanimura
★★
∠
> Scalaコンパイラは内部的にパターンマッチを、PartialFunction[A, B]へと変換するため、
> 1つの関数を引数としてとるメソッドにパターンマッチを送る事ができる
あ、なるほど。つまり、こういう事ですね。
val partialFunc: PartialFunction[Any,Unit] = {
case i: Int => println(i)
case _ => println("NaN")
}
List(0,1,2,3,4,"S") foreach partialFunc
val func = (each:Any) => each match {
case i: Int => println(i)
case _ => println("NaN")
}
List(0,1,2,3,4,"S") foreach func
2011/7/14 Naoki Tanimura <naoki.t...@gmail.com>:
> --
> ---
> Daimon.scala
> http://j.mp/daimonscala
> http://j.mp/daimonscalag
>
Listのcollectとか・・
List(1,2,3) collect { case(i) if i > 1 => i * 2 } // List(4,6)
val pf : PartialFunction[Int,Int] = { case i if i > 1 => i * 2 }
List(1,2,3) collect(pf) // List(4,6)
あとはcatch節なんかでよく使うと思います。
def nullpoga() = {
try {
throw new NullPointerException
} catch {
// ここはPartialFunction
case npe:NullPointerException => println("ぬるぽ")
case _ =>
} finally {
println("ガッ")
}
}
nullpoga()
以下だとThrowable型を一つ受け取ってUnit型の戻りになる関数で
イメージとしてはmatch式でfilterした後でmapしてる感じですね。
val catchPf: PartialFunction[Throwable,Unit] = {
case npe:NullPointerException => println("ぬるぽ")
case _ =>
}
def nullpoga2() = {
try {
throw new NullPointerException
} catch { catchPf
} finally {
println("ガッ")
}
}
nullpoga2()
2011/7/14 Kazuhiro SERA <ser...@gmail.com>:
Language Specification(www.scala-lang.org/docu/files/ScalaReference.pdf)に
ちゃんと説明されてました。
P124「8.5 Pattern Matching Anonymous Functions」
2011年7月14日9:48 Kazuhiro SERA <ser...@gmail.com>:
ありがとうございます。ちゃんとドキュメント読まないとダメですね。
引数一個のときはPartialFunctionでいけるからmatchを省略できるよって事ですね。
※PartialFunctionはFunction1のサブ型