※こちらの記事は初学者向け・丁寧に解説を心がけております。
その分、文章量も多いのでご了承ください。
プログラミングを学びたての頃、論理演算子の章で
-
&&を「かつ」 -
||を「または」
と覚えてしまった人も多いのではないでしょうか?
高校数学 A で習う「集合」のカリキュラムに登場する「A∩B」や「A∪B」の考え方に沿えば一番馴染みのある表現ではありますが、プログラムの振る舞いに着目すると厳密には異なります。
また、経験を積んでいくにつれて、&&を「かつ」、||を「または」と表現すると、説明がしづらいこと場面にも遭遇します。
この記事を読んで、「かつ」「または」という考え方から脱却していきましょう。
また、この記事では
- ブラウザですぐ実行できること
- 多くの初学者にも馴染みがある
という点からJavaScriptを基に解説していきますが、一部挙動は異なるものの、Ruby や PHP などでも考え方は同じです。
(追記:コメントをいただき調べましたところ、CやJava, PHPでは大きく挙動が異なるため、タイトルの先頭に**「JavaScriptの」**を追加させていただきました。誤解を生む表現を使ってしまい、申し訳ございませんでした。)
用語解説
AND 演算子
&&のこと
アンパサンド2つと書くのがめんどくさい。
OR 演算子
||のこと
パイプライン2つと書くのがめんどくさい。
オペランド
- 式を構成する要素だよ
- 演算子じゃない方だよ
- 数字とか変数とかだよ
a || bであれば、aやbのことです。
正式名称ではないかもしれませんが、この記事ではaを左オペランド、bを右オペランドと言わせてください。
Boolean
trueとfalse の2つの値を取るデータ型のこと。
先に結論
- AND演算子
&&は、左から順にfalseを探しにいき、 - OR演算子
||は、左から順にtrueを探しにいき、- 見つかればその値を返す。
- 見つからなければ最後の値を返すよ。
-
true/falseでない値は、-
trueに変換できるtruthyな値か、 -
falseに変換できるfalsyな値かに着目しよう。
-
よくある例題
初学者向け教材にはこんな例題が載っています。
[例題1]
数学と理科のテストの点数を変数に格納し、
- どちらも 70 点以上であれば「合格」
- そうでなければ「不合格」
と出力するプログラムを書いてみましょう。
const math = 80
const science = 65
if (math >= 70 && science >= 70) {
console.log('合格!')
} else {
console.log('不合格…')
}
頭の中で日本語に直すと
if 文は
・もし (「math が 70 以上」かつ「science が 70 以上」)ならば
という意味だな。math は 80 で 70 以上だから true だけど、science が 65 で 70 以上ではないので false。
2つとも true じゃないから、「かつ」を満たさない。
よって、if 文全体の評価は false となり、else の方に進み、「不合格」が表示される。
こんな感じでしょうか。
この思考自体は間違っていません。この例題の回答としては十分クリアできています。
「かつ」「または」という考え方の弊害
しかし、AND 演算子&&を「かつ」と暗記していると、こんなイジワル問題に遭遇した時に、瞬時に答えられないかもしれません。
[例題2]
以下のコードを実行すると、 console にどのように出力されるでしょうか?
const math = 80
const science = 65
if (math && science) {
console.log('ifの中です')
} else {
console.log('elseの中です')
}
変数を展開するとこうなりますが
if (80 && 65)
80かつ65ってなんなんだ?と混乱してしまいます。
つまり、「かつ」「または」と覚えたことの弊害は、左右のオペランドが論理式(70 >= 80 のように、比較して true か false が返される式)の場合に理解が限定されてしまうことです。
>や==などが出てこない場合でも、JavaScript がどのように判断しているのか理解できるようになりましょう。
AND 演算子&&
AND 演算子&&は、現代の JavaScript チュートリアルではこのように解説されています。
① 左から右にオペランドを評価します。
② それぞれのオペランドで、それを Boolean に変換します。
③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。
④ もしもすべての他のオペランドが評価された場合(i.e. すべて 真 のとき), 最後のオペランドを返します。
順番に見ていきましょう。
① 左から右にオペランドを評価する
これはプログラミングの原則通りの向きなので問題ないでしょう。
「評価する」という表現がわかりにくければ、「1つ1つ見ていく」と変換しても構いません。
② それぞれのオペランドで、それを Boolean に変換します。
Boolean という言葉が分かりにくければ、「それぞれのオペランドで、それを真か偽のどちらかに変換する」と言い換えることができます。
では「Boolean に変換する」とはどういうことでしょうか?
truthy / falsy
JavaScript には、
-
'リンゴ',''(空文字)などの文字列 -
0,-15,3.14などの数値 -
['アメリカ', '日本', 'ドイツ']などの配列 -
{ name: '鈴木', age: 20 }などのオブジェクト
他にも true, false, null, undefined など、様々な型の値が存在しますが、それらは全て true または false に変換することができます。
変換した時に true になるものをtruthy、false になるものをfalsyと言います。
JavaScript ではfalseそのものの他に、0, ''(空文字), null, undefinedなどは Boolean に変換するとfalseになることからfalsyな値です。
falsy な値以外は全て Boolean に変換するとtrueになることから、truthyな値です。
もっと詳しく知りたい方は以下の記事をご覧ください。
(ちなみにRubyでは**nilとfalseの2つだけが falsyで、それ以外は全てtruthy**になります。
数値の 0 が JavaScript では falsy でも、Ruby では truthy になります。
ポートフォリオ作りで両方同時に学ぶときには注意しましょう。)
これを踏まえて、例題2の80 && 65をもう1度考えてみましょう。
-
80は truthy な値なのでtrueに、 -
65も truthy な値なのでtrueに変換されます。
if (80 && 65)
↓
if (true && true)
③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。
80 も 65 もどちらも true に変換されたため、false がありません。
③ はスキップします。
④ もしもすべての他のオペランドが評価された場合(i.e. すべて 真 のとき), 最後のオペランドを返します。
④ に該当するため、最後のオペランド、今回は 65 を返します。
つまり、80 && 65は評価されると65になるのです。
if (80 && 65)
↓
-- if (true && true)を経由 --
↓
if (65)
論理演算子はなくなったものの、例題の答えにたどり着けていないので補足をすると、
if 文も AND 演算子のときと同様に、かっこの中の式を評価し、Boolean に変換した結果で、if の中に入るかどうかを決定します。
if (65) {
//if文がtrueと評価されるのでこちらに進みます。
console.log("ifの中です");
} else {
console.log("elseの中です");
}
先ほどと同様、65はtruthyな値なので、この if 文全体は true となり、例題の答えは「'ifの中です'と表示される」でした。
例題1を振り返る
改めて一般的な論理式が含まれた if 文で復習してみましょう。
const math = 80
const science = 65
if (math >= 70 && science >= 70) {
console.log('合格!')
} else {
console.log('不合格…')
}
① 左から右にオペランドを評価します。
② それぞれのオペランドで、それを Boolean に変換します。
評価対象が元々論理式の場合は、Boolean に変換するというより、評価結果そのものが true か false なので、
-
math >= 70の評価結果はtrue -
science >= 70の評価結果はfalseです。
と言うべきでしょうか。
if (math >= 70 && science >= 70)
↓
if (true && false)
③ もしも結果が false の場合、ストップしそのオペランドの本来の値を返します。
左から順に見ていって、左オペランドはtrueですが、右オペランドはfalseなので右側でストップし、本来の値(今回は評価結果のfalse)が返されます。
if (math >= 70 && science >= 70)
↓
-- if (true && false)を経由 --
↓
if (false)
--------------------------------
if (false) {
console.log('ifの中です');
} else {
//if文がfalseなのでこちらに進みます
console.log('elseの中です');
}
いかがでしょうか?
当然「かつ」と読んでいたとしても結果は変わりませんが、この記事を読むまで AND 演算子&&を「かつ」と考えてきた人は、左右のどちらもtrueであることで式全体が「真」と評価されるように、trueの方に重きを置かれがちですが、③ で見てきた通り、プログラムはfalseを探すことに一生懸命になっていますね。
着眼点が異なりますね。
この違いに慣れるまでは少し大変かもしれませんが、慣れてしまえば自然と読めてくるようになります。
OR 演算子||
OR 演算子||は、AND 演算子&&の③④の太字部分を反転させたものです。
① 左から右にオペランドを評価します。
② それぞれのオペランドで、それを Boolean に変換します。
③ もしも結果が true であれば、停止しオペランドの本来の値を返します。
④ もしもすべての他のオペランドが評価された場合(i.e. すべて 偽 のとき), 最後のオペランドを返します。
例題を見ながら流れを確認していきましょう。
[例題3]
以下のコードを実行すると、 console にどのように出力されるでしょうか?
if (0 || 0.1) {
console.log('ifの中です');
} else {
console.log('elseの中です');
}
AND 演算子のときほど丁寧には解説しませんが、
-
0は falsy な値なのでfalseに、 -
0.1は truthy な値なのでtrueに変換されます。 - 左から順に評価し、
trueを探しにいきます。 - 左オペランドは
falseですが、右オペランドがtrueなので、ここで処理がストップし、本来の値0.1が返されます。
if (0 || 0.1)
↓
-- if (false || true)を経由 --
↓
if (0.1)
--------------------------------
if (0.1) {
//if文がtrueと評価されるのでこちらに進みます。
console.log('ifの中です');
} else {
console.log('elseの中です');
}
OR 演算子||は、1つ1つtrueを探すので、「または」のように「どちらかが」という文脈には沿っていますね。
役立つ場面
論理演算子を「かつ」「または」ではなく、プログラム本来の振る舞いで意識できると役立つ場面を紹介します。
React の条件付きレンダー
React の JSX で使える書き方紹介の中に、AND 演算子&&が出てきます。
return (
<div>
<h1>Hello!</h1>
{unreadMessages.length > 0 &&
<h2>
You have {unreadMessages.length} unread messages.
</h2>
}
</div>
);
これが動作するのは、JavaScript では true && expression は必ず expression と評価され、false && expression は必ず false と評価されるからです。
条件付きレンダー – React
ここまで読んでくれた方であれば、上記解説の内容を既にご自分で理解できるようになっていると思いますが、
- AND 演算子
&&は左から順にfalseを探しにいきます。 - 左オペランドが
trueなら右オペランドを見にいきます。- 右オペランドの HTML タグは truthy なので、最後の要素である右オペランドのHTMLタグが返される(=表示される)
- 左オペランドが
falseならそこで処理はストップし、falseを返すため、右オペランドのHTMLタグは表示されない
という流れですね。
OR 演算子を用いた代入
a = b || cのような OR 演算子||を用いた代入はよく見かけます。
例えば、メモアプリの投稿フォームで、
- タイトルを入力して投稿したらそのタイトルを
- タイトルを空欄のまま投稿したら現在日時に置き換えてデータベースに保存する
このような処理を考えてみましょう。
const defaultTitle = dateTimeFunction(); // '20220116_235940'などの現在日時を返す関数とする
const enteredTitle = formData.title; // ユーザーがメモ投稿フォームのタイトル欄に入力した内容
const title = enteredTitle || defaultTitle;
右オペランドが必ずtruthyになるように文字列を与えることで、
- 左オペランドの
enteredTitleが何かしらの文字列なら、そこで処理はストップし、enteredTitleの中身を返す - 左オペランドの
enteredTitleが''(空文字)なら、右オペランドのdefaultTitleの中身を返す
となります。
他にも、a = a || bのように、
-
aがtruthyならa -
aがfalsyならb
を代入するものもあります。
(厳密には異なりますが、省略形の論理和代入||=なんてものもあったりしますが、滅多に出てきませんのであまり気にしないで大丈夫です。)
まとめ
論理演算子の左右のオペランドは、どのような値でもtrueかfalseに評価できることから、必ずしも論理式である必要はありません。
①〜④ のルールを覚えておくことで、&&や||の左右に複雑なものが含まれていても、コードリーティング等で不用意に臆することがなくなるでしょう。
少々アカデミックな堅苦しい内容となりましたが、皆様のプログラミング学習のお役に立てばこの上なく嬉しく思います。