finagleわかんないから流れを完全無視して、「BufferedReaderを読むときにどうやって回すよ?」の件を、scalaz iterateeで解いてみた。BufferedReaderの文字数を数えるだけ。 gist.github.com/2842998 #akskscala
— Hirokazu NISHIOKAさん (@nisshieeorg) 5月 31, 2012
この様なつぶやきが。
コードはこんな感じ。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.{ BufferedReader, FileReader } | |
import scalaz._, Scalaz._ | |
import scalaz.effects._ | |
import IterV._ | |
object App { | |
def main(args: Array[String]) { | |
val br = new BufferedReader(new FileReader("test2.txt")) | |
val io = byEnumerator(br) | |
io.unsafePerformIO | |
} | |
def byEnumerator(br: BufferedReader): IO[Unit] = { | |
val lineIter: IterV[String, Int] = head[String] map { | |
lineOpt => (lineOpt map (_.length)) | 0 | |
} | |
val iter: IterV[String, Stream[Int]] = repeat[String, Int, Stream](lineIter) | |
val enumerator: EnumeratorM[IO, String] = getReaderLines(br) | |
val read: IO[Int] = enumerator(iter) map { iter => | |
iter.run reduce (_ + _) | |
} | |
read map (_.toString) flatMap (putStrLn _) | |
} | |
} |
fmfm.
先頭行をとって長さをとって行数分ぬりつぶして合計をとっているようですね。
このコードはScalaz6なのですが、Scalaz7でも書けるかなーと思い、ちょっと書いてみました。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.{ Reader, FileReader } | |
import scalaz._, Scalaz._ | |
import effect._, IO._ | |
import iteratee._, Iteratee._ | |
object App extends SafeApp{ | |
val separator = sys.props("line.separator").head | |
override def run(args: ImmutableArray[String]): IO[Unit] = { | |
val r = new FileReader("test2.txt") | |
byEnumerator(r) | |
} | |
type IEOC = IoExceptionOr[Char] | |
def byEnumerator(r: Reader): IO[Unit] = { | |
val lineIter = takeWhile[IEOC, Stream](_ map (_ =/= separator) valueOr false) map (_.length) | |
val iter = repeatBuild[IEOC, Int, Stream](lineIter).up[IO] map (_.sum) | |
val enum = enumReader(r) | |
val read = iter &= enum | |
(read.run >>= putOut) >> putStrLn("") | |
} | |
} |
違いを1つ1つみていきます。
まず、Scalaz7ではgetReaderLinesがないので、BufferedReaderではなくReaderを使うようにしました。
なので、1行ずつではなく1文字ずつしかみれなくなります。
そこで、改行コードをみるようにします。
改行コードはシステムプロパティーのline.separatorで取得できるとのことなので、sys.propsから取得します。
もとのコードでは、先頭行を取得する為にheadを使っていますが、このコードではtakeWhileを使います。
ここでは、述語として改行コードかどうかの判定をとります。
さて、肝心のIterateeの要素ですが、IoExceptionOr[Char]となっています。
これはその名の通り、IoExceptionとの和で、どちらかが内包されています。
ここではvalueOrを使い、IoExceptionだった場合の値を決め、値を取得しています。
repeatBuildはScalaz6のscalaz.IterV.repeatと同じです。
on[IO]は、Iteratee[E, A]から、IterateeT[E, IO, A]に変換しています。
enumReaderはReaderからEnumeratorを作り、&=はEnumeratorをIterateeに流し込みます。
最後に実行の部分ですが、mainを定義しているわけではありません。
SafeAppはunsafePerformIOを明示的に呼び出さない仕組みで、runをオーバーライドすることで実行できます。
・・・・・とまあこんな感じなのですが、Scalaz6と比べて、かなり違うことが分かりますね。
とくにIteratee。
IterVとIterateeで別々のものになっていましたが、いまはIterateeTのみで大分使いやすくなった気がします。(Iteratee[E, A]はIteratee[E, Id, A]の別名)
ただ、enumReaderLinesのようなものは欲しかった。
IOはMonadIOとかMonadControlIOとかいろいろ高機能になっているのですが、基本的な機能が少ないので、相変わらず残念です。
せめてSourceとReaderとWriterのインスタンスくらい作ってくれればいいのに。
あと入出力系。
超便利とまでは言えないけれど、将来性はありそうなiterateeとeffect。
一度さわってみてはどうでしょう?
0 件のコメント:
コメントを投稿