Tôi đang cố gắng hiểu cách tổ chức lại một chương trình mà trước đây tôi đã viết dưới dạng một chuỗi chuyển đổi trạng thái:
Tôi có một số logic kinh doanh:
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
Với những điều này, tôi muốn xây dựng một chương trình để truyền vào một số Trạng thái ban đầu (một Bản đồ trống), đọc đầu vào từ stdin , chuyển đổi nó thành In
, chạy chuyển đổi trạng thái và xuất trạng thái hiện tại S
và đầu ra Out
sang stdout .
Trước đây, tôi đã làm một việc như thế này:
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)
Tuy nhiên, tôi thực sự đang loay hoay tìm cách kết nối phương pháp này (một luồng chuyển đổi trạng thái) với luồng scalaz . Tôi bắt đầu với điều này:
type Transition = S => (S, Out)
val NoTransition: Transition = s => (s, 0)
io.stdInLines.map(inputToIn).map(_.fold(NoTransition)(transition))
Đây là kiểu: Process[Task, Transition]
. Tôi thực sự không biết phải đi đâu từ đó.
- Làm cách nào để "chuyển vào" của tôi
InitialState
và chạy chương trình, xâu chuỗi đầu raS
ở mỗi bước làm đầu vàoS
cho bước tiếp theo? - Làm cách nào để lấy các giá trị của
S
vàOut
ở mỗi bước và in chúng ra stdout (giả sử tôi có thể chuyển chúng thành chuỗi)?
Khi cố gắng sử dụng một cách đọc duy nhất, tôi cũng gặp khó khăn tương tự:
for {
i <- Process.eval(Task.now(InitialState))
l <- io.stdInLines.map(inputToIn)
...
Bất kỳ trợ giúp nào cũng được đánh giá rất cao!
Bây giờ tôi đã đi xa hơn một chút.
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))
Sau đó
prog.run.run
Nó không hoạt động: Có vẻ như nhà nước không được luồng qua suối. Thay vào đó, ở mỗi giai đoạn, trạng thái ban đầu đang được chuyển vào.
Paul Chiusano đề nghị sử dụng cách tiếp cận của process1.scan
. Vì vậy, bây giờ tôi làm điều này:
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))
Có một vấn đề ở đây: Trong ví dụ cụ thể này, Out
kiểu của tôi là monoid , vì vậy trạng thái ban đầu của tôi có thể được tạo bằng cách sử dụng danh tính của nó nhưng điều này thường không đúng như vậy. Tôi sẽ làm gì sau đó? (Tôi đoán tôi có thể sử dụng Option
nhưng điều này có vẻ như nó không cần thiết.)