こくぶん研究室

プログラムを書く時のコツ

コードは文章

例えばこのサイトの Processing 編の「AR を作ろう」に、以下のようなコードがあります。

void draw() {
  if(cam.available() == false) {         // カメラが利用可能な状態でなければ
    return;                              // 何もせず処理を終える
  }
  cam.read();                            // 映像をキャプチャする
  image(cam, 0, 0);                      // キャプチャした画像を表示する
}

7行のコードですが、これは以下のように書くこともできます。

void draw(){if(cam.available()==false){return;}cam.read();image(cam,0,0);}

「書くこともできる」というか、実はコンピュータ側はこのような1行だと解釈して実行しています。プログラムは実行される時に「裏で自動的に」余分な改行や空白やコメント文を削除して、コンピュータにとって意味のある命令だけにしてから、実行します。

でも、たくさんの命令を1行にまとめると、人間は読みにくいですよね? また、コメント文が無いと、後から見返したり他の人が見た時に分かりにくいですよね?

ところでこのコードは、以下のように書くこともできます。

void draw() {
if(cam.available() == false) {
return;}
cam.read();
image(cam, 0, 0);}

これでもコンピュータ側は全く同じプログラムとして実行してくれます。でも、人間からは少し見にくい気がしませんか?

コードの左側がぎゅっと詰まっていると、どこからどこまでがまとまりなのか、パッと見て分かりにくいですね。普通の文章には「段落」があって、段落の始めは字下げをしたり、章や節の間で1行空けたりすることで、パッと見て「ここからここまでがひとつのまとまり」と分かりやすくなります。プログラムも同じように、段落が無いと読みにくくなります。

さて次に、以下のコードはエラーになりますが、どこが間違っているでしょうか? 間違いは3箇所あります。

void draw() {
if(cam.available() == false) {
return;
cam.read()
image(cam, 0, 0);

段落の無いコードは、読みにくいだけでなく、間違いを見つけにくくなります。上の間違いが見つけにくいコードも、段落をつけると発見しやすくなります(以下)。

void draw() {
    if(cam.available() == false) {
        return;
    cam.read()
    image(cam, 0, 0);

draw() { } と、if() { } に必要な閉じカッコ } が2箇所に無いことが見つけやすくなります。閉じカッコ } は普通の文章に例えると句点(。)です。プログラムに段落をつけると、コード(文章)の最後に句点(。)となる閉じカッコ } が無いことに気づきやすくなります。

でも、4行目の最後にセミコロン ; が無いのは、段落を作っても意外に気づきにくいですね。セミコロン ; は普通の文章に例えると読点(、)です。普通の文章では、読点を置くかどうかは書き手の自由度が高く、あってもなくても構わないものですが、プログラムでは必要なところには絶対に必要です。プログラミングを間違いなく書く(正しい文章を書く)のは、難しいですね...

▲TOP

読みやすく・間違いにくく

プログラミングは最終的には「動けば良い」のですが、間違いなく動かすには、書き方にコツがあります。コツを抑えて書くことで、「動かない」「間違いが分からない」という状況に陥って悩むことが大幅に減ります。プログラミングを楽しむために、ぜひコツを抑えておきましょう。

基本は「読みやすい」コツと「間違いにくい」コツです。挙げればいくらでもコツがあるのですが、ここでは五つを紹介します。

以下で順に紹介します。

▲TOP

インデントや改行する(段落)

インデントとは「字下げ」のことです。プログラムは命令がたくさん並んでいますが、「まとまり」があります。命令のまとまりごとに字下げをして、左側を揃えておくことで、パッと見てまとまりが分かりやすくなります。まとまりが分かりやすくなると、後から見返す時に読みやすく、間違い(バグ)を見つけやすく、後から処理を追加する時も場所が分かりやすく、自分以外の人が読んでも読みやすくなります。

例えばこのページの冒頭で以下のようなコードを挙げました。

void draw() {
  if(cam.available() == false) {
    return;
  }
  cam.read();
  image(cam, 0, 0);
}

2~6行目は2文字ずつインデント(字下げ)してあります。また、3行目はさらに2文字インデントしてあります。

このプログラムはまず、draw() { } という一番大きなまとまりのコードがあります。それが一番左になっています。

次に draw() { } の { } 内に 2~4行目の if() { } というまとまり1と、5行目の cam.read; という文章2と、6行目の image( ); という文章3があります。これらをインデントしておけば、draw() { } の { } 内には三つのまとまり(文章)があることがパッと見て分かります。

さらに if() { } の { } 内に 3行目の return; という文章があります。この文章は if() { } の中だけで動いている命令なので、さらに2文字インデントしています。こうすることで、cam.read(); や image( ); とは別のまとまり(階層)であることがパッと見て分かります。

インデント(字下げ)する文字数は何文字分でも構わないのですが、よく使われるのは、半角スペース2文字分、または、4文字分です。パソコンのキーボードの「Tab」キーを打てば、一気に数文字分のスペースが入ります。「Tab」キーを使ってプログラムの「まとまり」を「インデント(字下げ)」しましょう。

次に、改行も気にしておきましょう。例えば以下のように閉じカッコ } を改行せずに書くこともできます。

