斯卡拉 – 我如何做“如果…”在一个理解中?
我问一个最近困扰我的一个非常基本的问题.
我想写一个 Scala For表达式来做如下的事情: for (i <- expr1) { if (i.method) { for (j <- i) { if (j.method) { doSomething() } else { doSomethingElseA() } } } else { doSomethingElseB() } } 问题是,在多个生成器中对于表达式,我不知道我可以把每个表达体放在哪里. for {i <- expr1 if(i.method) // where can I write the else logic ? j <- i if (j.method) } doSomething() 如何重写Scala Style中的代码? 解决方法
你写的第一个代码是完全有效的,所以没有必要重写它.其他地方你说你想知道如何做到Scala风格.没有真正的“Scala风格”,但我会承担一个更有功能的风格和粘性.
for (i <- expr1) { if (i.method) { for (j <- i) { if (j.method) { doSomething() } else { doSomethingElseA() } } } else { doSomethingElseB() } } 第一个问题是这不返回任何价值.它所做的一切都是副作用,这也是要避免的.所以第一个变化就是这样: 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) 结果的类型是Array [AnyRef],而使用多个生成器将产生Array [Element].这个修复的简单部分是: val result = for { i <- expr1 element <- classifyElements(i) } yield element 但是,单独的方法将不起作用,因为classifyElements本身返回AnyRef,并且我们希望它返回一个集合.现在,validElements返回一个集合,所以这不是一个问题.我们只需要修复else部分.由于validElements返回一个IndexedSeq,所以我们返回一个else part.最后的结果是: 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实现的(见here或其他类似的问题和答案).这意味着上面的代码与此代码完全相同: for { i <- expr1 j <- i } doSomething 完全一样的事情当使用收益时,根本就不是这样.以下表达式不会产生相同的结果: for (i <- expr1) yield for (j <- i) yield j for (i <- expr1; j <- i) yield j 第一个片段将通过两个地图调用实现,而第二个片段将使用一个flatMap和一个映射. 因此,只有在产量的上下文中,甚至有意义的是担心嵌套循环或使用多个生成器.而且,事实上,发电机代表着一些事情的产生,这只是真正的理解(产生某些东西)的真实性. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |