在scala中定义函数,FunctionName和FunctionName之间的区别(_)

时间:2015-10-02 14:55:35

标签: scala

我尝试使用以下代码在Scala中定义递归函数:

object Factorial {
    val almostFactorial = (f: Int => Int) => (n: Int) => if(n == 0) 1 else n * f(n - 1)
    val factorial: Int => Int = almostFactorial(factorial)
    def main(args: Array[String]) {
        println(factorial(5))
    }
}

以上代码编译但运行异常:

Exception in thread "main" java.lang.NullPointerException

但如果我改变

val factorial: Int => Int = almostFactorial(factorial)

val factorial: Int => Int = almostFactorial(factorial(_))

然后运行良好。

这两个声明之间有什么区别,为什么第一个声明导致该异常?

2 个答案:

答案 0 :(得分:2)

您在这里使用高阶函数。在这种情况下,下划线(_)充当占位符。实际上是你的代码:

val factorial: Int => Int = almostFactorial(factorial(_))

实际上可以表示为:

val factorial: Int => Int = almostFactorial( x => factorial(x) )

<强> UPD:

@Clashsoft补充说,我发现了发生的事情。

将功能定义为:

val factorial: Int => Int = almostFactorial(factorial)

你一遍又一遍地指向 factorial 的完全相同的实例。但是当你将函数定义为:

val factorial: Int => Int = almostFactorial(factorial(_))

Scala创建另一个匿名函数,其中包含 factorial 。所以它没有直接指向自己。

答案 1 :(得分:2)

1。实现:为什么会导致空指针异常

val factorial: Int => Int = {
  println(s"Before recursive def: $factorial")
  almostFactorial(factorial)
}

运行此代码会向您显示递归定义{{​​1}}中的factorial。答案在于将Scala函数值实现为JVM中的对象。 由于对象的默认值为null,并且在分配给null时{factor}尚未初始化为val factorial : Int => Int

创建对象时,null会立即进行评估。因此编译器回退到默认值。

2。实现:为什么这样做

通过使用val语法,您可以将实现更改为fun _的函数。当您提供Int时将对其进行评估。因此,在评估val的同时不会对其进行评估。

因此,这个函数将通过调用main方法进行evalutaed。在运行程序时将调用它。

因此,添加一些打印语句(错误的样式,不要在生产代码中执行)将帮助您在对象创建时找出val的值:

partial applied function

运行第二个实现,您将获得此输出

  val factorial: Int => Int = {
    println(s"Before recursive def: $factorial")
    val partialFactorial = factorial(_)
    println(partialFactorial)
    almostFactorial(partialFactorial)
}

扩展示例:为什么使用部分应用函数不会自动保护您

因此在创建对象时函数为Before recursive def: null <function1> <- Partial applied factorial(_) 120 。如果您将val的实现中的值直接提供给部分应用函数,如下例所示:

null

你最终再次出现空指针异常:

  val factorial: Int => Int = {
    println(s"Before recursive def: $factorial")
    val partialFactorial = factorial(_)
    println(partialFactorial)
    println(partialFactorial(2)) // here we will again cause a null pointer exception
    almostFactorial(partialFactorial)
  }