2016-08-11 6 views
35

Rozważmy następujący linq przykład z pustej tablicy:C# LINQ All & Wszelkie pracuje inaczej na pustej tablicy

Kiedy Any() powraca false jak nie ma liczbę większą od zera, jak można All() powrót true przenoszącym wszystkie liczby większe od zera?

var arr = new int[] { }; 
Console.WriteLine(arr.Any(n => n > 0)); //false 
Console.WriteLine(arr.All(n => n > 0)); //true 
+4

Wydaje mi się logiczne. _Wszystkie liczby w 'arr' są większe od zera (co oznacza, że ​​jest _no_ liczba _nie większa niż zero), a liczba _no_ w' arr' jest większa od zera. –

+4

W obu dokumentach wyraźnie stwierdzono, że są to oczekiwane wyniki dla pustych sekwencji. – juharr

+1

@juharr Wydaje mi się, że pytanie dotyczy raczej tego, dlaczego tak zostało zaimplementowane (co oczywiście można wyjaśnić logiką boolowską). –

Odpowiedz

58

Wydaje mi się logiczne.

  • All: Czy wszystkie numery w arr większa od zera (czyli nie ma nie liczba nie większa niż zero) =>true
  • Any: Czy istnieje dowolny numer w arr że jest większe niż zero =>false

Ale ważniejsze, według Boolean Algebra:

arr.All(n => n > 0); 

daje true, ponieważ powinien być logiczne naprzeciwko z

arr.Any(n => !(n > 0)); 

co daje false (właściwie to co powyższe dwa punkty powiedzieć) .

+1

Implementacja poziomu kodu rozumiem, ale logicznie nie miało to dla mnie sensu. Twój powyższy punkt wyczyścił to! dzięki –

+4

Bardzo podoba mi się ta odpowiedź za wskazanie logicznej relacji między "Wszystkim" i "Dowolnym" (tj., że spełniają [prawa De Morgana] (https://en.wikipedia.org/wiki/De_Morgan%27s_laws)). – Kyle

+1

Kolejny przykład, prawdopodobnie prostszy i bardziej codzienny: wyobraź sobie, że jest pusty pokój. Ok, z "wszystkim" pytanie byłoby jak "wszyscy ludzie w pokoju to kobiety?" a odpowiedź brzmi: tak, ponieważ nie ma nikogo, więc "wszystkie" z nich to kobiety. W przypadku "jakiegokolwiek" pojawia się pytanie "czy w pokoju jest co najmniej jedna kobieta?", Na co oczywiście nie ma odpowiedzi. Innym sposobem, aby to ująć: przy "wszystkich" liczba kobiet powinna być taka sama jak całkowita liczba osób (0 == 0 jest nadal poprawna), podczas gdy "dowolna" liczba kobiet powinna wynosić> 1. – njy

5

The implementation dla All Zwraca true jeśli żaden element nie znajduje się na liście:

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { 
    if (source == null) throw Error.ArgumentNull("source"); 
    if (predicate == null) throw Error.ArgumentNull("predicate"); 
    foreach (TSource element in source) { 
     if (!predicate(element)) return false; 
    } 
    return true; // watch this 
} 

To wydaje się dość intuicyjne, ale that's jak to jest realizowane.

Jednakże dokumentacja jest jasne dla powrotnej wartości All:

prawdziwe, jeśli każdy element sekwencji źródłowej przechodzi test w określonym orzecznikiem lub jeśli sekwencja jest pusty;

24

Implementacja All pokazuje bardzo wyraźnie, dlaczego.

public static bool All<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { 
     if (source == null) throw Error.ArgumentNull("source"); 
     if (predicate == null) throw Error.ArgumentNull("predicate"); 
     foreach (TSource element in source) { 
      if (!predicate(element)) return false; 
     } 
     return true; 
    } 

To prowadzi foreach nad kolekcji. Jeśli w kolekcji nie ma elementów, pominie się wartość foreach i zwróci wartość true.

Co ciekawe, realizacja na Any

public static bool Any<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate) { 
     if (source == null) throw Error.ArgumentNull("source"); 
     if (predicate == null) throw Error.ArgumentNull("predicate"); 
     foreach (TSource element in source) { 
      if (predicate(element)) return true; 
     } 
     return false; 
    } 

Cleary ten pokazuje, że są przeciwieństwami.

3

Trochę perspektywy matematycznej: Any i All są uogólnione wersje || i && operatorów, tak jak Sum i Product (nie w LINQ) są uogólnienia + i *.

Operatory uogólnione podczas pracy na pustym zestawie zwracają neutral element operacji. Dla + jest 0, za * jest 1, więc emptyArray.Product() == 1 ponieważ jeden neutralny element * obsługi (wszystko: a * 1 == a), na || jest false (a || false == a) oraz && jest true (a || true == a).

Dzięki temu uogólnionym operatorom zachowuje się asocjatywność "oryginalnej" operacji, np. dla Sum: intersect(A,B) == EmptySet; S = union(A,B); S.Sum() == A.Sum() + B.Sum(), a to zadziała nawet wtedy, gdy jeden z zestawów A lub B jest pusty. Innymi słowy, matematycznie wygodne jest zdefiniowanie, że uogólniony operator na pustym zestawie zwraca element neutralny.

Powiązane problemy