本文给出一个Functional Programming和Lazy Code的一个例子。跟着思路走,关键的地方会有相应的说明。
我们想实现一个判断"素数"的小程序,如下:
using System; namespace FunctionalProgramming { class Program { static void Main(string[] args) { var number = int.Parse(Console.ReadLine()); var result = true; for(long i=2;i<number;i++) { if(number%i==0) { result = false; break; } } Console.WriteLine(result); } } }
上面的代码虽能完成功能,但不够整洁。不是我们想要的代码。
我们提取其中的方法,重新实现如下:
using System; namespace RefactorExample { class Program { static void Main(string[] args) { var number = int.Parse(Console.ReadLine()); var result = IsPrime(number);; Console.WriteLine(result); } //Refactor private static bool IsPrime(int number) { var result=true; for (long i = 2; i < number; i++) { if (number%i == 0) { result = false; break; } } return result; } } }
以此为基础,借助扩展方法,针对一个数组,我们可以方便的改写上面的代码如下:
using System; using System.Collections.Generic; using System.Linq; namespace AHigherCalling { static class Program { static void Main(string[] args) { var number = new [] {3,5,7,9,11,13}; foreach (var prime in number.Find(IsPrime).Take(2)) { Console.WriteLine(prime); } Console.ReadKey(); } private static IEnumerable<int> Find(this IEnumerable<int> values,Func<int,bool> test ) { var result = new List<int>(); foreach (var value in values) { if (test(value)) result.Add(value); } return result; } private static bool IsPrime(int value) { var result = true; for (long i = 2; i < value; i++) { if (value % i == 0) { result = false; break; } } return result; } //一些其他的类似IsPrime的方法 private static bool IsEven(int value) { return value%2 == 0; } private static bool IsOdd(int value) { return value%2 != 0; } } }
这样做的目的是提供更清洁的语法,并提高程序应对变化的能力。例如,我们实现的两个类似IsPrime方法。
单看Find方法。貌似没有什么问题,当然我们可以用ReShaper生成LINQ语句,替换掉那个for循环。
View Code
using System; using System.Collections.Generic; using System.Linq; namespace AHigherCalling { static class Program { static void Main(string[] args) { var number = new [] {3,5,7,9,11,13}; foreach (var prime in number.Find(IsPrime).Take(2)) { Console.WriteLine(prime); } Console.ReadKey(); } private static IEnumerable<int> Find(this IEnumerable<int> values,Func<int,bool> test ) { //LINQ替换掉了for循环 return values.Where(value => test(value)).ToList(); } private static bool IsPrime(int value) { var result = true; for (long i = 2; i < value; i++) { if (value % i == 0) { result = false; break; } } return result; } //一些其他的类似IsPrime的方法 private static bool IsEven(int value) { return value%2 == 0; } private static bool IsOdd(int value) { return value%2 != 0; } } }
程序正常运行。
貌似我们的程序没有什么问题了,那是因为我们这个数组不够大!我们把numbers变的很大!如下:
using System; using System.Collections.Generic; using System.Linq; namespace AHigherCalling { static class Program { static void Main(string[] args) { foreach (var prime in GetRandomNumbers().Find(IsPrime).Take(2)) { Console.WriteLine(prime); } Console.ReadKey(); } private static IEnumerable<int> GetRandomNumbers() { //yield return 3; //yield return 5; //yield return 7; //yield return 9; //yield return 11; //yield return 13; Random random = new Random(); while (true) { yield return random.Next(90000, 100000); } } private static IEnumerable<int> Find(this IEnumerable<int> values,Func<int,bool> test ) { //LINQ替换掉了for循环 return values.Where(value => test(value)).ToList(); } private static bool IsPrime(int value) { var result = true; for (long i = 2; i < value; i++) { if (value % i == 0) { result = false; break; } } return result; } //一些其他的类似IsPrime的方法 private static bool IsEven(int value) { return value%2 == 0; } private static bool IsOdd(int value) { return value%2 != 0; } } }
程序就也陷入死循环了!而我这次想获取的只是其前2个而已。
问题出在哪里?
不难发现,当然在Find这里!
还记得前面我实现的Find方法吗?如下:
private static IEnumerable<int> Find(this IEnumerable<int> values,Func<int,bool> test ) { var result = new List<int>(); foreach (var value in values) { if (test(value)) result.Add(value); } return result; }
使用ReShaper重构过如下:
private static IEnumerable<int> Find(this IEnumerable<int> values,Func<int,bool> test ) { //LINQ替换掉了for循环 return values.Where(value => test(value)).ToList(); }
简单分析问题所在后,可以想到的解决方法,如下:
使用迭代器:
private static IEnumerable<int> Find(this IEnumerable<int> values,Func<int,bool> test ) { foreach (var value in values) { if (test(value)) yield return value; } }
或是:
private static IEnumerable<int> Find(this IEnumerable<int> values,Func<int,bool> test ) { return values.Where(value => test(value)); }
这样程序就按我既定的想法运行了。
从ReShaper生成的代码来看,问题出在立即执行的ToList()那里!但毕竟ReShaper只是个工具,其翻译的是我们的代码,不推卸责任的说,问题出在我们,因为使用迭代器更正后,ReShaper不也生成了正确的代码了么?
update:
from: http://stackoverflow.com/questions/7062882/searching-a-tree-using-linq