第5回 JavaエンジニアのためのScalaハンズオン勉強会

25 views
Skip to first unread message

Kazuhiro SERA

unread,
Jun 30, 2011, 11:00:50 AM6/30/11
to Daimon.scala
瀬良(@seratch)です。

次回ハンズオン勉強会の告知です。

第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
ただ、この記事はボリュームが少ないし、色々もっと伝えるべき事があるはずなので
ちょっと考えてみます。

Kazuhiro SERA

unread,
Jul 7, 2011, 7:44:02 AM7/7/11
to Daimon.scala
瀬良(@seratch)です。

お疲れ様でした。

反省点として、会議室が暑かったので
(定時後はビル全体でエアコンがオフになるのです・・)
次回から始めるときに窓を開けるようにしましょう、ということで。

あと、今回も勉強会中に出た疑問点は宿題という事で
何か進展がありましたらぜひシェアをお願いします。

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を
うまく使ったコードが見たいという話が出ました。

よろしくお願いします。

Kazuhiro SERA

unread,
Jul 9, 2011, 10:29:19 AM7/9/11
to Daimon.scala
瀬良(@seratch)です。

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>:

谷村直樹

unread,
Jul 10, 2011, 5:25:41 PM7/10/11
to daimo...@googlegroups.com
谷村(@naokitanimura)です。

なるほど。Streamの遅延評価のメリットってことなんですね。

Streamの無限リストとして使いどころもなんとなく見えてきましたけど
きっと普通(って何かはあれですが)のWebアプリでは
必要になるような問題にぶちあたることはあまり無いんですかね。


--
Naoki Tanimura
★★

Naoki Tanimura

unread,
Jul 13, 2011, 4:53:40 PM7/13/11
to Daimon.scala
谷村(@naokitanimura)です。

> 1.マッチ式が省略できるメカニズムについて

Scalaプログラミング入門のP132「関数としてのパターンマッチ」で
それらしき事を説明していますが、朧げにしかピンときてません・・・。

説明では、

Scalaコンパイラは内部的にパターンマッチを、PartialFunction[A, B]へと変換するため、
1つの関数を引数としてとるメソッドにパターンマッチを送る事ができる

と書かれています。

Kazuhiro SERA

unread,
Jul 13, 2011, 8:48:17 PM7/13/11
to daimo...@googlegroups.com
瀬良(@seratch)です。

> 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
>

Kazuhiro SERA

unread,
Jul 13, 2011, 9:07:50 PM7/13/11
to daimo...@googlegroups.com
PartialFunctionについて補足です。

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>:

Naoki Tanimura

unread,
Jul 13, 2011, 9:14:48 PM7/13/11
to daimo...@googlegroups.com
谷村(@naokitanimura)です。

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>:

Kazuhiro SERA

unread,
Jul 13, 2011, 11:08:32 PM7/13/11
to daimo...@googlegroups.com
瀬良(@seratch)です。

ありがとうございます。ちゃんとドキュメント読まないとダメですね。

引数一個のときはPartialFunctionでいけるからmatchを省略できるよって事ですね。
※PartialFunctionはFunction1のサブ型

Kazuhiro Sera

unread,
Jul 13, 2011, 11:42:44 PM7/13/11
to Daimon.scala
> 以下だとThrowable型を一つ受け取ってUnit型の戻りになる関数で
> イメージとしてはmatch式でfilterした後でmapしてる感じですね。

これはList#collectと話がごっちゃになっていてウソなので訂正します。

PartialFunction[A,B]はFunction1[A,B]のサブ型で
isDefinedAt(A):Booleanというメソッドが使える関数ですね。

val pf1: PartialFunction[Int,Int] = { case i:Int if i > 2 => i*i }
// pf1: PartialFunction[Int,Int] = <function1>

pf1.isDefinedAt(2) // false
pf1.isDefinedAt(3) // true

引数の全ての入力パターンに対しては
まだパターンマッチが定義されていない関数を
オブジェクト化できるイメージですね。

orElse(PartialFunction[A,B])で別の部分関数と結合する事ができます。

val pf2: PartialFunction[Int,Int] = { case i if i < 0 => 0 }
List(-1,0,1,2,3) collect pf1 // List(9)
List(-1,0,1,2,3) collect { pf1 orElse pf2 } // List(0,9)

部分関数についてはもうちょっと知識を整理して
ブログに追記しておきたいです。
> 2011/7/14 Kazuhiro SERA <sera...@gmail.com>:
>
>
>
>
>
>
>
> > 瀬良(@seratch)です。
>
> >> 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.tanim...@gmail.com>:
Reply all
Reply to author
Forward
0 new messages