什么';Scala中两个函数的区别是什么?

什么';Scala中两个函数的区别是什么?,scala,Scala,这两个函数传递到test函数,并输出相同的结果 fun1和fun2有什么区别 在Scala术语中,您的代码表示两种方法。如果我们从语义上分析代码,这两个方法将为任何给定的输入产生相同的输出。这意味着,对于任意两个相等的域,这些方法将产生相同的值范围 如果我们从编译器的角度来看,这两个函数的不同之处在于前者接受Int并返回Int,而后者不接受参数并返回类型为Function1[Int,Int]的函数,函数本身接受Int并返回Int。从逻辑上讲,您可以将其视为Function0[Function1[

这两个函数传递到
test
函数,并输出相同的结果


fun1
fun2
有什么区别

在Scala术语中,您的代码表示两种方法。如果我们从语义上分析代码,这两个方法将为任何给定的输入产生相同的输出。这意味着,对于任意两个相等的域,这些方法将产生相同的值范围

如果我们从编译器的角度来看,这两个函数的不同之处在于前者接受
Int
并返回
Int
,而后者不接受参数并返回类型为
Function1[Int,Int]
的函数,函数本身接受
Int
并返回
Int
。从逻辑上讲,您可以将其视为
Function0[Function1[Int,Int]]
(如果您在REPL中调用
fun2
,您可以看到它),但实际上它只是调用
fun2

为了让前者编译,编译器执行eta扩展,将方法转换为函数。我们可以通过
scalac
的一个小标志看到这一点:

object App {
  def main(args: Array[String]) {
    println(test(1, fun1)) // result is 1
    println(test(1, fun2)) // result is 1, too
  }

  def test(i: Int, fun: Int => Int) = fun(i)

  // function fun1
  def fun1(i: Int) = i
  // function fun2
  def fun2 = (i: Int) => i
}

简而言之,发生的事情如下:

@SerialVersionUID(value = 0) final <synthetic> class $anonfun$fun2$1 extends scala.runtime.AbstractFunction1$mcII$sp with Serializable {
  def <init>(): <$anon: Function1> = {
    $anonfun$fun2$1.super.<init>();
    ()
  };
  final def apply(i: Int): Int = $anonfun$fun2$1.this.apply$mcII$sp(i);
  <specialized> def apply$mcII$sp(i: Int): Int = i;
  final <bridge> <artifact> def apply(v1: Object): Object = scala.Int.box($anonfun$fun2$1.this.apply(scala.Int.unbox(v1)))
}
->

vs

->

更新

您可以通过使用
\uu
将方法显式转换为函数:

// fun2 takes no arguments, so it's a valid call
// and its return type is consistent with test expectations
val res0 = fun2() // () are optional in Scala 
val res1 = test(1, res0)
println(res1)

这里有一个例子说明了它们的不同之处——我添加了类型注释,以使事情变得清晰,并安抚编译器

鉴于:

scala> fun1 _
res0: Int => Int = $$Lambda$1435/1367214620@3db13b89
第一个定义包括:

trait T {
  def fun(i: Int) : Int
}
但第二种情况并非如此:

object A extends T {
  // fun1 equivalent
  override def fun(i: Int) : Int = i
}

有关详细信息,请参见其他答案。

因此,对
fun2
的引用是一个方法调用,而对
fun1
的引用是一个简单的名称访问。@Elazar不确定“简单名称访问”是什么意思。对
fun2
的调用是对
Function0[Function1[Int,Int]]
的简单调用,其中对
fun1
的调用必须扩展以创建具体的类实例。这两者之间有明确的区别。您只需引用方法的名称即可。就像引用任何其他变量一样。@Elazar为了“仅仅引用”方法的名称,编译器必须根据命名方法的求值进行一些欺骗。正如您的代码片段所示,
fun2
不会被转换为
Function0[Function1[Int,Int]]
。当您使用
fun2
作为
test
Scala中的参数时,Scala将简单地对其求值,即调用方法
fun2
它需要哪种返回类型。我不确定此示例试图显示什么?语义相同并不意味着语法相同,正如我在回答中解释的那样。如果你的回答是为了说明这些方法在语法上是相同的,我想他知道。@YuvalItzchakov这些定义是不等价的。“语法相同”意味着“相同的程序文本”,这里当然不是这样。语义上,第一个声明覆盖trait的声明,而另一个不覆盖。OP想要知道的是它是否重要,将定义哪一个。答案是“是”,这里举例说明。你关于语义等价的回答是指定义函数的行为。我的回答(以及op的问题)涉及到定义本身。我的回答试图解决这两种方法的语义行为和编译器的解释,仅此而已。如果你认为这个答案对OP的问题有不同的解释,很好,那么OP从不同的角度得到了另一个答案。
// fun2 takes no arguments, so it's a valid call
// and its return type is consistent with test expectations
val res0 = fun2() // () are optional in Scala 
val res1 = test(1, res0)
println(res1)
scala> fun1 _
res0: Int => Int = $$Lambda$1435/1367214620@3db13b89
trait T {
  def fun(i: Int) : Int
}
object A extends T {
  // fun1 equivalent
  override def fun(i: Int) : Int = i
}
//error: object creation impossible,
//since method fun in trait T of type (i: Int)Int is not defined
object B extends T {
  // fun2 equivalent
  // error: method fun overrides nothing.
  override def fun : Int => Int = (i: Int) => i
}