为什么IQueryable.All()在空集合上返回true?

时间:2010-02-03 20:41:50

标签: .net linq logic

所以我遇到了今天的情况,其中一些生产代码正在失败,因为一个方法完全像documented in MSDN那样执行。因为没有阅读文档而感到羞耻。但是,我仍然在为为什么它的行为方式而烦恼,即使是“按设计”,因为这种行为正好与我预期的那样(以及其他已知行为)和因此似乎违反了最不惊讶的原则。

All()方法允许您提供谓词(例如lambda表达式)来测试IQueryable,返回一个布尔值,指示所有集合成员是否与测试匹配。到现在为止还挺好。这就是它变得奇怪的地方。如果集合为空,All()也会返回true。由于以下原因,这似乎完全落后于我:

  • 如果集合为空,那么这样的测试充其量是未定义的。如果我的车道是空的,我不能断言停在那里的所有车都是红色的。有了这种行为,在空车道上停放的所有车辆都是红色和蓝色的棋盘格 - 所有这些表达式都会返回true。
  • 对于熟悉SQL概念的人来说,NULL!= NULL,这是意外行为。
  • Any()方法的行为符合预期,并且(正确)返回false,因为它没有任何与谓词匹配的成员。

所以我的问题是,为什么All()表现得这样?它解决了什么问题?这是否违反了最不意外的原则?

我将此问题标记为.NET 3.5,但该行为也适用于.NET 4.0。

编辑好的,所以我掌握了这方面的逻辑方面,正如杰森和其他人所做的那样出色。不可否认,空集合是一种边缘情况。我想我的问题根源于斗争,只是因为某些东西是逻辑并不意味着如果你没有处于正确的思维框架中它必然会使有意义

12 个答案:

答案 0 :(得分:42)

  

如果我的车道是空的,我不能断言停在那里的所有车都是红色的。

请考虑以下陈述。

  

S1:我的车道是空的。

     

S2:停在车道上的所有车都是红色的。

我声称S1暗示S2。也就是说,声明S1 => S2是真的。我将通过表明其否定是错误来做到这一点。在这种情况下,S1 => S2的否定是S1 ^ ~S2;这是因为S1 => S2仅在S1为真且S2为假时才为假。 S2的否定是什么?它是

  

~S2:车道上停着一辆不是红色的车。

S1 ^ ~S2的真值是多少?我们把它写出来

  

S1 ^ ~S2:我的车道是空的,车道上停着一辆不是红色的车。

如果S1 ^ ~S2S1都为真,则~S2可以成立的唯一方法。但是S1说我的车道是空的,S2说我的车道上有一辆车。我的车道不能都是空的并且包含一辆车。因此,S1~S2都不可能成立。因此,S1 ^ ~S2为假,因此其否定S1 => S2为真。

因此,如果您的车道是空的,您可以断言停在那里的所有车辆都是红色的。

现在让我们考虑IEnumerable<T> elementsPredicate<T> p。我们假设elements是空的。我们希望发现

的价值
bool b = elements.All(x => p(x));

让我们考虑一下否定

bool notb = elements.Any(x => !p(x));

要使notb成立,x中必须至少有一个elements !p(x)为真。但elements为空,因此无法找到x!p(x)的{​​{1}}。因此notb不可能是真的,所以它必须是假的。由于notb是假的,它的否定是正确的。因此b为真,如果elements.All(x => p(x))为空,则elements必须为真。

这是另一种思考方式。如果p中的所有 x您找不到任何的,则谓词elements为真。但是如果elements中没有项目,那么找不到任何是错误的。因此,对于空集elementsp对于x

中的所有 elements都是如此

