Flutter Tips
【Dart超初心者編 #4】true・falseを使いこなそう(後編)

※この記事は、論理演算と計算の優先順位に関する記事の後編です。前編はこちら

論理演算をしてみよう

先生01
比較演算の次は、論理演算に移りましょう。
涼羽09
そもそも「論理演算」って何ですか? 今一つピンときません……。
先生02
簡単に言うと、true, falseを用いた、論理に関する演算を行なうもののことです。
涼羽16
な、なるほど……?
先生02
論理演算子にはいくつかあるのですが、今回は「!」「&&」「||」を学んでいきましょう。
涼羽04
お、お手柔らかにお願いします!
先生01
はい。まずは先ほども出てきた「!」から。「!」は否定(NOT)の意味があります。
void main(){
	print(!true);
	print(!false);
}
涼羽23
へー、「!」は真偽値の前にくっつけるんですね。
先生02
はい。このプログラムの実行結果はどのようになるでしょうか?
涼羽21
うーん……。否定ってことは、「じゃない」って意味ですよね……。「!true」は「trueじゃない」だからfalseで、「!false」は「falseじゃない」だから、trueですか?
実行結果false
true
先生01
正解です。「!」を翻訳するなら、「逆転させます」くらいの意味になりますね。
涼羽27
「!」はノット、逆転させる……覚えました!
先生01
「逆転させる」という考え方ができるのが、bool値のミソです。bool値以外の型は、逆転させることができません。
涼羽03
「!123」とか「!"あいうえお"」とか、意味わかんないですもんね。「123じゃないもの」「あいうえおじゃないもの」なんていくらでもありますし……。
先生05
そうなんですよ! これがわざわざ2択の型が設けられている理由です。trueでなければ絶対にfalse、falseでなければ絶対にtrueです。これは2択以外の型では不可能なことであり、この単純な事実が非常に大事です!
涼羽23
整数を使えば何択問題にだってできるのに、あえて「はい」「いいえ」の2択にするなんて不便じゃないのかな? って思ってました。でも、「逆転」がしたかったんですね!
先生02
実は、昔のプログラミング言語ではboolの考え方はありませんでした。整数値を用いて0か(X == 0)、0以外か(X != 0)を代わりに使っていたんです。
涼羽08
世の中には2種類の男しかいない。俺か、俺以外か。(キリッ)
先生08
それローラ○ドさんじゃないですか! ……こほんっ。
涼羽18
(先生、思ってたよりノリノリだ……)
先生02
整数ではNOTに相当するものが存在せず不便だったんですね。0の反対は何? ってなっちゃいますから。
涼羽02
それがきっかけでboolが誕生したというワケですね!
先生01
正確なところはわかりませんが、おそらくそういう経緯があったのでしょうね。
先生02
では次は「&&」です。以下のプログラムとその実行結果を見てみてください。
void main(){
	print(true && true);
	print(true && false);
	print(false && true);
	print(false && false);
}
実行結果true
false
false
false
涼羽13
えー!? 何ですか、これ!
先生01
落ち着いて、よく観察してみましょう。
涼羽14
左がtrueならtrueを返すってわけでもないし……。うーん……。
先生04
「true && true」とその他の違いは何でしょうか?
涼羽24
あ、「true && true」以外、全部falseになってます!
先生02
その通りです。「A && B」は「AかつB」で、「AND」の意味ですね。「「A・B」は両方ともtrueですか」と翻訳できます
涼羽25
だから、片方だけでもfalseだと実行結果がfalseになっちゃうんですね。
先生01
はい、その通りです。「&&」は、左右が両方trueの時だけtrueを返します
涼羽20
でもこれ、どういう時に使うんですか?
先生01
いい質問です。姫野さんはインターネットサービスの会員登録をする時、氏名や住所などの必須項目を入れ忘れてしまったことがありますか?
涼羽12
あ、やったことあります……。いっぱい入力したのに、「次へ」を押したら「必須項目を入力してください」とか言われて、ゼロからやり直しになって泣きました。
先生02
それならイメージしやすいですね。例えば「氏名が入力されている && 住所が入力されている && 電話番号が入力されている」がtrueであれば、次の画面に進めるというわけです。「&&」のイメージはつかめましたか?
涼羽27
はい! 何となくですが!
先生02
では次に、「||」を見ていきましょう。
涼羽10
「|」って何て読むんですか?
先生01
「バー」と読む人もいれば、「縦棒」と読む人もいますね。
涼羽05
ばーばーたてぼう。
先生02
それでは、「||」の意味を考えてみてください。
void main() {
	print(true || true);
	print(true || false);
	print(false || true);
	print(false || false);
}
実行結果true
true
true
false
涼羽19
ええと、さっきと逆……? ではないですね。わかるような、わからないような……。
先生04
じっくり観察してみましょう。実行結果がfalseになっているのはどんな時ですか?
涼羽24
……あ、わかりました! trueがない時ですね!
先生02
その通りです。ということは、「A || B」はどのように翻訳できるでしょうか?
涼羽26
A・Bのどっちかにtrueがありますか、とかですか?
先生05
素晴らしいです。「A || B」は、「「A・B」はtrueを含みますか」と翻訳できます
涼羽28
やったー!
先生01
「||」は「AまたはB」、すなわち「OR」の意味で、片側にtrueを含んでいれば、もう片方がtrueでもfalseでも、必ずtrueを返します
涼羽01
オアの演算子はわかりやすいですね!
先生02
「&&」も「||」も使いこなせるようにしましょうね。数学の命題や集合に応用することができます。

