我正在通过优秀的马丁奥德斯基的FP课程讲课,其中一个讲座通过牛顿的方法来演示高阶函数,以找出某些函数的固定点。在我认为类型签名被侵犯的讲座中有一个迫切的步骤,所以我会要求解释。 (道歉长介绍这是入站 - 它认为这是必要的。)实现这样的算法是这样给出scala在函数签名中是否会破坏类型?
方式一:
val tolerance = 0.0001
def isCloseEnough(x: Double, y: Double) = abs((x - y)/x)/x < tolerance
def fixedPoint(f: Double => Double)(firstGuess: Double) = {
def iterate(guess: Double): Double = {
val next = f(guess)
if (isCloseEnough(guess, next)) next
else iterate(next)
}
iterate(firstGuess)
}
接下来,我们试着计算通过定点平方根功能,而是通过
def sqrt(x: Double) = fixedPoint(y => x/y)(1)
幼稚尝试挫败,因为这样的方法振荡(所以,对于sqrt(2)
,结果将无限期地1.0和2.0之间交替)。
为了解决这个问题,我们引入平均阻尼,所以基本上我们计算两个最接近计算值的均值和收敛到解决方案,因此
def sqrt(x: Double) = fixedPoint(y => (y + x/y)/2)(1)
最后,我们介绍averageDamp
功能和任务是用fixedPoint
和averageDamp
写sqrt
。该averageDamp
定义如下:
def averageDamp(f: Double => Double)(x: Double) = (x + f(x))/2
这里来了,我不明白的部分 - 我最初的解决方案是这样的:
def sqrt(x: Double) = fixedPoint(z => averageDamp(y => x/y)(z))(1)
但教授。 Odersky的解决方案更简洁:
def sqrt(x: Double) = fixedPoint(averageDamp(y => x/y))(1)
我的问题是 - 为什么它的工作?根据函数签名,fixedPoint
函数应该采用函数(Double => Double
),但它并不介意传递一个普通的Double(这就是averageDamp
返回的值 - 事实上,如果试图显式指定返回类型Double averageDamp
,编译器不会抛出错误)。
我认为我的方法正确地遵循类型 - 所以我在这里错过了什么?它在哪里指定或暗示(?)averageDamp
返回一个函数,特别是在右边明显返回一个标量的情况下?你怎么能把一个标量传递给一个明确期望函数的函数呢?你如何推理似乎不符合类型签名的代码?
这个链接应该有所帮助http://www.codecommit.com/blog/scala/function-currying-in-scala – 2014-10-09 05:29:25
嗯,那个线程已经超过我的脑袋,但另一个链接有点帮助。所以,我理解它的方式(如果我错了,请纠正我)是因为currying基本上是一个延迟函数调用 - 推迟到所有f-args被满足(关闭)为止。在他们之前,部分应用的f返回结果为_another_ f(称为'f''),使得所有不满意的参数形成'f''的**左手**侧,而身体形成右手'f''的一侧并没有改变。我仍然无法理解引擎盖下发生了什么 - 谁将标量arg'x'传递给'averageDamp'?什么时候? – quantum 2014-10-10 03:21:05
我认为你掌握了柯里里的一般想法。 'fixPoint'将'x'部分应用于'averageDamp',就在'val next = f(guess)' – 2014-10-10 11:14:00