void draw() {
  if(cam.available() == false) {
    return;}
  cam.read();
  image(cam, 0, 0);}

長いプログラムになってくると、改行をケチって、なるべく行数を少なくして詰めたくなるかもしれません。しかし、改行しない方式で書くと、もしこれら(3行目と5行目の末尾)の閉じカッコを書き忘れていた場合、見つけるのが少し難しくなります。また、draw() { } という命令や、if() { } という命令がどこまで続いているのか、まとまりが見分けにくくなります。

閉じカッコ } ひと文字だけのために1行を使うのは無駄と思うかもしれません。でも、後々の見やすさのためには、まとまりの部分では積極的に改行しましょう。

ひとつのまとまりの中での改行だけでなく、まとまりの「間」でも、改行したり、空白行を入れましょう。例えば以下のコードは、2行目、4行目、12行目に空白行があります。

import processing.video.*;

Capture cam;

void setup() {
  size(640, 480);
  String[] cameras = Capture.list();
  printArray(cameras);
  cam = new Capture(this, cameras[0]);
  cam.start();
}

void draw() {
  if(cam.available() == false) {
    return;
  }
  cam.read();
  image(cam, 0, 0);
}

1行目は外部のライブラリを読み込む命令で、3行目は変数を宣言する命令です。目的が違う命令なので、1行空けています。こうしておくと、例えば後から別の変数を宣言する時に、3行目の次に書けばよいことがパッと分かります。

12行目も空けなくても全く構わないのですが、空けておけば、プログラムのまとまりが見やすくなります。「5~11行目と、13~19行目は別のことをしている」ということがパッと分かります。

さらに、時にはひとつの命令の中で改行することで見やすくすることもあります。例えば以下を見てください。

void setup() {
  size(640, 480, P3D);
  String[] cameras = Capture.list();
  printArray(cameras);
  cam = new Capture(this, cameras[0]);
  cam.start();
  mm = new MultiMarker(this,
             width,
             height,
             "camera_para.dat",
             NyAR4PsgConfig.CONFIG_PSG);
  mm.addNyIdMarker(0, 80);
}

7~11行目は、実はひとつの命令文です。普通は以下のように書きます。

mm = new MultiMarker(this, width, height, "camera_para.dat", NyAR4PsgConfig.CONFIG_PSG);

これは読みにくいですよね? 1行があまりに長くなってしまう場合は、改行すると読みやすくなります。また、この例の MultiMarker( ) というオブジェクトには 5個の引数を渡すということが、改行すると一目瞭然になります。

▲TOP

大枠を作ってから中身(句読点)

インデントや改行で読みやすく、間違いを見つけやすくなりますが、そもそも、書き間違えにくい書き方があります。例えば、以下のようなコードを書く時、皆さんならどう書きますか?

canvas.addEventListener("touchmove", (e) => {
    e.preventDefault();
    if(touching == true) {
        var endX = e.touches[0].pageX - canvas.offsetLeft;
        var endY = e.touches[0].pageY - canvas.offsetTop;
        context.beginPath();
        context.moveTo(startX, startY);
        context.lineTo(endX, endY);
        context.stroke();
    }
});

