Scalaでファイル操作

Scala勉強中。
テキストファイルを読み込んだり書き出したりしてみたくなりました。
いきなりピュアScalaで書くのは難しいだろうと思い、Javaのライブラリを使って書いたのですが、それでもちょっとハマりました。

代入式は Unit を返す

いつもJavaで書いてるのと同じように

    while( ( line = reader.readLine ) != null ) println( line )

って書いたらコンパイルで警告が出ました。

file_read_java2.scala:12: warning: comparing values of types Unit and Null using `!=' will always yield true
    while( ( line = reader.readLine ) != null ) println( line )

「あんた Unit != null とかやってるけど、そんなことしたら毎回trueになって無限ループになって地獄に堕ちるわよ!!」


Unit? どっからUnitなんて出てきたんだ。代入式は代入された値を返すんじゃないのか?
違うみたいです。

scala> var a, b = 0
a: Int = 0
b: Int = 0

scala> a = b = 1
<console>:8: error: type mismatch;
 found   : Unit
 required: Int
       a = b = 1
             ^

scala> a = ( b = 1 )
<console>:8: error: type mismatch;
 found   : Unit
 required: Int
       a = ( b = 1 )
               ^

代入式の値は、変数に代入された値ではなくUnitを返すのですね。へぇー。

こまったな

いつもの方法が使えないので途方にくれていましたが

  • while文の条件式にブロックが使える。
  • ブロックは最後の文の結果を返す。

っていうのを聞いたことがあったので、なんとか上手いこといきました。捨てる神あれば拾う神あり。

完成したソース

// file_read_java.scala

import java.io._

object FileReadJava {
  def main( args: Array[String] ) {

    // ストリームを作ってリーダーを作る
    val stream = new FileInputStream( "test.txt" )
    val reader = new BufferedReader( new InputStreamReader( stream ) )

    // ぐるぐる回して出力
    var line = ""
    while( { line = reader.readLine; line != null } ) println( line )

    // 閉じる
    reader.close
    stream.close
  }
}

Scala

勢いに乗ってピュアScala版を作ります。
scala.io.Source」っていうオブジェクトを使うと、ファイル操作が簡単に行えるようです。

// file_read_scala.scala

import scala.io.Source

object FileReadScala {
  def main( args: Array[String] ) {

    // 「ソース」っていうのを作る
    val source = Source.fromFile( "test.txt" )

    // ソースから出力
    for( line <- source.getLines ) {
      println( line.stripLineEnd )
    }
  }
}

動くには動いたけど

どうやってcloseするんだろう?


APIリファレンスを見てみる。
Source.fromFileはSourceクラスのオブジェクトを返すのだけど、Sourceクラスには「close」メソッドがない。
Source.fromFileが返すオブジェクトのクラス名を調べたら「scala.io.BufferedSource$$anon$1」っていうクラス名が出てきた。
この BufferedSource クラスにはcloseメソッドがあるので、これを呼んでやればいいのだと思うけど、それをやろうとするとコンパイラの厳正な型チェックによってエラーが出てしまう。
キャストすればいいのかな?キャストする方法がわからん。
ってゆーか、APIリファレンスに同じ Source っていう名前でクラスとオブジェクトが両方載ってる。なんじゃこりゃ?!どーなってるんだ


ダメだこりゃ。今日はもう寝よう。