今回は「式」と「式の評価」という考え方を習得して、whileの動作について理解を深めます。
比較演算子
前回の記事では「何かと何かが等しい」などの比較をする記号を習得しました。
==
!=
<
<=
>
>=
「比較演算子」ってなんだか難しい言葉ですが、これは「比較するための演算をする記号」という意味になっています。( 「子」は、「~するもの」「~するための道具」といった意味を持つ接尾語の一つです)
ここで疑問が出てきてしまいました。なぜ「比較する記号」なのに「比較する演算の記号」なのでしょうか?つまり計算する記号ということなのですが、なぜ計算する必要があるのか謎です。
そこで今回の記事では、なぜ「演算」という言葉が使われているかという疑問から出発して、比較演算子とwhileについて理解を深めていきます。
Arduinoの計算能力
ちょっと驚くかもしれませんが、Ardunoボードを始め、多くのコンピュータでは数字を直接比較することができないんです。とはいっても、前回のスケッチで
while( digitalRead(SWITCH) == 1 ) {
}
と書いて、スイッチの状態である「digitalRead(SWITCH)」と「1」が等しいかどうか比較していましたよね。これって、どこからどうみても比較しています。
一般的なコンピュータ、つまり計算機は、当然ながら計算は得意なのですが、できることはかなり限られています。Arduinoの場合、計算と言ってもできることはこれぐらいなんです。
- 整数の足し算
- 整数の引き算
- 計算結果が0か0以外か判定する
- 計算結果がプラスかマイナスか判定する
これだけといっても、実際には比較とか、掛け算、割り算、さらには少数の計算をしているのですが、一体どうしてるんでしょうか。
実は、Arduinoボードの内部では上の4種類の処理をうまく組み合わせて、スケッチに書かれた比較や計算をやっているんです。
そこで今回の記事では、Arduinoボードが「比較」という処理をどのように実現しているのか一緒に詳しくみていきましょう。
比較演算子の処理
これから次のスケッチのArduinoボード内部処理のようすを詳しくみていきます。
digitalRead(SWITCH) == 1
これはスイッチの状態を調べて、その値が1に等しいかどうか調べる、という動作内容のスケッチです。Arduinoボードはこのスケッチをどのように処理しているか、順番にみていきましょう。
この比較をするには、最初にdigitalRead(SWITCH)
を処理する必要があります。これは次ような値になるんでしたよね。
スイッチの状態 | digitalRead(SWITCH)の値 |
---|---|
OFF | 1 |
ON | 0 |
ここで、スイッチがOFFのケースを考えます。
スイッチOFFの場合はdigitalRead(SWITCH)
の値は1ですので、先ほどの比較のスケッチは「digitalRead(SWITCH)
」の部分が「1」になるので、Arduinoボードは内部で以下のように変換します。
1 == 1
これで比較ができそうですが、先ほど説明したようにArduinoボードは数値の比較はできません。
そこで、Arduinoボードは「 == 」の左辺の数字から右辺の数字を引き算します。つまり「 1 − 1 」を計算します。
結果はどうなるかというと「0」ですよね。
Arduinoボードは「計算結果が0か0以外か」という判定はできます。この判定を利用して、結果が「0」の場合はこの比較が成立しているので両辺の値は等しい、「0以外」の場合は比較が成立していないので等しくない、と判定します。
このように、Arduino内部では比較を行う場合、引き算をしてその結果がどうなるかを判定しています。
つまり「 == 」などの比較の記号は「比較するために計算する記号」つまり「比較演算子」だったわけです。
ここで説明した例は等しいケースですが、例えば「 A > B 」の比較を行う場合、Arduinoボードは最初に「 A - B 」という引き算を行います。「計算結果がプラスかマイナスか」判定することはできますので、この判定を利用して、結果がプラスであれば成立している、プラスでなければ成立していない、と判断します。
他の比較の記号も全て、左側の数字から右側の数字を引いて、その結果がどうなるか判定しているわけです。
ここまでが、Arduinoボードが比較演算子のスケッチが成立しているか、成立していないか判定するまでの処理です。
この先、さらにややこしくなります。すみません…
「演算子」はこのように説明しましたが、もともとは英語の「Operator」に由来しています。「Operator」は「操作するもの」というような意味ですが、日本語にするときに「演算子」となったようです。
真と偽
先ほどの比較演算子のように、コンピュータ内部では、何かが成立しているか成立していないか、という判定が頻繁に行われます。
例えば比較演算子で、次のような場合、
(1 + 2) == 3
比較演算子の左辺は3、右辺も3ですので成立しています。
また、次のような場合、
(2 + 3) == 8
比較演算子の左辺は5、右辺は8ですので、成立していません。
ここで新しい用語を確認しましょう。
成立している状態、正しい状態を「真」(しん)と呼びます。英語では「true」(トゥルー)と呼んでいます。
また、成立していない状態、正しくない状態を「偽」(ぎ)と呼びます。英語では「false」(フォルス)と呼んでいます。
このように成立している状態、成立していない状態は、日本語を話す人であれば「真」か「偽」、英語を話す人であれば「true」「false」ですが、コンピュータ内部では真偽を数字で表しています。
なんともわかりづらいですが、C++言語に限らず、他のプログラミング言語でも、真は0以外の数字、偽は0で表すことになっています。真の場合は0以外の数字ですが、一般的には数字の1が利用されています。
これまでのことをまとめると、以下のようになります。
状態 | 日本語 | 英語 | Arduino内部 |
成立している 正しい | 真 | true | 0以外 |
成立していない 正しくない | 偽 | false | 0 |
Arduinoボードは比較演算子を処理するとき、計算をして計算結果を見て成立しているか成立していないか判定しました。Arduinoボードの内部ではその判定結果を真か偽かを意味する0か1かに置き換えています。
それでは、Arduinoボードの中で、以下の比較がどのように行われているか、もう一度最初から順を追って見ていきましょう。
digitalRead(SWITCH) == 1
Arduinoボードは最初にdigitalRead(SWITCH)
を処理します。この時、スイッチがOFF、つまりdigitalRead(SWITCH)
の結果が1とします。Arduinoボードは上のスケッチを以下のように変えます。
1 == 1
これで比較ができるようになりましたので、Arduinoボードは「 == 」の左辺の数字から右辺の数字を引き算した結果を求めます。「 1 − 1 = 0 」ですので、結果は0です。「 == 」の場合、結果が0であれば成立している、0以外であれば成立していないという判定になりますので、この比較は計算結果が0ということで成立している、と判定します。
成立している、つまり真(true)ということがわかりましたので、Arduinoボードはこの比較を真を表す「1」に置き換えて処理が完了します。
1
Arduinoボードの内部ではなんだか不思議なことをしていて実感が湧きませんので、実際にArduinoボード内部の動作をシリアルモニタで確認してみましょう!
真か偽の判定
新規スケッチで確認してみます。スケッチ名は「true_false」にしておきましょう。
このスケッチでは、比較演算子が最終的に「1」(真)か「0」(偽)のどちらかに変換される様子をシリアルモニタで確認します。
具体的には、次の比較を行った結果がどうなるか、結果をシリアルモニタに表示して確認してみます。
(1 + 2) == 3
確認用に次のスケッチを作成しました。
/*
内容: 比較演算子の結果を表示する
*/
void setup() {
Serial.begin(9600); // シリアル初期化
}
void loop() {
// 1秒に1回、比較した結果をシリアルモニタに表示する
Serial.println( (1 + 2) == 3 );
delay(1000);
}
この内容でスケッチを作成したら、ArduinoボードをPCに接続して、シリアルモニタを開きます。準備ができたらスケッチをArduinoボードに送ります。
しばらく待っていると、以下のように「 (1 + 2) == 3 」の結果が表示されます。

