Showing posts with label scala. Show all posts
Showing posts with label scala. Show all posts

Friday, November 12, 2010

Scalaでいい感じにtap

最近のRubyにある、超便利メソッドtapの使い方のメモ。
Scalaでimplicit conversionを使ったtapの実装はScala で tap メソッドを使う - Rainy Day Codingsに書いてある。

メソッドチェーンを切らずにtapしてprintlnとかよくやるけど、今日はそれ以外の使い方を学んだ。うはは。

tapを使わない場合は気の毒で目も当てられない

import java.util.Properties
def loadProperties(path: String): Properties = {
  val p = new Properties()
  p.load(new FileInputStream(new File(path).getAbsolutePath))
  p
}
オブジェクト作って呼び出し元に返したいが、
初期化メソッドの戻り値がthisじゃないからローカル変数に一度入れてからreturnしなきゃならんっていう古いjavaライブラリによくあるケース。

tapを使うとローカル変数がいらなくなる

import java.util.Properties
def loadProperties(path: String): Properties = {
  new Properties().tap( _.load(new FileInputStream(new File(path).getAbsolutePath)))
}

いいねぇ!

Thursday, November 11, 2010

scalaでstructural typingでreflection

Scala: How do I dynamically instantiate an object and invoke a method using reflection?

にstructural typingを使ったサンプルが載ってたのでメモ

class Foo {
  def hello(name: String): String = "Hello there, %s".format(name)
}

object FooMain {

  def main(args: Array[String]) {
    val foo  = Class.forName("Foo").newInstance.asInstanceOf[{ def hello(name: String): String }]
    println(foo.hello("Walter")) // prints "Hello there, Walter"
  }
}

Monday, November 8, 2010

scalaでactor使ってparallel map その3 - Futures編

その1
その2
でparallel mapの実装を試みたところActorの理解が浅く、実はシングルスレッドだったという衝撃の事実が発覚して度肝を抜かれた。

めげずに、Using scala actor framework as fork-join computation?とかScalaActors.pdf
を参考にしてFuturesを使って実装してみた。

今回は、 FuturesをListの要素数分作って、mapでFuturesをapplyする場合と、
Futuresの数を制御したい場合の2パターンを実装してみた。

import scala.actors.Future
import scala.actors.Futures._

import java.util.concurrent._

trait Pmap {

  // FuturesをListの要素数分作る場合
  def pmap[T, U](xs: List[T])(fun: T => U): List[U] = {
    val mapfuncs = xs.map{ x => future { fun(x) } }
    mapfuncs.map{ _() }
  }

  // for Debug
  def pmap_debug[T, U](xs: List[T])(fun: T => U): List[U] = {
    val mapfuncs = xs.map{ x => future {
        println("start Thread " + currentThread.getId)
        val r = fun(x)
        println("end Thread " + currentThread.getId)
        r
    } }
    mapfuncs.map{ _() }
  }

  // Futuresの数を制限したい場合
  def pmap[T, U](xs: List[T], n: Int)(fun: T => U): List[U] = {
    val mapfuncs = divide(n, xs).map{ l =>
      future { l.map( fun(_) ) }
    }
    mapfuncs.flatMap{ _() }
  }

  // for Debug
  def pmap_debug[T, U](xs: List[T], n: Int)(fun: T => U): List[U] = {
    val mapfuncs = divide(n, xs).map{ l =>
      future {
        println("start Thread " + currentThread.getId)
        val result = l.map( fun(_) )
        println("end Thread " + currentThread.getId)
        result
      }
    }
    mapfuncs.flatMap{ _() }
  }

  def divide[T](n: Int, xs: List[T]): List[List[T]] = {
    val size = xs.size / n
    def r(ys: List[T], acc: List[List[T]]): List[List[T]] = {
      if(ys.size < size) (acc.head ::: ys) :: acc.tail
      else{
        val splitted = ys.splitAt(size)
        r(splitted._2, splitted._1 :: acc)
      }
    }
    r(xs, Nil).reverse
  }
}

object Main extends Application with Pmap {

  val l: List[Int] = (1 to 100000).toList
  pmap[Int, Int](l, 4)(x => x + 1)
  pmap_debug[Int, Int](l, 2)(x => x + 1)
  pmap[Int, Int](l)(x => x + 1)
  pmap_debug[Int, Int](l)(x => x + 1)
}


pmap_debugを実行すると
start Thread 13
start Thread 11
start Thread 12
start Thread 10
end Thread 10
end Thread 11
end Thread 12
end Thread 13

