もう数年も数学をやってないので、間違っていたら教えてください。
話題に乗っかって。
tureを返す、falseを返す、例外やその他の値を返す、といったリプライがついていますが、原則としてtrueを返すのが一般的です。実際の言語で検証していただければわかりますが、汎用的な配列操作関数であればまず間違いなくtrueを返す実装になっています。
JavaScript
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/every
.NET
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.all?view=net-7.0
Python
https://docs.python.org/ja/3/library/functions.html#all
Swift
https://developer.apple.com/documentation/swift/array/allsatisfy(_:)
なぜなのか ~Vacuous Truth~
ざっくり言うと、数学的にそれが一番自然であり、一般的だからです。古典理論や集合論でこのような状況はtrueです。これは初学者を混乱させる命題として、Vacuous Truthと呼ばれています。(決まった訳語は無いですが、「空虚な真」などと訳されます)
このワードで検索してもらえるとたくさん解説が出てくるほか、私が中途半端に古典理論を解説するより大学の講義資料等を参照していただくほうがまとまっているかと思いますので、細かい解説はそちらに任せようと思います。
ちなみに、このような「ある配列(集合)に対して、すべてのxが条件を満たす時~というのは、」数学の記号では$\forall$をつかって表すことができます。汎用的な「配列のすべての要素が条件を満たすならtrueを返す」関数はこれに基づいて実装されていると言えます。
trueになるべき理由
ということで、汎用的な状況タイトルの通りtrueになるべき理由は以下です
- trueを返すのが最も数学的に便利で合理的である
- 古典理論をやった人は、プログラミングでも同様にtrueを期待する
- 様々な言語の標準実装でも(主に上記の理由から)trueを返す
- 標準実装に慣れている人は、他の状況でも同様にtrueを期待する
一般的にtrueが期待されるので、true以外の結果になるのであれば説得力のある理由が求められることでしょう。
珍しい例
Swiftでは、import Foundationすると、"abc".contains("")
がfalseを返します。空文字でフィルタリングしたら全てfalseになってしまうので困るという記事がありました。
https://apprythm.com/382/
これは哲学的な理由……ではなく、どうやらCocoa(Objective-C)の呼び出し連携の都合らしいです。
https://github.com/apple/swift/issues/62269
他に、ある程度汎用的な関数でtrueを返さな例をご存知の方は教えてください。
補足
もちろん、具体的な状況では、falseや例外を返したい状況があります。
いくつか検討してみます。サンプルコードはJavaScript風の疑似言語です。
テストの点が全て70点以上なら合格
function all( 点数一覧 ) {点数一覧.every(x=> x>=70)}
all([70,80]); //true
all([50,80]); //false
all([]); //true
これは、テストを1つも受けていないので、falseにするのが正解かもしれません。
「テストを1つ以上受ける」という隠れた条件を書き忘れているのが問題です。
条件を全て満たすデータベースのレコードを削除する
function all( 値, 削除条件 ) {削除条件.every( x => x(値))}
val = 50;
if(all(val, [100以下, 偶数])) delete(val); // trueなので削除
if(all(val, [100以下, 奇数])) delete(val); // falseなので削除
if(all(val, [] )) delete(val); // 常にtrueなので、valの値に関わらず削除
条件が空だったら全部のレコードが削除されることになるでしょう。データベースの中身を全て削除する、という操作を普通は行わないのなら、例外を出して止まるのが安全です。deleteAllみたいな別の関数を呼び出すように誘導するのが良いでしょう。
どれにせよ、このような検証をする関数は「配列のすべての要素が条件を満たすならtrueを返す」関数
ではなく、特定のデータ検証を行う関数
と考えるべきです。allやeveryのように汎用な名前をつけず、具体的で慎重な命名が求められるでしょう。
まとめ
迷ったときには、使っている言語の標準ライブラリをすぐに確認しましょう。また、さらに複数の言語の標準ライブラリを調査することで歴史的な事情や数学的な裏付けが見えてくる場合もあります。
逆に言語ごとに差異があったり、変わったオプションが用意されている場合もあります。
例:空文字に対するSplit