问题 scala闭包/匿名函数中的多个返回点


据我所知,Scala中没有办法在匿名函数中有多个返回点,即

someList.map((i) => {
    if (i%2 == 0) return i // the early return allows me to avoid the else clause
    doMoreStuffAndReturnSomething(i) // thing of this being a few more ifs and returns
})

提出一个 error: return outside method definition。 (如果不提出这个问题,那么代码将不起作用,因为我希望它可以工作。)

我可以解决的一个解决方法是以下

someList.map({
    def f(i: Int):Int = {
        if (i%2 == 0) return i
        doMoreStuffAndReturnSomething(i)
    }
    f
})

但是,我想知道是否还有另一种“接受”的做法。也许有可能没有内部功能的名称?

(一个用例就是模仿一些有价值的东西 continue 在循环内构造。)

编辑

请相信我,有必要避免使用else语句,因为, doMoreStuff 部分可能看起来像:

val j = someCalculation(i)
if (j == 0) return 8
val k = needForRecalculation(i)
if (k == j) return 9
finalRecalc(i)
...

当你只有一个 if - else 可用的结构很容易弄乱。

当然,在我刚开始给出的简单例子中,它更容易使用 else。对不起,我觉得这很清楚。


4228
2018-05-24 17:27


起源

使用else语句有什么问题? - Patrick
在你给出的例子中,没有任何理由可以避免 else 关键词;如果你愿意的话,没有额外的表达式被评估 else,所以你在这里使用早期回报你什么都得不到。 - Jesper
对不起,我修改了它。以为很清楚 doMoreStuff 部分实际上 不 多一点。 - Debilski


答案:


如果你的匿名函数那么复杂,我会让它更明确。匿名函数不适用于比几行更复杂的东西。您可以通过在using方法中声明它来使其成为私有方法

def myF(i:Int):Int = {
    if (i%2 == 0) return i
    doMoreStuffAndReturnSomething(i)
}
someList.map(myF(_))

这是您的解决方法的变体,但更清洁。它们都将其保密为本地方法范围。


5
2018-05-24 17:41



这是一个很好的解决方法。我是scala newb所以我真的不明白为什么这是必需的,但它解决了这个问题。 - ripper234


在您的代码注释中,您写道,您要避免使用 else 关键字,但恕我直言这完全是你想要的,它甚至两个字符更短;-)

someList.map((i) => {
    if (i%2 == 0) i else
    doMoreStuffAndReturnSomething(i)
})

3
2018-05-24 17:43



请看我的编辑。 - Debilski
实际上,我会说它节省了两个以上的字符:一个明确的 return 迫使你无论如何都要声明返回类型......但是我的实际用例会比我最初给出的例子稍微复杂一些,所以它不仅仅是保存字符。 - Debilski


您给出的示例很容易通过if语句解决。这样做没有表现或其他处罚。

但是你可能还有其他一些情况,看起来大致如此

if (test) {
  if (anotherTest) {
    val a = someComputation()
    if (testOf(a)) return otherComputation()
  }
  else if (yetAnotherTest) return whatever()
}
bigComputation()

如果你想避免将if转换为没有返回的表单所需的if语句和/或代码重复的混乱,有几种方法可以处理这种情况。

您可以使用各种偷偷摸摸的东西 Option 要么 Either 保持国家流动(与... orElse 和 fold)这样你只需要进行计算。

你建议你最好创建一个def。但只是为了比较,考虑一种Option-wrapping风格:

i => {
  ( if ((i%2)==0) Some(i) 
    else None
  ).getOrElse(doStuffAndReturn(i))
}

在上面的大例子中,这种风格会给出

( if (test) {
    if (anotherTest) {
      val a = someComputation()
      if (testOf(a)) Some(otherComputation()) else None
    }
    else if (yetAnotherTest) Some(whatever())
    else None
}).getOrElse(bigComputation())

就个人而言,我认为它更清晰(并且肯定不会更快),但它是可能的。


3
2018-05-24 18:28



是的,它与没有返回的原始示例一样复杂(或稍微更均匀)。但当然,在其他情况下这是一种有效的方法。 - Debilski


我认为匿名函数中返回点的主要问题是匿名函数可能会出现在通常不会期望它的地方。因此,不清楚返回语句实际上属于哪个闭包。通过明确要求a来解决这个问题 def - return* 对应关系。

或者,人们需要围绕要返回的声明的警卫。 breakable - break 但遗憾的是无法返回一个值。一些基于延续的解决方案将能够实现这一目标,尽管我想等待一些普遍的接受和库。


1