营销型网站建设eyouc,长沙营销型网站,有什么网站是layui做的,休闲网站建设函数式编程其实就是按照数学上的函数运算思想来实现计算机上的运算。虽然我们不需要深入了解数学函数的知识#xff0c;但应该清楚函数式编程的基础是来自于数学。 例如数学函数f(x) x^2x#xff0c;并没有指定返回值的类型#xff0c;在数学函数中并不需要关心数值类型和返… 函数式编程其实就是按照数学上的函数运算思想来实现计算机上的运算。虽然我们不需要深入了解数学函数的知识但应该清楚函数式编程的基础是来自于数学。 例如数学函数f(x) x^2x并没有指定返回值的类型在数学函数中并不需要关心数值类型和返回值。F#代码为let f x x ** 2.0 xF#代码和数学函数非常类似其实这就是函数式编程的思想只考虑用什么进行计算以及计算的结果或者叫“输入和输出”并不考虑怎样计算。 其实你可以把任何程序看成是一系列函数输入是你鼠标和键盘的操作输出是程序的运行结果。你不需要关心程序是怎样运行的这些函数会根据你的输入来输出结果而其中的算法是以函数的形式而不是类或者对象。 下面我们就先了解一些函数式编程中函数相关的东西。 了解函数 不可变性 在一个函数中改变了程序的状态比如在文件中写入数据或者在内存中改变了全局变量我们称为副作用。像我们使用printfn函数无论输入是什么返回值均为unit但它的副作用是打印文字到屏幕上了。 副作用并不一定不好但却经常是很多bug的根源。我们分别用命令式和函数式求一组数字的平方和 let square x x * xlet sum1 nums let mutable total 0for i in nums do let x square itotal - total xtotallet sum2 nums Seq.sum (Seq.map square nums) 在sum2中使用了Seq模块中的函数这些函数将在稍候进行介绍。 可以看出函数式代码简短了许多且少了很多变量的声明。而且sum1是顺序执行若想以并行方式运行则需要更改所有代码但sum2只需要替换其中的Seq.sum和Seq.map函数。 函数和值 在我们接触到的非函数式编程语言包括C#中函数和数值总是有一些不同。但在函数式编程语言中函数也是值。比如函数可以作为其他函数的参数也可以作为返回值即高阶函数。而这在函数式编程中是非常常见的。需要注意的是我们叫“值”而不叫“变量”。因为在函数式编程中声明的东西默认是不可变的。在F#中不完全如此是因为F#包含了面向对象编程范式可以说并非纯函数式编程语言。 我们看下面以函数作为参数的代码求一组数字的负值 let negate x -x;;
val negate : x:int - int List.map negate [1..5];;
val it : int list [-1; -2; -3; -4; -5] 我们使用函数negate和列表[1..5]作为List.map的参数。 但很多时候我们不需要给函数一个名称只需使用匿名函数或叫Lambda表达式。在F#中Lambda表达式为关键字fun和参数加上箭头-和函数体。则上面的代码可以更改为 List.map (fun i- -i) [1..5];; 我们再看以函数作为返回值的例子假设我们定义一个powOf函数输入一个值返回一个该值求幂的函数 let powOf baseValue (fun exp - baseValue ** exp)let powOf2 powOf 2.0 // f(x) 2^xlet powOf3 powOf 3.0 // f(x) 3^xpowOf2 8. // 256.0powOf3 8. // 6561.0 其中powOf2即为powOf函数使用参数2返回的函数。其实这里涉及到闭包的内容就不详细解释了我们详细函数式编程时可能会再提及。 递归 递归大家都熟悉只是在F#中声明时需要添加rec关键字 let rec fact x if x 1 then 1else x * fact (x-1)
fact 5;;(* val fact : x:int - int val it : int 120 *) 其实需要显示声明递归是因为F#的类型推断系统无法在函数声明完成之前确定其类型而使用rec关键字后就允许在确定类型前调用该函数。 部分函数Partial Function 在函数式编程中还有一个叫Partial Function暂且叫部分函数吧的可以把接收多个参数的函数分解成接收单个参数即柯里化Currying。 我们知道使用函数printfn打印整数的语句为printfn %d i我们定义一个打印整数的函数 let printInt i printfn %d i;;
val printInt : i:int - unitlet printInt printfn %d;;
val printInt : (int - unit) 符号函数 在F#中如 - * /等运算符其实属于内建函数。而我们也可以使用这些符号来自定义符号函数。 我们用符号来重新定义上面的阶乘函数 let rec (!) x if x 1 then 1else x * !(x - 1)
!5;;(* val ( ! ) : int - int val it : int 120 *) 需要注意的是符号函数一般需要括号包裹如果符号函数的参数不止一个则符号函数是以中缀的方式来使用例如我们用~定义一个验证字符串是否和正则表达式匹配的函数 open System.Text.RegularExpressions;;let (~) str (regex : string) Regex.Match(str, regex).SuccessThe quick brown fox ~ The (.*) fox;;
(*val ( ~ ) : string - string - boolval it : bool true*) 而且符号函数也可以作为高阶函数的参数。 管道符|和| 我们再返回来看上面的平方和函数 let sum2 nums Seq.sum (Seq.map square nums) 假如函数层次非常多一层包裹一层则可读性非常差。 在F#定义了如下符号函数 let (|) x f f xlet (|) f x f x 我们称为“正向管道符”和“逆向管道符”。则上面的平方和函数可写作 let sum2 nums nums | Seq.map square | Seq.sum |虽然用得不多但常用来改变优先级而无需使用括号 let sum2 nums Seq.sum | Seq.map square nums 合成符和 我们也可以用函数合成符将多个函数组合成一个函数合成符也分正向和逆向。 let () f g x g(f x)
let () f g x f(g x) 还是以上面的求平方和为例Seq.map square即是一个部分函数 let sum2 nums (Seq.map square Seq.sum) numslet sum2 nums (Seq.sum Seq.map square) nums 常用模块函数 在上一篇中我们了解了集合类型。在F#中为这些集合类型定义了许多函数分别在集合名称对应的模块中例如Seq的相关函数位于模块Microsoft.FSharp.Collections.Seq中。而这也是我们最常用到的模块。 模块module是F#中组织代码的一种方式类似于命令空间namespace。但F#中也是有命名空间的其间的区别将在下一篇介绍。 下面简单介绍常用的函数并会列出与.Net的System.Linq中对应的函数。 如无特别说明该函数在三个模块中均可用但因为集合的实现方式不同函数的复杂度也会有区别在使用中根据实际情况选择合适的函数。 length 对应于Linq中的Count。即获得集合中元素的个数。 [1..10] | List.length;; // 10Seq.length {1..100};; // 100 虽然在Seq中也有length函数但谨慎使用因为Seq可能为无限序列。 exists 和 exists2 exists用于判断集合是否存在符合给定条件的元素对应于Linq中的Any。而exists2用于判断两个集合是否包含在同一位置且符合给定条件的一对元素。 List.exists (() 3) [1;3;5;7];; //trueSeq.exists (fun n1 n2 - n1n2) {1..5} {5..-1..1};; //true 第一行代码判断列表中是否包含等于3的元素其中() 3即为部分函数注意为符号函数。 第二行代码判断两个序列中因为{1;2;3;4;5}和{5;4;3;2;1}在索引2的位置存在元素符合函数(fun n1 n2 - n1n2)所以返回true。 forall 和 forall2 forall检查是否集合中所有元素均满足指定条件对应Linq中的All。 let nums {2..2..10}
nums | Seq.forall (fun n - n % 2 0);; //true 而forall2和exists2类似但当且仅当所有元素都满足相同位置且符合给定条件才返回true。接上一个代码片段 let nums2 {12..2..20}
Seq.forall2 (fun n n2 - n 10 n2) nums nums2;; //true find 和 findIndex find查找符合条件的第一个元素对应Linq中的First。需要注意的是当不存在符合条件的元素将引发KeyNotFoundException异常。 Seq.find (fun i - i % 5 0) {1..100};; //5 findIndex则返回符合条件的第一个元素的索引。 map 和 mapi map对应Linq中的Select将函数应用于集合中的每个元素返回值产生一个新的集合。 List.map ((*) 2) [1..10];; // [2; 4; 6; 8; 10; 12; 14; 16; 18; 20] mapi与map类似不过在应用的函数中还需要传入一个整数作为集合的索引。 Seq.mapi(fun i x - x*i) [3;5;7;8;0];;
// 将各个元素乘以各自的索引结果为[0; 5; 14; 24; 0] iter 和 iteri iter将函数应用于集合中的每个元素但函数返回值为unit。功能类似于for循环。而iteri与mapi一样需要在函数中传入一个索引。 Seq.iteri(fun i x - printfn 第%d个元素为%d i x) [3;5;7;8;0]
(*第0个元素为3第1个元素为5……
*) filter 和 where F#中filter和where是一样的对应于Linq中的Where。用于查找符合条件的元素。 {1..10} | Seq.filter (fun n - n%2 0);;//val it : seqint seq [2; 4; 6; 8; ...] fold fold对应Linq中的Aggregate通过提供初始值然后将函数逐个应用于每个元素返回单一值。 Seq.fold (fun acc n - acc n) 0 {1..5};; //15Seq.fold (fun acc n - acc string n) {1..10};;
//12345 首先将初始值与第一个元素应用于函数再将返回值与第二个元素应用于函数依此类推…… Linq中的Aggregate包含不需要提供初始值的重载其实F#中也有对应的reduce函数。类似的还有foldBack和reduceBack等逆向操作这里就不介绍了。 collect collect对应Linq中的SelectMany展开集合并返回所有二级集合的元素。 let lists [ [0;1]; [0;1;2]; [0;1;2;3] ]
lists | List.collect id;;
//[0; 1; 0; 1; 2; 0; 1; 2; 3] 其中id为Operators模块中的函数它的实现为fun n-n即直接对参数进行返回。 append append将两个集合类型合并成一个对应于Linq中的Concat。 Array.append [|1;3;1;4|] [|5;2;0|];;
val it : int [] [|1; 3; 1; 4; 5; 2; 0|] zip 和 zip3 zip函数将两个集合合并到一个里合并后每个元素是一个二元元组。 let list1 [ 1..3 ]
let list2 [ a;b;c ]List.zip list1 list2;;
// [(1, a); (2, b); (3, c)] zip3顾名思义就是将三个集合合并到一个里。 合并后的长度取决于最短的集合的长度。 rev rev函数反转一个列表或数组在Seq模块中没有这个函数。 sort sort函数基于compare函数第二篇中的“比较”介绍过对集合中的元素进行排序。 List.sort [1;3;-2;2];;
val it : int list [-2; 1; 2; 3] 数学函数 Linq中包含Max、Min、Average和Sum等数学函数。F#集合模块中也有对应的函数。 List.max [1..10] //10Seq.min {1..5} //5[1..10] | List.map float | List.average //5.5List.averageBy float [1..10] //5.5[0..100] | Seq.where (fun x - x % 2 0) | Seq.sum | printf 0到100中的奇数的和为%i// 0到100中的奇数的和为2500 需要注意的是average函数需要集合中的元素支持精确除法Exact division即实现了DivideByInt函数的类型。不知道为什么是ByInt。而F#中又不支持隐式类型转换所以对int集合求平均值只能先转换为float或float32或使用averageBy函数。 sum函数的示例代码将第一篇中由C#翻译过来的命令示示例代码转换成了函数式的代码。 集合间转换 三种集合类型的对应模块中均提供转换到to另外两种集合类型和从of另外两种类型转换的函数。 如Seq模块通过Seq.toList和Seq.toArray函数转出通过Seq.ofList和Seq.ofArray转入。 Seq.toList {1..5};; //[1; 2; 3; 4; 5]List.ofArray [|1..5|];; //[1; 2; 3; 4; 5] 函数式编程核心就是函数的运用。上面介绍的这些在C#中也经常使用到对应的方法但F#提供的函数非常丰富大家可通过MSDN了解更多 Seq模块List模块Array模块 因为F#中的List和Array均实现了IEnumarableT接口所以Seq模块的函数也可以接收List类型和Array类型的参数。当然反之则不行。 到现在为止我们了解的F#都是在交互窗口中。下一篇我们再简单介绍项目创建和代码组织即模块相关。 相关文章 如果你也会C#那不妨了解下F#1F# 数据类型如果你也会C#那不妨了解下F#2数值运算和流程控制语法如果你也会C#那不妨了解下F#3F#集合类型和其他核心类型【送书活动】机器学习项目开发实战《机器学习项目开发实战》送书活动结果公布 原文地址http://www.cnblogs.com/hjklin/p/fs-for-cs-dev-4.html .NET社区新闻深度好文微信中搜索dotNET跨平台或扫描二维码关注