Scalaz中组合状态的标准方法

4
考虑一下,假设你有一组状态(Nel代表非空列表,为了缩短,以下同),并且你想使用一些函数f和g将这些状态合并为一个状态,其中f用于左部分,g用于右部分。因此,你想要类似这样的东西:
def foldStatesG[S, A](in: NonEmptyList[State[S, A]])(f: (A, A) => A)(g: (S, S) => S): State[S, A] = {
    in.foldLeft1((s1, s2) => State(e => {
      val ss1 = s1(e)
      val ss2 = s2(e)
      g(ss1._1, ss2._1) -> f(ss1._2, ss2._2)
    }))
  }

我相信自己正在发明自行车,而这样的事情已经存在了,可能以一种更普遍的方式存在。但是,浏览scalaz时,我没有找到或认识到它。对这个话题的任何帮助表示感激。


第二个问题描述了我如何遇到这样的问题。我想做一个小模拟,当你有一个敌人(考虑它只是一个Double),和所有可能打击他的法术Nel [Spell]。所以基本上我想生成所有可能的序列。例如,如果Nel [Spell] =($,#),那么给定一个敌人E,进展将如下:

E -> (), then Nel(E -> $, E -> #), then Nel(E -> $$, E -> ##, E -> $#, E-> #$) etc.. (pseudo code)

不需要详细解释,我需要类似这样的东西:

def combineS(variations: NonEmptyList[Spell]): State[NonEmptyList[(Enemy, List[Spell])], Boolean]

换句话说,您提供所有可能的移动,它模拟所有可能的状态。您可以将其视为一种决策树,在每个步骤上分支。 因此,我定义了如何处理一个咒语。
def combineS1(spell: Spell): State[(NonEmptyList[(Enemy, List[Spell])]), Boolean]
    // some logic

问题是,我无法通过简单遍历来实现它:
def combineS(variations: NonEmptyList[Spell]): State[NonEmptyList[(Enemy, List[Spell])], Boolean] = {
    val x = variations traverse combine1 // State[Nel[..], Nel[Boolean]
    x map { _.suml1(conjunction)}
}   

由于在遍历过程中,Nel并没有被添加到彼此之后,而是被丢弃了。 这就是我想出这个解决方法的原因:
  def combineS(variations: NonEmptyList[Spell]): State[NonEmptyList[(Enemy, List[Spell])], AllDead] = {
    val x: NonEmptyList[State[NonEmptyList[(Enemy, List[Spell])], Boolean]] = variations map combineS
    foldStates(x)(_ && _)(append)
  } 

那段代码实际上是有效的,但我感觉有一种方法可以遍历它,而不需要额外的foldState。
在概念层面上,还有哪些其他的功能性方法可以进行这样的模拟呢?我可以简单地编写函数式代码,而不使用高级概念,但这个练习的目的正是为了学习高级内容。 (也许这正是Writer Monad或Comonad的工作?)
此外,如果stackoverflow不是这样的问题的最佳场所,请指出应该在哪里提出这样的问题?

我认为这是一个很适合在Stack Overflow上提问的问题,只是还没有机会回答。 - Travis Brown
1个回答

2

好的,回答第一个问题,如果我们处于一个Scalaz已经有Biapplicative类型类的替代宇宙中,我们可以按照以下方式使用它:

def foldStatesG[S, A](states: NonEmptyList[State[S, A]], s: (S, S) ⇒ S, a: (A, A) ⇒ A): State[S, A] =
  states.foldLeft1(IndexedStateT.indexedStateTBiapplicative[Id, S].bilift2(s, a))

但是我们没有,所以我们需要像以下这样的东西(注意:我没有确保这在法律上做得好):
trait Biapplicative[F[_, _]] extends Bifunctor[F] {
  def bipure[A, B](a: ⇒ A, b: ⇒ B): F[A, B]
  def biapply[A, B, C, D](fab: ⇒ F[A, B])(f: ⇒ F[AC, BD]): F[C, D]
  def bilift2[A, B, C, D, E, G](fab: (A, B) ⇒ C, fde: (D, E) ⇒ G): (F[A, D], F[B, E]) ⇒ F[C, G] =
    (fad: F[A, D], fbe: F[B, E]) ⇒
      biapply(fbe)(bimap[A, D, BC, EG](fad)(fab.curried, fde.curried))
}

object Biapplicative {
  implicit def indexedStateTBiapplicative[F[_], S1](implicit F: Applicative[F]) =
    new Biapplicative[IndexedStateT[F, S1, ?, ?]] {
      def bipure[A, B](a: ⇒ A, b: ⇒ B) =
        StateT.constantIndexedStateT(b)(a)
      def biapply[A, B, C, D]
          (fab: ⇒ IndexedStateT[F, S1, A, B])
          (f: ⇒ IndexedStateT[F, S1, AC, BD]) =
        new IndexedStateT[F, S1, C, D] {
          def apply(initial: S1): F[(C, D)] =
            F.ap(fab(initial))(F.map(f(initial)) {
              case (a2c, b2d) ⇒ Bifunctor[Tuple2].bimap(_)(a2c, b2d)
            })
        }
    }
}

非常感谢您的评论! - I See Voices

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接