13

「条件付きZip」を実行できるC#の関数はすでにありますか?

つまり

異なる長さの入力を許可し、小さいソースの列挙子をいつインクリメントするかを決定する述語を取り、大きいソースのすべての要素が表示されるようにする関数はありますか?

不自然な例として、素数の列挙可能オブジェクトと整数の列挙可能オブジェクト(両方とも昇順でソート)があると仮定します。前の素数以降の素数とすべての整数を保持する新しい列挙型を作成したいと思います。

{2, 3, 5, 7, 11}

{1, 2, 3, 4, 5, 6, 7, 8, 9, 10,}

{2, [1]}, {3,[]}, {5, [4]}, {7, [6]}, {11, [8,9,10]}
4

3 に答える 3

5

私の解決策:

public static IEnumerable<Tuple<T1, IEnumerable<T2>>> ConditionalZip<T1, T2>(
    this IEnumerable<T1> src1,
    IEnumerable<T2> src2,
    Func<T1, T2, bool> check)
{
    var list = new List<T2>();
    using(var enumerator = src2.GetEnumerator())
    {
        foreach(var item1 in src1)
        {
            while(enumerator.MoveNext())
            {
                var pickedItem = enumerator.Current;
                if(check(item1, pickedItem))
                {
                    list.Add(pickedItem);
                }
                else
                {
                    break;
                }
            }
            var items = list.ToArray();
            list.Clear();
            yield return new Tuple<T1, IEnumerable<T2>>(item1, items);
        }
    }
}

両方の列挙が 1 回だけ列挙されることが保証されます。

使用法:

var src1 = new int[] { 2, 3, 5, 7, 11 };
var src2 = Enumerable.Range(1, 11);
Func<int, int, bool> predicate = (i1, i2) => i1 > i2;
var result = src1.ConditionalZip(src2, predicate);
于 2012-09-19T13:32:43.863 に答える
4

それはいいものです。既製の関数を .net 内で直接見つけることはできないと思いますが、必要な操作が数学の標準アクションである場合、それを行うライブラリがどこかにあると確信しています。ただし、自分でやりたい場合は、group by を使用できます。この特定のシナリオでは:

var primes = new List<int> {2, 3, 5, 7, 11};
var numbers = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

var groups =
    from number in numbers
    group number by primes.First(prime => prime >= number) into gnumber
    select new {
        prime = gnumber.Key,
        numbers = gnumber.Where(n => n != gnumber.Key)
    };

それは十分に単純な解決策のようです。2 つのメンバーを持つ匿名型の列挙型を作成します。それを辞書に変換できます。

var dict = groups.ToDictionary(g => g.prime, g=> g.numbers);

編集:これが機能するには素数を注文する必要があります。

于 2012-09-19T13:17:39.313 に答える
0

これは私が行ったものです(醜い実装)が、列挙可能なものを一度だけ列挙します。

/// <summary>
    /// Merges two sequences by using the specified predicate function to determine when to iterate the second enumerbale.
    /// </summary>
    /// 
    /// <returns>
    /// An <see cref="T:System.Collections.Generic.IEnumerable`1"/> that contains merged elements of two input sequences.
    /// </returns>
    /// <param name="larger">The first sequence to merge.</param><param name="smaller">The second sequence to merge.</param>
    /// <param name="resultSelector">A function that specifies how to merge the elements from the two sequences (a flag is passed into the dunction to notify when elements of the second source are exhausted.</param>
    /// <typeparam name="TFirst">The type of the elements of the first input sequence.</typeparam>
    /// <typeparam name="TSecond">The type of the elements of the second input sequence.</typeparam>
    /// <typeparam name="TResult">The type of the elements of the result sequence.</typeparam>
    public static IEnumerable<TResult> ConditionalZip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> larger, IEnumerable<TSecond> smaller, Func<TFirst, TSecond, bool> predicate, Func<TFirst, TSecond, bool, TResult> resultSelector)
    {
        if (larger == null)
            throw new ArgumentNullException("larger");
        if (smaller == null)
            throw new ArgumentNullException("smaller");
        if (resultSelector == null)
            throw new ArgumentNullException("resultSelector");
        else
            return ConditionalZipIterator(larger, smaller, predicate, resultSelector);
    }

    private static IEnumerable<TResult> ConditionalZipIterator<TFirst, TSecond, TResult>(IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, bool> predicate, Func<TFirst, TSecond, bool, TResult> resultSelector)
    {
        using (IEnumerator<TFirst> enumerator1 = first.GetEnumerator())
        {
            using (IEnumerator<TSecond> enumerator2 = second.GetEnumerator())
            {
                if (!enumerator2.MoveNext())
                {
                    secondIsFinished = true;
                }
                currentSecond = secondIsFinished ? default(TSecond) : enumerator2.Current;

                while (enumerator1.MoveNext())
                {

                    while (!secondIsFinished && !predicate(enumerator1.Current, currentSecond))
                    {
                        if (!enumerator2.MoveNext())
                        {
                            secondIsFinished = true;
                        }
                        currentSecond = secondIsFinished ? default(TSecond) : enumerator2.Current;
                    }


                    yield return resultSelector(enumerator1.Current, currentSecond, secondIsFinished);
                }
            }
        }
    }

使い方

var primes = new int[] {2, 3, 5, 7, 11}.ThrowIfEnumeratedMoreThan(1); var ints = Enumerable.Range(1, 20).ThrowIfEnumeratedMoreThan(1);

        var results = ints.ConditionalZip(primes, (i, prime) => i <= prime, (i, prime, isEmpty) => new {i, prime, wasMatched=!isEmpty})
            .Where(x => x.wasMatched)
            .GroupBy(x => x.prime)
            .Select(x => new {Prime = x.Key, Values = x.Where(n => n.i != n.prime).Select(n=>n.i).ToArray()})
            .ToArray();
于 2012-09-19T15:01:06.873 に答える