【问题标题】:Scala return statements in anonymous functionsScala 匿名函数中的返回语句
【发布时间】:2013-07-19 06:19:45
【问题描述】:

为什么匿名函数中的显式返回语句(使用return 关键字)从封闭的命名函数返回,而不仅仅是从匿名函数本身返回?

例如以下程序导致类型错误:

def foo: String = {
  ((x: Integer) => return x)
  "foo"
}

我知道建议避免使用 return 关键字,但我感兴趣的是为什么显式和隐式返回语句在匿名函数中具有不同的语义。

在以下示例中,返回语句在m 执行完毕后“存活”,程序导致运行时异常。如果匿名函数没有从封闭函数返回,则无法编译该代码。

def main(args: Array[String]) {
  m(3)
}

def m: (Integer => Unit) =
  (x: Integer) => return (y: Integer) => 2

【问题讨论】:

    标签: scala return closures anonymous-function


    【解决方案1】:

    正式来说,return 定义为总是从最近的封闭命名方法返回

    返回表达式 return e 必须出现在 some 的主体内 封闭命名的方法或函数。最里面的封闭命名 源程序中的方法或函数 f 必须具有显式 声明的结果类型,并且 e 的类型必须符合它。回报 表达式计算表达式 e 并将其值作为 f 的结果。任何语句或表达式的评估 后面的返回表达式被省略。

    所以它在 lambda 中没有不同的语义。问题在于,与普通方法不同,从 lambda 创建的闭包可以逃避对封闭方法的调用,并且如果在这样的闭包中有返回值,您可能会得到异常。

    如果返回表达式本身是匿名函数的一部分,它 f 的封闭实例可能已经返回 在执行返回表达式之前。在这种情况下,抛出 scala.runtime.NonLocalReturnException 不会被捕获,并且会 向上传播调用堆栈。

    现在,至于“为什么”。一个较小的原因是美学:lambdas 是表达式,当一个表达式及其所有子表达式具有相同的含义时,无论嵌套结构如何,这都很好。 Neal Gafter 在http://gafter.blogspot.com/2006/08/tennents-correspondence-principle-and.html

    上谈到了这一点

    不过,它存在的主要原因是它允许您轻松模拟命令式编程中常用的控制流形式,但仍然允许您将事物抽象为更高阶的函数。作为一个玩具示例,Java 的 foreach 构造 (for (x : xs) { yada; }) 允许在循环内返回。 Scala 没有语言级别的 foreach。相反,它将 foreach 放入库中(不计算没有 yield 的“for expression”,因为它们只是对 foreach 脱糖)。具有非本地返回意味着您可以采用 Java foreach 并直接转换为 Scala foreach。

    顺便说一句,Ruby、Smalltalk 和 Common Lisp(我想不到)也有类似的“非本地”返回。

    【讨论】:

    • 你有一些更严肃的例子来说明为什么需要语义上的差异吗?因为您列出的内容可以很容易地用具有谓词参数的修改后的foreach 进行模拟。
    【解决方案2】:

    return 关键字是为(类)方法保留的,不能在函数中使用。您可以轻松地对其进行测试:

    object Foo {
      val bar = (i: Int) => return i + i
    }
    

    这给了

    <console>:42: error: return outside method definition
           object Foo { val bar = (i: Int) => return i + i }
                                              ^
    

    大多数情况下,您可以将方法和函数视为相同,因为函数的 apply 方法在语法上类似于调用方法,并且所谓的 eta-expansion 允许将方法作为函数参数传递。

    在这种情况下,情况会有所不同。定义为方法时是合法的:

    object Foo { 
      def bar(i: Int): Int = return i + i
    }
    

    总而言之,您应该只在允许有条件(提前)返回的方法中使用return请参阅this post,了解有关方法与函数的讨论。

    【讨论】:

    • 这个答案实际上并没有回答 why,即该决定背后的语言设计推理。
    • @jcz 这会变得非常模糊和不直观。想想 lambda,例如xs.foreach { x =&gt; if (x == y) return x } 之类的。
    猜你喜欢
    • 2014-05-29
    • 1970-01-01
    • 2011-02-23
    • 1970-01-01
    • 2023-02-22
    • 2013-03-29
    • 1970-01-01
    相关资源
    最近更新 更多