【问题标题】:How to stop state transition when state satisfies some condition?当状态满足某些条件时如何停止状态转换?
【发布时间】:2018-05-28 21:56:20
【问题描述】:

我对 scalaz/cats 很陌生,并且对 State monad 有疑问(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之后知道状态是否满足条件。

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

【问题讨论】:

    标签: scala functional-programming scalaz scala-cats


    【解决方案1】:

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

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

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

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

    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.")
    }
    

    【讨论】:

    • 感谢您的回复,但是当我们到达None 时准确停止如何。我的意思是我们不知道初始大小(例如 100)。我想通过执行状态转换来弹出所有元素。 100 行长的理解看起来有点吓人……
    • 我怀疑我需要将StateT[Option, Stack, Int] 包装到另一个单子中...不是吗?
    • 实际上,我可以以固定大小的批次执行状态转换……然后检查它是否达到我需要的状态……在我的情况下,它的批次大小 = 3 (3 @987654334 @s) 然后在每次转换后运行。是不是太诡异了?
    • 使用递归:def popAll: StateT[Option, Stack, Int] = pop flatMap (i =&gt; popAll)。如果初始pop 返回None,则flatMap 的参数将永远不会执行。如果它返回一些 int i,这个 i 将传递给 flatMap 的参数,我们继续它返回的内容,在这种情况下递归调用 popAll
    猜你喜欢
    • 1970-01-01
    • 2021-06-05
    • 2018-04-05
    • 1970-01-01
    • 2020-01-11
    • 2019-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多