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

Gal*_*boy 48 .net linq logic

所以我遇到了今天的情况,其中一些生产代码正在失败,因为一个方法完全按照MSDN中的说明执行.因为没有阅读文档而感到羞耻.然而,我仍然在摸索为什么它会以这种方式行事,即使是"按设计",因为这种行为正好与我预期的(和其他已知行为)相反,因此似乎违反了原则最不惊讶的.

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

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

所以我的问题是,为什么All()这样做?它解决了什么问题?这是否违反了最不惊讶的原则?

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

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

jas*_*son 43

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

请考虑以下陈述.

S1:我的车道是空的.

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

我声称这S1意味着S2.也就是说,陈述S1 => S2是真的.我将通过表明其否定是错误来做到这一点.在这种情况下,否定S1 => S2S1 ^ ~S2; 这是因为S1 => S2只有当S1为真且S2为假时才是假的.什么是否定S2?它是

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

什么是真值S1 ^ ~S2?我们把它写出来吧

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

唯一的方法S1 ^ ~S2可以是真实的是,如果这两个S1~S2是真实的.但是S1说我的车道是空的,并且S2说我的车道上有一辆车.我的车道不能都是空的并且包含一辆车.因此,它是不可能S1~S2双方是真实的.因此,S1 ^ ~S2是假的,所以它的否定S1 => S2是正确的.

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

所以现在让我们考虑一个IEnumerable<T> elements和一个Predicate<T> p.让我们假设这elements是空的.我们希望发现它的价值

bool b = elements.All(x => p(x));
Run Code Online (Sandbox Code Playgroud)

让我们考虑一下它的否定

bool notb = elements.Any(x => !p(x));
Run Code Online (Sandbox Code Playgroud)

对于notb是真实的,必须有至少一个xelements为这!p(x)是真的.但是elements是空的,所以它是不可能找到一个x为这!p(x)是真的.因此notb不可能是真的,所以一定是假的.既然notb是假的,那么它的否定就是真的.因此b是真的,elements.All(x => p(x))如果elements是空的则必须为真.

这是另一种思考方式.谓词p如果是真实的一切 xelements你找不到任何为它是假的.但如果当时没有任何物品,elements则无法找到任何错误的物品.因此,对于一个空的集合elements,p是真正为所有 xelements

现在,约elements.Any(x => p(x))elements是一个空IEnumerable<T>并且pPredicate<T>如上?我们已经知道结果将是错误的,因为我们知道它的否定是正确的,但无论如何我们都要通过它来推理它.直觉很有价值.对于elements.Any(x => p(x))是真实的,必须有至少一个xelements为这p(x)是真的.但是,如果没有任何 xelements它是不可能找到任何 x针对这p(x)是真的.因此,elements.Any(x => p(x))如果elements是空的则为假.

最后,这里有一个相关的解释,为什么s.StartsWith(String.Empty)s一个非null实例时为真string:

  • 如果 IEnumerable 为空,则 All 应抛出异常,或者应将其称为“AllOrEmpty”,或者在为空时返回 false 并称为“AllAndNotEmpty” (3认同)
  • 计算机科学和逻辑取决于语义,实际上使意义有所不同。 (2认同)
  • 问题是错误的陈述不是事实,仅仅是因为您无法证明它是错误的。展览1:[意大利面怪兽飞翔](https://en.wikipedia.org/wiki/Flying_Spaghetti_Monster)。它最多是不确定的。说“我的车道上的所有汽车都是红色的”在语义上是错误的,因为这意味着您的车道上至少有一辆红色的汽车。因此,所实现的行为确实符合单词“ All”的含义。使用单词命名方法的全部目的是赋予其含义给读者。该方法的名称是似是而非的,最好命名为“ Method1”。 (2认同)

Des*_*ted 10

如果返回的项目true数与所有项目的数量相同,则返回true.就那么简单:

Driveway.Cars(a => a.Red).Count() == Driveway.Cars.Count()
Run Code Online (Sandbox Code Playgroud)

相关解释:为什么"abcd".StartsWith("")返回true?


Jef*_*dge 6

Any()All()只是常用数学运算符 \xe2\x88\x83 (“存在限定符”或“存在”)和 \xe2\x88\x80 (“通用限定符”或“对于所有人”)的实现。

\n\n

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

\n\n

“全部”意味着不存在任何谓词为假的项目。对于空集合来说,这始终是正确的。

\n


fin*_*nnw 5

"如果收集是空的,那么这样的测试充其量是不确定的.如果我的车道是空的,我不能断言停在那里的所有车都是红色的."

是的你可以.

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

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

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

  • 这两种说法都是对的.(1)你的车道上没有车是红色的;(2)你的车道上没有车不是**红色.那里没有矛盾. (3认同)