首页 > 解决方案 > 当状态满足某些条件时如何停止状态转换?

问题描述

我对 scalaz/cats 很陌生,并且对Statemonad 有疑问(catsscalaz无关紧要)。考虑以下众所周知的示例Stack

object StateTest {

  type Stack = List[Int]

  def main(args: Array[String]) = {
    println(transition.run(List(1, 2, 3, 4)).value) //(List(4),Some(3))
    println(transition.run(List(1, 2)).value) //(List(),None)

  }

  def transition: State[Stack, Option[Int]] = for {
    _ <- pop
    _ <- pop
    a <- pop
  } yield a

  def pop: State[Stack, Option[Int]] = State {
    case x::xs => (xs, Some(x))
    case Nil => (Nil, None)
  }
}

问题是我想执行状态转换(pop)直到状态(List[Int])满足某些条件(我想检查List[Int]::isEmpty),然后立即停止。

在当前的实现中,我只能知道调用后状态是否满足条件run

是否可以在猫/scalaz 中使用 State monad 这样做,或者我需要其他东西?

标签: scalafunctional-programmingscalazscala-cats

解决方案


您将使用由另一个表示终止的 monad 参数化的 state monad。

通常,这种参数化的 monad 称为monad 转换器。在这种特定情况下,您将使用StateTmonad 转换器。取模一些实现细节,StateT相当于

type StateT[F[_], S, A] = S => F[(S, A)]

现在可以选择Fbe Option,代表立即终止。

import scalaz.StateT
import scalaz.std.option._

object StateTest {

  type Stack = List[Int]

  def main(args: Array[String]) = {
    println(transition.run(List(1, 2, 3, 4))) // Some((List(4), 3))
    println(transition.run(List(1, 2)))       // None
  }

  def transition: StateT[Option, Stack, Int] = for {
    _ <- pop
    _ <- pop
    a <- pop
  } yield a

  def pop: StateT[Option, Stack, Int] = StateT {
    case x::xs => Some((xs, x))
    case Nil   => None
  }
}

如果你想B在提前终止的情况下返回一些类型的值,你可以使用Either[B, ?]而不是Option参数化StateT

type ErrorOr[A] = Either[String, A]

def pop1: StateT[ErrorOr, Stack, Int] = StateT {
  case x::xs => Right((xs, x))
  case Nil   => Left("Cannot pop from an empty stack.")
}

推荐阅读