前回、プログラムは順次・選択・反復の3つでできているという話を書きました。今回はその続きで、一度作った処理を、何度も呼び出して使う「関数」を扱います。
これも、どの講座でも同じ回を設けています。使う言語は違っても、やっていることは同じだからです。
呼び名だけは変わります。Python や C++ では「関数」ですが、LEGO SPIKE では「マイブロック」と呼びます。指すものは同じです。
同じ処理を、何度も書かない
たとえばロボットに「1マス進む」をさせるとき、実際には5つの命令が必要です。モーターAを回し、モーターBを回し、0.8秒待ち、Aを止め、Bを止める。
迷路を進むプログラムでは、この5つの命令が何度も出てきます。曲がったあとにも、まっすぐ進むときにも、同じ5つを書くことになります。
そこで、この5つをひとまとめにして「1マス進む」という名前をつけ、あとはその名前で呼び出します。それが関数です。中身を書くのは一度だけで、使うのは何度でも。
何度も使えると、何がいいのか
いいことが3つあります。
① 書く量が減る。「1マス進む」を10か所で使っても、中身を書くのは1回で済みます。
② 名前が説明になる。「モーターを回して0.8秒待って止める」は読み解く必要がありますが、「1マス進む」は読めばわかります。
③ 直す場所が1か所になる。これが一番効きます。①の裏返しです。
大会の会場で、床が思ったより滑って、ロボットが1マスぶん行きすぎてしまった。待ち時間を少しだけ短くしたい。
名前をつけていなければ、同じ処理を書いた場所を全部探して直すことになります。10か所あれば10か所。1か所直し忘れれば、そこだけずれます。
関数にしてあれば、中身を1か所直すだけです。会場で使える調整時間は長くありません。この差がそのまま結果に出ます。
① ブロック(LEGO SPIKE)
LEGO SPIKE には「関数」という言葉は出てきません。かわりに「マイブロック」という名前で、同じものが用意されています。
名前をつけて自分でブロックを作ると、パレットに新しいブロックが増えます。
作ったブロックは、最初からあるブロックと同じように置いて使えます。関数が本当に「ブロック」として目に見える形になるのは、ブロックならではのところです。
② Python
def forward(cm): # ← ここが「定義」
left.start(50)
right.start(50)
sleep(cm * 0.04)
left.stop()
right.stop()
forward(30) # ← 呼ぶ
def は define(決める)の略です。字下げされているあいだが中身で、戻ったらそこで終わり。ブロックの「定義」のコの字と、やっていることは同じです。
③ C++
void forward(int mm) { // ← void = 返すものがない
motorLeft(50);
motorRight(50);
delay(mm * 3);
stopAll();
}
forward(300); // ← 呼ぶ
C++ では波かっこ { } で囲みます。先頭の void は「返すものがない」という意味です。
あとから、入口と出口をつける
ここまでは、名前をつけて呼び出すだけの関数でした。慣れてくると、関数に入口と出口をつけたくなる場面が出てきます。順に見ていきます。
自動販売機と同じです。お金を入れると飲み物が出てくる。中でどういう仕組みが動いているかは、使う人は知らなくて構いません。
入口が要るのは、数だけ変えたいとき
迷路を走らせていると、こういう場面が出てきます。
曲がった直後は1マスだけ進みたい。でも、まっすぐな通路に出たら3マス続けて進みたい。動きは同じで、マスの数だけが違う。
入口がなければ、「1マス進む」「2マス進む」「3マス進む」を別々に作ることになります。マイブロックが3つに増えます。
そして床が滑って調整するときには、3つとも直すことになります。せっかく関数にまとめたのに、また同じ問題が起きてしまう。
入口をつければ、1つで足ります。
定義: ( masu )マス進む
└─ 【 masu 】回繰り返す
└─ (これまでの1マス分の動き)
呼ぶとき: 【( 1 )マス進む】
【( 3 )マス進む】
文字の言語でも、やることは同じです。
def forward(masu): # masu が入口
for i in range(masu):
...
forward(1)
forward(3)
出口が要るのは、調べた結果で決めたいとき
こんどは、こういう場面です。
前の壁までの距離をセンサーで測って、近ければ曲がる、遠ければ進む。
「距離を調べる」という処理を関数にまとめたくなります。ところが調べただけでは、何も決められません。調べた数字を受け取って、はじめて「曲がるか、進むか」を判断できます。
そこで出口をつけて、関数に答えを返してもらいます。
int distFront() { // ← int = 整数を返す
return analogRead(A0) * 2; // ← これが答え
}
if (distFront() < 150) { // 150mmより近ければ
turnLeft(90); // 曲がる
} else {
goOneCell(); // 進む
}
distFront() と書いた場所が、そのまま「前の壁までの距離」という数字になります。センサーの配線や計算式を知らなくても、この4行を読めばロボットの判断がわかります。
ここは言語によって事情が違うところで、ブロックのマイブロックは答えを返せません。かわりに変数を使って、「呼ぶ → 変数を読む」の2段構えにします。
囲み方が違うだけ
3つを並べると、違うのは囲み方だけだとわかります。
| 呼び名 | 関数の範囲 | 出口 | |
|---|---|---|---|
| LEGO SPIKE | マイブロック | 「定義」のコの字 | なし(変数で代用) |
| Python | 関数 | 字下げ | return |
| C++ | 関数 | 波かっこ { } | return |
どの講座でも、同じ話をしている
小学生はマイブロックに名前をつけ、高校生は def や void を書きます。やっているのは、どちらも「一度作ったものを、何度も呼び出して使う」ことです。
この見方ができていると、ブロックから文字の言語に移るときに覚え直すことがほとんどありません。新しい書き方を知るだけで済みます。
ロボカップジュニア2027 に向けて、レスキュー(災害救助)とサッカーの2種目に取り組みます。プログラミングと電子工作の経験は問いません。
前回:ブロックでも C++ でも、プログラムの構造は同じ →
ロボサイエンスのコース紹介 →
体験授業・お問い合わせ →