と出るのでマルチスレッドで非同期に実行されていることが確認できた。

Futures10万個の場合では実際に(2コア2スレッドのマシンでは)4スレッドで切り盛りしていた。
(環境依存。4コア8スレッドのマシン上では合計16スレッドだった)

2コア2スレッドマシン
% sort /tmp/output.txt |uniq -d
end Thread 10
end Thread 11
end Thread 12
end Thread 13
start Thread 10
start Thread 11
start Thread 12
start Thread 13

どっちがよいかは場合によるだろう。

リストの数がそれほど大きくなくて、mapにそこそこ時間がかかる場合を考えてみる。
リストをまず分割して、その断片にFuturesを割り当てる方式だと、
分割したリスト断片間を処理する時間にばらつきがあると、一番遅い断片の処理時間に引っ張られそう。
リストの要素ごとにFuturesを作った方が処理時間が平準化されてトータルでは早くなりそうだ。

今回のようにリストの数が膨大だとFuturesの数を抑えたが方が、高速でメモリにもやさしそうだ。

リストの要素数、mapの処理内容、CPUコア数によって変わるだろうから実際に動かして決めるのが良さそうだな。

scalaでactor使ってparallel map その2

-----------
これもシングルスレッドだ。パラレルじゃない。Futureを使わないFork-Joinは再チャレンジする(11/8追記)
----------

前回の実装はイケてない。
pmapの関数定義の結果型がList[Any](実際はList[List[T]])だった。
map的にはList[T]を返した方が自然だよな。

ということで改良してみた。

import scala.actors.Actor._

@SuppressWarnings(Array("unchecked"))
def pmap[T, U](xs: List[T], n: Int)(fun: T => U): List[U] = {

  def divide(n: Int, xs: List[T]): List[List[T]] = {
    val size = xs.size / n
    def r(ys: List[T], acc: List[List[T]]): List[List[T]] = {
      if(ys.size < size) (acc.head ::: ys) :: acc.tail
      else{
        val splitted = ys.splitAt(size)
        r(splitted._2, splitted._1 :: acc)
      }
    }
    r(xs, Nil).reverse
  }

  val mapped: List[List[U]]  = divide(n, xs).map{ l =>
    val a = actor { receive { case _ => reply(l.map(fun(_))) } }
    a !? "s" match {
      case l: List[U] => l
      case _ => Nil
    }
  }

  mapped.flatten
}

val l = (1 to 100).toList
val result = pmap[Int, Int](l, 4)(_ + 1)

println(result)


実行すると、
warning: non variable type-argument U in type pattern List[U] is unchecked since it is eliminated by erasure
case l: List[U] => l
^
one warning found
List(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101)
となる。

erasureによって型Uが消されてるからcase List[U]がU以外でもマッチしてあぶねーよ、っていわれてる。
@SuppressWarnings(Array("unchecked"))書いても警告がでるのね。
無視スリゃいいんだろうけど気持ち悪い。

scalaでactor使ってparallel map その3 - Futures編に続く。

Saturday, November 6, 2010

scalaでactor使ってparallel map その1

-----------
これ、シングルスレッドだ。パラレルじゃない。Futureを使わないFork-Joinは再チャレンジする(11/8追記)
----------

試しに書いてみた。

pmapの結果型がList[T]だと型チェックエラーでコンパイルできない。
replyすると型情報が消えちまうのか?

パラレルマップの正解はこれとかこれかもしれない。
また今度試す。

import scala.actors.Actor._

def pmap[T](xs: List[T], n: Int)(fun: T => T): List[Any] = {

  def divide(n: Int, xs: List[T]): List[List[T]] = {
    val size = xs.size / n
    def r(ys: List[T], acc: List[List[T]]): List[List[T]] = {
      if(ys.size < size) (acc.head ::: ys) :: acc.tail
      else{
        val splitted = ys.splitAt(size)
        r(splitted._2, splitted._1 :: acc)
      }
    }
    r(xs, Nil).reverse
  }

  divide(n, xs).map{ l =>
    val a = actor { receive { case _ => reply(l.map(fun(_))) } }
    a !? "s"
  }
}

val l = (1 to 100).toList
val result = pmap(l, 4)(_ + 1)

println(result)


実行結果
List(List(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26), List(27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51), List(52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101))

scalaでactor使ってparallel map その2に続く。

Wednesday, November 3, 2010

scala + ctags + vim + taglistできた

scalaの開発環境改善中。

まずは、ctagsをscalaに対応させる方法を見て、関数定義にタグジャンプできるようにした。

