【问题标题】:C# pointers, iterators and genericsC# 指针、迭代器和泛型
【发布时间】:2012-02-27 10:09:24
【问题描述】:

我很困惑

如何在 C# 中像使用 C++ 迭代器一样使用迭代器?我找不到 Begin() 或 End() 访问器,我什至不知道如何声明迭代器。我已阅读有关 Ienumerator 的信息。我的目标是实现合并功能。这是我用 C++ 编写的合并函数的一部分。大多数情况下,我正在寻找与所示内容相同的 C#,但我将使用引用类型而不是整数。

void merge(vector<int>::iterator left, vector<int>::iterator right, vector<int>::iterator      leftEnd, vector<int>::iterator rightEnd, vector<int>::iterator full)
{

    while(left != leftEnd && right!= rightEnd) //compare left and right until the end of the vector is reached
    {
        if(*right < *left)      //right < left so insert right to the output vector and advance the iterators
        {
            *full++ = *right++;
        }
        else                   //left < right so insert left to the output vector and advance the iterators
        {
            *full++ = *left++;
        }
    }

    while(left != leftEnd)    //copy any remaining elements into the output from left 
    {
        *full++ = *left++;
    }
}

另外,我应该使用什么集合? (目前我一直在尝试List&lt;T&gt;LinkedList&lt;T&gt;)。

【问题讨论】:

标签: c# c++ generics collections iterator


【解决方案1】:

听起来你想要这样的东西:

bool leftValid = left.MoveNext();
bool rightValid = right.MoveNext();

while (leftValid && rightValid)
{
    if (right.Current < left.Current)
    {
        full.Add(right.Current);
        rightValid = right.MoveNext();
    }
    else
    {
        full.Add(left.Current);
        leftValid = left.MoveNext();
    }
}

while (leftValid)
{
    full.Add(left.Current);
    leftValid = left.MoveNext();    
}

while (rightValid)
{
    full.Add(right.Current);
    rightValid = right.MoveNext();    
}

这里的full 需要是某种IList&lt;T&gt; - .NET 迭代器不允许您更改基础集合。

不应该尝试编写“桥接”代码来让您使用 .NET 迭代器(如 C++ 迭代器);当您使用 .NET 时,最好尝试从 .NET 迭代器的角度开始思考。

请注意,在 .NET 中传递迭代器是非常罕见的。将您的方法设为IEnumerable&lt;T&gt; 参数会更自然,并执行以下操作:

using (IEnumerable<T> leftIterator = leftSequence.GetEnumerator())
{
    using (IEnumerable<T> rightIterator = rightSequence.GetEnumerator())
    {
        // Code as above, just using leftIterator and rightIterator
        // instead of left and right
    }
}

【讨论】:

  • 请注意,full 不能是迭代器,因为 .NET 枚举器是只读的。您必须改为传递 IList 之类的内容。
  • @MattiVirkkunen:确实 - 会注意到这一点。
  • @CodeInChaos:对不起,是的,我将澄清关于不变性的一点。我不确定 .NET 不支持创建迭代器的副本是什么意思 - 您可以创建一个需要两个 IEnumerator&lt;T&gt;s 的方法,它可以正常工作。请注意,使用IEnumerable&lt;T&gt; 会更有意义。我会为此进行编辑。
  • @JonSkeet 这些迭代器可能都指向同一个容器中的不同元素。将它们视为列表中的索引,但处理不支持快速索引的集合。
  • leftSequence.GetEnumerator()和rightSequence.GetEnumerator()是什么
【解决方案2】:

.net 容器不支持 C++ 风格的迭代器。他们唯一拥有的是一个

  • 简单的前向迭代器称为IEnumerator&lt;T&gt;
  • 无法修改集合
  • 不是随机访问
  • 不能复制(有些集合有可以复制的值类型迭代器,但这是个棘手的事情,很少使用)
  • 在大多数集合上,每当您修改集合时也会失效

几乎他们唯一能做的就是在foreach 语句中进行迭代。


您可能想查看允许随机访问的IList&lt;T&gt; 接口,但仅在支持快速索引的集合上受支持。在这样的集合上,您可以使用索引来实现就地合并排序。

void Merge<T>(IList<T> container,int left, int right, int leftEnd, int rightEnd, int full)

然后使用container[left] 而不是*left


不幸的结果是,您无法像 C++ 那样实现高效的就地容器无关排序功能。

【讨论】:

  • 如果我们将迭代器定义为一个方便的容器无关句柄,该句柄对保持当前位置并启用导航的集合进行处理,.NET 支持的唯一迭代器类型是前向只读迭代器。这是一个遗憾。在许多重要算法中,双向或随机访问迭代器有很多用途。
【解决方案3】:

我认为您需要 GetEnumerator()、MoveNext() 和 Current。

通常,您可以只使用 foreach 进行迭代,但您的情况很特殊。

事实上,与其使用“full”,不如将其组织为一个迭代器块并懒惰地合并两个可枚举。

IEnumerable<T> Merge<T>(IEnumerable<T> left, IEnumerable<T> right)
{
    ... yield return Min<T>(left.Current, right.Current); ..,
}

【讨论】:

    【解决方案4】:

    您可以使用具有固定大小的数组或List&lt;T&gt;,在其他语言中也称为ArrayLists。它们的项目可以通过索引器 (list[i]) 访问,并且项目可以附加list.Add(item);。它们会自动生长。 LinkedLists 无法通过索引器访问,必须遍历。

    你可以像这样声明方法

    void merge(IEnumerator<int> left, IEnumerator<int> right, 
               List<int> full)
    {
        // Jon Skeet's code goes here
    }
    

    你可以得到这样的枚举器

    IEnumerable<int> intEnumerable = ...;
    IEnumerator<int> intEnumerator = intEnumerable.GetEnumerator();
    

    IEnumerable&lt;T&gt; 由大多数通用集合类型实现。非泛型集合通常实现IEnumerable

    (根据@CodeInChaos 的评论编辑)。

    【讨论】:

    • 没有可以传递给left/right的枚举。这些是部分迭代的迭代器的副本。
    • 我认为您的编辑没有解决问题。这些迭代器可能都指向同一个容器中的不同元素。将它们视为列表中的索引,但处理不支持快速索引的集合。
    • 一个IEnumerator&lt;T&gt;(不是IEnumerable&lt;T&gt;!)列表中的一种索引。 leftright 现在可以指向同一个集合中的不同位置。 (我将它们的类型从IEnumerable&lt;int&gt; 更改为IEnumerator&lt;int&gt;
    • 但是(通常)你不能复制它们。因此,您不能将它们按值传递给辅助方法。而且我认为这种复制能力在这样的算法中是必要的。
    • 我没有意识到List&lt;T&gt; 将枚举器实现为结构。我现在将它们更改为ref 参数。
    猜你喜欢
    • 1970-01-01
    • 2011-05-22
    • 2012-01-28
    • 1970-01-01
    • 2010-09-05
    • 2014-12-05
    • 2014-10-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多