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

时间:2016-08-07 14:08:51

标签: scala

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
}

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

fun1fun2的区别是什么?

3 个答案:

答案 0 :(得分:4)

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

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

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

def main(args: Array[String]): Unit = {
  scala.this.Predef.println(scala.Int.box(App.this.test(1, {
    {
      (new <$anon: Function1>(): Function1)
    }
  })));
  scala.this.Predef.println(scala.Int.box(App.this.test(1, App.this.fun2())))
};

我们还可以看到为前者创建的自动生成的类转换为实际的Function1[Int, Int]对象:

@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)))
}

答案 1 :(得分:1)

简而言之,这就是:

println(test(1, fun1)) // result is 1

- &GT;

// fun1 is a method which takes one argument,
// but we are using it without arguments, so
// it must be a partial application
// and test expects a function,
// so let's convert it to a function 
val res0 = (x: Int) => fun1(x)
val res1 = test(1, res0)
println(res1)

VS

println(test(1, fun2)) // result is 1, too

- &GT;

// 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

答案 2 :(得分:0)

以下是它们如何不同的示例 - 我添加了类型注释以使事情变得清晰并安抚编译器。

假设:

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
}

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