加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

斯卡拉 – 我如何做“如果…”在一个理解中?

发布时间:2020-12-16 09:15:15 所属栏目:安全 来源:网络整理
导读:我问一个最近困扰我的一个非常基本的问题. 我想写一个 Scala For表达式来做如下的事情: for (i - expr1) { if (i.method) { for (j - i) { if (j.method) { doSomething() } else { doSomethingElseA() } } } else { doSomethingElseB() }} 问题是,在多个生
我问一个最近困扰我的一个非常基本的问题.
我想写一个 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和一个映射.

因此,只有在产量的上下文中,甚至有意义的是担心嵌套循环或使用多个生成器.而且,事实上,发电机代表着一些事情的产生,这只是真正的理解(产生某些东西)的真实性.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读