10.2.3.2 在 C# 中以函数风格使用数组

来源:互联网 发布:2017淘宝新店如何推广 编辑:程序博客网 时间:2024/06/05 23:12

10.2.3.2 在 C# 中以函数风格使用数组

 

由于有了 LINQ to Object,在 C# 3.0 中,我们已经可以使用许多函数结构来处理数组。大多数 LINQ 运算符不返回数组:如果在数组上调用 Enumerable.Select,结果将返回 IEnumerable<T>。在某些情况下,我们还是愿意将结果保存在数组中,避免调用Enumerable.ToArray,将结果序列复制回数组的开销。

针对数组的一些常用函数式操作,已经成为 System.Array 类中的静态方法;但它们的命名规则不同于 F# 和 LINQ,例如,你会发现映射操作,在 ConvertAll 名下。我们将用标准的名字,实现我们的操作版本,进行演示。清单10.14 中还增加了类似于 F# 的 Array.int 函数的方法。

 

清单 10.14 函数式处理数组的方法(C#)

static class ArrayUtils { 

  publicstatic T[] int<T>(int length, Func<int, T> init) {  [1]

    T[] arr = new T[length]; 

    for (int i = 0; i < length; i++) arr[i] = init(i); 

    return arr; 

  } 

  publicstatic R[] Select<T, R>(this T[] arr, Func<T, R> map) {  [2]

    R[] res = new R[arr.Length]; 

    for (int i = 0; i < arr.Length; i++) res[i] = map(arr[i]); 

    return res; 

  } 

}

 

int 方法是通常的静态方法[1],它的参数为函数 init,用来初始化数组中的元素。Select 方法是扩展方法,把映射函数应用到原来数组中的每个元素,结果返回一个新数组的;它隐藏了由 LINQ 提供的、标准的 Select 操作。我们来使用这些方法的方式,与以前对应的 F# 函数类似:

 

var rnd = new Random(); 

var numbers = ArrayUtils.int(5, n =>rnd.Next(20)); 

var squares = numbers.Select(n => new {Number = n, Square = n*n }); 

 

foreach (var sq in squares) 

 Console.Write("({0}, {1}) ", sq.Number, sq.Square);

 

就像在 F# 版本一样,数组一旦创建,就不能修改。从更高层次来看,它就是处理不可变数据结构的纯函数代码;当然,我们实际能够进行修改,但仅限于 ArrayUtils 类中,c及只有在没有公开给任何其他代码的集合上进行。修改对外面是不可见的;在 C# 中,以这种方式写代码,是更有价值的,因为,这里使用函数式列表要比在 F# 中更难。

我们本章的最后一个主题是关于连续(continuations)的。可能有点难于理解,但是,一旦理解以后,可能会很吃惊。好消息是,如果你曾经在 .NET 中过写任何异步代码,从某种意义上说,就已经使用了连续,而 F# 使之更容易。我们将在第十三章更详细讨论,使用连续,对于递归函数是非常重要的优化方法,因此,我们将在这里关注一下。

0 0