我想知道,为什么这段代码不能编译?
val files = (new java.io.File(".")).listFiles
for (file <- files if file.getName.endsWith(".scala")) {
yield file
}
我想知道,为什么这段代码不能编译?
val files = (new java.io.File(".")).listFiles
for (file <- files if file.getName.endsWith(".scala")) {
yield file
}
简短的回答是,您的代码包含语法错误,因为yield关键字包含在提交给 for-comprehension 的代码块内。当编译器在您的for之后直接遇到花括号时,它会将其视为影响 for 理解的副作用,并且不理解您的意思yield。
对于长答案,我们需要更多地了解理解。
For-comprehensions 有许多可能的表达方式,每个看似微小的差异都会极大地改变 for-comprehension 的行为。
我们来看看最简单的语法
for(generator1) { code_block }
请注意,generator1和code_block是符号,而不是文字代码。生成器具有以下语法...
identifier <- expression
请注意,任何表达式都不起作用。对于上面的示例语法,表达式必须评估为定义foreach方法的某个对象,因为这种用于理解的语法表示code_block应该是副作用代码(即在某些外部范围内改变某些变量,或写入数据库, 或其他...),并且 for-comprehension 本身不应评估为一个值(事实上,如果您尝试将 for 表达式分配给一个变量,您将获得的值将是 Unit)。
在您的情况下,您打算编写的是以下稍微复杂的语法。
for(generator1 guard1) yield { code_block }
在此语法中,我们引入了一些新的语法元素,并且在幕后还引入了一些新要求。首先,让我们分解一下。
和以前一样,guard1确实是一个符号,实际语法实际上看起来更像...
if boolean_expression
当你有一个保护表达式时,这也意味着我们之前讨论的生成器1 的表达式也必须计算为定义过滤器方法的对象。请特别注意,我们还没有讨论特定的 trait/interface/abstract 类。Scala 的“for-comprehensions”利用了鸭子类型,因此任何实现了您使用的特定语法所需方法的对象都可以在 for-comprehension 构造中使用。这是非常强大的。
无论如何,回到我们的新语法,另一个关键区别是yield关键字。这个关键字对我们的生成器表达式施加了另一个限制,因为现在我们的生成器必须定义一个map方法。但是,这也解除了使用foreach方法的限制,因为在同一个 for-comprehension 中不能同时存在和不存在yield关键字。
使用yield关键字,现在期望code_block是无副作用的代码,而是某种形式的转换。此外,for-comprehension 现在计算为一个值(类似于用于生成器的表达式的输出类型)。这是因为,在幕后,for-comprehension 实际上只是调用map。
这应该足以回答您的问题,但我强烈建议您阅读 map/flatmap/foreach 和 for-comprehensions,因为还有很多我没有在这里介绍的内容。
这在你看来如何?
if (flag) {
// some code
} { else
// other code
}
else
看起来是在正确的地方吗?与 for-comprehensions 相同:yield
必须位于第一个括号(或大括号)和产生的表达式之间。
输出大括号
scala> val r = for (file <- files if file.getName.endsWith(".scala")) yield file
r: Array[java.io.File] = Array()
如果你愿意,你可以在 yield 之后写大括号:yield {file}