2011-12-01 11 views
6

コレクション内の連続する値の数を見つけるための拡張メソッドを作成しました。それは一般的なので、呼び出し元に "次の"値の存在をチェックするために値をインクリメントするFunc <>である "インクリメンタ"を定義することができます。カスタム列挙子で無限再帰を避けるにはどうすればよいですか?

しかし、呼び出し元が不適切なインクリメンタ(x => x)を渡すと、無限の再帰的ループが発生します。これを防ぐためのきれいな方法に関する提案はありますか?

public static int CountConsecutive<T>(this IEnumerable<T> values, T startValue, Func<T, T> incrementor) 
{ 
    if (values == null) 
    { 
     throw new ArgumentNullException("values"); 
    } 
    if (incrementor == null) 
    { 
     throw new ArgumentNullException("incrementor"); 
    } 
    var nextValue = incrementor(startValue); 
    return values.Contains(nextValue) 
     ? values.CountConsecutive(nextValue, incrementor) + 1 
     : 1; 
} 
+2

単純な解決策は、発信者を書く人がまともであると仮定することです。時にはあなたの人をあなたのせいにしなければならないこともあります。しかし、これは興味深い質問ですので、私は他の人がテーブルにもたらすことができるものを見たいと思います。 – Polynomial

+1

[停止問題](http://ja.wikipedia.org/wiki/Halting_problem)? –

+0

IEnumerableが大きく連続している(インクリメンタが指定されている)場合、これはStackOverflowExceptionsの影響を受けます。 –

答えて

0

最も純粋な意味では、これはHalting Problemの試みであり、決めることはできません。最も単純な場合を除き、あなたのメソッドを呼び出す人を信用しなければなりません。

他の人が示したように、次の値が異なることを示す等価性の単純なチェックを行うことができます。訪問したすべての訪問者を保存するとTは機能しますが、メモリは、最終的にはと心配する必要があります。

ここでは簡単に実装されているStackOverflowExceptionがあるため、連続した値に多くのデータセットを注意する必要があります。

var x = Enumerable.Range(1, 100000).CountConsecutive(1, x => x+1); 
+0

スタックオーバーフローは再帰の深さによって引き起こされますか? –

+0

はい。この例では、 'x => x + 1'と指定すると連続していますが、これは私のマシン上では約77Kオーバーフローします。 –

1

あなたは(あなたがIComparableをを実装するためにTが必要になります)のstartValueにnextValueメソッドを比較することができます。

これはこのバグを解決し、ループを返す厄介なインクリメンタルバグを解決しません - a1、a2、a3、...、an、a1。私は最も単純なケースに対処するために

+1

IComparableを実装する必要はありません - 等価である必要があります。 –

4

かかわらず、あなたがこれを行うことができ、あなたはにこのケースを処理したいとは思わない:一般的なケースでは

var nextValue = incrementor(startValue); 
if (nextValue.Equals(startValue)) { 
    throw new ArgumentException("incrementor"); 
} 

を、次の操作を行います。

public static int CountConsecutive<T>(this IEnumerable<T> values, T startValue, Func<T, T> incrementor) { 
    if (values == null) { 
     throw new ArgumentNullException("values"); 
    } 
    if (incrementor == null) { 
     throw new ArgumentNullException("incrementor"); 
    } 
    ISet<T> seen = new HashSet<T>(); 
    return CountConsecutive(values, startValue, incrementor, seen); 
} 

private static int CountConsecutive<T>(IEnumerable<T> values, T startValue, Func<T, T> incrementor, ISet<T> seen) { 
    if (!seen.Add(startValue)) { 
     throw new ArgumentException("incrementor"); 
    } 
    var nextValue = incrementor(startValue); 
    return values.Contains(nextValue) 
     ? values.CountConsecutive(nextValue, incrementor) + 1 
     : 1; 
} 
+1

+1:インクリメンタがFunc として定義されている場合、同じ値を返すとほとんど意味をなさないでしょう。もちろん、何らかの静的な内部呼び出し回数を保持していて、1,1,2,2,3,3のようなものを返すようにしていない限り、そのようなインクリメンタをサポートする必要があるならば驚くでしょう。 incrementor(x)!= xを必要とすることは妥当と思われ、またそれが非連続的であることも必要です。 –

関連する問題