Arduinoボードは、「 (1 + 2) == 3 」を演算して、その結果真であると判定したため「1」(真)と表示しています。
次は、比較が成立しないように書き換えて、その結果がどうなるか確認してみてください。例えば「 (1 + 3) == 5 」は成立しませんので、この結果を表示するとどうなるか確認してみてください。
成立しない場合は「0」(偽)が表示されますよね。
C++言語に限らず、プログラミングの世界では「真」は0以外の数字(一般的には数字の1)、「偽」は数字の0で表現する、ということは非常に重要ですので、十分理解しておきましょう!
「式」と「式の評価」
Arduinoボード内部ではdigitalRead(SWITCH) == 1
にしても(1 + 2) == 3
にしても、「 == 」の左辺から右辺を引き算してその結果が0か0以外かでこの比較が成立するか判定していました。
ここで、新しい言葉を覚えておきましょう。
digitalRead(SWITCH) == 1
や(1 + 2) == 3
などの計算や比較を「式」と呼んでいます。数学でも式が出てきますので特に違和感はないですよね。
またArduino内部では比較の式を、左辺から右辺を引き算してその結果が0か0以外かで成立するか判定しています。この判定する処理のことを「評価する」と呼びます。
言い方としては「 『 1 + 2 == 3 』という式を評価すると結果は真」などと言います。
「評価」というと、何かランク付けするイメージが強いかもしれませんが、プログラミングの世界では「評価」をこのような意味合いで使用しています。この言い方はC++言語に限らず、全てのプログラミング言語で共通です。
whileの処理
前回の記事で、whileは以下のように書きます、と説明しました。
while(条件)
命令;
whileは「条件」に書かれている内容を評価して、その結果が真か偽か判定しています。
つまり「条件」の部分が0か0以外か判定しているだけなんです。
この性質を利用して、「スイッチが押されるまで待つ」(スイッチがOFFの間、何もしない)というスケッチは次のようにも書けます。(一見するとわかりづらい書き方ですが…)
while( digitalRead(SWITCH) ) {
}
whileの「条件」の部分にdigitalRead(SWITCH)
だけ書いています。
digitalRead(SWITCH)
は、スイッチがOFFの時は1、ONの時は0になります。つまり、スイッチOFFの時は「1」=「真」、スイッチONの時は「0」=「偽」になるため、わざわざ「digitalRead(SWITCH) == 1」と書いて、1と比較する必要はないわけです。
このような書き方は「条件」の部分が比較ではないので一見わかりづらいですよね。でも他の人が書いたスケッチをみると、意外にこの書き方がされていることもあります。
このような書き方に出会ったら、戸惑わないようにしていただければと思います。
while部分をもう少しわかりやすく書く
whileの説明の最後に、スケッチをもう少しわかりやすく変更します。
と言われても、どこがどうわかりにくいの?って感じですよね。
最初にわかりにくい背景を説明します。わかりにくい部分はここです。
while(digitalRead(SWITCH) == 1){
}
何がどうわかりにくいのか、ちょっと???ですよね。
今まで一緒にスケッチを作ってきましたので、私たちはこのスケッチの意味がわかっています。
でも他の人が急にこのスイッチを見た場合を考えてみてください。
digitalRead(SWITCH)
という部分は、読み取っている端子はおそらくスイッチだろう、ということが想像できます。でも右辺の「1」ってなんなんでしょうか。スイッチがONなのかOFFなのか、直感的にわかりづらいですよね。
このように式の中に数字を直接書くとわかりづらいことが多々あります。そこで、スイッチONの時とスイッチOFFの時で、digitalRead(SWITCH)
の戻り値がどのような値になるのか、次のように#defineで明確にしておきます。
#define SWITCH_OFF 1
#define SWITCH_ON 0
このように定義しておくと、先ほどのwhile構文は以下のように書けます。
while( digitalRead(SWITCH) == SWITCH_OFF ){
}
これを初めてみた人は「スイッチの値を読んで、その値がスイッチOFFの間、何もしないんだな」ということが想像できます。
今までの内容をスケッチにまとめます。
/*
キッチンタイマー
内容: スイッチ、LED、スピーカーを使ったキッチンタイマー
変更履歴:
2024.11.25: 新規作成
2024.12.01: スイッチが押されたらLED点滅開始
2024.12.02: スイッチ関連の#define追加
*/
// 秒を表現するLED関連の定義
#define BYOU_LED 12 // 秒を表現するLEDの接続端子
#define BYOU_ON 50 // 秒を表現するLEDを点灯している時間(単位:ミリ秒)
#define BYOU_OFF (1000 - BYOU_ON) // 秒を表現するLEDを消している時間(単位:ミリ秒)
// タイマースタートスイッチ関連の定義
#define SWITCH A5 // スイッチを接続している端子名
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON 0 // スイッチONの時のdigitalReadの値
void setup() {
// 端子の設定
pinMode(BYOU_LED, OUTPUT); // 秒表現のLED接続端子の出力設定
pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子をプルアップ設定
// スイッチが押されるまで何もしないで待つ
while( digitalRead(SWITCH) == SWITCH_OFF ) {
}
}
void loop() {
// 秒表現LEDを点滅する
digitalWrite(BYOU_LED, HIGH);
delay(BYOU_ON);
digitalWrite(BYOU_LED, LOW);
delay(BYOU_OFF);
}
【補足】ずっと繰り返す処理の書き方
電子工作のプログラミングでは、ずっと何かの処理を繰り返す、という動作をすることがいろいろな場面で出てきます。
他の人が書いたスケッチでもずっと繰り返すという処理がよくみられます。そのようなスケッチを読めるように、いくつか書き方を覚えておきましょう。
ずっと繰り返す、ということは、whileの「条件」が常に「真」であればOKです。真は「1」ですので以下のように書くと「指示」をずっと繰り返します。
while(1){
指示;
}
「真」は0以外であれば良いので、上のスケッチではwhile(2)
でも問題ありません。「1」という数字自体には深い意味がありませんので、このような書き方はちょっと意味が取りづらいことがあります。
実際には上のような書き方をする人もいますが、他の書き方をする人もいます。
実は、Arduino IDEでは、真と偽について、裏で以下のように定義してくれています。
名前 | 値 |
true | 1 |
false | 0 |
「true」は「真」、「false」は「偽」の意味でしたよね。Arduino IDEではデフォルトでこの定義をしてくれていますので、いつでも使えます。
そのため、他の人のスケッチを見ると、ずっと繰り返す場合は以下の書き方もよく見かけます。
while(true){
指示;
}
whileの扱いに関しては前回と今回の記事で解説は終わりです。
スタートスイッチの機能が実現できましたので、次回からいよいよ時間を計測するスケッチを作成していきむます。
ミニチャレンジ課題
ちょっと無理やり感のある課題ですが、以下のスケッチを作成してみてください。
すでにLEDを1秒間に1回「ピカッ、ピカッ」とずっと繰り返し光らせるスケッチを作成しました。LED制御はずっと繰り返しますので、loopの中に命令を書きました。
今回は、loopの中に何も命令を書かない、つまりsetupだけを使って同じ制御をするスケッチを作成してみてください。
「スイッチを押している間、点滅を続ける」というスケッチを作成してみてください。
スイッチを押し続けている間はLEDは点滅を繰り返し、スイッチを離しているときはLEDは消えたまま、という動作になるようにしてみてください。この動作はloopに書いてください。
ミニチャレンジ課題の解答例
スケッチの書き方は唯一の正解があるわけではありませんので、以下の解答例は参考にしていただければと思います。
どんなスケッチでも思った通りに動けば正解です!
ミニチャレンジ課題1の解答例
ずっと処理を繰り返す処理はloopに書くべきですが、この課題はwhileの理解を深めるための無理矢理感漂う課題です。
loopには何も書かず、電源投入後に最初に1度だけ実行されるsetupだけでずっとLEDの点滅を繰り返す処理が必要です。
「ずっと繰り返す」場合、プログラミングの定石の「while(true){…}」を使えば実現できますので、次のようなスケッチにしてみました。
/*
* ミニチャレンジ課題1スケッチ例
* これが唯一の正解ではありませんが、参考にしていただければと思います。
* このスケッチと違っても動けばそれが正解です!
*/
// LEDの接続端子
#define BYOU_LED 12
void setup() {
// setupはArduino電源投入直後に1度実行されるだけなので、
// ここでずっと繰り返す処理をwhile文を使って書く
// LED接続端子の設定
pinMode(BYOU_LED, OUTPUT);
// 1秒に1回青色LEDを点滅する処理をずっと繰り返す
// (LEDの点灯時間は50msで固定)
while(true) {
digitalWrite(BYOU_LED, HIGH);
delay(50);
digitalWrite(BYOU_LED, LOW);
delay(950);
}
}
void loop() {
// loop内には何も処理を書かない
}
ミニチャレンジ課題2の解答例
課題1に続いて、こちらもちょっと無理矢理感が出ている内容です。
「〇〇の間繰り返す」という処理は、whileが使えます。
「スイッチが押されている間、LED点滅を繰り返す」という処理をしたいので、whileの条件式でスイッチがONだったら繰り返す、という処理にしてみました。
/*
* ミニチャレンジ課題2スケッチ例
* これが唯一の正解ではありませんが、参考にしていただければと思います。
* このスケッチと違っても動けばそれが正解です!
*/
// LEDの接続端子
#define BYOU_LED 12
// スイッチ関連の定義
#define SWITCH 23 // スイッチを接続している端子番号
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON 0 // スイッチONの時のdigitalReadの値
void setup() {
// 端子の設定
pinMode(BYOU_LED, OUTPUT); // 青色LED接続端子設定
pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子の設定
}
void loop() {
// スイッチがONの間、点滅を繰り返す
// スイッチがOFFの時、このwhile文を終了しますが、
// loop内なので再度while文が実行され、
// 再度スイッチがONの間、点滅を繰り返す、という処理がされます
while( digitalRead(SWITCH) == SWITCH_ON ) {
// 青色LEDを点滅する
digitalWrite(BYOU_LED, HIGH);
delay(50);
digitalWrite(BYOU_LED, LOW);
delay(950);
}
}
今回の記事ではwhile文がテーマなので、上のようなスケッチを解答例としました。
動作させてみると、スイッチを押している間はLEDが点滅、スイッチを離すと点滅が停止する、という動作に見えます。
でも実際には、スイッチのON/OFFの判定はwhileの条件判定時の一瞬で行われています。whileの処理部分の「LEDを点灯する➡︎50ms待つ➡︎消す➡︎950ms待つ」の1秒間の間はスイッチ状態は関係ありません。
厳密に「スイッチを押している間、LEDを点滅」という動作にはなっていない点に注意してください。(厳密に「スイッチが押されている間、点滅を繰り返す」という処理は実はかなり難しい知識が必要になります。今回はこのスケッチを解答例としました)
なお、本来であれば上のような処理をしたい場合、このシリーズ記事の後半で出てくる「 if 」を使う方がより良いです。以下のスケッチでも動作しますので参考にしてみてください。
/*
* ミニチャレンジ課題2スケッチ例
* このような処理の場合、while文よりはif文を使うべきですが、
* if文の解説はこの後出てきますので、以下のスケッチは参考となります。
*/
// LEDの接続端子
#define BYOU_LED 12
// スイッチ関連の定義
#define SWITCH 23 // スイッチを接続している端子番号
#define SWITCH_OFF 1 // スイッチOFFの時のdigitalReadの値
#define SWITCH_ON 0 // スイッチONの時のdigitalReadの値
void setup() {
// 端子の設定
pinMode(BYOU_LED, OUTPUT); // 青色LED接続端子設定
pinMode(SWITCH, INPUT_PULLUP); // スイッチ接続端子の設定
}
// スイッチがONの間、点滅を繰り返す
void loop() {
// loopは何度も繰り返しますので、
// loop内ではスイッチがONの場合、LEDを点滅する、という処理をif文で書きました。
// if文はシリーズ後半で解説していますが、動作としてはif(条件)で条件が成立していれば
// その後の処理を実行する、という制御になります
if( digitalRead(SWITCH) == SWITCH_ON ) {
// 青色LEDを点滅する
digitalWrite(BYOU_LED, HIGH);
delay(50);
digitalWrite(BYOU_LED, LOW);
delay(950);
}
}
更新履歴
日付 | 内容 |
---|---|
2019.8.17 | 新規投稿 |
2021.8.22 | 新サイトデザイン対応 |
2022.2.13 | ミニチャレンジ課題追加 |
2024.10.24 | ミニチャレンジ課題解答例追加 |
2024.12.2 | 説明内容変更 Arduino IDE2対応 |
ミニチャレンジの回答スケッチが欲しいです。
コメントどうもありがとうございました。
ご参考にミニチャレンジ課題の解答例を追加しました。
ただ、スケッチ(プログラム)は唯一の正解はありませんので、他のスケッチでも動作すれば正解です。
ただスケッチに慣れていないと「これでもいいのかな?」と思うこともあるかと思います。その時はコメント欄からご相談いただければと思います!