12.3.1 用迭代器转换序列

来源:互联网 发布:照片视频制作软件下载 编辑:程序博客网 时间:2024/06/10 02:41
12.3.1 用迭代器转换序列



    到目前为止,我们只能用迭代器,来从单一的数据块(如果任何)生成序列。然而,迭代器的一个常见用途,是以某种方式把一个序列变换成另一个。作为一个简单的例子,这里有一个方法,取一个数字的序列,返回平方序列:



IEnumerable<int> Squares(IEnumerable<int> numbers) {
  foreach(int i in numbers)
    yield return i * i;
}



    我们将使用熟悉的 foreach 结构,但请记住, foreach 包含 yield return 语句,有不同的含义。它不会提前运行循环,而是在需要时才计算。foreach 语句允许我们写代码,为循环的每次迭代生成一些元素,它对应于,从输入序列中取单个元素,把零或多个元素推到输出序列(在前面的例子中,我们始终只生成一个元素)。如果想要实现来自 LINQ to Objects 的、泛型的 Where 和 Select 方法,可以使用完全相同的方法。

    一个更复杂的例子,我们实现 Zip 方法,与 F# 中的 Seq.zip 函数的行为相同。给它两个序列,将返回一个序列,包含的元素是把给定的序列连接成元组。这个方法在 .NET 4.0 的库中是可用的,我们要看一下,因为它展示了一个有趣的问题。我们不能使用 foreach 同时从两个源序列取元素。你可以在清单 12.8 中看到,我们唯一的选择是直接使用 IEnumerable<T> and IEnumerator<T> 接口。



Listing 12.8 Implementing the Zip method (C#)



public static IEnumerable<Tuple<T1, T2>> Zip<T1, T2>
    (IEnumerable<T1> first, IEnumerable<T2> second) {
  using(var firstEn = first.GetEnumerator())
  using(var secondEn = second.GetEnumerator()) {
    while (firstEn.MoveNext() && secondEn.MoveNext()) {
      yield return Tuple.Create(firstEn.Current, secondEn.Current);
    }
  }
}



    从方法的签名来看,我们可以看到,它取两个序列作为参数。这个方法是泛型的,每个输入的序列有单独的类型参数。我们使用泛型的 C# 元组,所以,返回的序列包含 Tuple<T1, T2> 类型的元素。在实现中,我们首先问每个序列的我们可以用来遍历元素的枚举器,我们要在每个枚举上反复调用 MoveNext 方法,从两个序列中获得下一个元素。如果有一个序列已经结束,我们就产生一个元组,包含每个枚举器的当前的元素。

    这个示例表明,有时,处理方法需要显式使用 IEnumerator <T> 接口。foreach 循环给我们一种方法,从一个源中一个接一个取出元素,但是,一旦我们需要从多个源中以交替的顺序,取出元素,就麻烦了。如果我们想要在 F# 中实现 Seq.zip,将不得不使用相同的技术。我们既可以在序列表达式中使用 while 循环,也可以递归序列表达式。我们需要的大部分处理函数,在 .NET 和 F# 库中已经是可用了,所以,我们既可以显式使用,也可以通过使用 C# 的查询表达式语法。


原创粉丝点击