モナドトランスフォーマー (1)
ListとList[Option]とOptionの共存
val intList: List[Int] = List(1, 2, 3, 5) val strOptList: List[Option[String]] = List(Some("a"), Some("c"), None, Some("d")) val option: Option[Int] = Option(1) val t: OptionT[List, (Int, String, Int)] = for { i <- OptionT.liftF(intList) s <- OptionT(strOptList) o <- OptionT.fromOption[List](option) } yield (i, s, o) // List(Some((1,a,1)), Some((1,c,1)), None, Some((1,d,1)), Some((2,a,1)), Some((2,c,1)), None, Some((2,d,1)), Some((3,a,1)), Some((3,c,1)), None, Some((3,d,1)), Some((5,a,1)), Some((5,c,1)), None, Some((5,d,1))) println(t.value)
プログラミングhaskell 9章 version scala 〜後半(ライフゲーム)〜
type Board = List[Pos] val width: Int = 5 val height: Int = 5 def showcells(b: Board): IO[Unit] = seqn(b.map(p => writeat(p)("○"))) def isAlive(b: Board)(p: Pos): Boolean = b.contains(p) def isEmpty(b: Board)(p: Pos): Boolean = !isAlive(b)(p) def neighbs(p: Pos) = List( (p._1 - 1, p._2 - 1), (p._1, p._2 - 1), (p._1 + 1, p._2 - 1), (p._1 - 1, p._2), (p._1 + 1, p._2), (p._1 - 1, p._2 + 1), (p._1, p._2 + 1), (p._1 + 1, p._2 + 1) ).map(wrap) def wrap(p: Pos): Pos = (((p._1 - 1) % width) + 1, ((p._2 - 1) % height) + 1) def liveneigbs(b: Board)(p: Pos): Int = neighbs(p).count(isAlive(b)) def survivers(b: Board): List[Pos] = b.filter(p => List(2, 3).contains(liveneigbs(b)(p))) def births(b: Board): List[Pos] = b.flatMap(p => neighbs(p)) .distinct .filter(p => isEmpty(b)(p)) .filter(p => liveneigbs(b)(p) == 3) def nextgen(b: Board): List[Pos] = survivers(b) ++ births(b) def life(b: Board): IO[Unit] = for { _ <- cls _ <- showcells(b) _ <- IO(Thread.sleep(500L)) _ <- life(nextgen(b)) } yield () life(List((4, 2), (2, 3), (4, 3), (3, 4), (4, 4))).unsafeRunSync()
Stateモナドの使い道 [フィボナッチ]
case class S(current: Int, next: Int) { def process() = S(next, current + next) } val fib: Stream[State[S, Int]] = Stream.continually(State(s => (s.process(), s.current))) val state: State[S, Stream[Int]] = fib.take(15).sequence println(state.run(S(0, 1)).value._2.toList) // List(0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377)
プログラミングhaskell 9章 version scala 〜後半〜
val box: List[String] = List( "+---------------+", "| |", "+---+---+---+---+", "| q | c | d | = |", "+---+---+---+---+", "| 1 | 2 | 3 | + |", "+---+---+---+---+", "| 4 | 5 | 6 | - |", "+---+---+---+---+", "| 7 | 8 | 9 | * |", "+---+---+---+---+" ) val buttons: String = { val standard = "qcd=123+456-789*0()/" val extra = "QCD \033\010\014\n" standard + extra } val showbox: IO[Unit] = seqn((1 to 13).zip(box).map(t => writeat((1, t._1))(t._2)).toList) def display(s: String): IO[Unit] = for { _ <- writeat((3, 2))(" ") _ <- writeat((3, 2))(s.toCharArray.takeRight(13).mkString) } yield () def calc(s: String): IO[Unit] = for { _ <- display(s) c <- getChar _ <- if (buttons.contains(c)) process(c)(s) else for { _ <- beep _ <- calc(s) } yield () } yield () def process(c: Char)(s: String): IO[Unit] = s match { case _ if "qQ\033".contains(c) => quit case _ if "dD\010\014".contains(c) => delete(s) case _ if "=\n".contains(c) => eval(s) case _ if "cC".contains(c) => clear case _ => process(c)(s) } def quit = goto((1, 14)) def delete(s: String) = s match { case "" => calc("") case xs => calc(xs.head.toString) } import ParserMain._ def eval(s: String): IO[Unit] = expr.run(s) match { case Some((_, n)) => calc(n.toString) case None => for { _ <- beep _ <- calc(s) } yield () } def clear: IO[Unit] = calc("") def press(c: Char)(s: String): IO[Unit] = calc(s + c.toString) val run: IO[Unit] = for { _ <- cls _ <- showbox _ <- clear } yield () run.unsafeRunSync()
プログラミングhaskell 9章 version scala 〜前半〜
def getChar: IO[Char] = IO(System.in.read().toChar) def putChar(c: Char): IO[Unit] = IO(print(c)) def echo: IO[Unit] = for { c <- getChar _ <- putChar('\n') _ <- putChar(c) _ <- putChar('\n') } yield () def getLine: IO[List[Char]] = for { x <- getChar r <- if (x == '\n') IO(Nil) else for { xs <- getLine } yield x :: xs } yield r def putStr(s: String): IO[Unit] = s.toCharArray.toList match { case Nil => IO(()) case x :: xs => for { _ <- putChar(x) _ <- putStr(xs.mkString) } yield () } def putStrLn(s: String): IO[Unit] = for { _ <- putStr(s) _ <- putChar('\n') } yield () def strlen: IO[Unit] = for { _ <- putStr("Enter a string: ") x <- getLine _ <- putStr("The string has ") _ <- putStr(x.length.toString) _ <- putStrLn(" characters") } yield () def cls = putStr("\033[2J") type Pos = (Int, Int) def goto(p: Pos) = putStr("\033[" + p._2.toString + ";" + p._1.toString + "H") def writeat(p: Pos)(s: String): IO[Unit] = for { _ <- goto(p) _ <- putStr(s) } yield () def seqn[T](seq: List[IO[T]]): IO[Unit] = seq match { case Nil => IO(()) case a :: as => for { _ <- a _ <- seqn(as) } yield () } seqn((1 to 9).map(_.toString charAt 0).map(putChar).toList).unsafeRunSync()
Writerモナドの使い道 [自作モノイド]
case class Product(money: Int) case class Coins(gohyakuen: Int, hyakuen: Int, gojuen: Int, juen: Int) { def +(other: Coins) = Coins(gohyakuen + other.gohyakuen, hyakuen + other.hyakuen, gojuen + other.gojuen, juen + other.juen) def sum(): Int = gohyakuen * 500 + hyakuen * 100 + gojuen * 50 + juen * 10 } implicit val monoidCoins = new Monoid[Coins] { override def empty: Coins = Coins(0, 0, 0, 0) override def combine(x: Coins, y: Coins): Coins = x + y } def buyProduct(coins: Coins): Writer[Coins, Product] = for { _ <- Writer.tell(coins) r <- Writer.value[Coins, Product](Product(coins.sum())) } yield r def buyProducts(coinsList: List[Coins]): Writer[Coins, List[Product]] = coinsList.map(buyProduct).sequence // 500円玉3枚、100円玉3枚、50円玉5枚、10円玉6枚使用 // 230円と710円と1170円の品をかう // (Coins(3,3,5,6),List(Product(230), Product(710), Product(1170))) println(buyProducts(List(Coins(0, 1, 2, 3), Coins(1, 2, 0, 1), Coins(2, 0, 3, 2))).run)