その後、taglist.vimを使ってみたのが、そのままではタグの一覧が表示されなかったのでググってみたらドンピシャな記事を発見。

Has anyone got the vim taglist plugin working with Scala?

↑に書いてあるとおり、taglist.vimの、
s:tlist_def_yacc_settings
の後ろに↓の2行追加したらできました。

let s:tlist_def_scala_settings = 'scala;t:trait;c:class;T:type;' .
\ 'm:method;C:constant;l:local;p:package;o:object'

ちなみに、タグジャンプ等のキーバインドは、
Vimの極め方 tags-and-searchesを使い易くする
この設定がシックリきてます。

Tuesday, November 2, 2010

vimからsbtを実行できた

ここに、scalaのakkaプロジェクト創始者のjboner君のvimrcがあったので、これは!!と思って覗いてみたら、vimからsimple-build-toolを使う部分があったので、試してみた。

ちょっとハマったのでメモしておく。

"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
" For SBT
"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
set makeprg=sbt-no-color\ test-compile
...省略...
set errorformat=%E[error]\ %f:%l:\ %m,%C[error]\ %p^,%-C%.%#,%Z,
\%W[warn]\ %f:%l:\ %m,%C[warn]\ %p^,%-C%.%#,%Z,
\%-G%.%#
となっている。

sbt-no-colorってなんぞや思いつつも、.vimrcにコピって:makeしてみたけど、sbt-no-colorがないと怒られる。

そこで、sbt-no-colorをsbtに変更して:makeすると、sbtでコンパイルしてるようなんだけど、
[0m[...のように文字化けてる風味で出力される。

:!sbt test-compile 2>&1| tee /tmp/vQCb1aG/2
[0m[ [0minfo [0m] [0mBuilding project test 1.0 against Scala 2.8.0 [0m
...省略...
[0m[ [32msuccess [0m] [0mBuild completed successfully. [0m

15分位ググっても引っかからん。うーんうーん唸っていたら僕ちゃん、気づきました。

「色」だ。 no-colorだ。

ということで。sbtのソース落としてgrep "color" **/*.scalaとかやったら出てきた。
in Logger.scala
private val formatEnabled = ansiSupported && !formatExplicitlyDisabled
private[this] def formatExplicitlyDisabled = java.lang.Boolean.getBoolean("sbt.log.noformat")

sbtを起動するシェルスクリプトを↓のように書き換え、
java -Dsbt.log.noformat=true -Xmx512M -jar /path_to_/sbt-launch-0.7.4.jar "$@"

sbt-no-colorという名前で保存後、jboner君のvimrcで、:makeでvim上からsbt使ってコンパイルすることができました。

---------
ちなみに、gvim上でエラー行にマークするには、
errormarker.vimがいいよ。

vimでscalaをomni補完するプラグイン codefellowが動かん

vimでscalaのomni補完ができるという
codefellow
を試したけど、1時間イジって動かなかったので諦めた。。
scalaのファイルを開くと固まる。

screeencast見たらすごすぎておったまげた。
ぜひ使わせてもらいたかったんだがなー。


もう少し時が満ちてから試す。
備忘録としてメモっとく。

Monday, November 1, 2010

scalaでDir.glob("**/*.sql")

RubyだとDir.glob("**/*.sql")で指定したディレクトリから、
再帰的に*.sqlのファイルパスを検索してくれるが、
scalaには見当たらなかったので、書いてみた。

  // glob **/*.sql 
  val globSqlFiles = glob((f: File) => 
    !f.isDirectory && f.getName.endsWith(".sql")) _
  
  // curried function
  def glob(filter: (File) => Boolean)(dirs: List[String]): List[File] = {
    def recursive(dir: File, acc: List[File]): List[File] = 
      Option(dir.listFiles) match {
        case None => throw new FileNotFoundException(dir.getAbsolutePath)
        case Some(lists) => 
          val filtered = lists.filter{ c =>  filter(c) }.toList
          val childDirs = lists.filter{ c => c.isDirectory && !c.getName.startsWith(".") }
          return ( (acc ::: filtered) /: childDirs){ (a, dir) => recursive(dir, a)}
    }
    dirs.flatMap{ d => recursive(new File(d), Nil)}
  }

  // 使い方。対象ディレクトリはListで複数指定できる。
  globSqlFiles(List("/tmp/sql"))

汎用的になるようにカリー化してみた。
3、4行目の条件に正規表現を組み込めば曖昧検索できる。

S-99: Ninety-Nine Scala Problemsを頑張ってやっているせいか、
immutableなListの再帰に対してアレルギーがなくなってきた。