过了许久的时间,终于趁闲暇的时间来继续将函数式编程这个专辑连载下去,这段时间开头是为 IOS 这个新方向做准备,将 OC 的教程写成了 SWIFT 版,当然我个人是支持 Xamarin ,但是我一般会先掌握原生态的开发,再掌握 Xamarin 。后面剩下的时间开发了一个 Xamarin App 项目,用了十几天完成的。 今天的内容将对比较简单,就是讲述如何利用函数式编程来实现列表推导。说的简单点就是列表的数据并不像我们平时开发那样实现写好的,而是通过一定的算法计算出来的 (不是程序一打开就计算完成的,而是在使用的时候才计算的,并且只计算到我们使用的那一项为止) 。
首先我们当然是先编写一个高阶函数来实现列表推导的主要功能部分,具体的代码如下所示:
1 public static IEnumerable<T> Sequence<T>(Func<T, T> getNext, T startVal, Func<T, bool> endReached) 2 { 3 if (getNext == null) 4 yield break; 5 yield return startVal; 6 T val = startVal; 7 while (endReached == null || !endReached(val)) 8 { 9 val = getNext(val); 10 yield return val; 11 } 12 }
这个函数的作用就是根据一定的算法进行迭代,而这里利用了 C# 2.0 才有的语法。该函数首先是根据 startVal 开始迭代,并且根据 getNext 计算下一个值,而 endReached 则用来约束结束条件。所以我们可以看到如果 endReached 为 NULL 也是可行的,这样这个序列就是无穷的,当然不是死循环。下面我们利用一段简单的代码来掩饰如何使用这个函数:
static void Main(string[] args) { var oddNumbersForm1 = Sequence<int>(x => x + 2, 1, x => x >= 20); foreach (int x in oddNumbersForm1) { Console.WriteLine(x); } Console.ReadKey(); }
最后我们在命令行界面中可以看到如下的显示:
很多时候我们使用的序列都是很简单的,而利用上面的 Sqquence 函数则会变得复杂,同时这个函数还无法实现一些我们需要的功能,比如需要判断一个值时候存在范围中等等,所以我们下面将利用一个简单的 Range 来实现这些功能,当然内部还是使用的如上的 Sequence 函数,下面是具体的代码:
1 public class Range<T> : IEnumerable<T> 2 { 3 private T start; 4 private T end; 5 private Comparison<T> compare; 6 private IEnumerable<T> sequence; 7 8 private static int Compare<U>(U one,U other) 9 { 10 return Comparer<U>.Default.Compare(one,other); 11 } 12 13 public Range(T start, T end, Func<T, T> getNext, Comparison<T> compare) 14 { 15 this.start = start; 16 this.end = end; 17 this.compare = compare; 18 this.sequence = Sequence<T>(getNext, start, v => compare(getNext(v), end) > 0); 19 } 20 21 public Range(T start, T end, Func<T, T> getNext) 22 : this(start, end, getNext, Compare) { } 23 24 public IEnumerator<T> GetEnumerator() 25 { 26 return sequence.GetEnumerator(); 27 } 28 29 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 30 { 31 return ((IEnumerable<T>)this).GetEnumerator(); 32 } 33 34 public bool Contains(T value) 35 { 36 return compare(value, start) >= 0 && compare(end, value) >= 0; 37 } 38 }
通过上面的代码我们可以看到 Range 的功能远远比 Sequence 函数要强大的多,并且我们可以看到我们还提供了 Contains 方法用来判断指定的值是否在值域中,另外我们还可以获得 IEnumerable 借口的各种扩展方法供我们使用。
有时我们的迭代器的实现可能很复杂,如果还要加上限制不仅仅会影响到其性能,同时还会导致代码的可读性较差,所以我们可以将这两者进行分离,负责生成序列的函数只需要没有任何限制,而将限制移转到外部,比如函数 TFunc 是一个返回序列的函数,并且没有限制,而 CFunc 则接受一个最大值,以及用来计算序列的函数,并且返回负责条件的序列,那么我们就可以像如下这样循环获取值:
1 foreach(int x in CFunc(10,TFunc)){ 2 Console.WriteLine(x); 3 }
至此关于函数式编程中的序列就结束了,后面的时间笔者将会抓紧时间尽快将这个专辑结束掉,开始集中在 Xamarin.Android 这个系列的教程上。