Livedoorブログからの移動

忘れててひどい目にあったものを書いておく.

scala.collection.Setとscala.Predef.Setは違う
scala.Predef.Setは, immutableなSetのこと. 一方, scala.collection.Setはmutableも含む.
mutableであるものをmutableと明示しようとして, 
 
def func(s: Set[Int]) = println(s)
import scala.collection._
val set1 = mutable.Set[Int]()
val set2 = Set(1, 2, 3)
func(set2)
とかやると, funcの引数のsはscala.Predef.Set, set2はscala.collection.Setとなってエラーを起こす.
こういう場合は面倒くさくても import scala.collection.mutableと書くべき.

Arrayのequalsは要素比較をしない.
scalaのArrayは要するにJavaの配列のラッパ. なので, toStringしても要素が並ぶわけではないし, 例えば, Array(1, 2, 3) == Array(1, 2, 3)はfalseとなる.
どうするのが適切なのかはわからないが, toSeqとかでSeqに直せば比較できる. 

scala.sys.processにおける"" (2015/1/2追記)
scala.sys.processは面倒くさい処理を全部シェルスクリプトに投げられる便利なライブラリ. 特に自分はファイルアクセスなどに使っている. 例えば, カレントディレクトリ以下の拡張子がscalaのファイルすべてを取得する際, sys.processを使えば以下だけで良い(Unix専用? windowsでscala使ったことがないので知らない).
// scala.sys.processを使う場合
import scala.sys.process._
("find ./ -name *.scala" lines_!) map (x => s"cat $x".!!)
これをjavaの標準ライブラリでやろうとした場合にどれだけの行数になるかは考えたくもないし, scala.ioは遅い.
もちろんOS依存が強いので, 個人的に使うプログラムや, 小規模なスクリプトに使用は限定するべき.

ただ, ""がいわゆるシェルスクリプトの""や''と違うことに注意する必要がある. 例えば, 以下は1列目だけを表示する簡単なawkスクリプトを実行するコマンドである.
$ awk '{print $1}' file
この時, shell(bash, zsh等, 他は知らない)は, まず"awk", "'{print $1}'" "file"の3引数に分解し, 次に, '{print $1}'を{print $1}と評価し(評価っていうのだろうか), "awk" "{print $1}" "file"という3引数として実行する.

一方,  
import scala.sys.process._
"awk \"{print $1}\" file".!!
は, "awk" "{print" "$1}" "file"の4引数に分解される. なのでこれはawkスクリプトとしてparseできずにエラーを吐いて死ぬ.
こういう場合はSeq[String]からProcessへのimplicit conversionを用いて,  
Seq("awk", "{print $1}", "file").!!
と書かなければならない. ""と''と``の違いなど, shell scriptをそのまま実行しようとするには面倒くさいことが多いので, しょうがない仕様だとは思うが, 若干面倒.

Console.outとかの取得方法

標準入力や標準出力の内容を取得したくなった時の方法について. まぁ特にテスト中に用いるかな? javaだと, System.setOut(...)して, その後, System.out.println(...)をして, また, System.setOut(original)で戻すみたいな流れを踏む.
scalaの場合は昔はConsole.setOutがあったんだけど非推奨になっていたので, 代わりのAPIを見たらwithOutというのがあった.
最初見た時は"without"だと思ったんだけど, "with (std)out"という意味らしい. 使い方は,
Console.withOut(new ByteArrayOutputStream) {
  println("foo")
  println("bar")
}
みたいな感じ. こうすると, printlnしているところだけstdoutがByteArrayOutputStreamになり, それ以外は標準出力に行く.
遅延評価と複数引数リストを組み合わせたこの定義は割とscalaらしいと思った.