Scalaのfor式がうまく動かない件について
Scalaの練習がてら、引数で受け取ったパスのファイルを読み込んで単純Grepするコードを書いていたときの話です。
以下のようなソースコードを書いてみて、ひとまず動くことを確認しようと思っていたのですが、
(動作確認: Scala code runner version 2.8.1.final)
import scala.io.Source object MyGrep { def main(args: Array[String]) { val lines = Source.fromFile(args(0)).getLines val size = lines.size for (line <- lines) println(line) // ←表示されない println(size + " Lines.") } }
これだと、
println(size + " Lines.")
の部分しか結果が表示されないみたいです。なぜなんだ。
試行錯誤して、
for (line <- lines) println(line) // ←表示されない
となっているところを
for (line <- Source.fromFile(args(0)).getLines) println(line) // ←これだと動く
とするとうまく動いたのですが、for式の <- はいったいどういう条件で右側の式を受け付けるのだろうか…
まだまだScalaの修行が足りないな。
追記その1(2011-02-12)
Scalaっぽく書くなら、サイズを返す関数を定義してみるといいよという話をうかがったので、ちょっと書き直してみました。
import scala.io.Source object MyGrep { def main(args: Array[String]) { val lines = Source.fromFile(args(0)).getLines def getSize() :Int = { var size = 0 for (line <- lines) { println(line) size = size + 1 } size } println(getSize + " Lines.") } }
とりあえずこれで動くんですが、こんな感じでよかったのでしょうか。
追記その2(2011-02-12)
さらにfoldLeftを使った例を教えていただきました。これは素敵。やっぱりScalaは関数脳になればなるほど素敵に書けるんだなぁ。
import scala.io.Source object MyGrep { def main(args: Array[String]) { val size = (0 /: Source.fromFile(args(0)).getLines()){ (s, line) => println(line) s + 1 } println(size + " Lines.") } }
この例ではsizeはprintlnしながら蓄積して行きつつ、最後に利用しています。
追記その3(2011-02-12)
一応比較のためにGroovyでやってみた。
new File(args[0]).readLines().with { it.each { println it } println "${it.size()} Lines." }
Groovyでもinjectを使ってfoldLeftっぽくやったら同じようにできるんだろうけど、そもそも#readLines()した時点でList
人それぞれ好みはあるんだろうけど、追記その2で書いた内容はすっきりしていていいな。Scalaの魅力はたぶんあの辺なんだろうな。