我想知道,为什么这段代码不能编译?
val files = (new java.io.File(".")).listFiles
for (file <- files if file.getName.endsWith(".scala")) {
yield file
}
答案 0 :(得分:10)
简短的回答是,您的代码包含语法错误,因为 yield 关键字包含在传递给for-comprehension的代码块中。当编译器在 for 之后直接遇到花括号时,它会将其视为副作用的理解,并且不理解 yield 的含义。
对于长篇答案,我们需要更多地了解for-comprehension。
For-comprehensions有许多可能的表达方式,每个看似微小的差异都会彻底改变for-comprehension的行为。
让我们来看看最简单的语法
for(generator1) { code_block }
请注意, generator1 和 code_block 是符号,不是文字代码。生成器具有以下语法...
identifier <- expression
请注意,任何表达式都不起作用。对于上面的示例语法,表达式必须求值为定义 foreach 方法的某个对象,因为for-comprehension的此语法表示 code_block 应该是副作用代码(即在一些外部作用域中改变一些变量,或者写入数据库,或者其他......),并且for-comprehension本身不应该计算为一个值(事实上,如果你试图将for表达式赋给变量,你得到的值就是Unit)。
在您的情况下,您打算写的是以下,稍微复杂的语法。
for(generator1 guard1) yield { code_block }
在这种语法中,我们引入了一些新的语法元素,并在幕后提出了一些新的要求。首先,让我们分解一下。
和以前一样, guard1 实际上是一个符号,实际的语法实际上看起来更像......
if boolean_expression
当你有一个保护表达式时,这也意味着我们之前为 generator1 所讨论的表达式也必须评估为定义过滤器方法。特别注意我们没有谈到特定的特征/接口/抽象类。 Scala的“for-comprehensions”利用duck typing,因此任何实现您使用的特定语法所需方法的对象都可以在for-comprehension构造中使用。这非常强大。
无论如何,回到我们的新语法,另一个关键区别是 yield 关键字。此关键字对我们的生成器表达式施加了另一个限制,因为现在我们的生成器必须定义 map 方法。但是,这也解除了使用 foreach 方法的限制,因为您不能同时存在 yield 关键字并且不存在于相同的for-understanding中。
在那里使用 yield 关键字,现在的期望是 code_block 是非副作用的代码,而是某种形式的转换。此外,for-comprehension现在评估为一个值(类似于用于生成器的表达式的输出类型)。这是因为,在幕后,for-comprehension实际上只是调用 map 。
这应该是足够的信息来回答你的问题,但我强烈建议你阅读map / flatmap / foreach和for-comprehensions,因为我在这里没有涉及到更多内容。
答案 1 :(得分:2)
这对你来说如何?
if (flag) {
// some code
} { else
// other code
}
else
看起来是否在正确的位置?与for-comprehensions一样:yield
必须介于第一个括号(或大括号)和正在产生的表达式之间。
答案 2 :(得分:1)
这就是为什么圣经说:
Avoid the temptation to write things like this:
for (file <- filesHere if file.getName.endsWith(".scala")) {
yield file // Syntax error!
}
chapter and verse,就在this anchor之前。
希望这会有所帮助:for
表示foreach后的expr;在for means map之后的yield expr。对SO的影响当然有答案。
答案 3 :(得分:1)
将产量放出大括号
scala> val r = for (file <- files if file.getName.endsWith(".scala")) yield file
r: Array[java.io.File] = Array()
如果需要,您可以在yield之后编写大括号:yield {file}