【问题标题】:Multiple return points in scala closure/anonymous functionscala闭包/匿名函数中的多个返回点
【发布时间】:2011-02-23 08:12:57
【问题描述】:

据我了解,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)
...

当你只有一个可用的ifelse 结构时,很容易搞砸。

当然,在我一开始给出的简单示例中,使用else 会更容易。抱歉,我以为这很清楚。

【问题讨论】:

  • 使用 else 语句有什么问题?
  • 在您给出的示例中,完全没有理由避免使用 else 关键字;如果您使用else,则不会计算额外的表达式,因此在此处使用提前返回将一无所获。
  • 对不起,我修改了。认为很明显doMoreStuff 部分实际上做了更多。

标签: scala closures return-value anonymous-function


【解决方案1】:

如果您的匿名函数如此复杂,我会使其更加明确。匿名函数不适合比几行更复杂的事情。您可以通过在 using 方法中声明它来使其成为私有方法

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

这是您的变通方法的一种变体,但更简洁。它们都将其保持为本地方法范围的私有。

【讨论】:

  • 这是一个很好的解决方法。我是 scala newb,所以我真的不明白为什么需要这样做,但它解决了问题。
【解决方案2】:

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

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

【讨论】:

  • 实际上,我会说它节省了两个以上的字符:显式的return 无论如何都会强制您声明返回类型……但是我的实际用例会比示例复杂一些我本来给的,所以不只是为了保存角色。
【解决方案3】:

您给出的示例很容易通过 if 语句解决。这样做不会造成任何性能或其他处罚。

但你可能还有一些其他的情况,大概是这样的

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

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

您可以使用 OptionEither 做各种偷偷摸摸的事情来保持状态流动(使用 orElsefold),这样您就可以只进行所需的计算。

您最好按照您的建议创建一个 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())

就个人而言,我不认为它更清晰(当然也不会更快),但这是可能的。

【讨论】:

  • 是的,它与没有返回的原始示例一样复杂(或者甚至更复杂)。但在其他情况下,这当然是一种有效的方法。
【解决方案4】:

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

或者,需要在返回的语句周围设置警卫。 breakablebreak 但遗憾的是无法返回值。一些基于 continuation 的解决方案将能够实现该目标,但我想等待那里的一些普遍接受和库。

【讨论】:

    猜你喜欢
    • 2014-05-29
    • 2013-07-19
    • 1970-01-01
    • 2012-01-08
    • 2021-12-21
    • 2020-08-12
    • 2013-03-29
    相关资源
    最近更新 更多