首页 > 代码库 > 6.7.3.1 在 C# 中实现 fold
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]两行。当执行映射时,只要调用给定的函数,而对于筛选,可能要决定是否追加当前元素。
有关高阶函数,我们结论是,需要重点理清处理列表的函数与处理选项值函数之间的关系。
与 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
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。