Saw*_*yer 18 scala for-comprehension
我问的是一个让我最近困惑的基本问题.我想编写一个Scala For表达式来执行以下操作:
for (i <- expr1) {
if (i.method) {
for (j <- i) {
if (j.method) {
doSomething()
} else {
doSomethingElseA()
}
}
} else {
doSomethingElseB()
}
}
Run Code Online (Sandbox Code Playgroud)
问题是,在多个生成器For表达式中,我不知道我可以在哪里放置表达式主体.
for {i <- expr1
if(i.method) // where can I write the else logic ?
j <- i
if (j.method)
} doSomething()
Run Code Online (Sandbox Code Playgroud)
如何在Scala Style中重写代码?
Dan*_*ral 19
您编写的第一个代码完全有效,因此无需重写它.在其他地方,你说你想知道如何做Scala风格.没有真正的"Scala风格",但我会假设一种更具功能性的风格.
for (i <- expr1) {
if (i.method) {
for (j <- i) {
if (j.method) {
doSomething()
} else {
doSomethingElseA()
}
}
} else {
doSomethingElseB()
}
}
Run Code Online (Sandbox Code Playgroud)
第一个问题是这不会返回任何值.它所做的就是副作用,这也是要避免的.所以第一次改变是这样的:
val result = for (i <- expr1) yield {
if (i.method) {
for (j <- i) yield {
if (j.method) {
returnSomething()
// etc
Run Code Online (Sandbox Code Playgroud)
现在,两者之间存在很大差异
for (i <- expr1; j <- i) yield ...
Run Code Online (Sandbox Code Playgroud)
和
for (i <- expr1) yield for (j <- i) yield ...
Run Code Online (Sandbox Code Playgroud)
他们返回不同的东西,有时你想要的是后者,而不是前者.不过,我会假设你想要前者.现在,在我们继续之前,让我们修复代码.这是丑陋的,难以遵循和没有信息.让我们通过提取方法来重构它.
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)
Run Code Online (Sandbox Code Playgroud)
它已经更加清洁,但它并没有像我们期望的那样回归.让我们来看看差异:
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)
Run Code Online (Sandbox Code Playgroud)
result
有的类型Array[AnyRef]
,虽然使用多个发电机将产生Array[Element]
.修复的简单部分是这样的:
val result = for {
i <- expr1
element <- classifyElements(i)
} yield element
Run Code Online (Sandbox Code Playgroud)
但仅此一点是行不通的,因为classifyElements本身会返回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
Run Code Online (Sandbox Code Playgroud)
这与您提供的循环和条件完全相同,但它更易读,更容易更改.
关于收益率
我认为重要的是要注意所提出的问题.让我们简化它:
for (i <- expr1) {
for (j <- i) {
doSomething
}
}
Run Code Online (Sandbox Code Playgroud)
现在,这是实现的foreach
(见这里,或其他类似的问题和答案).这意味着上面的代码与此代码完全相同:
for {
i <- expr1
j <- i
} doSomething
Run Code Online (Sandbox Code Playgroud)
完全一样的事情.当一个人使用时,这根本不是真的yield
.以下表达式不会产生相同的结果:
for (i <- expr1) yield for (j <- i) yield j
for (i <- expr1; j <- i) yield j
Run Code Online (Sandbox Code Playgroud)
第一个代码段将通过两个map
调用实现,而第二个代码段将使用一个flatMap
和一个map
.
因此,只有在这样的情况下yield
,担心嵌套for
循环或使用多个生成器才有意义.而且,事实上,生成器代表着正在生成某些事物的事实,这只适用于真正的理解(yield
某些东西).
归档时间: |
|
查看次数: |
23450 次 |
最近记录: |