#navi(NQC入門)
ロボットに複雑な動作をさせようとすると、 当然プログラムの方も複雑になってきます。 そこでプログラムが複雑になっても デバッグ(プログラムの間違い探し)がしやすく、 また改良もしやすいように、 なるべく一連の動作を部品にわけて記述しましょう。 以下部品に分かる方法をいくつか紹介します。 

目次
#contents

* 6.1 サブルーチンを使う [#d4e4939d]

以下のような、直進したり左右に曲がるロボットのプログラムを考えましょう。

 #define TURN_TIME 200     // 旋回に必要な時間
 
 task main()
     OnFwd(OUT_A+OUT_C); Wait(300);   // 3秒間直進
     OnRev(OUT_A); OnFwd(OUT_C); Wait(TURN_TIME); // 左に曲がる
     Off(OUT_A+OUT_C);
     OnFwd(OUT_A+OUT_C); Wait(400);   // 4秒間直進
     OnFwd(OUT_A); OnRev(OUT_C); Wait(TURN_TIME); // 右に曲がる
     Off(OUT_A+OUT_C);
 }

もちろんこの程度の簡単なプログラムであれば、ロボットにさせたい動作を順番に書き下していってもそれほど大変ではありません。しかし、もう少し複雑なプログラムになってくると、同じ動作を記述する命令を何度も繰り返して記述するのが面倒になってきたり、その一連の動作を少し変更させたいときにも、関連するすべての箇所を書き換えなければいけません。

そこで一連の動作を「サブルーチン」という形で定義しておくことで、プログラムが見やすくなるだけでなく、改良も加えやすくなります。
以下は、左に曲がるという動作を記述した turn_left という名前のサブルーチンと、右に曲がるという動作を記述した turn_right という名前のサブルーチンを定義したサンプル・プログラムです。

 #define TURN_TIME 200     // 旋回に必要な時間
 
 sub turn_left()
 {
     OnRev(OUT_A); OnFwd(OUT_C); Wait(TURN_TIME);
     Off(OUT_A+OUT_C);
 }
 
 sub turn_right()
 {
     OnFwd(OUT_A); OnRev(OUT_C); Wait(TURN_TIME);
     Off(OUT_A+OUT_C);
 }
 
 task main()
 {
     OnFwd(OUT_A+OUT_C); Wait(300);   // 3秒間直進
     turn_left();                     // 左に曲がる
     OnFwd(OUT_A+OUT_C); Wait(400);   // 4秒間直進
     turn_right();                    // 右に曲がる
 }

このようにサブルーチンを定義するには sub というキーワードを使い、
そのサブルーチンをメインルーチン(メインタスク)から呼び出すときには、
サブルーチン名のあとに()をつけます。
 
★ 上の例題について、「直進する」というサブルーチンを go_straight という名前で定義して書き換えなさい。


* 6.2 関数を使う [#a7a6e4cb]

上の例を、今度は「関数」を使って書き直して見ましょう。
ここで言う「関数」とは、中学校や高校の数学で習った、ある独立変数(入力)に
対して従属変数(出力)が決まる、y=f(x) のような形の場合もありますが、
この例のように、入力された値に従って何かの動作をするだけで何の値も返さない、そういう関数もあります。このことは他のプログラミング言語でも同様です。

 #define TURN_TIME 200
 
 void turn_left()
 {
     OnRev(OUT_A); OnFwd(OUT_C); Wait(TURN_TIME);
     Off(OUT_A+OUT_C);
 }
 
 void turn_right()
 {
     OnFwd(OUT_A); OnRev(OUT_C); Wait(TURN_TIME);
     Off(OUT_A+OUT_C);
 }
 
 void go_straight(int t)           // 関数は引数を取ることができる
 {
     OnFwd(OUT_A+OUT_C); Wait(t);  // t/100 秒直進する
     Off(OUT_A+OUT_C);
 }
 
 task main()
 {
     go_straight(300);  // 3秒間直進
     turn_left();       // 左に曲がる
     go_straight(400);  // 3秒間直進
     turn_right();      // 右に曲がる
 }

C言語などでは、関数が返す値の型 (例えば、整数型を表す int や 倍精度の小数を表す double) を関数の名前の前に書きます。 一方、例えば、ある文字列を表示するだけ、という関数のように値を返さない関数の場合には void というキーワードを 書きます。 

ところで実は NQC の場合、値を返す関数というのがサポートされていないので、 全ての関数が void という型なります。 

もし何かの値を返したい場合には、その値をグローバル変数として定義しておいて関数の中でその変数に値を代入するようにすれば、一応同等のことができます
(現在では一般に推奨されない方法ですが)。 

また、サブルーチンと違って関数はコンパイル時に展開されてしまうので、何度も関数を呼ぶようなプログラムを書けば、プログラムのサイズがとても大きくなってしまいます。 

* 6.3 マクロを使う [#b3f5a436]

プログラムを部品化するもう一つの方法は、定数を定義したのと同じ、#define を使う方法です。#define で定義した一連の動作はマクロと呼ばれます。

 #define TURN_TIME 200
 #define turn_left OnRev(OUT_A);OnFwd(OUT_C);Wait(TURN_TIME);Off(OUT_A+OUT_C);
 #define turn_right OnFwd(OUT_A);OnRev(OUT_C);Wait(TURN_TIME);Off(OUT_A+OUT_C);
 #define go_straight(t) OnFwd(OUT_A+OUT_C);Wait(t);Off(OUT_A+OUT_C);
 
 task main()
 {
     go_straight(300);  // 3秒間直進
     turn_left;         // 左に曲がる
     go_straight(400);  // 4秒間直進
     turn_right;        // 右に曲がる
 }

ただし、サブルーチンや関数と違ってマクロは必ず一行で書く必要があります。
マクロを実行させる場合にはマクロ名を書くだけでよく、()は必要ありません。

また関数と同様にマクロは引数を取ることができます。 そして、これも関数と同様、マクロはコンパイル時に展開されます。 

★ 練習問題

これまでに書いたプログラムを サブルーチンや関数やマクロを使って わかりやすいプログラムに書き換えなさい。

#navi(NQC入門)

トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS