Подтвердить что ты не робот

С# Linq All & Any работает по-разному на пустом массиве

Рассмотрим следующий пример linq с пустым массивом:

Когда Any() возвращает false, поскольку числа больше нуля, как All() возвращает true, передавая все числа больше нуля?

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

Ответ 1

Кажется логичным для меня.

  • All: все числа в arr больше нуля (это означает, что число не больше нуля) = > true
  • Any: Есть ли число в arr, которое больше нуля = > false

Но более важно, согласно Булевая алгебра:

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

дает true, потому что он должен быть логически противоположным

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

который дает false (на самом деле это то, что говорят выше).

Ответ 2

Реализация All очень четко показывает, почему.

    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;
    }

Он выполняет foreach по коллекции. Если в коллекции нет элементов, он пропустит foreach и вернет true.

Интересно, что реализация на 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;
    }

Это ясно показывает, что они противоположности.

Ответ 3

Реализация для All возвращает true, если в списке нет элемента:

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
}

Это кажется довольно противоречивым, но вот как это реализовано.

Тем не менее документы достаточно понятны для возвращаемого значения All:

true, если каждый элемент исходной последовательности проходит тест в указанный предикат, или если последовательность пуста;

Ответ 4

Немного математической перспективы: Any и All являются обобщенными версиями операторов || и &&, так же как Sum и Product (не в LINQ) являются обобщениями + и *.

Обобщенные операторы при работе с пустым набором возвращают нейтральный элемент операции. Для + это 0, для * это 1 так emptyArray.Product() == 1, потому что 1 является нейтральным элементом операции * (для всех a: a * 1 == a), для || это false (a || false == a), а для && это true (a || true == a).

Благодаря этому обобщенные операторы сохраняют ассоциативность "исходной" операции, например. для суммы: intersect(A,B) == EmptySet; S = union(A,B); S.Sum() == A.Sum() + B.Sum(), и это будет работать, даже если одно из наборов A или B пусто. Другими словами, математически удобно определить, что обобщенный оператор на пустом множестве возвращает нейтральный элемент.