【发布时间】:2017-11-19 17:08:50
【问题描述】:
我正在阅读 Scala 中的函数式编程 的第 6 章。我已经接近本章的最后一个练习(即:6.11),它使 我怀疑某些代码行。
这是练习的描述:
让我们实现一个模拟简单糖果的有限状态自动机 自动售货机。机器有两个输入:你可以插入硬币,或者你 可以通过转动旋钮分配糖果。它可以是两个之一 状态:锁定或解锁。它还跟踪剩余的糖果数量 以及它包含多少硬币。
- 如果还剩下糖果,将硬币插入上锁的机器会解锁。
- 转动未锁定机器上的旋钮将使其分配一颗糖果并返回锁定状态。
- 转动锁定机器上的旋钮或将硬币插入未锁定机器没有效果。
- 没有糖果的机器会忽略任何类型的输入。
simulateMachine方法应该基于一个 输入列表并返回剩余的硬币和糖果的数量 机器。例如,如果输入 Machine 有 10 个硬币和 5 个 糖果,一共买了4颗糖果成功,输出 应该是 (14, 1)。
以下是我自己实现的可测试单元。
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] 类型定义,上面的 sn-p 就无法编译。
为什么编译器不能推断出实际类型?这只是类型推断限制的问题还是我在这里遗漏了什么?
希望我的问题很清楚,谢谢!
【问题讨论】:
标签: scala functional-programming type-inference