Stream.allMatch()が空のストリームに対してtrueを返すのはなぜですか?


84

同僚と私には、空のストリーム呼び出しallMatch()が返されるという想定に起因するバグがありましたfalse

if (myItems.allMatch(i -> i.isValid()) { 
    //do something
}

もちろん、ドキュメントを想定して読んでいないのは私たちのせいです。しかし、私が理解していないのはallMatch()、空のストリームのデフォルトの動作がなぜ戻るのかということですtrue。これの理由は何でしたか?同様にanyMatch()(逆に偽を返す)、この操作はモナドを出発不可欠な方法で使用され、おそらくで使用if声明。これらの事実を考慮すると、ほとんどの用途でallMatch()デフォルトでtrue空のストリームを使用することが望ましい理由はありますか?


4
それは少し奇妙です。allMatchtrueを返す場合は、そうする必要があると予想されますanyMatch。さらに、空の場合についても、allMatch(...) == noneMatch(...)これも奇妙です。
radiodef 2015年

6
ウィキペディアはそれが慣習であると言う:en.wikipedia.org/wiki/Universal_quantification#The_empty_set
アレックス- GlassEditor.com

構文について簡単に説明します。述語をとしてi -> i.isValid()記述する代わりに、次のように書くことができますFoo::isValidFooもちろん、ストリーミングしているクラスはどこにありますか)
MattPutnam 2015年

2
「この操作は、モナドを離れる命令的な方法で使用されます」-私はこの要因が決定に影響することを疑っています。
user253751 2015年

回答:


115

これは空虚な真として知られています。空のコレクションのすべてのメンバーは、あなたの条件を満たす。結局のところ、そうでないものを指すことができますか?

同様に、条件に一致するコレクションの要素が見つからないため、をanyMatch返しますfalse。これは多くの人を混乱させますが、空のセットに対して「任意」と「すべて」を定義するための最も便利で一貫した方法であることがわかります。


3
ねえ、私はブール論理が嫌いです。私はあなたが何を言っているのか理解できたと思います。ネガティブがないことはポジティブですが、ポジティブがないことはネガティブではありません。
tmn 2015年

13
@ThomasN。同様に、空の数値セットの積の値は、空の数値セットの1合計は0です。それらは乗算/加算の中立的な要素です。ブール値の場合、あなたはそれを持っているTrue and x = xFalse or x = x、したがって、あなたが一般た場合andor配列に(何ということallanyされている)あなたが終わるTrueFalse、空の場合の、すなわち、彼らのそれぞれの中立的要素。
バクリウ2015年

9
@ThomasN。anyMatch陽性がないことをallMatchテストし、陰性がないことをテストします。
user253751 2015年

3
このようにして、ド・モルガンの法則のような素晴らしいことができることに注意してください。stream.allMatch(predicate)は!stream.anyMatch(predicate.negate())と同じです。同様に、!stream.allMatch(predicate.negate())はstream.anyMatch(predicate)と同じです。
ハンス

1
@PatrickBard:「そうするものを指すことができますか」と言うことができ、それは完全に有効ですが、それが意味するのは、コレクションのすべてのメンバーが条件を満たさないということです。コレクションのすべてのメンバーが条件を満たし、コレクションのすべてのメンバーが条件を満たしていません。これらは、「コレクションのすべてのメンバーが条件を満たすことは誤りです」とは異なるステートメントです。私が言ったように、それは混乱しています。
user2357112は、2018

10

これについて考える別の方法は次のとおりです。

allMatch()ある&&sum()にあります+

次の論理ステートメントについて考えてみます。

IntStream.of(1, 2).sum() + 3 == IntStream.of(1, 2, 3).sum()
IntStream.of(1).sum() + 2 == IntStream.of(1, 2).sum()

sum()は単なる一般化であるため、これは理にかなってい+ます。ただし、もう1つの要素を削除するとどうなりますか?

IntStream.of().sum() + 1 == IntStream.of(1).sum()

IntStream.of().sum()特定の方法で、、または空の数列の合計を定義することが理にかなっていることがわかります。これにより、合計の「単位元」、つまり何かに追加しても効果がない値が得られます(0)。

同じ論理をBoolean代数に適用できます。

Stream.of(true, true).allMatch(it -> it) == Stream.of(true).allMatch(it -> it) && true

より一般的に:

stream.concat(Stream.of(thing)).allMatch(it -> it) == stream.allMatch(it -> it) && thing

その場合stream = Stream.of()でも、このルールを適用する必要があります。&&の「単位元」を使用してこれを解決できます。true && thing == thing、そうStream.of().allMatch(it -> it) == true


6

電話をかけるときlist.allMatch(または他の言語の類似物)、list述語と一致しない項目があるかどうかを検出したいと思います。アイテムがない場合、一致しないアイテムはない可能性があります。私の次のロジックはアイテムを選択し、それらが述語と一致することを期待します。空のリストの場合、アイテムを選択しませんが、ロジックは引き続き健全です。

空のリストに対してallMatch返さfalseれた場合はどうなりますか?

私の単純なロジックは失敗します:

 if (!myList.allMatch(predicate)) {
   throw new InvalidDataException("Some of the items failed to match!");
 }
 for (Item item : myList) { ... }

チェックをに置き換えることを忘れないでください!myList.empty() && !myList.allMatch()

要するに、空のリストをallMatch返すtrueことは論理的に健全であるだけでなく、実行の幸せな道にあり、より少ないチェックを必要とします。


あなたはおそらく意味しましたif (!allMatch)
assylias 2015年

3

そのベースは数学的帰納法のようです。コンピュータサイエンスの場合、これを適用すると、再帰的アルゴリズムの基本ケースになる可能性があります。

ストリームが空の場合、定量化は空虚に満たされていると言われ、常に真です。Oracle Docs:ストリーム操作とパイプライン

ここで重要なのは、それが「空虚に満足」しているということです。これは、本質的に、いくぶん誤解を招くものです。ウィキペディアはそれについてまともな議論をしています。

純粋数学では、空虚な真の言明は一般にそれ自体では関心がありませんが、数学的帰納法による証明の基本ケースとして頻繁に発生します。ウィキペディア:空虚な真


1

この質問はすでに何度も正しく答えられていますが、もっと数学的なアプローチを取り入れたいと思います。

そのために、ストリームを(数学的な意味で)セットと見なしたいと思います。次に

emptyStream.allMatch(x-> p(x))

ここに画像の説明を入力してくださいwhileに対応

emtpyStream.anyMatch(x -> p(x))

に対応しここに画像の説明を入力してくださいます。

空のセットには要素がないため、2番目の部分がfalseであることは非常に明白です。最初のものはもう少しトリッキーです。あなたはそれが定義上真実であると受け入れるか、それがそのようにすべき理由のいくつかのために他の答えを調べることができます。

この違いを説明する例は、「火星に住むすべての人間は3本の足を持っている」(true)や「火星に3本の足を持って住んでいる人間がいる」(false)などの命題です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.