您编写的第一个代码完全有效,因此无需重写。在其他地方,您说您想知道如何以 Scala 风格进行操作。没有真正的“Scala 风格”,但我会假设一种更实用的风格并加以解决。
for (i <- expr1) {
if (i.method) {
for (j <- i) {
if (j.method) {
doSomething()
} else {
doSomethingElseA()
}
}
} else {
doSomethingElseB()
}
}
第一个问题是 this 没有返回值。它所做的只是副作用,这也是要避免的。所以第一个变化是这样的:
val result = for (i <- expr1) yield {
if (i.method) {
for (j <- i) yield {
if (j.method) {
returnSomething()
// etc
现在,两者之间有很大的不同
for (i <- expr1; j <- i) yield ...
和
for (i <- expr1) yield for (j <- i) yield ...
他们返回不同的东西,有时你想要后者,而不是前者。不过,我假设您想要前者。现在,在我们继续之前,让我们修复代码。它是丑陋的,难以理解且信息量不足。让我们通过提取方法来重构它。
def resultOrB(j) = if (j.method) returnSomething else returnSomethingElseB
def nonCResults(i) = for (j <- i) yield resultOrB(j)
def resultOrC(i) = if (i.method) nonCResults(i) else returnSomethingC
val result = for (i <- expr1) yield resultOrC(i)
它已经干净多了,但并没有像我们期望的那样返回。我们来看看区别:
trait Element
object Unrecognized extends Element
case class Letter(c: Char) extends Element
case class Punct(c: Char) extends Element
val expr1 = "This is a silly example." split "\\b"
def wordOrPunct(j: Char) = if (j.isLetter) Letter(j.toLower) else Punct(j)
def validElements(i: String) = for (j <- i) yield wordOrPunct(j)
def classifyElements(i: String) = if (i.nonEmpty) validElements(i) else Unrecognized
val result = for (i <- expr1) yield classifyElements(i)
的类型result
是Array[AnyRef]
,而使用多个生成器会产生Array[Element]
。修复的简单部分是这样的:
val result = for {
i <- expr1
element <- classifyElements(i)
} yield element
但仅此一项是行不通的,因为分类元素本身返回AnyRef
,我们希望它返回一个集合。现在,validElements
返回一个集合,所以这不是问题。我们只需要修复else
部分。既然validElements
是返回一个IndexedSeq
,让我们也返回那个else
部分。最终结果是:
trait Element
object Unrecognized extends Element
case class Letter(c: Char) extends Element
case class Punct(c: Char) extends Element
val expr1 = "This is a silly example." split "\\b"
def wordOrPunct(j: Char) = if (j.isLetter) Letter(j.toLower) else Punct(j)
def validElements(i: String) = for (j <- i) yield wordOrPunct(j)
def classifyElements(i: String) = if (i.nonEmpty) validElements(i) else IndexedSeq(Unrecognized)
val result = for {
i <- expr1
element <- classifyElements(i)
} yield element
这与您介绍的循环和条件的组合完全相同,但它更具可读性和易于更改。
关于产量
我认为重要的是要注意所提出的问题的一件事。让我们简化一下:
for (i <- expr1) {
for (j <- i) {
doSomething
}
}
现在,这是用foreach
(见这里,或其他类似的问题和答案)实现的。这意味着上面的代码与此代码完全相同:
for {
i <- expr1
j <- i
} doSomething
完全一样的东西。当一个人使用yield
. 以下表达式不会产生相同的结果:
for (i <- expr1) yield for (j <- i) yield j
for (i <- expr1; j <- i) yield j
第一个片段将通过两个map
调用实现,而第二个片段将使用 oneflatMap
和 one map
。
因此,只有在这种情况下yield
,担心嵌套for
循环或使用多个生成器才有意义。而且,实际上,生成器代表正在生成某物的事实,这仅适用于真正的理解(the one 正在生成yield
某物)。