1行目から書き始めて、次に2行目、次に3行目... と書いていって、最後に11行目を書きますか? そうしたくなりますが、それだと間違いやすくなります。例えば、10行目や11行目の閉じカッコ } や ) や最後のセミコロン ; は書き忘れがちです。当然、書き間違いや書き忘れがあれば、プログラムは動きません。

上の例のプログラムは、たくさんの階層構造で成り立っています。それぞれのカッコ { } や セミコロン ; がどこと対応しているのかが見にくいプログラムです。上から順番に書いていくと、頭がゴチャゴチャしてきます。

こういう場合は、まず以下のように書きます。

canvas.addEventListener();

addEventListener( ) というメソッドは、二つの引数を取ります。第1引数がイベント名、第2引数がそのイベントが起きた時に実行される関数です。さらにその関数の中に処理を書きます。これを順番に書いていくと頭がゴチャゴチャしてくるので、まずは、addEventListener( ); というメソッドだけを書ききってしまいます。閉じカッコ ) とセミコロン ; まで書ききってしまいます。

その次に、以下のように二つの引数を書ききってしまいます。イベント名を指定するダブルクォーテーション " " も閉じるのを忘れがちですし、第2引数の関数のとじカッコ } も忘れがちです。これを最初に書ききってしまいます。

canvas.addEventListener("", (e) => {});

addEventListener( ) の大枠ができたら、次にそれぞれの中身を入れていきます。まずは第1引数のイベント名。

canvas.addEventListener("touchmove", (e) => {});

次に第2引数の関数の中身を、{ と } の間に改行を入れて...

canvas.addEventListener("touchmove", (e) => {
    e.preventDefault();
});

この次の if () { } 文も同じように大枠を書ききって(閉じカッコ } まで書いて)...

canvas.addEventListener("touchmove", (e) => {
    e.preventDefault();
    if() {}
});

if () の ( ) 内に条件式を書いて...

canvas.addEventListener("touchmove", (e) => {
    e.preventDefault();
    if(touching == true) {}
});

{ と } の間に改行を入れて、その条件式が満たされた時の処理を書きますが...

canvas.addEventListener("touchmove", (e) => {
    e.preventDefault();
    if(touching == true) {
        var endX = ;
    }
});

4行目の計算式も焦らずに、最後のセミコロン ; を先に書いてしまいます。計算式を頭で考えていると、気持ちが数式に集中するものです。すると、数式を書き終えた後に気持ちが安心してしまって、セミコロン ; を書くのを忘れがちです。だから最初に書いておきます。

4~6行目を書いたら、次に context.moveTo(startX, startY); や context.lineTo(endX, endY); を書きますが、その時も...

        context.moveTo();
        context.lineTo();

のように、moveTo() や lineTo() の ( ) 内の引数は後回しにして、閉じカッコ ) や、最後のセミコロン ; まで書ききってしまいます。その後に引数を指定していきます。

        context.moveTo(startX, startY);
        context.lineTo(endX, endY);

以上のように「まずは大枠を書ききって(閉じカッコやセミコロンまで書いてしまって)から、引数や処理などの詳しい内容を書く」という順番で書いていくことで、カッコの対応がとれていなかったり、セミコロンの書き忘れが原因でプログラムが動かないということを、大幅に減らすことができます。また、長いプログラムでも、頭の中がゴチャゴチャせず「ひとつひとつ」作っていくことができます。

▲TOP

コメントを書く(注記)

このページの最初に挙げた例文では、コードの右のほうにコメント文が書いてありますね。

void draw() {
  if(cam.available() == false) {         // カメラが利用可能な状態でなければ
    return;                              // 何もせず処理を終える
  }
  cam.read();                            // 映像をキャプチャする
  image(cam, 0, 0);                      // キャプチャした画像を表示する
}

自分でプログラムを書いている時は、自分の頭のなかで理解しながら書いていますから、いちいちコメント文を書くのは面倒なものです。でも、例えば数日後に自分が書いたプログラムを見返した時に、「あれ? これって何やってたんだっけ?」「なんでこんな数式にしたんだっけ?」ということがよくあります。コメント文は、未来の自分のためにたくさん書きましょう。

コメント文を書くコツは、「全ての行に書く」ことと「できるだけ分かりやすい言葉に翻訳する」ことです。

いちいちコメント文を書くのは面倒なので、例えば以下のように、難しいと思われる部分だけにコメント文を書きたくなります。

