C# 中排列枚举的组合迭代?

发布于 2024-07-13 19:54:55 字数 341 浏览 9 评论 0原文

有没有办法对 C# 中的排列枚举进行 foreach 样式迭代? 对于可下标列表,我知道可以使用常规的 for 循环在索引范围内迭代 int,但我真的更喜欢 foreach 而不是 for for有很多原因。

如果它能在 C# 2.0 中运行,那就加分了。

澄清:我说的是多个排列的枚举。 就像一排排的铅笔和纸。 我希望每支铅笔都能在相应的纸上写字。

注意:最初这个问题使用术语并行而不是排列

Is there a way to do foreach style iteration over lined up enumerables in C#? For subscriptable lists, I know one could use a regular for loop iterating an int over the index range, but I really prefer foreach to for for a number of reasons.

Bonus points if it works in C# 2.0.

Clarification: I am talking about multiple lined up enumerables. Like arrays of pencils and papers. I want each pencil to write on the corresponding paper.

Note: Originally this question used the term parallel instead of lined up.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

朕就是辣么酷 2024-07-20 19:54:55

.NET 4 的 BlockingCollection 使这变得非常简单。 创建一个BlockingCollection,在可枚举方法中返回其.GetConsumingEnumerable()。 然后 foreach 只是将其添加到阻塞集合中。

例如

private BlockingCollection<T> m_data = new BlockingCollection<T>();

public IEnumerable<T> GetData( IEnumerable<IEnumerable<T>> sources )
{
    Task.Factory.StartNew( () => ParallelGetData( sources ) );
    return m_data.GetConsumingEnumerable();
}

private void ParallelGetData( IEnumerable<IEnumerable<T>> sources )
{
    foreach( var source in sources )
    {
        foreach( var item in source )
        {
            m_data.Add( item );
        };
    }

    //Adding complete, the enumeration can stop now
    m_data.CompleteAdding();
}

希望这有帮助。
顺便说一句,我昨晚发布了一篇关于此的博客

安德烈

.NET 4's BlockingCollection makes this pretty easy. Create a BlockingCollection, return its .GetConsumingEnumerable() in the enumerable method. Then the foreach simply adds to the blocking collection.

E.g.

private BlockingCollection<T> m_data = new BlockingCollection<T>();

public IEnumerable<T> GetData( IEnumerable<IEnumerable<T>> sources )
{
    Task.Factory.StartNew( () => ParallelGetData( sources ) );
    return m_data.GetConsumingEnumerable();
}

private void ParallelGetData( IEnumerable<IEnumerable<T>> sources )
{
    foreach( var source in sources )
    {
        foreach( var item in source )
        {
            m_data.Add( item );
        };
    }

    //Adding complete, the enumeration can stop now
    m_data.CompleteAdding();
}

Hope this helps.
BTW I posted a blog about this last night

Andre

野の 2024-07-20 19:54:55

简短的回答,不。 foreach 一次仅适用于一个可枚举值。

但是,如果您将并行枚举合并为一个,则可以对合并的枚举进行 foreach 。 我不知道有任何简单的内置方法可以做到这一点,但是以下应该可以工作(尽管我还没有测试过):

public IEnumerable<TSource[]> Combine<TSource>(params object[] sources)
{
    foreach(var o in sources)
    {
        // Choose your own exception
        if(!(o is IEnumerable<TSource>)) throw new Exception();
    }

    var enums =
        sources.Select(s => ((IEnumerable<TSource>)s).GetEnumerator())
        .ToArray();

    while(enums.All(e => e.MoveNext()))
    {
        yield return enums.Select(e => e.Current).ToArray();
    }
}

然后您可以对返回的可枚举进行 foreach

foreach(var v in Combine(en1, en2, en3))
{
    // Remembering that v is an array of the type contained in en1,
    // en2 and en3.
}

Short answer, no. foreach works on only one enumerable at a time.

However, if you combine your parallel enumerables into a single one, you can foreach over the combined. I am not aware of any easy, built in method of doing this, but the following should work (though I have not tested it):

