[Scala] (Stream.constとforをつかった)あついイディオム
via @tanigon
このイディオム? 熱いわ for(i <- Stream.const(()=>in.read(buf)).map(_()).takeWhile(_ != -1))...
を受けて、ちょっと実験してみた。
ようは、InputStreamなどから、ある条件になるまで読み込んで何かする時のイディオム。
ただ、最初に全部読んだりはしないで、必要に応じて読んでゆく。
テスト用のソースは
import java.io.{FileInputStream, BufferedInputStream, InputStreamReader, BufferedReader} import scala.io.Source def test1(filename:String) { val in = new BufferedInputStream( new FileInputStream(filename)) val buf = new Array[Byte](BUF_SIZE) val stream = Stream.const(() => in.read(buf)) for(i <- stream.map(_()).takeWhile{_ != -1}) { println(i) println("[" + buf.subArray(0,i).map(b => String.format("%X", b.asInstanceOf[Object])).mkString + "]") } } def test2(filename:String) { val in = new BufferedReader( new InputStreamReader( new FileInputStream(filename),encode)) val buf = new Array[Char](BUF_SIZE) val stream = Stream.const(() => in.read(buf)) for(i <- stream.map(_()).takeWhile{_ != -1}) { println(i) println("[" + buf.subArray(0,i).mkString + "]") } } def test3(filename:String) { val in = new BufferedReader( new InputStreamReader( new FileInputStream(filename),encode)) val stream = Stream.const(() => in.readLine) .map(_()) .takeWhile{_ != null} for(line <- stream) { println("[" + line + "]") } } val BUF_SIZE = 64 val encode = "shift-jis" val filename = "input.txt" println("----------") println(バイト単位) println("in:InputStream, buf:Array[Byte]") test1(filename) println("----------") println(文字単位(含む改行コード)) println("in:Reader, buf:Array[Char]") test2(filename) println("----------") println(行単位の読み込み(除く改行コード)) test3(filename)
入力ファイルは
line(行): 1 line(行): 2 line(行): 3 line(行): 4 line(行): 5 line(行): 6 line(行): 7 line(行): 8 line(行): 9 line(行): 10 line(行): 11 line(行): 12 line(行): 13 line(行): 14 line(行): 15 line(行): 16 line(行): 17 line(行): 18 line(行): 19 line(行): 20
で実行結果は・・・
---------- バイト単位 in:InputStream, buf:Array[Byte] 64 [6C696E6581698D73816A3A2031DA6C696E6581698D73816A3A2032DA6C696E6581698D73816A3A2033DA6C696E6581698D73816A3A2034DA6C696E65] 64 [81698D73816A3A2035DA6C696E6581698D73816A3A2036DA6C696E6581698D73816A3A2037DA6C696E6581698D73816A3A2038DA6C696E6581698D73] 64 [816A3A2039DA6C696E6581698D73816A3A203130DA6C696E6581698D73816A3A203131DA6C696E6581698D73816A3A203132DA6C696E6581698D7381] 64 [6A3A203133DA6C696E6581698D73816A3A203134DA6C696E6581698D73816A3A203135DA6C696E6581698D73816A3A203136DA6C696E6581698D7381] 55 [6A3A203137DA6C696E6581698D73816A3A203138DA6C696E6581698D73816A3A203139DA6C696E6581698D73816A3A203230DA] ---------- 文字単位(含む改行コード) in:Reader, buf:Array[Char] 64 [line(行): 1 line(行): 2 line(行): 3 line(行): 4 line(行): 5 line] 64 [(行): 6 line(行): 7 line(行): 8 line(行): 9 line(行): 10 line(行)] 64 [: 11 line(行): 12 line(行): 13 line(行): 14 line(行): 15 line(行] 59 [): 16 line(行): 17 line(行): 18 line(行): 19 line(行): 20 ] ---------- 行単位の読み込み(除く改行コード) [line(行): 1] [line(行): 2] [line(行): 3] [line(行): 4] [line(行): 5] [line(行): 6] [line(行): 7] [line(行): 8] [line(行): 9] [line(行): 10] [line(行): 11] [line(行): 12] [line(行): 13] [line(行): 14] [line(行): 15] [line(行): 16] [line(行): 17] [line(行): 18] [line(行): 19] [line(行): 20]
いい感じですね。
3番目のは、結果だけ見れば以下とほぼ同じ
def test4(filename:String) { val source = Source.fromFile(filename, "shift-jis") .getLines for(i <- source) { val line = i.stripLineEnd println("[" + line + "]") } }
ちなみに、
@ScalaTohoku によると、
Scala 2.8 では、もう少しスマートに for(i<-Stream.continually(in.read(buf)).takeWhile(_ != -1)){...} と書けるようになりますね。
だそうです。
覚えといて損は無い感じです。
1点だけ心残りなのは、test1,test2の
val stream = Stream.const(() => in.read(buf)) for(i <- stream.map(_()).takeWhile{_ != -1}) { println(i) println("[" + buf.subArray(0,i).map(b => String.format("%X", b.asInstanceOf[Object])).mkString + "]") }
の部分をくくりだして共通化したかったのだけれど、よくわからなかった><
[追記]
ちなみに、現状はここで止まっている。
type Readable[A] = { def read(buf:Array[A]):Int } def test0[A](in:Readable[A], buf:Array[A])(func:(Int) => Any) { val stream = Stream.const(() => in.read(buf)) for(i <- stream.map(_()).takeWhile{_ != -1}) { func(i) } } def test1b(filename:String) { val in = new BufferedInputStream( new FileInputStream(filename)) val buf = new Array[Byte](BUF_SIZE) test0(in,buf){ i => println(i) println("[" + buf.subArray(0,i).map(b => String.format("%X", b.asInstanceOf[Object])).mkString + "]") } }
上記のコードはコンパイルは通るけど、
java.lang.NoSuchMethodException: java.io.BufferedInputStream.read(scala.runtime.BoxedArray)
がでる。(惜しい感じ・・・)