読者です 読者をやめる 読者になる 読者になる

No Programming, No Life

新しいNPNLです。http://d.hatena.ne.jp/fumokmm/ から引っ越してきました。

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になってるから#size()で取れるから必要ないな。

人それぞれ好みはあるんだろうけど、追記その2で書いた内容はすっきりしていていいな。Scalaの魅力はたぶんあの辺なんだろうな。