「!」は、「逆転させます」と翻訳できます。2択を表す真偽値にだけつくことのできる演算子です。
「A && B」は、「「A・B」は両方ともtrueですか」と翻訳できます。左右が両方trueの時だけtrueを返します。
「A || B」は、「「A・B」はtrueを含みますか」と翻訳できます。片側にtrueを含んでいれば、もう片方がtrueでもfalseでも、必ずtrueを返します。
言い換えると「||」は左右が両方falseの時だけfalseを返すと表現することもでき、「&&」と見比べると表裏一体であることがわかります。

計算の優先順位

涼羽02
先生、今までに出てきた演算子の練習問題とかありますか?
先生05
姫野さんは向学心があってとても素晴らしいです。では、こちらのプログラムの実行結果を予想してみてください。
void main() {
	print(123 == 123 && 123 != 123);
}
涼羽10
うーん……。演算子が3つもありますよね? どこから計算すればいいんですか?
先生02
非常にいい質問です。基本的には左側から計算するのですが、演算子には優先順位が存在します。例えば、「==」「!=」は「&&」「||」よりも優先順位が高いです。
涼羽23
なるほど……。そうすると、「&&」の左にある「123 == 123」から計算するってことですね!
先生01
その通りです。
涼羽03
「123 == 123」は、「123は123と同じですか」ですよね。同じだからtrueっと。
先生02
この時点の状況は、 print(true && 123 != 123); となりますね。
涼羽24
確かに! それで次は……「&&」と「!=」では「!=」のほうが優先順位が高いから、「123 != 123」を計算するんですね。「123は123と異なりますか」なのでfalseですね!
先生01
そうなります。すると、print(true && false); という状況になります。
涼羽26
わかりやすい! 「true && false」ってことは、「true・falseは両方ともtrueですか」って意味で、片方falseになっちゃってるから……答えはfalseですか!?
先生05
正解です!
涼羽28
やったー! 先生のおかげでちょっとわかってきた気がします!
先生05
では続けて2問目。
涼羽27
わかりました、どんどんいきましょう!
void main() {
	print(123 < 123 || 123 <= 123);
}
涼羽09
「<」と「<=」も、「&&」や「||」より優先順位が高いんでしょうか?
先生01
仰る通りです。初めに演算するのはどこでしょう?
涼羽05
「123 < 123」ですかね、一番左にあるし。123は123より小さくないからfalseです。
先生02
いいですね。この時点での状況は、print(false || 123 <= 123); となります。
涼羽23
次に計算するのは「123 <= 123」ですね! 123は123以下だから、こっちはtrueになります!
先生01
その調子です。状況としてはprint(false || true); となりますね。
涼羽26
……あ、わかっちゃいました! 「false・trueはtrueを含みますか」ってことだから、答えはtrueですよね!?
先生05
はい、正解です。よくできましたね。
涼羽09
やっぱりわたし、天才なんじゃ……?
先生10
そ、そうですね……。
涼羽08
(ドヤァ)
先生02
計算式が順次、計算済みの値に置き換わっていくイメージを持っていただけると、難しい問題であってもわかりやすくなります。このイメージを持つことが非常に大事で、この先のプログラミング人生を左右すると言っても過言ではありません。
涼羽18
えぇっ、ちょっとそれは大袈裟じゃないですか?
先生01
少し大袈裟に聞こえるかもしれませんが、ぜひ初心者のうちにこそ覚えておくべきことのひとつですよ!

演算子には優先順位があります。現時点で学ぶべき演算子の優先順位は以下の通りです。

優先順位

 

 

 

 

 

 

演算子

()

!

* /

+ -

> >=

< <=

==

!=

&&

||