void draw() {
  if(cam.available() == false) {
    return;
  }
  cam.read();
  mm.detect(cam);                        // キャプチャした画像内でマーカを探す
  mm.drawBackground(cam);                // ウィンドウの背景にキャプチャした画像を設定
  if(mm.isExist(0) == false) {
    return;
  }
  mm.beginTransform(0);                  // マーカ[0] の位置にもとづいて座標の投射(変換)を始める
    translate(0, 0, 40);
    fill(255, 165, 0, 127);
    box(80);
  mm.endTransform();                     // 座標の投射(変換)を終了
}

if 文や return などはどんなプログラムでも使う標準的なコードなので、いちいち書くのは面倒になりがちです。しかし、その if がなぜそこにあって、何をしているのかは、プログラムごとに違います。

また、translate() や fill() などは Processing では標準的なメソッドなので、これらもいちいちコメントを書くのが面倒なものです。しかし、( ) 内の引数の意味は、書いた本人でないと分かりません。

たとえ標準的なコードでも、その「意味」を書いておくことで、後から見返した時に「なぜ?」と悩むことが減ります。長いプログラムになるほど、一日で作り終わることは少ないものです。些細なことでも「全ての行に書く」ようにしましょう。全ての行に書くのは面倒に思えますが、後々、効率的になるのです。

次に、例えば以下のようなコメントを見かけることがあります。

void draw() {
  if(cam.available() == false) {         // カメラ利用不可
    return;
  }
  cam.read();                            // キャプチャ
  mm.detect(cam);                        // 検出
  mm.drawBackground(cam);                // バックグラウンド
  if(mm.isExist(0) == false) {           // マーカ[0] なし
    return;
  }
  mm.beginTransform(0);                  // トランスフォーム開始
    translate(0, 0, 40);                 // トランスレート
    fill(255, 165, 0, 127);              // フィル
    box(80);                             // ボックス
  mm.endTransform();                     // トランスフォーム終了
}

ほとんど全ての行には書かれていますが、コメント文の中身が抽象的です。例えば「トランスフォーム開始」。書いた本人や、書いた当日はよいかもしれませんが、別の人や、別の日に読んだら「なんのこと?」になります。コメント文を読んで意味が分からない場合は、コードを読み返していって処理の内容を「理解し直す」必要がでてしまいます。

プログラム言語というのは、コンピュータが理解しやすい言葉と人間が理解しやすい言葉の中間、くらいの言葉です。相当に慣れ親しんだプロでもなければ、プログラムのコードを読むのは「翻訳」みたいなものです。つまり、コードを読むのは疲れるのです。

疲れるようでは、せっかくのプログラミングの面白さを楽しめません。コメント文を書く時は、できるだけ分かりやすい言葉に翻訳して、いつ誰が読んでも理解できるようにしておきましょう。そうしておくことで、後々、効率的になるのです。

また、以下のようなコメント文もよくあります。

void draw() {
  if(cam.available() == false) {         // if the camera is not available
    return;                              // do nothing and return
  }
  cam.read();                            // capture web cam image
  mm.detect(cam);                        // detect the marker within web cam image
  mm.drawBackground(cam);                // fill window background with cam image
  if(mm.isExist(0) == false) {           // if the marker[0] is not exist
    return;                              // do nothing and return
  }
  mm.beginTransform(0);                  // begin transform procedure
    translate(0, 0, 40);                 // translate the origin
    fill(255, 165, 0, 127);              // set fill color
    box(80);                             // draw box
  mm.endTransform();                     // end of the transform procedure
}

コメント文を英語(的な言葉)で書いたものです。実はプログラミングの世界では伝統的に、コメント文を英語(的な言葉)で書く習慣があります。

昔は日本語などの文字(2バイト文字)はコンピュータでは扱えませんでした。日本語が入力できなかったり、入力できたとしても環境によっては文字化けしたり、そもそも日本語があるとプログラムが動かないなどがありました。

また、インターネットで世界中の人々がプログラムのノウハウを情報交換する時代です。そこで、コメント文を英語(的な言葉)で書いておけば、世界中の人々からアドバイスをもらったり、情報交換できる可能性が高まります。

