帮我解释一下这个F#递归示例程序

时间:2011-04-04 06:54:15

标签: f# recursion

let rec aggregateList (f:int->int->int) init list = 
    match list with
    | [] -> init
    | hd::tl -> 
        let rem = aggregateList f init tl
        f rem hd

let add a b = a + b
let mul a b = a * b

//to use in F# Interactive:
//aggregateList add 0 [1..5];;

来自Thomas Petricek的“真实世界的功能编程”中的这个例子

我在第二个分支中不了解该模式匹配: f rem hd 。 有人能帮助我吗?

3 个答案:

答案 0 :(得分:4)

让我们先分解aggregateList函数声明。该函数有三个参数:

  1. 一个名为f的函数,它接受两个int并返回第三个int
  2. 开始汇总的初始值。
  3. 值列表。
  4. 然后,该函数将其提供的列表与两种可能性之一匹配:

    1. 列表为空,在这种情况下,它返回init
    2. 的值
    3. 该列表不为空,在这种情况下,它将获取第一个项目并将其分配给hd(或头部)以及列表的其余部分,并将其分配给tl(或尾部)。然后它执行递归调用aggregateList f init tl。返回时,它会获取结果并将其分配给rem。然后,它会在frem上调用hd
    4. 正如其他人所指出的,这与List.foldback function in the basic F# library

      的作用相同

      当然要小心选择init值,因为如果您执行了aggregateList mul 0 somelist;;,无论您提供哪个列表,都只会获得0

答案 1 :(得分:1)

它调用函数f(其中一个参数),给出递归调用的结果和下一个项目。

rem是余数,或者在这种情况下是剩余值的结果。

hd是下一个项目,如模式匹配的| hd::tl ->部分所示。

实际上,这个聚合函数需要一个函数,一个起点和一个列表。表示示例行的方法是:

(1 + (2 + (3 + (4 + (5 + 0)))))

答案 2 :(得分:1)

为了好玩,让我们做一些printf样式调试:

> aggregateList (fun acc x -> printf "%i " x; acc + x) 0 [1..10];;
10 9 8 7 6 5 4 3 2 1 val it : int = 55

看起来该函数等同于List.foldBack(或其他语言中的fold_right):它从右到左遍历列表中的每个项目并在它们上调用函数f

让我们以几种不同的方式重写函数:

// functional version
let rec foldBack f seed = function
    | [] -> seed
    | x::xs -> let res = foldBack f seed xs in f res x

// imperative version
let foldBack f seed xs =
    let mutable result = seed
    for x in List.rev xs do
        result <- f result x
    result

// C# equivalent
public static U FoldBack<T, U>(Func<T, U> f, U seed, IEnumerable<T> xs) {
    foreach(T x in xs.Reverse())
        seed = f(seed, x);
    return seed;
}

你可以使用这样的功能:

let sum = foldBack (+) 0 [1..10] // returns 55
let sumOfSquares = foldBack (fun acc x -> acc + x * x) 0 [1..10];; // 385

  

我在第二个分支中不明白   模式匹配:f rem hd。可以   有人帮帮我吗?

让我们从我们已经了解的F#函数开始:

  • f是一个类型为int -> int -> int的函数。你传递函数就好像它们是任何其他变量,如整数或字符串。
  • 通过传递以空格分隔的参数列表来调用函数。 f rem hd使用两个参数frem调用函数hd
  • 在函数中计算的最后一个表达式被视为函数的返回值。

回到原来的功能:

let rec aggregateList (f:int->int->int) init list = 
    match list with
    | [] -> init
    | hd::tl -> 
        let rem = aggregateList f init tl   // 1
        f rem hd                            // 2

在第1行中,我们使用aggregateList重复调用tl。由于列表越来越小,我们最终会遇到nil的情况,返回init

在第2行中,f rem hd是函数的返回值。但是,由于我们在向列表末尾移动时向下移动堆栈,因此当我们向上走回堆栈跟踪时,我们将为每个元素调用此函数(按从右到左的顺序)。 / p>

给定aggregateList (+) 0 [1..10],nil大小写返回0,因此我们调用:

  • 返回值 = f rem hd = f 0 10 = 0 + 10 = 10
  • 返回值 = f rem hd = f 10 9 = 9 + 10 = 19
  • 返回值 = f rem hd = f 19 8 = 19 + 8 = 27
  • 返回值 = f rem hd = f 27 7 = 27 + 7 = 34
  • 返回值 = f rem hd = f 34 6 = 34 + 6 = 40
  • 返回值 = f rem hd = f 40 5 = 40 + 5 = 45
  • 返回值 = f rem hd = f 45 4 = 45 + 4 = 49
  • 返回值 = f rem hd = f 49 3 = 49 + 3 = 52
  • 返回值 = f rem hd = f 52 2 = 52 + 2 = 54
  • 返回值 = f rem hd = f 54 1 = 54 + 1 = 55

列表中没有其他项目,因此整个函数返回55

可以想象,aggregateList中的嵌套调用会像这样评估长度为n的列表:

f(f(f(f(f(f(f init hd n )hd n-1 )hd n-2 )hd n-3 )... hd 2 )hd 1 )hd 0