同じ優先順位の演算子が存在する場合は、左側から順番に演算します。

問題にチャレンジ!

先生05
天才の姫野さんのために、プログラムの実行結果を予想する練習問題を、あと5つ用意しました。
涼羽13
ひええ、5つも……!? が、頑張ります!
void main() {
	print( (true != !true) && (true || true) );
	print(!false || (!true || false ) );
	print(12 * 3 == 30 + 6 && (!false != true) );
	print( (false != true) || 12 + 3 == 123);
	print(!( !( !( !( !true ) ) || !( !( !( ! false) ) ) ) ));
}
涼羽14
うわあ、どれも難しそう……。
先生01
今までやってきたことの積み重ねでしかありません、一つ一つこなしていきましょう。
まず1問目、print( (true != !true) && (true || true) ); はどうなるでしょうか? 今までのことを思い出して、自分で考えてみてください。
涼羽20
ええと……。
まず「&&」の左側の丸括弧から計算します。「true・trueの逆転のfalseは異なりますか」だからtrueですね。これを①とします。
次に「&&」の右側の丸括弧を計算します。「true・trueはtrueを含みますか」だからtrueですね。これを②とします。
①と②から、「true && true」になるので、これを計算すると「true・trueは両方ともtrueですか」だから……答えは、trueです!
先生02
はい、答えはtrueで合っています。
涼羽01
数学のテストはいつも平均いくかいかないかくらいでしたけど、証明問題みたいで楽しいですね! 証明問題が楽しいなんて思わなかったなー。
先生04
姫野さんの説明はおおむね正しいのですが、より厳密に説明すると次のようになります。

・優先順位表に従って計算する
・同じ優先順位の演算子が存在する場合は、左側から順番に演算する

 この原則に当てはめると、

①一番内側の括弧は2箇所存在する。「(true != !true)」「(true || true)」
 これを左から順番に演算する。
②計算対象:左側の括弧の内側「true != !true」。「!=」と「!」の優先順位の比較を行い、「!」の方が優先順位が高いため、先に演算する。
 この時点での状況:print( (true != false) && (true || true) );
③計算対象:左側の括弧の内側「true != false」。「!=」を演算すると、「trueとfalseは異なりますか」となり、trueとなる。
 この時点での状況:print( (true) && (true || true) );
④計算対象:左側の括弧の内側「true」。演算子が存在しなくなったため、()は役割を終えた。
 この時点での状況:print( true && (true || true) );
⑤計算対象:右側の括弧の内側「true || true」。「||」を演算すると、「「true・true」はtrueを含みますか」となり、trueとなる。
 この時点での状況:print( true && (true) );
⑥計算対象:右側の括弧の内側「true」。演算子が存在しなくなったため、()は役割を終えた。
 この時点での状況:print( true && true );
⑦計算対象:printの括弧の内側「true && true」。「&&」を演算すると、「「true・true」は両方ともtrueですか」となり、trueとなる。
 この時点での状況:print( true );
⑧計算対象:printの括弧の内側「true」。演算子が存在しなくなったため演算を終了し、printが画面に「true」と出力する。
→実行結果はtrue

涼羽27
何かカッコいいですね……! 何ていうか、プログラミングって感じ!
先生01
では、この調子で2問目以降も解いていきましょう。
涼羽02
わたしなりに頑張ってみます!
void main() {
	print(!false || (!true || false ) );
	print(12 * 3 == 30 + 6 && (!false != true) );
	print( (false != true) || 12 + 3 == 123);
	print(!( !( !( !( !true ) ) || !( !( !( ! false) ) ) ) ));
}

print(!false || (!true || false ) );
①計算対象: 丸括弧内の「!」を含む !true→「trueを逆転させます」→false
 この時点でprint(!false || (false || false))
②計算対象: 丸括弧内の (false || false)→「「false・false」はtrueを含みますか」→false
 この時点でprint(!false || false)
③計算対象: 「!」を含む !false→「falseを逆転させます」→true
 この時点でprint(true || false)
④計算対象: true || false→「「true・false」はtrueを含みますか」→true
 演算子が存在しなくなったため演算終了、trueを出力
→実行結果はtrue

print(12 * 3 == 30 + 6 && (!false != true) );
①計算対象:丸括弧内で「!」の演算子があるfalse→「falseを逆転させます」→true
 この時点でprint(12 * 3 == 30 + 6 && (true != true) ); となる
②計算対象:丸括弧内の (true != true)→「trueとtrueは異なりますか」→false
 この時点でprint(12 * 3 == 30 + 6 && false); となる
③計算対象:一番左にある12 * 3→36
 この時点でprint(36 == 30 + 6 && false); となる
