Scala"< - "用于理解

Fel*_*lix 23 loops scala compiler-theory for-comprehension

我发现Scala总是对任何东西都有"自然的解释".总是像"哦,但这只是一个函数被调用此函数和该对象与此参数".从某种意义上说,我们从其他语言中知道它并不是真正的编译器魔法.

我的问题是在以下代码中使用的< -运算符:

for(i <- 0 to 10) println(i)
Run Code Online (Sandbox Code Playgroud)

在这个例子中,我可以看到它被重写为:

0.to(10).foreach((i:Int)=>println(i))
Run Code Online (Sandbox Code Playgroud)

但这并没有解释如何进入foreach函数内的匿名函数.在你写i的时候,它不是一个对象,也不是一个声明的变量.那是什么呢,它是如何被带到foreach的内部的呢?

我的猜测是,我终于发现了一些实际上是编译魔术的东西

谢谢你的时间.

为了澄清,我的问题是:如何做的< -在代码的第一线操作人员的工作,因为我不上它可作为函数调用的对象.

mis*_*tor 59

为了增加Dave的答案,这里是Scala语言规范中"for-comprehensions"的翻译模式:

理解for (enums) yield e评估e枚举器枚举生成的每个绑定的表达式.枚举器序列始终以生成器开始; 接下来可以是其他生成器,值定义或保护.

生成器p <- e从表达式生成绑定,该表达式e以某种方式与模式匹配p.值定义val p = e将值名称p(或模式中的多个名称p)绑定到计算表达式的结果e.一个保护if e包含一个布尔表达式,它限制枚举绑定.

发电机和警卫的确切含义是通过翻译定义为四种方法调用:map,filter,flatMap,和foreach.对于不同的载波类型,可以以不同的方式实现这些方法.

翻译方案如下.在第一步中,每个生成器p <- e,其中p不是无可辩驳的(第8.1节)的类型e被替换为

 p <- e.filter { case p => true; case _ => false }
Run Code Online (Sandbox Code Playgroud)

然后,重复应用以下规则,直到消除所有理解为止.

  • 理解for (p <- e) yield e0被翻译成e.map { case p => e0 }.

  • 理解for (p <- e) e0被翻译成e.foreach { case p => e0 }.

  • 一个理解for (p <- e; p0 <- e0 . . .) yield e00,在哪里...是一个(可能是空的)发电机或警卫序列,被翻译成:
    e.flatMap { case p => for (p0 <- e0 . . .) yield e00 }.

  • 一个理解的for (p <- e; p0 <- e0 . . .) e00地方...是一个(可能是空的)发电机或警卫序列,被翻译成:
    e.foreach { case p => for (p0 <- e0 . . .) e00 }.

  • 发电机p <- e后跟保护if g装置转换为单个发电机:
    p <- e.filter((x1, . . . , xn) => g )
    其中x1,...,xn是自由变量p.

  • 的发电机p <- e,随后的值定义val p0 = e0被转换为双值,其中的下列发生器xx0新鲜的名称:

    val (p, p0) <- 
      for(x@p <- e) yield { val x0@p0 = e0; (x, x0) }
    
    Run Code Online (Sandbox Code Playgroud)


Ran*_*ulz 18

<-是一个语言定义的关键字符号,=>但与->(与定义的符号)形成鲜明对比.因为它是基本Scala语法的一部分,所以它可以用于创建绑定(对于i您的示例),这是用户定义的构造无法完成的.

  • @Felix:它在规范中有记录.几乎所有市场上出售的Scala书都涵盖了它. (4认同)

Dav*_*ith 7

在这种情况下,它确实是一些编译魔术.从for-comprehension到filter/map/flatmap表单的翻译是一个特殊的desugaring,就像转换特殊形式的update和apply方法一样.