しかし、「誰のためにコメント文を書くか」を考えましょう。例えば自分の趣味として遊んでいるだけなら、自分の書いたプログラムを誰にも見せることはないわけですから、自分がいちばん読みやすい言葉で書いておけば構いません。また、仕事であっても、その仕事に携わっている人たちがもっとも読みやすい言葉で書いておくことが大事です。いちいち英語(的な言葉)に翻訳して書くのも、読むたびにいちいち翻訳するのも、疲れます。私はいつも自分がいちばん読み書きしやすい日本語で書くことにしています。

もちろん、英語(的な言葉)でコメント文を書く練習をしておくことは有意義です。いつの日か趣味をこえて仕事になったり、世界中の人と情報交換して大きなプロジェクトに参加する日が来るかもしれません。その時には、英語(的な言葉)でコメント文を書ければ、おおいに仕事がはかどることでしょう。

▲TOP

エディタを使う(文法)

プログラムのコードは「プレーンテキスト」です。書くコード(文字)は、フォント指定も不要ですし、太字や斜体や文字の色も不要です。ですから、いわゆる「メモ帳」アプリでも書けます。しかし、プログラミング用の「エディタ」を使うことを強くオススメします(Processing の場合は開発環境そのもので書けばよいです)。

私は以下のエディタをオススメします。

これらのエディタがメモ帳より便利なことはたくさんありますが、なかでも、プログラミングを効率的にしてくれるのが「シンタックスハイライト」と「コード補完」の機能です。「コード補完」については次の節で詳しく書きます。ここでは「シンタックスハイライト」について紹介します。

例えばメモ帳で書いたプログラムは以下のようになります。

メモ帳で書いたプログラム
メモ帳で書いたプログラム

以下は Visual Studio Code で書いた場合です。

Visual Studio Code で書いたプログラム
Visual Studio Code で書いたプログラム

メモ帳は全てのコードが黒い文字で表示されています。それに比べて Visual Studio Code では、コードが色付けされています。Visual Studio Code のようなプログラミング用のエディタでは、書いているプログラミング言語を自動で識別して、その言語のキーワード、変数、プロパティ、メソッド、関数、文字列、数値、オブジェクト、コメント文などを別々の色で表示してくれます。これを「シンタックスハイライト」と言います。プログラミング言語の文法(シンタックス)を認識して、色付けする機能です。

このページでプログラムの表示に使っている以下の黒い枠内も、同じようにシンタックスハイライト機能を使っています。

<title>写真を撮る・見る</title>
</head>
<body>
<input  id="chooser" type="file" accept="image/*">      <!-- ファイル選択ダイアログ(カメラも使える) -->
<canvas id='canvas' width='300' height='400'></canvas>  <!-- 絵を描くcanvas要素 -->
<script>
// canvas要素に描画するためのお決まりの2行
var canvas  = document.getElementById("canvas");        // canvas 要素の取得
var context = canvas.getContext("2d");                  // 描画用部品を取得
// ファイルを読む(カメラを使う)準備
var chooser = document.getElementById("chooser");       // ファイル選択用 input 要素の取得
var reader  = new FileReader();                         // ファイルを読む FileReader オブジェクトを作成
var image   = new Image();                              // 画像を入れておく Image オブジェクトを作成
// ファイルを読み込む処理
chooser.addEventListener("change", () => {              // ファイル選択ダイアログの値が変わったら
    var file = chooser.files[0];                        // ファイル名取得
    reader.readAsDataURL(file);                         // FileReader でファイルを読み込む
});
reader.addEventListener("load", () => {                 // FileReader がファイルの読み込みを完了したら
    image.src = reader.result;                          // Image オブジェクトに読み込み結果を入れる
});
image.addEventListener("load", () => {                  // Image オブジェクトに画像が入ったら
    context.drawImage(image, 0, 0, 300, 400);           // 画像を canvas に描く(Image, Left, Top, Width, Height)
});
</script>

シンタックスハイライト機能があると、まず、スペルミスに気づきやすくなります。例えば JavaScript で変数宣言をするためによく使う var は、けっこう val のようにタイプミスしてしまいがちです。var は JavaScript のキーワードなので、エディタは色付けしてくれます。もし val と打ち込んだら、色付けされません。正しく書いた「つもり」でも、「タイプミス?」と気づかせてくれます。