④計算対象:「+」の演算子を含む30 + 6→36
 この時点でprint(36 == 36 && false); となる
⑤計算対象:「==」の演算子を含む36 == 36→「36と36は同じですか」→true
 この時点でprint(true && false); となる
⑥計算対象: true && false→「「true・false」は両方ともtrueですか」→false
 演算子が存在しなくなったため演算終了、falseを出力
実行結果はfalse

※なお、②の計算結果において「&& false」が存在するため、以降の計算を省略して「false」とすることも可能。

print( (false != true) || 12 + 3 == 123);
①計算対象:丸括弧内の (false != true)→「falseとtrueは異なりますか」→true
 この時点でprint(true || 12 + 3 == 123); となる
②計算対象:「+」の演算子を含む12+ 3→15
 この時点でprint(true || 15 == 123); となる
③計算対象:15 == 123→「15と123は同じですか」→false
 この時点でprint(true || false); となる
④計算対象: true || false →「「true・false」はtrueを含みますか」→true
 演算子が存在しなくなったため演算終了、trueを出力
実行結果はtrue

print(!( !( !( !( !true ) ) || !( !( !( ! false) ) ) ) ));
①計算対象:最も左で内側の丸括弧内に位置し「!」の演算子を含む !true→「逆転させます」→false
 この時点でprint(!( !( !( !false ) || !( !( !( ! false) ) ) ) ));
②計算対象:最も左で内側の丸括弧内に位置し「!」の演算子を含む !false→「逆転させます」→true
 この時点でprint(!( !( !true || !( !( !( ! false) ) ) ) ));
③計算対象:最も内側の丸括弧内に位置し「!」の演算子を含む !false→「逆転させます」→true
 この時点でprint(!( !( !true || !( !( !true) ) ) ));
④計算対象:最も内側の丸括弧内に位置し「!」の演算子を含む !true→「逆転させます」→false
 この時点でprint(!( !( !true || !( !false) ) ));
⑤計算対象:最も内側の丸括弧内に位置し「!」の演算子を含む !false→「逆転させます」→true
 この時点でprint(!( !( !true || !true ) ));
⑥計算対象:最も内側の丸括弧内で左側に位置し「!」の演算子を含む !true→「逆転させます」→false
 この時点でprint(!( !( false || !true ) ));
⑦計算対象:最も内側の丸括弧内に位置し「!」の演算子を含む !true→「逆転させます」→false
 この時点でprint(!( !( false || false ) ));
⑧計算対象:最も内側の丸括弧内にある false || false→「「false・false」はtrueを含みますか」→false
 この時点でprint(!( !false ));
⑨計算対象:最も内側の丸括弧内に位置し「!」の演算子を含む !false→「逆転させます」→true
 この時点でprint(!true);
⑩計算対象:!true→「逆転させます」→false
 演算子が存在しなくなったため演算終了、falseを出力
実行結果はfalse

先生02
姫野さん、いかがでしたか?
涼羽27
どの問題も結構考えましたけど、何とか解けました……!
先生01
それはよかったです。今回学んだ内容は、プログラミング人生において毎日向き合うと言っても過言ではないほどによく使うので、ぜひマスターしてくださいね。
涼羽28
はい、しっかり復習しておきます!

今回のまとめ

①真偽値
・真偽値は値の一種で、「true(真)」「false(偽)」の2択を表す概念
・文字列とは異なるので、ダブルクォーテーションでは囲まない

②比較演算子「==」「!=」「>」「<」「>=」「<=」
・「==」は、==の左側と右側が同じであればtrueを、異なればfalseを返す。「A == B」は「AとBは同じですか」と翻訳できる
・「!=」は、!=の左側と右側が異なればtrueを、同じであればfalseを返す。「A != B」は「AとBは異なりますか」と翻訳できる
・「==」と「!=」は表裏一体
・数値での比較演算では、「==」「!=」の他に、「>」「<」「>=」「<=」も使える

③論理演算子「!」「&&」「||」
・「!」は、!trueならfalseを、!falseならtrueを返す。「逆転させます」と翻訳できる
・「A && B」は、左右が両方trueの時だけtrueを返す。「「A・B」は両方ともtrueですか」と翻訳できる
・「A || B」は、片側にtrueを含んでいれば、もう片方がtrueでもfalseでも、必ずtrueを返す。「「A・B」はtrueを含みますか」と翻訳できる
・「&&」と「||」は表裏一体

④計算に際して
・計算は基本的に左側から行う
・演算子には優先順位が存在する
・計算の際は、計算式が順次、計算済みの値に置き換わっていくイメージを持つとよい

おすすめの記事