无法弄清楚 Scala 撰写函数中的语法

Mas*_*son 1 scala

我正在阅读红皮书,在检查我的练习答案时,我发现其中一个练习 (6.11) 的解决方案完全不同(并且比我自己的解决方案更优雅和神秘)。

这是代码:

object Candy {
  def update: Input => Machine => Machine = (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, candy, coin)) =>
        Machine(false, candy, coin + 1)
      case (Turn, Machine(false, candy, coin)) =>
        Machine(true, candy - 1, coin)
    }

  def simulateMachine(inputs: List[Input]): State[Machine, (Int, Int)] = for {
    _ <- State.sequence(inputs map (modify[Machine] _ compose update))
    s <- get
  } yield (s.coins, s.candies)

  def modify[S](f: S => S): State[S, Unit] = for {
    s <- get // Gets the current state and assigns it to `s`.
    _ <- set(f(s)) // Sets the new state to `f` applied to `s`.
  } yield ()

  def get[S]: State[S, S] = State(s => (s, s))

  def set[S](s: S): State[S, Unit] = State(_ => ((), s))
Run Code Online (Sandbox Code Playgroud)

我不确定的是这一行: inputs map (modify[Machine] _ compose update)

我了解 compose 的工作原理,但这种特殊的语法确实让我陷入了循环。有没有一种方法可以重写它,而不是那么紧凑以帮助菜鸟理解?

提前致谢。

对于其他试图更好地理解此代码如何工作的人,我发现这篇文章很有帮助。

Jör*_*tag 5

这里使用了几个语法特性:

运算符语法

Scala 允许消息发送使用空格而不是句点作为消息发送操作符,即

foo.bar(baz, quux)
Run Code Online (Sandbox Code Playgroud)

也可以写成

foo bar(baz, quux)
Run Code Online (Sandbox Code Playgroud)

运算符语法,第 2 部分

当使用带有单个参数的单个参数列表的运算符语法时,括号可以省略,即

foo bar(baz)
Run Code Online (Sandbox Code Playgroud)

也可以写成

foo bar baz
Run Code Online (Sandbox Code Playgroud)

占位符语法

Scala 中的匿名函数可以使用下划线_作为参数的占位符来编写。粗略地说,每个下划线都被最接近的词法封闭匿名函数的每个参数替换,按照在源文本中出现的顺序,即

val adder: (Int, Int) => Int = (x, y) => x + y
Run Code Online (Sandbox Code Playgroud)

也可以写成

val adder: (Int, Int) => Int = _ + _
Run Code Online (Sandbox Code Playgroud)

不是 ?-扩展

下划线_在 Scala 中有很多用途,所以有时如果你不够仔细,你可能会混淆不同的用法。在这种情况下,粗略一看,似乎下划线_可能意味着modify? 扩展为方法值,但事实并非如此。下划线_是匿名函数参数占位符。

结论

因此,如果我们将上述三个语法特征放在一起,则有问题的代码段将脱糖为

inputs.map(
//    ? Operator Syntax
  (
    f => modify[Machine](f)
//  ? Placeholder Syntax ?
  ).compose(update)
// ? Operator Syntax
)
Run Code Online (Sandbox Code Playgroud)

如果你有什么特定的代码方式的语法,你可以打印出Scala编译器或斯卡拉REPL /“解释”在使用的各个阶段的内部状态的疑虑-Xprint:<name-of-phase>命令行选项scala。例如,这是-Xprint:parser相关代码段的打印内容:

inputs.map((modify[Machine]: (() => <empty>)).compose(update))
Run Code Online (Sandbox Code Playgroud)

这是-Xprint:typer

inputs.map[this.State[this.Machine, Unit]](
  (
    (f: this.Machine => this.Machine) => $anon.this.State.modify[this.Machine](f)
  ).compose[this.Input](Candy.this.update)
)
Run Code Online (Sandbox Code Playgroud)