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
被替换为Run Code Online (Sandbox Code Playgroud)p <- e.filter { case p => true; case _ => false }
然后,重复应用以下规则,直到消除所有理解为止.
理解
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
被转换为双值,其中的下列发生器x
和x0
新鲜的名称:Run Code Online (Sandbox Code Playgroud)val (p, p0) <- for(x@p <- e) yield { val x0@p0 = e0; (x, x0) }
Ran*_*ulz 18
<-
是一个语言定义的关键字符号,=>
但与->
(与定义的符号)形成鲜明对比.因为它是基本Scala语法的一部分,所以它可以用于创建绑定(对于i
您的示例),这是用户定义的构造无法完成的.
在这种情况下,它确实是一些编译魔术.从for-comprehension到filter/map/flatmap表单的翻译是一个特殊的desugaring,就像转换特殊形式的update和apply方法一样.