我正在阅读 Scala中的函数式编程的第6章。我已经接近了章节的最后一次练习(即:6.11) 我怀疑某些代码行。
这是练习的描述:
让我们实现一个模拟简单糖果的有限状态自动机 分配器。机器有两个输入:您可以插入硬币,或者您 可以通过旋转旋钮来分配糖果。它可以是两个中的一个 状态:锁定或解锁。它还跟踪剩下多少糖果 它包含多少钱币。
- 如果仍有糖果,将硬币插入锁定的机器将解锁。
- 转动未锁定机器上的旋钮将使其分配一个糖果并返回锁定状态。
- 在锁定的机器上旋转旋钮或将硬币插入未锁定的机器无效。
- 没有糖果的机器会忽略任何输入。
方法
simulateMachine
应该基于a操作机器 输入列表并返回剩余的硬币和糖果数量 机。例如,如果输入机器有10个硬币和5个硬币 糖果,共有4个糖果成功购买,产量 应该是(14,11)。
以下是我自己实施的可测试单元。
object Test extends App {
class Comparison(value: Int) {
def shouldBe(other: Int): Boolean = value == other
}
implicit def enrichIntegers(value: Int) = new Comparison(value)
val machine = Machine(locked = true, candies = 5, coins = 10)
val input = List(Coin, Turn, Coin, Turn, Coin, Turn, Coin, Turn)
val result = StateChange.simulateMachine(input)
val tuple: ((Int, Int), Machine) = result.run(machine)
val ((coins, candies), _) = tuple
(coins shouldBe 14) && (candies shouldBe 1) match {
case true => println("Ok")
case false => println("Ko")
}
}
sealed trait Input
case object Coin extends Input
case object Turn extends Input
case class Machine(locked: Boolean, candies: Int, coins: Int)
case class State[S,+A](run: S => (A, S)) {
def map[B](f: A => B): State[S, B] = State(
s => {
val (a, newState) = this.run(s)
(f(a), newState)
}
)
def map2[B,C](sb: State[S, B])(f: (A, B) => C): State[S, C] = State(
s => {
val (a, newState) = this.run(s)
val (b, finalState) = sb.run(newState)
(f(a, b), finalState)
}
)
def flatMap[B](f: A => State[S, B]): State[S, B] = State(
s => {
val (a, state): (A, S) = this.run(s)
f(a).run(state)
}
)
}
object State {
def get[S]: State[S, S] = State(s => (s, s))
def set[S](s: S): State[S, Unit] = State(_ => ((), s))
def modify[S](f: S => S): State[S, Unit] = {
for {
s <- get
_ <- set(f(s))
} yield ()
}
def unit[S, A](a: A): State[S, A] = State(s => (a,s))
def sequence[S, A](fs: List[State[S,A]]): State[S, List[A]] = {
fs.foldRight[State[S, List[A]]](State.unit(Nil))((elem, acc) => elem.map2(acc)((a,b)=>a::b))
}
}
object StateChange {
def update(i: Input)(s: Machine) = {
(i,s) match {
case (_, Machine(_,0,_)) => s
case (Coin, Machine(false,_,_)) => s
case (Turn, Machine(true,_,_)) => s
case (Coin, Machine(true,candies,coins)) => Machine(false,candies,coins+1)
case (Turn, Machine(false,candies,coins)) => Machine(true,candies-1,coins)
}
}
def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = {
import State._
val list: State[Machine, List[Unit]] = sequence(inputs.map(input => modify(update(input))))
list.flatMap((_: List[Unit]) =>
State(
s => {
val currentState: State[Machine, Machine] = get // <= my doubt is here
val (currentMachine, newState) = currentState.run(s)
((currentMachine.coins, currentMachine.candies), newState)
}
)
)
}
}
我怀疑simulateMachine
定义中的以下一行:
val getState: State[Machine, Machine] = get
simulateMachine
实现了我为了更好地理解原文的内部原因而编写的一个令人遗憾的版本,我实际上是为解决这个练习而编写的。
我的问题是:
为什么我需要明确指定currentState
的类型来编译代码?实际上,如果没有State[Machine, Machine]
类型定义,上面的代码片段就无法编译。
为什么编译器无法推断实际类型?这只是类型推理限制的问题,还是我在这里遗漏了什么?
希望我的问题很明确,谢谢!
答案 0 :(得分:3)
如果您没有向get
提供类型参数,并且没有明确指定currentState
的类型,则Scala无法知道该类型。它必须从((currentMachine.coins, currentMachine.candies), newState)
和val (currentMachine, newState) = currentState.run(s)
向后推断类型,但Scala的类型推断不足以做到这一点。