また、コメント文のようにコード以外の部分を分けることで、読み返す時に「コードとそれ以外」のように、大事な部分とそうでない部分をパッと見て分かりやすくなります。

さらにシンタックスハイライトがあると、後で読み返す時や別の人が読む時、プログラムの中身を全部読み返さなくとも、キーワードだけをたどっていくことで、素早く理解しやすいというメリットもあります。

▲TOP

コード補完を使う(単語)

プログラミング用のエディタのとても便利な機能が「コード補完」または「コードヒント」や「スニペット」と呼ばれる機能です。ひとつのプログラミング言語には、それはそれはたくさんの命令があります。いわば単語のようなものです。これらをすべて覚えないとプログラミングできないのではたいへんです。そこで、エディタ側が書いているコードを理解して、「次はこれではないでしょうか?」のように、次に書きそうなコードの候補を提案してくれたり、候補から選べば自動で入力してくれる機能です。

例えば JavaScript で以下のようなコードを書こうとしているとします。

var txt = document.getElementById("div");

まず var と書くわけですが、例えば Visual Studio Code で v と打つと...

v と打つだけで
v と打つだけで

v ときて次に書きそうなコードの候補が表示されます。この例の場合しかも「var」の色がハイライトされています。候補がでたら、書きたいコードを矢印キーかマウスで選んで「Enter」キーや「Tab」キーを押せば、その候補が自動で打ち込まれます。

var に続けて同じように document と書きたいので、doc と打つと...

doc と打つだけで
doc と打つだけで

同じように document の候補がハイライトされて出てきますから、「Enter」するだけで document と打ち込まれます。次に .ge と打ち込めばさらに...

.ge と打つだけで
.ge と打つだけで

getElementById という長いメソッド名も簡単に打ち込めます。

コード補完の最大のメリットは、数文字打ち込めば候補が出てきてあとは自動で打ち込んでくれるので、キーボードを打つ回数が大幅に減ることです。長いプログラムを書いていると、それだけでも大いに楽になります。

入門者や初心者にありがたいのは、様々な命令(単語)がうろ覚えであっても、候補から適切そうな単語を思い出して入力していけることです。

そしてなにより、スペルミス(タイプミス)を大幅に減らすことができます。例えば JavaScript では、上で例に挙げた getElementById のような長いメソッド名、プロパティ名、オブジェクト名が数多くあります。addEventListener、accelerationIncludingGravity、XMLHttpRequest、などなど。これらの単語は長いので、後からスペルミス(タイプミス)を探すのも簡単ではありません。またこれらの単語は長いだけでなく、大文字小文字が入り混じっています。大文字小文字を間違えてもプログラムは動きません。

コード補完機能があれば、タイプミスが少なく、しかも楽に・速くプログラムが書けるのです。

Processing のコード補完機能

Processing の場合、その開発環境自体にコード補完機能が付いています(ただし、Ver.3 以降)。Processing を初めてインストールした時にこの機能が ON になっていない場合もあるので、以下の手順でコード補完機能を ON にしましょう。

まず「ファイル」メニューから「設定」を選びます。出てきた設定画面の中ほどにある「□コード補完」にチェックを入れて、「OK」ボタンで閉じてください。

Processing でコード補完を使う
Processing でコード補完を使う

Processing でコードを入力していて「ヒントが欲しい」と思ったら「Ctrl」+「スペース」キーを押します。すると以下のようにコードの候補が出てくるので、選んで「Enter」します。

Processing のコード補完
Processing のコード補完

▲TOP

まとめ

上でも書きましたが、プログラミングは最終的には「動けば良い」のです。でもたいていは様々な書き間違いがあって動かないのがプログラムです。せっかく楽しいプログラミングなのに、動かないことで悩んだり諦めるのはとてももったいないことです。

今回紹介した「五つのコツ」を抑えて書くことで、プログラミングが速く、確実で、楽しいものになります。

もちろん、プログラミングでは本来、書き方は「自由」です。これら五つのコツは「決まり」や「規則」ではありません。あくまで私がオススメする「コツ」にすぎません。皆さんも自身で様々なコツを探していきましょう。そして「良いコツ」があったら、他の人にも勧めてみましょう。

▲TOP