15

テキスト ファイルの解析中に 2 つの配列を作成しました。1 番目には列名が含まれ、2 番目には現在の行の値が含まれます。マップを作成するには、両方のリストを一度に反復処理する必要があります。現在、私は次のものを持っています:

var currentValues = currentRow.Split(separatorChar);
var valueEnumerator = currentValues.GetEnumerator();

foreach (String column in columnList)
{
    valueEnumerator.MoveNext();
    valueMap.Add(column, (String)valueEnumerator.Current);
}

これは問題なく機能しますが、私のエレガントな感覚を完全には満足させません。また、配列の数が 2 つよりも多い場合 (私は時々そうしなければなりません)、非常に面倒になります。誰かが別の簡潔なイディオムを持っていますか?

4

6 に答える 6

23

最初のコードに明らかではない疑似バグがあります -IEnumerator<T>拡張IDisposableするので、それを破棄する必要があります。これは、イテレータ ブロックでは非常に重要です。配列では問題ありませんが、他のIEnumerable<T>実装では問題になります。

私は次のようにします:

public static IEnumerable<TResult> PairUp<TFirst,TSecond,TResult>
    (this IEnumerable<TFirst> source, IEnumerable<TSecond> secondSequence,
     Func<TFirst,TSecond,TResult> projection)
{
    using (IEnumerator<TSecond> secondIter = secondSequence.GetEnumerator())
    {
        foreach (TFirst first in source)
        {
            if (!secondIter.MoveNext())
            {
                throw new ArgumentException
                    ("First sequence longer than second");
            }
            yield return projection(first, secondIter.Current);
        }
        if (secondIter.MoveNext())
        {
            throw new ArgumentException
                ("Second sequence longer than first");
        }
    }        
}

その後、必要なときにいつでもこれを再利用できます。

foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar),
             (column, value) => new { column, value })
{
    // Do something
}

または、ジェネリック ペア型を作成し、ペアアップ メソッドで射影パラメーターを取り除くこともできます。

編集:

Pair 型の場合、呼び出しコードは次のようになります。

foreach (var pair in columnList.PairUp(currentRow.Split(separatorChar))
{
    // column = pair.First, value = pair.Second
}

それはあなたが得ることができるのと同じくらい簡単に見えます。はい、再利用可能なコードとして、ユーティリティ メソッドをどこかに配置する必要があります。私の見解ではほとんど問題はありません。複数の配列の場合...

配列が異なるタイプの場合、問題が発生します。ジェネリック メソッド/型宣言で任意の数の型パラメーターを表現することはできません。最大 4 つのデリゲート パラメーターのデリゲートがあるように、必要な数の型パラメーターに対して PairUp のバージョンを作成できますが、可能Actionです。Func恣意的にしないでください。

ただし、値がすべて同じ型であり、配列にこだわる場合は簡単です。(非配列も問題ありませんが、事前に長さのチェックを行うことはできません。) 次のようにすることができます。

public static IEnumerable<T[]> Zip<T>(params T[][] sources)
{
    // (Insert error checking code here for null or empty sources parameter)

    int length = sources[0].Length;
    if (!sources.All(array => array.Length == length))
    {
        throw new ArgumentException("Arrays must all be of the same length");
    }

    for (int i=0; i < length; i++)
    {
        // Could do this bit with LINQ if you wanted
        T[] result = new T[sources.Length];
        for (int j=0; j < result.Length; j++)
        {
             result[j] = sources[j][i];
        }
        yield return result;
    }
}

次に、呼び出しコードは次のようになります。

foreach (var array in Zip(columns, row, whatevers))
{
    // column = array[0]
    // value = array[1]
    // whatever = array[2]
}

もちろん、これには一定量のコピーが必要です-毎回配列を作成しています。次のような別のタイプを導入することで、これを変更できます。

public struct Snapshot<T>
{
    readonly T[][] sources;
    readonly int index;

    public Snapshot(T[][] sources, int index)
    {
        this.sources = sources;
        this.index = index;
    }

    public T this[int element]
    {
        return sources[element][index];
    }
}

これはおそらくほとんどの人にとってやり過ぎと見なされるでしょう;)

正直に言うと、あらゆる種類のアイデアを思いつき続けることができます...しかし、基本は次のとおりです。

  • 少しの再利用可能な作業で、呼び出しコードをより良くすることができます
  • タイプの任意の組み合わせの場合、ジェネリックの動作方法により、パラメーターの数 (2、3、4...) ごとに個別に実行する必要があります。
  • 各パーツに同じタイプを使用することに満足している場合は、より良いことができます
于 2009-01-30T19:01:29.187 に答える
17

各行の要素と同じ数の列名がある場合、for ループを使用できませんか?

var currentValues = currentRow.Split(separatorChar);

for(var i=0;i<columnList.Length;i++){
   // use i to index both (or all) arrays and build your map
}
于 2009-01-30T18:56:00.197 に答える
3

配列を実際に使用している場合、おそらく最善の方法はfor、インデックスを使用して従来のループを使用することです。確かにそれほど良くはありませんが、私の知る限り、.NET はこれを行うためのより良い方法を提供していません。

と呼ばれるメソッドにコードをカプセル化することもできます。zipこれは一般的な高次リスト関数です。ただし、C# には適切な Tuple 型がなく、これは非常に厄介です。あなたIEnumerable<KeyValuePair<T1, T2>>はあまり良くない を返すことになります。

ところで、実際IEnumerableに代わりに使用していますか、IEnumerable<T>またはなぜCurrent値をキャストしますか?

于 2009-01-30T18:55:39.167 に答える
2

2 つの別個の配列を作成する代わりに、2 次元配列または辞書を作成することができます (こちらの方がよいでしょう)。しかし、実際には、それが機能する場合、私はそれを変更しようとはしません。

于 2009-01-30T18:56:24.080 に答える