Я намагаюся зрозуміти, як реорганізувати програму, яку я раніше писав би як послідовність переходів стану:
У мене є певна ділова логіка:
type In = Long
type Count = Int
type Out = Count
type S = Map[Int, Count]
val inputToIn: String => Option[In]
= s => try Some(s.toLong) catch { case _ : Throwable => None }
def transition(in: In): S => (S, Out)
= s => { val n = s.getOrElse(in, 0); (s + (in -> n+1), n+1) }
val ZeroOut: Out = 0
val InitialState: S = Map.empty
За допомогою них я хочу побудувати програму для передачі в якомусь початковому стані (порожня карта), прочитати вхідні дані з stdin , перетворити їх на In
, виконати перехід стану і вивести поточний стан S
і вихідні дані Out
у stdout .
Раніше я б зробив щось подібне:
val runOnce = StateT[IO, S, Out](s => IO.readLn.map(inputToIn) flatMap {
case None => IO((s, ZeroOut))
case Some(in) => val (t, o) = transition(in)(s)
IO.putStrLn(t.toString) |+| IO.putStrLn(o.toString) >| IO((t, o))
})
Stream.continually(runOnce).sequenceU.eval(InitialState)
Однак я справді намагаюся побачити, як зв'язати цей підхід (потік переходів стану) із scalaz-stream . Я почав з цього:
type Transition = S => (S, Out)
val NoTransition: Transition = s => (s, 0)
io.stdInLines.map(inputToIn).map(_.fold(NoTransition)(transition))
Це типу: Process[Task, Transition]
. Я насправді не знаю, куди звідти йти.
- Як я "передаю" свою
InitialState
і запускаю програму, додаючи вихідні даніS
на кожному кроці як вхідні даніS
для наступного? - Як отримати значення
S
іOut
на кожному кроці та надрукувати їх у stdout (за умови, що я можу перетворити їх у рядки)?
Намагаючись використати єдине для розуміння, я так само застряю:
for {
i <- Process.eval(Task.now(InitialState))
l <- io.stdInLines.map(inputToIn)
...
Будь-яка допомога дуже вдячна!
Зараз я трохи далі.
type In_ = (S, Option[In])
type Out_ = (S, Out)
val input: Process[Task, In_]
= for {
i <- Process.emit(InitialState)
o <- io.stdInLines.map(inputToIn)
} yield (i, o)
val prog =
input.pipe(process1.collect[In_, Out_]) {
case (s, Some(in)) => transition(in)(s)
}).to(io.stdOutLines.contramap[Out_](_.toString))
Тоді
prog.run.run
Вона не працює: Схоже , що держава не різьбові через потік. Навпаки, на кожному етапі передається початковий стан.
Пол Кьюзано запропонував використовувати підхід Росії process1.scan
. Отже, зараз я роблю це:
type In_ = In
type Out_ = (S, Out)
val InitialOut_ = (InitialState, ZeroOut)
val program =
io.stdInLines.collect(Function.unlift(inputToIn)).pipe(
process1.scan[In_, Out_](InitialOut_) {
case ((s, _), in) => transition(in)(s)
}).to(io.stdOutLines.contramap[Out_](_.shows))
Тут є проблема: у цьому конкретному прикладі мій Out
тип є моноїдом , тому мій початковий стан можна створити, використовуючи його ідентичність, але це може бути не так. Що б я тоді зробив? (Думаю, я міг би використати, Option
але це здається непотрібним.)