来自foreach的短路功能

时间:2012-11-30 21:06:05

标签: scala functional-programming

我和我的同事对以下代码的行为感到有点困惑。

def a: String = {
  None.foreach(return "1")
  return "2"
}

def b: String = {
  None.foreach(x => return "1")
  return "2"
}

正如预期的那样,调用b会返回“2”。但是,调用a会返回“1”。在return "1"执行时,a到底是什么时候被评估的?

5 个答案:

答案 0 :(得分:12)

表格的所有*功能评估

f({code})

相当于

val temp = { code }
f(temp)

所以,在第一种情况下,

val temp = return "1"
None.foreach(temp)   // Never reach this point!

在第二个时,

val temp = (x: Nothing) => return 1
  // Equivalent: new Function1[Nothing,String]{ def apply(x: Nothing) = return "1" }
None.foreach(temp)   // Never call that weird function!

所以一切都好。

但是,等一下,foreach需要A => Unitreturn "1"这样的功能怎么样?好吧,Scala以尽可能最具体的类型开始(Nothing,它是任何事物的子类,因此承诺做任何你要求的事情,除非它不存在)。然后,由于语句没有产生任何值(控制通过返回转义),它永远不会从Nothing修改它。实际上,NothingFunction1[A,Unit]的子类。

要生成Nothing - 好吧,假装生成它 - 你实际运行代码,然后返回。

* 实际上,如果参数是按名称传递的,则会秘密转换为() => { Code }并在未经评估的情况下传入。

答案 1 :(得分:2)

其他人已经解释了这种行为,但最好不要使用return

我会写类似的东西。

def b: String = {
  None.map(x => "1").getOrElse("2")
}

如果是List我会使用collectFirst,如果我想返回匹配内容的第一个项目。

编辑:我看到问题被标记为functional programming。如果尝试以函数式编程,则应避免使用return。拍摄A =>的地图B.如果您使用return,则表明该类型签名。

def a: String = { 
  val a = Option(5).map(x => if(x==6) return "aa" else 6); // isnt returning a `B` when x == 5.
  a.toString
};

答案 2 :(得分:1)

那真的很酷 - 我不知道。查看语言参考,http://www.scala-lang.org/docu/files/ScalaReference.pdf的第91页:

  

返回表达式返回 e 必须出现在一些封闭的名称的主体内   方法或功能。最里面的封闭命名方法或函数   源程序 f 必须具有显式声明的结果类型,以及 e 的类型   必须符合它。返回表达式计算表达式e并返回它   值为 f 的结果。

     

...

     

由编译器生成的apply方法作为匿名函数的扩展,不计入源程序中的命名函数,因此永远不会成为返回表达式的目标。

     

通过抛出并捕获scala.runtime.NonLocalReturnException来实现从嵌套匿名函数返回。返回点和封闭方法之间的任何异常捕获都可能会看到异常。密钥比较确保这些异常仅由返回终止的方法实例捕获。

     

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

答案 3 :(得分:1)

在a中,foreach在到达return "1"行之前执行return "2"。即使认为它是在foreach中代码仍然执行,因此它返回1.在b中,x通过“自我类型注释”(http://www.scala-lang.org/node/124)分配给return "1"的操作

关于scala继承的这篇文章可能有用:Using this keyword to inherit?

分享并享受

答案 4 :(得分:1)

Scala使用急切的应用订单评估。这意味着在替换函数的参数之前评估参数。此外,值得回顾的是,功能可以自我评估。函数 application 评估函数的返回值。现在让我们看看每个参数评估的内容。

scala> :t () => "1"
() => java.lang.String

所以,在第二种情况下,我们得到一个函数。它在传递之前被评估(自身),但从未应用,因为None没有元素。但是,不需要应用return "1"。它只需要进行评估,它具有重定向控制的副作用,然后从包含它的函数返回“1”。