博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#函数式编程练习
阅读量:4912 次
发布时间:2019-06-11

本文共 4577 字,大约阅读时间需要 15 分钟。

C#3.5后,可以使用lambda表达式、拓展方法、yield关键字(这个C#2.0就有)等技术,非常自然的写出函数式风格的代码。最近看了一些资料,这里略写一些

 

映射

代码如下:

public static IEnumerable
Map
(this IEnumerable
items, Func
func) { if (func == null) throw new ArgumentNullException("func should not be null"); foreach (var item in items) { yield return func(item); } }

实际上就是Select方法(多个重载中的最简单的一个),把T类型映射成TResult类型,T和TResult可以相同,比如都为int;也可以不同,比如一个int,一个String,如下

List
l = new List
(){1,2,3,4,5};var result1 = l.Map(a => a.ToString());var result2 = l.Map(a => a*2);

result1是IEnumerable<String>类型,里面是{“1”,”2”,”3”,”4”,”5”},即把int 转为相应的String

result2是IEnumerable<int>类型,里面是{2,4,6,8,10};

Map方法实际就是把一个映射函数,应用到items上的每一个元素,然后返回结果的集合。

 

过滤

代码如下:

public static IEnumerable
Filter
(this IEnumerable
items,Func
filter ) { //检查ArgumentNullException,为了简便,下面都不写了 foreach (var item in items) { if (filter(item)) yield return item; } }

实际上就是Where方法,过滤掉集合中不满足条件的元素。这个就不举例~~

 

Fold

不知道该怎么翻译,直接上代码吧

public static T Fold
(this IEnumerable
items, T init, Func
func){ T result = init; foreach (var item in items) { result = func(result, item); } return result;}

给一个初值,然后不断的迭代集合的每一元素,不好解释,大家自己好好体会一下吧。

这里返回值没必要和集合中的元素一样,不过一样的话,理解起来更简单一下。下面看一下两个用例:

List
l = new List
(){1,2,3,4,5};var sum = l.Fold(0, (a, b) => a+b);var factorial = l.Fold(1, (a, b) => a*b);IEnumerable
ls = l.Map(a => a.ToString());var result = ls.Fold("jzl:", (a, b) => a + b); //result 的值为 "jzl:12345"

Sum就是集合元素的和,结果是15,如果初值是1,那个结果就是16.

factorial 就是阶乘了。

 

Monad

这个是Haskell语言的一个让人非常费解的知识点大家可以google一下,函数签名是:

L a –> (a -> L b)  -> L b

具体点,把L想象成List,a、b想象成int、String之类的类型,函数接受两个参数,List<a>和一个函数(这个函数接受a类型的参数,然后返回List<b>),然后返回List<b>

//L a -> (a -> L b) -> L b
//Monad(1)        public static IEnumerable Monad
(this IEnumerable
items, Func
> func ) { foreach (var item in items) { foreach (var i in func(item)) { yield return i; } } } //Monad(2) public static Nullable Monad
(this Nullable
nA,Func
> func ) where A:struct where B:struct { if (!nA.HasValue) return null; else return func(nA.Value); }

对于Monad(1),其实就是SelectMany方法,不过C#编译器对SelectMany方法提供了一些语法糖,写起来会更自然。

对于Monad(2),首先对照着函数签名,好好体会一下。这里虚拟一个场景,来说明它的用法

有个人为了保持平衡(Balance),左手和右手拿东西的差值不能大于3,现在有2个操作,向这个人的左手添加东西和向右手添加东西。然后我不断添加,问最后他能不能保持平衡。代码模拟一下吧:

//Nullable
,T必须是值类型 public struct Balance { private int _left; private int _right; public Balance(int left,int right) { this._left = left; this._right = right; } public Nullable
AddLeft(int l) { if ( Math.Abs(_left + l - _right) > 3 ) return null; else return new Balance(_left+l,_right); } public Nullable
AddRight(int r) { if (Math.Abs(_right + r - _left) > 3) return null; else return new Balance(_left,_right+r); } }//.... static void Main(string[] args) { Nullable
balance = new Balance(0,0); var r = balance.Monad(b => b.AddLeft(1)) .Monad(b => b.AddRight(1)); var r1 = balance.Monad(b => b.AddLeft(10)) .Monad(b => b.AddRight(10)); Console.WriteLine(r.HasValue );//True Console.WriteLine(r1.HasValue);//False Console.ReadKey(); }

中间任何一个时刻,平衡被打破,即左右手之差大于3,那么就会产生Null,然后一直是Null,所以最终结果是Null的话,就知道中间有个过程出错了。如果只需要最终结果,这种调用非常直观有效。

这个例子的问题在于,我最终知道错了,却无法确定哪里错了。这需要在出错的时候,记录错误信息,并传下去。

大家注意理解思想,一个更实际的例子是编译器的Parser,可以一直解析,最终看一下结果,就可以知道中间出错了没有,当然需要传递出错的行号、列号、Error信息等。

编译的例子是听一位大神讲的,暂时还没能通过代码演示出来。不过可以想象一下,Parser实际就是一个状态机,根据输入,不断从当前状态转到下一个状态,如果状态是Error,那么就一直Error下去吧。Monad可以很好的体现这一过程。

挺绕吧,实际本人对Monad也是一知半解,如果有错,请大神们赐教。

转载于:https://www.cnblogs.com/jzlxiaohei/archive/2013/05/30/3108057.html

你可能感兴趣的文章
XML - 报表数据的新大陆
查看>>
echart在X轴下方添加字
查看>>
Map集合的两种取出方式
查看>>
GridView,Repeater增加自动序号列
查看>>
SMO算法精解
查看>>
第k小元素学习记录
查看>>
avi文件格式详解【转】
查看>>
django
查看>>
Java学习从入门到精通
查看>>
查找目录下的所有文件中是否含有某个字符串 linux
查看>>
66. Plus One 数组加1
查看>>
范式原则
查看>>
2018年各大互联网前端面试题四(美团)
查看>>
一起学Python:字符串介绍
查看>>
学习笔记:树状数组
查看>>
洛谷P1772 [ZJOI2006]物流运输 题解
查看>>
CF519E A and B and Lecture Rooms
查看>>
python-redis之数据类型二
查看>>
Java类加载机制
查看>>
数据库的最简单实现
查看>>