现在,如果elements.Any(x => p(x))为空elementsIEnumerable<T>p,那么Predicate<T>如何?我们已经知道结果将是错误的,因为我们知道它的否定是正确的,但无论如何我们都要通过它来推理它。直觉很有价值。要使elements.Any(x => p(x))为真,x中必须至少有一个elements p(x)为真。但如果x中没有任何 elements,就无法找到{em>任何 x p(x)是真的。因此,如果elements.Any(x => p(x))为空,则elements为false。

最后,当s.StartsWith(String.Empty)s的非空实例时,string为{{1}}为真,这是related explanation

答案 1 :(得分:7)

如果返回true的项目数与所有项目的数量相同,则返回true。很简单:

Driveway.Cars(a => a.Red).Count() == Driveway.Cars.Count()

相关说明:Why does "abcd".StartsWith("") return true?

答案 2 :(得分:4)

  

“如果集合为空,则进行测试   像这样,充其量是未定义的。如果   我的车道是空的,我无法断言   停在那里的所有车都是红色的。“

是的,你可以。

为了证明我的错,请在空车道上给我看一辆不是红色的车。

  

对于熟悉SQL概念的人来说,NULL!= NULL,这是意外行为。

这是SQL的一个怪癖(并不完全正确:NULL = NULLNULL <> NULL都是未定义的,并且都不匹配任何行。)

答案 3 :(得分:3)

我认为这是有道理的。在逻辑上,FOR ALL的补充不是(存在)。 FOR ALL就像All()。 EXERE就像Any()

因此IQueryable.All()相当于!IQueryable.Any()。如果您的IQueryable为空,则两者都会根据MSDN文档返回true。

答案 4 :(得分:3)

Any()All()只是通常的数学运算符∃(“存在的量子”或“存在”)和∀(“通用量子”或“为所有人”)的实现。

“任何”表示存在谓词为真的某个项目。对于空集合,这将是错误的。

“全部”表示不存在谓词为假的任何项目。对于空集合,这总是如此。

答案 5 :(得分:1)

返回true也是合乎逻辑的。你有两个陈述:“有车?”“它是红色的吗?”如果第一个陈述是false,那么无关紧要第二个陈述是,结果是true modus ponens

答案 6 :(得分:1)

你会在数学或计算机科学的其他领域经常发现这种行为。

如果范围无效(SUM从0到-1),Math中的SUM运算符将返回0(+的中性元素)。 MULTIPYL运算符将返回1(乘法的中性元素)。

现在,如果你有布尔表达式,它就非常相似:OR的中性元素是falsea OR false = a),而AND的中性元素是true

现在Linq的ANYALL:他们与此类似:

ANY = a OR b OR c OR d ...
ALL = a AND b AND c AND d ...

因此,如果您有数学/ cs背景,这种行为正是“您所期望的”。

答案 7 :(得分:1)

因为对空集的任何命题都是vacuous truth

答案 8 :(得分:1)

现在已经说了一切,请不要破坏语义并创建新的扩展方法:

  public static Boolean AllOrFalseIfEmpty<T>(this IEnumerable<T> source, Func<T, Boolean> predicate) {
     return source.Any() && source.All(predicate);
  }

答案 9 :(得分:0)

False意味着即使没有谓词,查询也不会返回任何结果。这只是一个不完整的方式来说明Dested在我输入时发布的内容。

答案 10 :(得分:0)

这与数字零的基本概念非常相似。即使它代表缺席的存在,它仍然拥有并代表一种价值。 IQueryable.All()应返回true,因为它将成功返回集合的所有成员。恰好如果集合为空,该函数将不会返回任何成员,但不会因为函数无法返回任何成员。这只是因为没有成员返回。话虽这么说,为什么IQueryable.All()由于缺乏集合的支持而必须经历失败?它愿意,它能够......它有能力。听起来像收藏品无法阻止他们讨价还价的结束......

http://mathforum.org/dr.math/faq/faq.divideby0.html

答案 11 :(得分:0)

All(x => x.Predicate)Any(x => !x.Predicate)相反(&#34;所有车辆都是红色的?&#34;与#34相反;是否有任何车辆都没有红色?&#34;。)

Any(x => !x.Predicate)会为空集合返回false(这对于#34;任何&#34;的共同理解显得很自然。)

因此All(x => x.Predicate)应该(并且确实)为空集合返回true