我尝试使用以下代码在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(_))
然后运行良好。
这两个声明之间有什么区别,为什么第一个声明导致该异常?
答案 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)
val factorial: Int => Int = {
println(s"Before recursive def: $factorial")
almostFactorial(factorial)
}
运行此代码会向您显示递归定义{{1}}中的factorial
。答案在于将Scala函数值实现为JVM中的对象。
由于对象的默认值为null,并且在分配给null
时{factor}尚未初始化为val factorial : Int => Int
。
创建对象时,null
会立即进行评估。因此编译器回退到默认值。
通过使用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)
}