【问题标题】:Why does IQueryable.All() return true on an empty collection?为什么 IQueryable.All() 在空集合上返回 true?
【发布时间】:2011-01-12 19:26:22
【问题描述】:

所以我今天遇到了一种情况,其中一些生产代码恰好因为一个方法完全按照documented in MSDN 执行而失败。为不阅读文档而感到羞耻。然而,我仍然在摸不着头脑为什么它的行为方式,即使是“设计”,因为这种行为与我的预期完全相反(以及其他已知行为)并且因此似乎违反了最小意外原则。

All() 方法允许您提供谓词(例如 lambda 表达式)来测试 IQueryable,返回一个布尔值,指示是否所有集合成员都匹配测试。到目前为止,一切都很好。这就是它变得奇怪的地方。如果集合为空,All() 也会返回 true。这对我来说似乎完全倒退了,原因如下:

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

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

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

编辑好的,所以我掌握了这一点的逻辑方面,正如 Jason 和你们其他人所阐述的那样。诚然,空集合是一种极端情况。我想我的问题源于这样的斗争,仅仅因为某些东西是逻辑并不意味着如果你的心态不正确,它就一定是有意义

【问题讨论】:

  • False 表示即使没有谓词,查询也没有返回任何结果。这只是说明我在输入此内容时 Dested 发布的内容的不完整方式。
  • 我正在从您的帖子中删除 [subjective] 标签;这不是一个主观问题。这些是逻辑规则,它们非常合乎逻辑。一旦你对这些想法进行了足够的练习,它们就会变得直观。
  • “它们变得直观...” 是的,我在大学学习逻辑表等... 15 年前。 :)
  • 我的想法是关于它是否具有“实用”意义的意见分歧取决于你在练习什么。如果您关心“所有”的意思是“没有相反的实例”,那么硬逻辑定义非常适合您。但是对于许多问题,我们可能正在寻找“没有相反的实例,并且至少有一个我想要的场景的实例”。这实际上是两个声明,所以这将是 Linq 中的两个不同条件。计数(条件)> 0 和全部(条件)。我确实很好奇是否有一种聪明的方法可以做到这一点。

标签: .net linq logic


【解决方案1】:

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

考虑以下陈述。

S1: 我的车道是空的。

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

我声称S1 暗示S2。也就是说,声明S1 => S2 是正确的。我将通过证明它的否定是错误的来做到这一点。在这种情况下,S1 => S2 的否定是S1 ^ ~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> elementsPredicate<T> p。让我们假设elements 是空的。我们希望发现

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

让我们考虑它的否定

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

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

这是另一种思考方式。谓词p 为真,如果对于elements 中的all x,您找不到它为假的any。但是如果elements 中没有项目,那么就不可能找到它为假的任何。因此,对于空集合elementspall x in elements

为 true

现在,当elements 是一个空的IEnumerable<T> 并且p 是一个Predicate<T> 时,elements.Any(x => p(x)) 呢?我们已经知道结果将是错误的,因为我们知道它的否定是正确的,但是无论如何让我们推理一下;直觉是有价值的。要使elements.Any(x => p(x)) 为真,elements 中必须至少有一个x,其中p(x) 为真。但是如果elements 中没有any x,则不可能找到p(x) 为真的any x。因此,如果elements 为空,则elements.Any(x => p(x)) 为假。

最后,这里有一个related explanation,说明为什么sstring 的非空实例时s.StartsWith(String.Empty) 为真:

【讨论】:

  • 计算机科学和逻辑依赖于语义,实际上意义不同。
  • 问题是一个错误的陈述不是事实,因为你无法证明它是错误的。图表 1:Flying Spaghetti Monster。它最多是不确定的。说“我车道上的所有汽车都是红色的”在语义上是不正确的,因为这意味着您的车道上至少有一辆红色汽车。所以实现的行为确实符合“全部”这个词的含义。使用文字来命名方法的全部目的是为它的读者赋予意义。此方法的名称似是而非,最好命名为Method1
  • 如果我所有的孩子都有红头发,那么我就会得到加薪。我没有孩子。酷,我得到了加薪:-D
  • 我从逻辑的角度理解他们为什么这样做,但它可能会产生相当不直观的副作用。
  • 如果 IEnumerable 为空,All 应该抛出异常,或者应该调用 'AllOrEmpty',或者当为空时返回 false 并调用 'AllAndNotEmpty'
【解决方案2】:

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

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

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

【讨论】:

    【解决方案3】:

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

    是的,你可以。

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

    对于熟悉 NULL != NULL 的 SQL 概念的任何人来说,这是出乎意料的行为。

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

    【讨论】:

    • 所以给我看一辆在我空荡荡的车道上不是红色的车,那是红色的。
    • 两种说法都是正确的。 (1) 您的车道上没有红色的汽车,并且 (2) 您的车道上没有红色的汽车。没有矛盾。
    • 我认为这就是我要解决的问题 - 与整个 All => !(!Any) 问题分开,从没有元素的角度来看,测试可以是真假not 匹配,但也没有 does 匹配的元素。
    【解决方案4】:

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

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

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

    【讨论】:

      【解决方案5】:

      我认为这是有道理的。在逻辑上, FOR ALL 的补码是 NOT (THERE EXIST)。 FOR ALL 就像All()。 THE EXIST 就像Any()

      所以IQueryable.All() 等价于!IQueryable.Any()。如果您的IQueryable 为空,则根据 MSDN 文档两者都返回 true。

      【讨论】:

      • IQueryable.All() 不等同于 !IQueryable.Any()。您还需要否定谓词。
      【解决方案6】:

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

      【讨论】:

        【解决方案7】:

        All(x => x.Predicate)Any(x => !x.Predicate) 的反义词(“所有汽车都是红色的吗?”是“有没有不是红色的汽车?”的反义词)。

        Any(x => !x.Predicate) 为空集合返回 false(这对于“任何”的普遍理解来说是很自然的)。

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

        【讨论】:

          【解决方案8】:

          既然都说完了,不要破坏语义,创建一个新的扩展方法:

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

          【讨论】:

            【解决方案9】:

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

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

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

            现在在 Linq 的 ANYALL 上:它们与此类似:

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

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

            【讨论】:

              【解决方案10】:

              返回true 也是合乎逻辑的。您有两个语句:“有车吗?”“它是红色的吗?” 如果第一个语句是 false,那么第二个语句是什么都没关系也就是说,结果是true by modus ponens

              【讨论】:

                【解决方案11】:

                它与数字零的基本概念非常相似。尽管它代表了缺席的存在,但它仍然具有并代表了一种价值。 IQueryable.All() 应该返回 true,因为它将成功返回集合的所有成员。碰巧的是,如果集合为空,该函数将不会返回任何成员,但不是因为函数无法返回任何成员。只是因为没有成员可以返回。话虽如此,为什么 IQueryable.All() 必须由于缺乏集合的支持而经历失败?它愿意,它有能力……它有能力。在我看来,这个系列无法抵挡他们的讨价还价......

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-03-12
                  • 2020-04-12
                  • 2016-03-05
                  • 2015-07-25
                  • 2014-09-25
                  • 2020-06-19
                  相关资源
                  最近更新 更多