反对for行动

Francesco Cirillo于不久前发起了“反对if行动”,受此影响,Matthew Podwysocki也用这种方式提出了自己的声明,即“反对for行动”。

\

Matthew Poswysocki生活在华盛顿特区,作为微软的高级咨询师,维护或参与了诸多社区活动(如DC ALT.NET讨论组),并致力于推广各种敏捷实践。这次他提出,在代码中应该尽量使用和构建可以进行组合的函数,而不是显式的循环语句(包括for、foreach和while)。

\

Matthew认为,通过循环来实现的功能往往可以分为以下三种情况:

\
  1. 查询(映射、过滤等等) \\
  2. 聚合(求和、计数等等) \\
  3. 进行一些有副作用(Side Effect)的操作(读取文件、发送消息等等) \

Matthew看来,使用for循环来处理“查询”和“聚合”时,最大的问题在于将关注点放在了如何做(How)而不是做什么(What)。他举了一个例子,“找出100以内所有质数”,并给出了一个实现:

\
var numbers = Enumerable.Range(1, 100);\var output = new List\u0026lt;int\u0026gt;();\\foreach(var number in numbers)\    if(IsPrime(number)) output.Add(number);
\

Matthew认为:

\
\

这里的问题在于我们很难将现有逻辑与另一个操作进行组合,因为这里的实现涉及了“怎么做”。我们应该使用.NET 2.0以上版本中的泛型及延迟加载的特性进行函数式的构造。这样可以带来“声明式(declarative)”的感觉,而关注点就只有“做什么”了:

\
\
var primes = Enumerable.Range(1, 100)\    .Where(x =\u0026gt; IsPrime(x));
\

Matthew提出,应该尽量避免在一个循环中进行多种操作,这样会为代码的可读性和维护性带来负面影响。而在Martin Fowler的Refactoring站点中,拆分循环内部逻辑的重构方式被命名为“Split Loop”。Matthew认为较好的方式是将逻辑进行组合,例如求出“100以内质数的数量”便可以这样实现:

\
var primesCount = Enumerable.Range(1, 100)\    .Where(x =\u0026gt; IsPrime(x))\    .Count();
\

对于产生副作用的情况,Matthew引用了Eric Lippert对于“为什么IEnumerable\u0026lt;T\u0026gt;没有ForEach扩展方法”的回应。Eric认为,引入IEnumerable\u0026lt;T\u0026gt;的ForEach扩展方法事实上带来了副作用,而违背了IEnumerable\u0026lt;T\u0026gt;的设计初衷。此外,ForEach还会形成闭包,可能会造成一些难以发现的引用问题。

\

Matthew并没有赞同这种说法,不过它对这个看法表示理解。他认为,如果是使用C#进行编程,使用foreach来遍历一个IEnumerable没有太大问题。不过在F#中,最好还是使用iter或iteri方法进行遍历。关于这点,他使用F#交互命令行进行了演示:

\
\u0026gt; let flip f y x = f x y\- [1..10]\-   |\u0026gt; List.map((*) 2)\-   |\u0026gt; List.filter(flip (%) 3 \u0026gt;\u0026gt; (=) 0)\-   |\u0026gt; List.iter(printfn \"%d\");;\6\12\18\\u0026gt; [1..10]\-   |\u0026gt; List.map((*) 2)\-   |\u0026gt; List.filter(flip (%) 3 \u0026gt;\u0026gt; (=) 0)\-   |\u0026gt; List.iteri(printfn \"%d\\t%d\");;\0       6\1       12\2       18
\

社区中有人认为,这个行动的目的是希望在面向对象编程环境中融入部分函数式编程的理念。C# 3.0引入了Lambda表达式,将高阶函数在.NET中的应用切实地推广开来。同时,其他平台也在进行着类似的改变。例如最近颇受好评的Scala语言也引入了函数式编程特性。

\

你对for的使用有何看法,并且对函数式编程的看法如何?

\

你可能感兴趣的:(反对for行动)