2

我目前正在学习 Martin Odersky 的 Coursera 课程的视频讲座,Scala 中的函数式编程原理。在第 2.1 课中,他演示了使用基函数sum(). 他实现了没有尾递归的阶乘,但我尝试了尾递归,因为它仍然只是一行代码。结果,我得到了类型不匹配,我认为 Odersky 没有。在定义sum()中,参数f接受一个Int并返回一个Int。我知道我可以通过调整我的函数*来解决这个问题,但这让我想知道如何设计高阶函数。 有没有办法可以调整或规避这种类型定义,以允许函数采用灵活数量的参数? 曾经有人给我看了一点 Haskell,我想知道 Scala 的函数参数是否可以以类似的松散方式键入……或者也许有一个更适合 Scala 的不同解决方案。请假设我昨天刚开始使用 Scala,并且在您的解释中计算机科学知识有限,因为情况确实如此。

def sum(f: Int => Int, a: Int, b: Int): Int =
    if (a > b) 0 else f(a) + sum(f, a+1, b) 


//Factorial with tail recursion.  The one in the lesson DOES NOT use tail recursion.
//prev is an accumulator.
def fact(a: Int, prev:Int = 1): Int =
        if (a == 0) prev else fact(a-1, a * prev)


def sumFactorials(a: Int, b: Int): Int = sum(fact, a, b)

*我知道我可以通过将我的 current 嵌套fact()在另一个函数中来解决这个问题,如下所示:

def factorial(a: Int): Int ={
    def fact(n: Int, prev:Int = 1): Int =
            if (n == 0) prev else fact(n-1, n * prev)
    fact(a)
}

我从前面提到的 Haskell 经验中得到的印象是,函数式编程促进了只需要一个参数的函数来允许柯里化。无论如何,这与实际问题有点相切,但如果我只是以我的问题的精神可怕地屠杀 FP,请随时在评论中解决这个问题。

4

1 回答 1

2

我认为这里发生了几件事。

首先,我认为您尝试对事实函数执行的操作(使用具有默认值的函数,就好像它的类型实际上少了一个参数一样)是不可能的。即使第二个 Int 默认为 1,(Int,Int) => Int 仍然是 (Int,Int) => Int。您不能将其作为 Int => Int 传递。不过,我可以看到您认为可以的地方。

其次,您的事实和阶乘函数不等价。无论您递归多少次,阶乘函数都会一遍又一遍地使用“a”的原始值。fact 函数在每次调用中使用递减的“a”。

根据您的“固定”阶乘函数,该函数有效地采用三个参数:原始 a、递减 n 和乘法 prev。下面的代码是等效的。

def sum(f: (Int,Int,Int) => Int, a: Int, b: Int): Int = 
    if (a > b) 0 else f(a,a,1) + sum(f, a+1, b) 

def fact(a: Int, n: Int, prev:Int): Int = 
    if (n == 0) prev else fact(a, n-1, a * prev)

def sumFactorials(a: Int, b: Int): Int = sum(fact, a, b)

您也不能将 sum 写为:

def sum(f: (Int, Int) => Int, a: Int, b: Int): Int =
    if (a > b) 0 else f(a) + sum(f, a+1, b) 

传入的函数 f 可能有一个默认参数,也可能没有,因此编译器允许 f(a) 调用是不安全的。

我认为默认参数的实现更像是“语法糖”,正如他们所说,附加到实际函数而不是函数柯里化。您可以使用柯里化来做到这一点,但您必须显式声明柯里化函数。假设您想使用事实的逻辑(而不是阶乘),您可以这样做:

def sum(f: Int => Int, a: Int, b: Int): Int =
    if (a > b) 0 else f(a) + sum(f, a+1, b) 

def fact(prev:Int = 1)(a: Int): Int =
        if (a == 0) prev else fact(a * prev)(a-1)

def sumFactorials(a: Int, b: Int): Int = sum(fact(1), a, b)

请注意,我们必须切换事实参数的位置。

于 2013-11-04T17:25:17.177 回答