2011-12-01 1 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://en.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

nextValue와 startValue를 비교할 수 있습니다 (IComparable을 구현하려면 T가 필요합니다).

이렇게하면이 버그를 해결할 수 있습니다. 즉 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를 요구하는 것도 합리적입니다. –

관련 문제