public IEnumerable<TSource[]> Combine<TSource>(params object[] sources)
{
    foreach(var o in sources)
    {
        // Choose your own exception
        if(!(o is IEnumerable<TSource>)) throw new Exception();
    }

    var enums =
        sources.Select(s => ((IEnumerable<TSource>)s).GetEnumerator())
        .ToArray();

    while(enums.All(e => e.MoveNext()))
    {
        yield return enums.Select(e => e.Current).ToArray();
    }
}

Then you can foreach over the returned enumerable:

foreach(var v in Combine(en1, en2, en3))
{
    // Remembering that v is an array of the type contained in en1,
    // en2 and en3.
}
从﹋此江山别 2024-07-20 19:54:55

Zooba 的答案很好,但您可能还想查看“如何同时迭代两个数组” 的答案。

Zooba's answer is good, but you might also want to look at the answers to "How to iterate over two arrays at once".

暗地喜欢 2024-07-20 19:54:55

我从 .NET4 并行库编写了 EachParallel() 的实现。 它与 .NET 3.5 兼容: Parallel ForEach C# 3.5 中的循环
用法:

string[] names = { "cartman", "stan", "kenny", "kyle" };
names.EachParallel(name =>
{
    try
    {
        Console.WriteLine(name);
    }
    catch { /* handle exception */ }
});

实施:

/// <summary>
/// Enumerates through each item in a list in parallel
/// </summary>
public static void EachParallel<T>(this IEnumerable<T> list, Action<T> action)
{
    // enumerate the list so it can't change during execution
    list = list.ToArray();
    var count = list.Count();

    if (count == 0)
    {
        return;
    }
    else if (count == 1)
    {
        // if there's only one element, just execute it
        action(list.First());
    }
    else
    {
        // Launch each method in it's own thread
        const int MaxHandles = 64;
        for (var offset = 0; offset < list.Count() / MaxHandles; offset++)
        {
            // break up the list into 64-item chunks because of a limitiation             // in WaitHandle
            var chunk = list.Skip(offset * MaxHandles).Take(MaxHandles);

            // Initialize the reset events to keep track of completed threads
            var resetEvents = new ManualResetEvent[chunk.Count()];

            // spawn a thread for each item in the chunk
            int i = 0;
            foreach (var item in chunk)
            {
                resetEvents[i] = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(new WaitCallback((object data) =>
                {
                    int methodIndex = (int)((object[])data)[0];

                    // Execute the method and pass in the enumerated item
                    action((T)((object[])data)[1]);

                    // Tell the calling thread that we're done
                    resetEvents[methodIndex].Set();
                }), new object[] { i, item });
                i++;
            }

            // Wait for all threads to execute
            WaitHandle.WaitAll(resetEvents);
        }
    }
}

I wrote an implementation of EachParallel() from the .NET4 Parallel library. It is compatible with .NET 3.5: Parallel ForEach Loop in C# 3.5
Usage:

string[] names = { "cartman", "stan", "kenny", "kyle" };
names.EachParallel(name =>
{
    try
    {
        Console.WriteLine(name);
    }
    catch { /* handle exception */ }
});

Implementation:

/// <summary>
/// Enumerates through each item in a list in parallel
/// </summary>
public static void EachParallel<T>(this IEnumerable<T> list, Action<T> action)
{
    // enumerate the list so it can't change during execution
    list = list.ToArray();
    var count = list.Count();

    if (count == 0)
    {
        return;
    }
    else if (count == 1)
    {
        // if there's only one element, just execute it
        action(list.First());
    }
    else
    {
        // Launch each method in it's own thread
        const int MaxHandles = 64;
        for (var offset = 0; offset < list.Count() / MaxHandles; offset++)
        {
            // break up the list into 64-item chunks because of a limitiation             // in WaitHandle
            var chunk = list.Skip(offset * MaxHandles).Take(MaxHandles);

            // Initialize the reset events to keep track of completed threads
            var resetEvents = new ManualResetEvent[chunk.Count()];

            // spawn a thread for each item in the chunk
            int i = 0;
            foreach (var item in chunk)
            {
                resetEvents[i] = new ManualResetEvent(false);
                ThreadPool.QueueUserWorkItem(new WaitCallback((object data) =>
                {
                    int methodIndex = (int)((object[])data)[0];

                    // Execute the method and pass in the enumerated item
                    action((T)((object[])data)[1]);

                    // Tell the calling thread that we're done
                    resetEvents[methodIndex].Set();
                }), new object[] { i, item });
                i++;
            }

            // Wait for all threads to execute
            WaitHandle.WaitAll(resetEvents);
        }
    }
}
ㄟ。诗瑗 2024-07-20 19:54:55

