6.7.3.1 在 C# 中实现 fold

6.7.3.1 在 C# 中实现 fold


与 fold 有相同行为的操作,在 .NET 库中也有,但是,名字叫Aggregate(聚合)。通常,它是能够在任何集合类型上运行的扩展方法,我们也可以像 F# 函数一样使用它。清单 6.21 是我们用 C# 3.0 重写前面示例的代码。在 F# 中,我们用元组来保存在聚合过程中的状态。你也许还记得以前的几章中,我们曾提到过,C# 3.0 中的匿名类型有时也能用于这一目的。这是一个非常适合的示例:


var res =
  places.Aggregate(new { StartOfLine = true, Result = "" },
  (r, pl) => {
    var n = r.StartOfLine ? pl.Name.PadRight(20) : (pl.Name + "\n");
    return new { StartOfLine = !r.StartOfLine, Result = r.Result + n };
  }).Result;


在 C# 中,初始值由第一个参数值指定;创建的匿名类型,有一个标志 StartOfLine(用作临时值),和属性Result,用于保存连接的字符串;第二个参数值是 lambda 函数,功能同前面的 F# 示例,但返回的结果也是匿名类型,与初始值有同样的结构。为使代码更有效,我们还可以使用 StringBuilder 类代替连接字符串,但是,我们想让示例尽可能的最简单。
我们已经知道了如何在 C# 中使用函数,现在,应该看看它是如何实现的。在清单 6.24 中,可以看到两个实现,一个是典型的函数式方法,用于第三章的函数式列表,另一个是命令式方法,用于泛型的 .NET List 类型,在原理上,它与 .NET 库中聚合扩展方法相同。


清单 6.24 Fold 的函数式和命令式实现 (C#)


// Functional implementation using ‘cons list‘
R Fold<T, R>(this FuncList<T> list, Func<R, T, R> func, R init) {  [1]
  if (list.IsEmpty)
    return init;    [2]
  else {
    var state = func(init, list.Head)    | [3]
    return list.Tail.Fold(func, state);   |
  }
}


// Imperative implementation using ‘List<T>‘
R Fold<T, R>(this List<T> list, Func<R, T, R> func, R init) {  [4]
  R temp = init;
  foreach(var item in list)
    temp = func(temp, item);  [5]
  return temp;
}


除了使用不同的集合类型之外,这两个方法[1][4]的签名是一样的;它相当于前面 F# 的声明,虽然,我们必须显式地写出类型参数。两种情况下,列表都是作为第一个参数,实现了集合类型的扩展方法。
在函数式版本中,有两个分支。第一个处理空列表[2],第二个递归地处理 cons cell,并使用 fun(函数)参数[3]聚合结果。命令式版本,声明了一个局部可变值,保存聚合过程中的当前结果。通过遍历所有元素计算出聚合后的值,并在每次迭代[5]中更新这个值。
我们曾经提到过,实现其他操作的过程非常类似。在 map 和 filter 的函数式版本中,在[2]中应该返回空列表,在命令式版本中,使用可变列表作为临时值;其他改变在[4]和[5]两行。当执行映射时,只要调用给定的函数,而对于筛选,可能要决定是否追加当前元素。
有关高阶函数,我们结论是,需要重点理清处理列表的函数与处理选项值函数之间的关系。

6.7.3.1 在 C# 中实现 fold

上一篇:WPF Litbox样式和模板


下一篇:delphi各种错