F#入门-第三章 功能性-第三节 高阶函数(fold)
2010-09-15 16:45
267 查看
本节介绍高阶函数fold。在英语中,fold单词是指折起来,折叠的意思,在F#中,所谓“折叠”,是指对集合中的各元素进行统一运算,最后返回一个值的意思。
下面的介绍中,用列表举例代替集合进行说明。例如,拿“列表求和”举例,输入:[1; 2; 3; 4; 5; 6; 7; 8; 9; 10],输出:55。原来的int值的集合(int list),变成了单一的int值。因此,这种抽象化计算的时候,就使用到了fold函数。另外,有时统一运算后返回的值也可就是集合,所以输出值的类型并不一定比输入值的类型简单。(例如后例中的rev就是(int list -> int list))
fold函数,根据计算顺序分为两种。
List.fold_left ('state -> 'a -> 'state) -> 'state -> 'a list -> 'state
如果将第一个参数称为f,第二个参数称为init,第三个参数称为[l1;l2;..;ln]
则fold_left执行如下运算
f (... (f (f (f init l1) l2) l3) ... ln)
例:计算fold_left (fun x y->x y) 0 [1..3]为((0 1) 2) 3
List.fold_right ('a -> 'state -> 'state) -> 'a list -> 'state -> 'state
如果将第一个参数称为f,第二个参数称为[l1;..;ln-1;ln],第三个参数称为init
则fold_right执行如下运算
f l1 (... (f ln-1 (f ln init)) ...)
例:计算fold_right (fun x y->x y) [1..3] 0为1 (2 (3 0))
要想熟练使用fold函数,必须深刻理解并记住fold函数的第一个函数型参数的意义。第一个参数为带两个参数的函数,这两个参数为“到现在为止的fold计算的累计值”和“列表的元素”。暂且将累计值称为acc,列表元素称为x,init是从acc方向传递给x方向。同时,在fold_left与fold_right中acc与x的放置顺序不一致,fold_left的第一个参数为f acc x,fold_right的第一个参数为f x acc,请注意到这一不同。
fold是使用起来非常方便,理解起来比较困难的函数,为了让大家对fold的使用场合有个印象,我们拿使用fold可以替代的函数进行举例。
想要用fold进行替代的函数
如您所见,以上函数都使用相同形式进行定义的.这些固定形式的函数都可以用fold进行替代。
用fold进行替代的例子
以下是fold_left和fold_right的使用实例。
fold_left和fold_right
事实上也可以用fold定义map。
用fold定义map
让我们来看个更为复杂的例子。
更为复杂的例子
最后看一个fold的封装例子。fold_left是末尾递归的形式,fold_right不是这种形式,所以执行时fold_left的性能会更好一些。
fold的封装例子
下面的介绍中,用列表举例代替集合进行说明。例如,拿“列表求和”举例,输入:[1; 2; 3; 4; 5; 6; 7; 8; 9; 10],输出:55。原来的int值的集合(int list),变成了单一的int值。因此,这种抽象化计算的时候,就使用到了fold函数。另外,有时统一运算后返回的值也可就是集合,所以输出值的类型并不一定比输入值的类型简单。(例如后例中的rev就是(int list -> int list))
fold函数,根据计算顺序分为两种。
List.fold_left ('state -> 'a -> 'state) -> 'state -> 'a list -> 'state
如果将第一个参数称为f,第二个参数称为init,第三个参数称为[l1;l2;..;ln]
则fold_left执行如下运算
f (... (f (f (f init l1) l2) l3) ... ln)
例:计算fold_left (fun x y->x y) 0 [1..3]为((0 1) 2) 3
List.fold_right ('a -> 'state -> 'state) -> 'a list -> 'state -> 'state
如果将第一个参数称为f,第二个参数称为[l1;..;ln-1;ln],第三个参数称为init
则fold_right执行如下运算
f l1 (... (f ln-1 (f ln init)) ...)
例:计算fold_right (fun x y->x y) [1..3] 0为1 (2 (3 0))
要想熟练使用fold函数,必须深刻理解并记住fold函数的第一个函数型参数的意义。第一个参数为带两个参数的函数,这两个参数为“到现在为止的fold计算的累计值”和“列表的元素”。暂且将累计值称为acc,列表元素称为x,init是从acc方向传递给x方向。同时,在fold_left与fold_right中acc与x的放置顺序不一致,fold_left的第一个参数为f acc x,fold_right的第一个参数为f x acc,请注意到这一不同。
fold是使用起来非常方便,理解起来比较困难的函数,为了让大家对fold的使用场合有个印象,我们拿使用fold可以替代的函数进行举例。
想要用fold进行替代的函数
//返回列表长度 let rec count = function | [] -> 0 | x::xs -> 1 (count xs);; //返回列表中各元素之总和 let rec sum = function | [] -> 0 | x::xs -> x (sum xs);; //将列表逆序。 //重点是没有使用::而使用@ let rec rev = function | [] -> [] | x::xs -> (rev xs) @ [x];; |
用fold进行替代的例子
//照原代码进行逐字替代 let count ls = List.fold_left (fun acc x -> 1 acc) 0 ls;; let sum ls = List.fold_left (fun acc x -> x acc) 0 ls;; let rev ls = List.fold_right (fun x acc -> acc @ [x]) ls [];; //如果使用fold,也可以用::进行定义 let rev ls = fold_left (fun acc x -> x::acc) [] ls;; |
fold_left和fold_right
List.fold_right (fun x y -> x^y) ["a";"b";"c"] "R";; List.fold_right (fun x y -> y^x) ["a";"b";"c"] "R";; List.fold_left (fun x y -> x^y) "L" ["a";"b";"c"];; List.fold_left (fun x y -> y^x) "L" ["a";"b";"c"];; List.fold_left (fun x y -> y::x) [0] [1..3];; List.fold_right (fun x y-> x::y) [1..3] [0];; //List.fold_left (fun x y -> x::y) [0] [1..3];; //这样是错的 //List.fold_right (fun x y-> y::x) [1..3] [0];; //这样是错的 解释器上的运行结果(从上往下顺序) val it : string = "abcR" val it : string = "Rcba" val it : string = "Labc" val it : string = "cbaL" val it : int list = [3; 2; 1; 0] val it : int list = [1; 2; 3; 0] |
事实上也可以用fold定义map。
用fold定义map
//用fold定义map let mmap f ls = rev <| fold_left (fun acc x -> (f x)::acc) [] ls;; //从尾端开始执行的map let mmapr f ls = fold_right (fun x acc -> (f x)::acc) ls [];; //执行测试 let f x = printfn "%d" x;x in let ls = [1..3] in List.map f ls;mmap f ls;mmapr f ls;; |
让我们来看个更为复杂的例子。
更为复杂的例子
//组合排列(重复时不执行) let permutation lst = let rec p a b = function | [] -> a::b | ls -> fold_left (fun x y -> p (y::a) x (filter ((<>)y) ls)) b ls in p [] [] lst;; permutation [1..3];; |
fold的封装例子
let rec foldl f init = function | [] -> init | x::xs -> foldl f (f init x) xs;; let rec foldr f ls init = match ls with | [] -> init | x::xs -> f x (foldr f xs init);; |
相关文章推荐
- F#入门-第三章 功能性-第二节 高阶函数(map)
- F#入门-第三章 功能性-第四节 词法闭包
- F#入门-第三章 功能性-第一节 柯里化
- F#入门-第五章 F#开发实例-第三节 最好知道的注意事项
- F#入门-第四章 面向对象-第三节 成员函数
- F#入门-第三章 功能性-第五节 运算符的优先顺序
- F#入门-第一章 概述-第三节 F#的安装
- 第三部分:Android 应用程序接口指南---第三节:应用程序资源---第三章 处理运行时改变
- Python初入门(三)(Head First Python 第三章 文件与异常)
- C#4.0入门 第三章 Task类与PLINQ—第二页 PLINQ(转)
- Python编程:从入门到实践的动手试一试答案(第三章)
- Perl语言入门笔记 第三章 列表和数组
- F#入门-第一章 概述-第四节 解释器的使用方法
- 入门经典训练指南第三章例题1.UVa 11995 I Can Guess the Data Structure!
- Jsp入门第三章——Jsp的隐藏对象
- Maven入门之依赖-第三节
- 【MDNjs笔记】——入门——第三章——对象.构造函数.原型链.继承
- 第三章 Makefile 快速入门
- 第三章: 语法入门
- 【算法竞赛入门经典】【第三章】课后习题(第一部分)