如果您想坚持基础知识 - 我以更简单的方式重写了当前接受的答案:

    public static IEnumerable<TSource[]> Combine<TSource> (this IEnumerable<IEnumerable<TSource>> sources)
    {
        var enums = sources
            .Select (s => s.GetEnumerator ())
            .ToArray ();

        while (enums.All (e => e.MoveNext ())) {
            yield return enums.Select (e => e.Current).ToArray ();
        }
    }

    public static IEnumerable<TSource[]> Combine<TSource> (params IEnumerable<TSource>[] sources)
    {
        return sources.Combine ();
    }

If you want to stick to the basics - I rewrote the currently accepted answer in a simpler way:

    public static IEnumerable<TSource[]> Combine<TSource> (this IEnumerable<IEnumerable<TSource>> sources)
    {
        var enums = sources
            .Select (s => s.GetEnumerator ())
            .ToArray ();

        while (enums.All (e => e.MoveNext ())) {
            yield return enums.Select (e => e.Current).ToArray ();
        }
    }

    public static IEnumerable<TSource[]> Combine<TSource> (params IEnumerable<TSource>[] sources)
    {
        return sources.Combine ();
    }
裂开嘴轻声笑有多痛 2024-07-20 19:54:55

这对你有用吗?

public static class Parallel
{
    public static void ForEach<T>(IEnumerable<T>[] sources,
                                  Action<T> action)
    {
        foreach (var enumerable in sources)
        {
            ThreadPool.QueueUserWorkItem(source => {
                foreach (var item in (IEnumerable<T>)source)
                    action(item);
            }, enumerable);
        }
    }
}

// sample usage:
static void Main()
{
    string[] s1 = { "1", "2", "3" };
    string[] s2 = { "4", "5", "6" };
    IEnumerable<string>[] sources = { s1, s2 };
    Parallel.ForEach(sources, s => Console.WriteLine(s));
    Thread.Sleep(0); // allow background threads to work
}

对于 C# 2.0,您需要将上面的 lambda 表达式转换为委托。

注意:此实用程序方法使用后台线程。 您可能想要修改它以使用前台线程,并且可能需要等到所有线程完成。 如果您这样做,我建议您创建 sources.Length - 1 线程,并将当前执行线程用于最后一个(或第一个)源。

(我希望我可以在代码中包含等待线程完成的内容,但很抱歉我还不知道该怎么做。我想你应该使用 a WaitHandle< /del> Thread.Join()。)

Would this work for you?

public static class Parallel
{
    public static void ForEach<T>(IEnumerable<T>[] sources,
                                  Action<T> action)
    {
        foreach (var enumerable in sources)
        {
            ThreadPool.QueueUserWorkItem(source => {
                foreach (var item in (IEnumerable<T>)source)
                    action(item);
            }, enumerable);
        }
    }
}

// sample usage:
static void Main()
{
    string[] s1 = { "1", "2", "3" };
    string[] s2 = { "4", "5", "6" };
    IEnumerable<string>[] sources = { s1, s2 };
    Parallel.ForEach(sources, s => Console.WriteLine(s));
    Thread.Sleep(0); // allow background threads to work
}

For C# 2.0, you need to convert the lambda expressions above to delegates.

Note: This utility method uses background threads. You may want to modify it to use foreground threads, and probably you'll want to wait till all threads finish. If you do that, I suggest you create sources.Length - 1 threads, and use the current executing thread for the last (or first) source.

(I wish I could include waiting for threads to finish in my code, but I'm sorry that I don't know how to do that yet. I guess you should use a WaitHandle Thread.Join().)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文