LoginSignup
12
2

More than 1 year has passed since last update.

空の配列に対してallSatisfy(_:)を実行するとtrueが返ることとvacuous truth

Last updated at Posted at 2021-09-29

allSatisfy(_:)

allSatisfy(_:)はSwift4.4で追加されたSequenceのメソッドで、すべての要素が与えられた条件を満たすかを判定するメソッドなのですが、このメソッドを空の配列で使うとtrueを返します。

このことはアップルの公式ドキュメントにも記載があります。

If the sequence is empty, this method returns true.

誤解: falseを返すと思っていた

自分は直感的にfalseを返すだろう、と思ってある判定式に使っていたのですがユニットテストが失敗してこの振る舞いに気が付きました。
空なんだから各要素は何も条件を満たしていないじゃないか、という考えでした。

ググってみるとswiftのフォーラムでも同じ疑問を持った方が質問をしていました。

質問への回答が2つあったので引用します。(改行しています)

回答1

true is what I would expect as the result. 
All elements of the (empty) sequence satisfy the predicate (a “vacuous truth” 104).

回答2

The current behavior is the logical one.
One way to see why: we would expect that

a.allSatisfy(predicate) && b.allSatisfy(predicate)

…if and only if…

(a + b).allSatisfy(predicate)

What if b is empty?

上の回答1の大意は「vacuous truth」だから、ということでしょうか(後述)。
下の回答2をもう少し具体的なコードにすると以下のような感じでしょうか。predicateは文字列の判定にしています

let array: [String] = ["A", "B", "C"]
let emptyArray: [String] = []

let predicate = { (x: Any) -> Bool in
    return x is String
}

array.allSatisfy(predicate) && emptyArray.allSatisfy(predicate) // true

let newArray = array + emptyArray // ["A", "B", "C"]

newArray.allSatisfy(predicate) // true

つまりemptyArray.allSatisfy(predicate)falseなら、newArray.allSatisfy(predicate)falseにならないとおかしいじゃないか(実際はtrue)、ということでしょうか。

vacuous truth

先程のフォーラムの回答にあったvacuous truthについてです。
英語のWikipediaにページがあったので、最初の一段落を引用します。
https://en.wikipedia.org/wiki/Vacuous_truth

In mathematics and logic, a vacuous truth is a conditional or universal statement 
(a universal statement that can be converted to a conditional statement) that is true 
because the antecedent cannot be satisfied.[1][2] For example, 
the statement "all cell phones in the room are turned off" will be true when there are 
no cell phones in the room. In this case, the statement "all cell phones in the room are 
turned on" would also be vacuously true, as would the conjunction of the two: "all cell 
phones in the room are turned on and turned off". For that reason, it is sometimes said 
that a statement is vacuously true because it does not really say anything.[3]

よくわかりません。

vacuous truthについて解説されているブログがあったので、こちらからも引用します。(改行を行っています)

(ここに来た他人用の説明):条件式(A⇒B)のAを「前件」と,そしてBを「後件」と言うのであった.
前件が偽ならば後件に何を置いても条件式全体は真と評価される.

このような場合に条件式が真になることを特に “vacuous truth” と呼ぶことがある.(決まった訳語はないらしい).
古典論理のなかで,入門者を少しばかり悩ませる話題として有名である.

ちょっと分かった気になりました。

Wikipediaの方に「携帯電話の例」が書かれていので、それについてもう少し考えます。

具体例: 携帯電話、トランプが当選したら全裸で街を歩く

「部屋にあるすべての携帯電話の電源がオフである」という条件式にたいして「部屋に携帯電話が無い」ケースでは条件式に対して「真になる」、とWikipediaにvacuous truthの例として書かれています。

これは「部屋にあるすべての携帯電話の電源がオフである」が「部屋に携帯電話がある」「すべての携帯電話がオフである」という2つの条件式であり、その内の前件である「部屋に携帯電話がある」が偽であるため、後件の「すべての携帯電話がオフである」が何であれ(例えば「すべての携帯電話がオンである」や「部屋にあるすべての携帯電話がiPhoneである」であっても)条件式全体としては真となるということになります。

あまりピンときません。

また先程のブログ記事(vacuous truth - 萌えとかプログラミングとか)からの引用になります。(改行しています)

日常言語でも前件として「絶対に起こらない(であろうと思われる)」ことを置いて何かを言うことがある.

少し前の話題だがマスコミ各社が「トランプが大統領になるはずがない」と報じていたときに,
「トランプが大統領になったら全裸で街を歩く」といった類のツイートしている人たちがいた.

前件が絶対に偽だ(とそのときには思われていた)から後件にどんなヤバいことを書いても平気というわけである.

少しわかったような気になります。

allSatisfy(_:)の前件と後件

allSatisfy(_:)については「要素が存在する」が前件であり、「引数にとるpredicate」が後件に当たるのだと思われます。

なので空の配列については前件である「要素が存在する」が偽であるため、後件にあたる引数のpredicateがどんなものであっても条件式全体としてのallSatisfy(_:)trueを返すことになるのだと思われます。

まとめ

個人的にはここまで調べてもまだ、分かりづらいなぁというのが正直な感想です。

Appleのドキュメントにも「シーケンスが空の場合、このメソッドはtrueを返します」とわざわざ書かれているくらいなのだから、直感的にはなかなか理解しづらい話なのだと思います。

この辺をちゃんと理解するには論理学とかやれば良いのかもしれないです。

とりあえず普段の開発で気をつけていこうと思います。

12
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
2