我正在尝试理解如何重新组织一个程序,我之前会将其编写为状态转换的序列:
我有一些业务逻辑:
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
我希望能构建一个程序,传入一个初始状态(一个空Map),从标准输入读取输入并将其转换为In
,运行状态转换并将当前状态S
和输出Out
输出到标准输出。请保留HTML标签。
之前,我可能会这样做:
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-comprehension时,我遇到了类似的问题:
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
它不起作用:似乎状态没有通过流进行线程化。相反,在每个阶段,初始状态被传递。
Paul Chiusano建议使用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
类型是一个幺半群,因此可以使用其身份创建初始状态,但这可能并不总是如此。那我该怎么办呢?(我想我可以使用选项
,但这似乎是不必要的。)
StateT
构造?`type Carrier[A] = StateT[Task, S, A]; val input: Process[Carrier, Option[In]] = ...; prog.run.run(initialValue).run // prog.run 是一个 Carrier[Unit],即 StateT`
- lmm