Created
February 19, 2012 11:35
-
-
Save nisshiee/1863320 to your computer and use it in GitHub Desktop.
ScalazのIterateeでファイルを読み込むTipsを埋め込んだ
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
package iteratee.practice | |
import scalaz._ | |
import Scalaz._ | |
import IterV._ | |
import effects._ | |
import java.io.File | |
/** | |
* scalazのIterateeを使ってファイルを読み込む練習 | |
* http://code.google.com/codejam/contest/889487/dashboard | |
* <pre> | |
* M C W | |
* A1 B1 | |
* ... | |
* AC BC | |
* </pre> | |
* | |
* @author nishioka | |
* | |
*/ | |
object Practice3 extends App { | |
/** 読み込みたい型 */ | |
case class Cut(a: Int, b: Int) | |
case class Problem(m: Int, c: Int, w: Int, cuts: List[Cut]) | |
/** parseMcw, parseCutを作るための布石 */ | |
def line2strlist: String => List[String] = _.split(" ").toList | |
/* parseIntの失敗を握りつぶすのはホントはよくないけど、今回メインじゃないので割愛 */ | |
def strlist2intlist: List[String] => List[Int] = l => | |
(l map (_.parseInt.toOption)).flatten | |
def line2intlist: String => List[Int] = line2strlist >>> strlist2intlist | |
def intlist2mcw: List[Int] => Option[(Int, Int, Int)] = { | |
case m :: c :: w :: Nil => (m, c, w).some | |
case _ => None | |
} | |
def intlist2cut: List[Int] => Option[Cut] = { | |
case a :: b :: Nil => Cut(a, b).some | |
case _ => None | |
} | |
/** 文字列からの型変換(この時点ではIOとかIterateeとか無縁) */ | |
def parseMcw = line2intlist >>> intlist2mcw | |
def parseCut = line2intlist >>> intlist2cut | |
/* ----- Iterateeチックなのはここから ----- */ | |
/** MCWを読み込むIterV */ | |
def readMcw = head[String] map (_ >>= parseMcw) | |
/** pureするのに便利なのでIterVのエイリアス作っとく */ | |
type IM[A] = IterV[String, A] | |
/** | |
* n個のCutをOption[List[Cut]]として読み込むIterV | |
* | |
* IterV[E, Option[A]]をn(有限)個つなげてIterV[E, List[Option[A]]]を作るには、 | |
* foldRightでListにアキュムレートしていくのが良さそう | |
* (ホントはsequence[IM, Option[A]]できれば一番かっこいいのになぁ) | |
* | |
* 最後にList[Option[A]]にsequence[Option, A]を適用して、 | |
* Option[List[A]]として返す | |
* flattenはしないことで、「失敗するかもしれない文脈」を引き継ぐ | |
*/ | |
def readCut(n: Int) = { | |
val listIterV = (head[String] map (_ >>= parseCut)).replicate[List](n) | |
listIterV.foldRight(List[Option[Cut]]().pure[IM])((i, acc) => for { | |
cut <- i | |
rest <- acc | |
} yield cut :: rest) map (_.sequence[Option, Cut]) | |
} | |
/** | |
* ProblemをOption[Problem]として読み込むIterV | |
* | |
* readMcwとreadCutの組合せだが、mcwのcの値をreadCutに引き渡す必要があるので、 | |
* for式で書いた方が綺麗 | |
* | |
* また、Optionによる「失敗するかもしれない文脈」には気を遣い続ける | |
*/ | |
def readProb = for { | |
mcw <- readMcw | |
cut <- mcw match { | |
case Some(_@ (_, c, _)) => readCut(c) | |
case _ => none[List[Cut]].pure[IM] | |
} | |
} yield (mcw, cut) match { | |
case (Some(_@ (m, c, w)), Some(cut)) => Problem(m, c, w, cut).some | |
case _ => none[Problem] | |
} | |
/** | |
* ファイルを読み終わるまでreadProbを繰り返す | |
* | |
* 最後にsequenceを使ってList[Option[Problem]]をOption[List[Problem]]に直す | |
* これもflattenを使わずに、「失敗するかもしれない文脈」を引き継ぐ | |
*/ | |
def readRepeat = repeat[String, Option[Problem], List](readProb) map (_.sequence[Option, Problem]) | |
/** | |
* 1行目を捨てる処理を加え、これで入力全体を読み込むIterVの完成 | |
*/ | |
def read = drop[String](1) >>=| readRepeat | |
/* ----- IOが絡むのはここから ----- */ | |
/** FileクラスはJavaのもの */ | |
val f = new File("practice/3-1.in") | |
/** EnumeratorMはscalaz.effectsパッケージを使えば一発取得 */ | |
val lines = getFileLines(f) | |
/** | |
* 作成したIterVを使ってファイルを読み込み、その結果を標準出力に出す | |
* 入力と出力を>>=で繋げて、入力から出力までをまとめて1つのIOにする | |
* | |
* また、ここまでずっと引き継いできた「失敗するかもしれない文脈」は | |
* ここが唯一の分岐 | |
*/ | |
val io = lines(read) >>= { iterv => | |
iterv.run match { | |
case Some(l) => { | |
val listIO = l map (_.toString) map putStrLn | |
listIO.sequence[IO, Unit] map (_ => ()) | |
} | |
case None => putStrLn("parse error") | |
} | |
} | |
/* 全てのIO処理はここに全て集約される */ | |
io.unsafePerformIO | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment