2014-10-09 26 views
4

我正在通过优秀的马丁奥德斯基的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功能和任务是用fixedPointaverageDampsqrt。该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返回一个函数,特别是在右边明显返回一个标量的情况下?你怎么能把一个标量传递给一个明确期望函数的函数呢?你如何推理似乎不符合类型签名的代码?

回答

6

您的解决方案是正确的,但它可以更简洁。

让我们仔细检查averageDamp函数。

def averageDamp(f: Double => Double)(x: Double): Double = (x + f(x))/2 

添加返回类型注释使其更清楚。我认为你缺少的是在这里:

但它并不介意被通过一个普通双人间(这是什么averageDamp返回 - 事实上,如果你试图明确指定双到averageDamp的 返回类型,编译器不会抛出 错误)。

averageDamp(y => y/x)确实会返回一个Double => Double函数! averageDamp需要通过TWO参数列表返回Double

如果函数只接收一个参数,它仍然希望另一个参数完成。因此,不是立即返回结果,而是返回一个函数,说“我在这里仍然需要一个参数,喂我这个,所以我会返回你想要的”。

MO教授确实通过ONE函数参数到它,而不是两个,所以averageDamp部分地施加,在它返回一个Double => Double功能的读出。

课程也会告诉你有多个参数列表功能是本语法糖形式:

def f(arg1)(arg2)(arg3)...(argN-1)(argN) = (argN) => f(arg1)(arg2)(arg3)...(argN-1) 

如果你给一个大于f需求少的说法,它只是回归方程右边,也就是,一个函数。所以,请注意averageDamp(y => x/y),这个参数传递给fixPoint,实际上是一个函数应该可以帮助你理解这个问题。

通知:有部分应用功能(或功能柯里)和多个参数列表功能

之间的一些差异例如,你不能声明这样

val a = averageDamp(y => y/2) 

编译器会抱怨这因为“方法不是部分应用功能”。

区别在这里解释:What's the difference between multiple parameters lists and multiple parameters per list in Scala?

+0

这个链接应该有所帮助http://www.codecommit.com/blog/scala/function-currying-in-scala – 2014-10-09 05:29:25

+0

嗯,那个线程已经超过我的脑袋,但另一个链接有点帮助。所以,我理解它的方式(如果我错了,请纠正我)是因为currying基本上是一个延迟函数调用 - 推迟到所有f-args被满足(关闭)为止。在他们之前,部分应用的f返回结果为_another_ f(称为'f''),使得所有不满意的参数形成'f''的**左手**侧,而身体形成右手'f''的一侧并没有改变。我仍然无法理解引擎盖下发生了什么 - 谁将标量arg'x'传递给'averageDamp'?什么时候? – quantum 2014-10-10 03:21:05

+0

我认为你掌握了柯里里的一般想法。 'fixPoint'将'x'部分应用于'averageDamp',就在'val next = f(guess)' – 2014-10-10 11:14:00

1

多参数列表是函数的语法糖,它返回另一个函数。您可以在斯卡拉壳看到这一点:

scala> :t averageDamp _ 
(Double => Double) => (Double => Double) 

我们可以写相同的功能,而不语法糖 - 这是我们会做它在例如方式的Python:

def averageDamp(f: Double => Double): (Double => Double) = { 
    def g(x: Double): Double = (x + f(x))/2 
    g 
} 

返回的功能可以看起来有点怪异下手,但它是传递一个函数作为参数的补充,使一些非常强大的编程技术。函数只是另一种类型的值,如IntString

在您的原始解决方案中,您正在重新使用变量名称y,我认为这使它稍微混乱;我们可以翻译你写什么到:

def sqrt(x: Double) = fixedPoint(z => averageDamp(y => x/y)(z))(1) 

有了这种形式,你可以希望看到的格局:

def sqrt(x: Double) = fixedPoint(z => something(z))(1) 

,希望这是现在很明显,这是一样的:

def sqrt(x: Double) = fixedPoint(something)(1) 

这是Odersky的版本。

+0

这一行''''''''谢谢,我已经更新了这个问题,但没有重用变量 - 它让我困惑,我不知道我可以像这样介绍一封新信。 – quantum 2014-10-10 03:17:29