加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 什么是适当的monad或序列理解来映射和携带状态?

发布时间:2020-12-16 09:20:32 所属栏目:安全 来源:网络整理
导读:我正在编写一个编程语言翻译器. 我需要正确的代码习语来评估一系列表达式以获取其值的序列,并在评估发生时将状态从一个评估者传播到下一个评估者.我想要一个功能性的编程成语. 这不是折叠,因为结果像地图一样出现.这不是一个地图,因为国家的支柱. 我所用的是
我正在编写一个编程语言翻译器.

我需要正确的代码习语来评估一系列表达式以获取其值的序列,并在评估发生时将状态从一个评估者传播到下一个评估者.我想要一个功能性的编程成语.

这不是折叠,因为结果像地图一样出现.这不是一个地图,因为国家的支柱.

我所用的是这个代码,我用来试图弄清楚这一点.熊先生几条测试台:

// test rig
class MonadLearning extends JUnit3Suite {

  val d = List("1","2","3") // some expressions to evaluate. 

  type ResType = Int 
  case class State(i : ResType) // trivial state for experiment purposes
  val initialState = State(0)

// my stub/dummy "eval" function...obviously the real one will be...real.
  def computeResultAndNewState(s : String,st : State) : (ResType,State) = {
    val State(i) = st
    val res = s.toInt + i
    val newStateInt = i + 1
    (res,State(newStateInt))
  }

我目前的解决方案使用更新的var,因为地图的正文被评估:

def testTheVarWay() {
    var state = initialState
    val r = d.map {
      s =>
        {
          val (result,newState) = computeResultAndNewState(s,state)
          state = newState
          result
        }
    }
    println(r)
    println(state)
  }

我有我认为使用foldLeft的不可接受的解决方案,这就是我所说的“把它当成你的折叠”成语:

def testTheFoldWay() {

// This startFold thing,requires explicit type. That alone makes it muddy.
val startFold : (List[ResType],State) = (Nil,initialState)
val (r,state) = d.foldLeft(startFold) {
  case ((tail,st),s) => {
    val (r,ns) = computeResultAndNewState(s,st)
    (tail :+ r,ns) // we want a constant-time append here,not O(N). Or could Cons on front and reverse later
  }
}

println(r)
println(state)

}

我也有几个递归的变化(这是显而易见的,但也不清楚或有很好的动机),一个使用几乎可以容忍的流:

def testTheStreamsWay() {
  lazy val states = initialState #:: resultStates // there are states
  lazy val args = d.toStream // there are arguments
  lazy val argPairs = args zip states // put them together
  lazy val resPairs : Stream[(ResType,State)] = argPairs.map{ case (d1,s1) => computeResultAndNewState(d1,s1) } // map across them
  lazy val (results,resultStates) = myUnzip(resPairs)// Note .unzip causes infinite loop. Had to write my own.

  lazy val r = results.toList
  lazy val finalState = resultStates.last

  println(r)
  println(finalState)
}

但是,我无法确定任何像上面原来的“var”解决方案那样紧凑或清晰的东西,我愿意和他一起生活,但是我认为吃/喝/睡眠monad成语的人会说..使用这个…(希望!)

解决方法

使用映射与累加器组合器(简单的方法)

你想要的高阶函数是mapAccumL.这是在Haskell的standard library,但是对于Scala,你将不得不使用像Scalaz这样的东西.

首先进口(请注意,我在这里使用Scalaz 7;以前版本您将导入Scalaz._):

import scalaz._,syntax.std.list._

然后它是一个单行:

scala> d.mapAccumLeft(initialState,computeResultAndNewState)
res1: (State,List[ResType]) = (State(3),List(1,3,5))

请注意,我不得不扭转评估者参数的顺序和返回值元组,以匹配mapAccumLeft所预期的签名(在这两种情况下都是第一位).

与国家monad(稍微不那么简单的方法)

正如彼得·普拉达(PetrPudlák)在另一个答案中指出的那样,你也可以使用状态monad来解决这个问题. Scalaz实际上提供了许多与国家monad一起使用的设施,比他的回答建议的版本要容易得多,而且不符合评论的要求,所以我把它们添加到这里.

首先,Scalaz确实提供了一个mapM,它只是被称为遍历(这在PetrPudlák在他的评论中注意到一点).所以假设我们有以下(我在这里再次使用Scalaz 7):

import scalaz._,Scalaz._

type ResType = Int
case class Container(i: ResType)

val initial = Container(0)
val d = List("1","3")

def compute(s: String): State[Container,ResType] = State {
  case Container(i) => (Container(i + 1),s.toInt + i)
}

我们可以写这个:

d.traverse[({type L[X] = State[Container,X]})#L,ResType](compute).run(initial)

如果你不喜欢这个丑陋的lambda,你可以像这样去掉它:

type ContainerState[X] = State[Container,X]

d.traverse[ContainerState,ResType](compute).run(initial)

但它变得更好! Scalaz 7给你一个专为国家monad的横越版本:

scala> d.traverseS(compute).run(initial)
res2: (Container,List[ResType]) = (Container(3),5))

而如果这还不够,还有一个版本的内置游戏:

scala> d.runTraverseS(initial)(compute)
res3: (Container,5))

仍然不如mapAccumLeft版本好,在我看来,但很干净.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读