2017 年度「計算数学a」 2017-10-13

§2.1 プログラムを書くとは

(2.1.1) プログラムを書くに当たっては, テキストエディタで「ソースファイル」を作成し, 「コンパイラ」でそれを「コンパイル」し,「実行ファイル」を作る (のが普通である)。

        +----------------+                                 +----------------+
        | ソースファイル |  ---------------------------->  |  実行ファイル  |
        +----------------+     コンパイラでコンパイル      +----------------+
    (テキストエディタで書く)                              (これを動かす)
         (読んでわかる)                               (機械にしかわからない)

(2.1.2) 「実行ファイル」はコンピュータが読んで実行するもので, 人間が読んでもまずわからない。 「ソースファイル」は文字だけが並んでいるテキストファイルで, 訓練をつめば読み書きできるようになる。 「コンパイル」はここでは「変換する」「翻訳する」ぐらいの意味。 ソースファイルを書く際に用いる言語はいろいろあるが, ここでは「C言語」(シーげんご)を用いる。 C言語で書かれたソースファイルを実行ファイルにコンパイルするコンパイラのことを 「Cコンパイラ」とも言う。 Cコンパイラの使い方は,この授業で扱う範囲では極めて簡単である。 よって, これから学ぶことの中心はC言語によるソースファイルの書き方である。

(2.1.3) 【注意】 コンピュータにおいて, バックスラッシュ「」と円マーク「」 は半角文字では同じものと思ってよい。 スラッシュ「」は別物である。向きが違う。混同するな。 右手で文字を書く人にとって書きやすいほうがスラッシュ, 逆向きがバックスラッシュと覚えるとよい。

§2.2 初めてのCプログラム

(2.2.1) 次の例はカーニハン/リッチー著・石田晴久訳 「プログラミング言語C第2版」(共立出版) (以下 K&R2 と略す)の §1.1 とほとんど同じである。 画面に「hello, world」と出力するもの。

#include <stdio.h>

int main() {
    printf("hello, world\n");
}

(2.2.2) このプログラムについては, 「(いわゆる)ホームページからコピー & ペースト(以下「コピペ」という。)」 「キーボードから自分で打ち込む」 の両方で試せ。解説と注意は,(2.2.4) 以下に書いてある。 まずはコピペで試すとよいだろう。

(2.2.3) これを作成し,コンパイルし,実行する際の手順は次の通り。

  1. z ドライブに新規テキストファイルを作り,hello.c に改名する。
  2. ダブルクリックして秀丸エディタで開く。
  3. プログラムを,コピペするか,自分で打ち込む。 その内容は上のとおり。保存するのを忘れずに。 (秀丸エディタは終了しないほうが,修正する際に便利である。)
  4. cmd に「gcc hello.c」と打ち込んでコンパイルする。 何も出力されずにプロンプトが出れば成功である。 そのときは実行ファイル a.exe ができている。
  5. cmd に「a」と打ち込んで,プログラムを実行する。

失敗したときは 3 に戻ってやり直し。

(2.2.4) 1 行目。 きょうのところはこれは「決まり文句」と考えよう。 「#」は Shift しながら 3 のキーを打つ。 この記号は「シャープ」と呼んでおくが,正式名称は別にあったと思う。 include は英単語そのままだ。 この次にスペースを入れているが,入れなくてもよい。 stdio.h を囲む 「<」,「>」は不等号をカッコの代わりに使っているもの。 Shift しながら,それぞれ,カンマ,ピリオドのキーを打つ。

(2.2.5) 2 行目は空行である。見やすくするためにおいたもの。意味はない。

(2.2.6) 3 行目と 5 行目もCプログラムに必ずおくもの。 「int main」の次にあるのは小カッコの開きと閉じ。 Shift しながら 8 と 9 のキーを打つ。 この次にスペースを入れているが,入れなくてもよい。 その次にあるのは中カッコの開き。 5 行目にあるのが中カッコの閉じ。 中カッコは,Shift しながら大カッコのキー(Enter のすぐ左にある)を打つ。 これらの中カッコは 4 行目を囲んでいる。つまり, 「int main() { ... }」と同じである。 こう書かずに上のようにする理由はそのうちわかってくる。 また,この中カッコの書き方は K&R2 とやや異なる。 この授業では一貫して私の書き方で教えるので, すでに自分流のつけ方を編み出している人以外はとりあえずそれにならえ。

(2.2.7) よって,4 行目がこのプログラムのうちで最も意味のある部分ということになる。

3行目の最後の「{」を打って Enter キーを押した人は, 4行目の冒頭になにやら記号が出て, カーソルが4文字分右にずれていることに気づいたであろう。 この記号は,「タブ」を意味する。 秀丸エディタで .c ファイルを編集するときには, タブはスペース 4 つに展開される。 すなわち,(左端を 0 と数えて)4 の倍数になる位置に「タブストップ」 が設定され,タブがあると, そこから右を見て一番近いタブストップの位置まで移動する。

よって,そのまま,そこから printf... を打ち込めばよい。 この空白を置く理由はそのうちわかってくる。 このように行頭に空白を置くことを「字下げ」「インデント」という。

プログラムを(いわゆる)ホームページからコピペした場合には, インデントはタブではなくスペース四つになるが, コンパイル結果には影響しないので気にするな。

(2.2.8) printf() は元々用意されている「関数」である。 「プリントエフ」と読むことが多いかと思う。 関数の原語は function で,ここでは本来の意味である「機能」に近い。 この関数の機能は後ろの小カッコの中の文字列を印字する(=画面に書く) ことである。 文字列は二重引用符「" "」 (Shift しながら 2 のキーを打つ)で囲む。 ここでは印字される文字列に「\n」が含まれている。 「\n」はこの二文字で「改行」を意味する。 すなわち,これを画面に書くとそこで改行する。 よって,「hello, world\n」を画面に書くということは, 「hello, world」を画面に書いて最後に改行する, ということになる。

(2.2.9) 4 行目の最後にはセミコロン「;」がついている。 C言語では文はセミコロンで終わることになっているので,忘れるな。 また,この「セミコロン」などの名称もこの機会にきちんと覚えよ。

(2.2.10) 4 行目の最後のセミコロンを打って Enter キーを押すと, カーソルは次の行に移動し,また,左端にタブが一つあるだろう。 この状態で BackSpace キーを打つと,そのタブが消え, カーソルは左端まで移動する。それから「}」を打ち, Enter キーを押す。 すると,[EOF] が 6 行目冒頭に移ったであろう。 この状態で上書き保存する。

[EOF] は,ファイルの終わり,end of file を意味する。 そこでファイルが終わっているのであり, [EOF] という文字列がはいっているわけではない。

(2.2.11) 練習: 出力される文字列を別のものに変えて, コンパイル,実行してみよ。

(2.2.12) 練習: タブは,Tab キーを押すことでも入力できる。 また,Delete キー,BackSpace キーで削除できる。 試して,慣れよ。

§2.3 ごく簡単な計算

(2.3.1) 以下では,ファイル名は適当に決めよ。 C言語のソースファイルは「.c」で終わる名前でなければならない。

(2.3.2) 次のプログラムは,まずはコピペして実行してみよ。 「2 たす 3 は 5 です.」と画面に書くだけのプログラムである。

(2.3.3) 細かい説明は (2.3.4) 以下に書いたが,1行目の 「/*」と「*/」とで囲まれた部分だけ先に説明する。 これはコメントである。 何を書いてもコンパイルしてできる実行ファイルには関係ないので, 自由にメモを書くことができる。 ここでは,何をするプログラムか,いつ誰が作ったかを書いている。 その下のコメントは以下で説明する際の便宜のためにつけたものである。 これらは諸君は打ち込まなくてよい。

    /* たし算  1990-04-28, written by Iwase */

#include <stdio.h>

int main() {
    int a, b, s;                /* 変数の宣言 */

    a = 2;                      /* 代入 */
    b = 3;
    s = a + b;

    printf("%d たす %d は %d です.\n", a, b, s);
}

(2.3.4) 「変数の宣言」の行。 変数を使う場合,前もって「宣言」しておく必要がある。 ここでは「変数 a, b, sint 型」と宣言している。 変数名には小文字を使うのが普通である。 「int 型」は整数のみを格納できる「整数型」の一つで, この名前は整数 integer に由来している。 私は「イントがた」と読んでいる。 変数の宣言はこの位置(i.e. main() が始まってすぐのところ) で行なわなければならない。 その次には空行をおくと見やすくなる。 なお,中カッコの中の行は,空行以外,すべて字下げする。

(2.3.5) 「代入」の行。 「a = 2;」は,「a は 2 に等しい」という平叙文ではなく, 「a に 2 を代入せよ」という命令文である。 その下の二行も同様。 プログラムは特に指定しなければ上から下へと実行されるので, s = a + b; を実行したあとの s の値は 5 になる。 なお, 「=」は Shift しながら「-」のキー, 「+」は Shift しながらセミコロンのキーを打つ。 「a = 2;」の 「a」「=」「2」の間にはスペースを置いても置かなくてもよい。

(2.3.6) printf 文の行。 記号「%d」は,順に後ろの変数の値に置き換わって画面に出力される。 ここでは, 一つめの %da の値で, 二つめの %db の値で, 三つめの %ds の値で置き換わる。

    printf("%d たす %d は %d です.\n", a, b, s);
            --      --    --           -  -  -
            |       |     |            |  |  |
            +-------|-----|------------+  |  |
                    +-----|---------------+  |
                          +------------------+

よって,出力は「2 たす 3 は 5 です.」のあと改行,となる。 「%」は Shift しながら 5 のキーを打つ。 (句点「」の代わりに半角のピリオドを使っているのは単に趣味の問題である。)

(2.3.7)

(2.3.8) 練習: 出力の文の二重引用符の間の文字列からスペースを除去し, 「printf("%dたす%dは%dです.\n", a, b, s);」 としたら出力はどう変わるか。 「printf("%d%d%d\n", a, b, s);」としたらどうなるか。

§2.4 scanf() によるキーボードからの入力

(2.4.1)

#include <stdio.h>

int main() {
    int a, b;

    printf("数を入れてください.\n");
    scanf("%d", &a);                        /* 数の入力 */
    printf("もう一つ数を入れてください.\n");
    scanf("%d", &b);
    printf("%d たす %d は %d です.\n", a, b, a + b);
}

(2.4.2) 「数の入力」の行。 ここでプログラムは止まってキーボードからの入力を待つ。 関数 scanf() はそのための関数である。 私は「スキャンエフ」と読んでいる。 変数名 a の前に & がついて &a となっていることに注意。 なぜ & が必要かは,いまは説明できない。

(2.4.3) 練習:(2.3.7) で説明した, 余りを求める演算子「%」を試すプログラムを書け。

§2.5 for によるループ

(2.5.1) C言語でくり返しを実行させる方法には三種類あるが, ここでは for だけを説明し, whiledo-while はとりあげない。 K&R2 では §1.2, §1.3, §3.5 を見よ。 do-while は §3.6 にある。

(2.5.2)

#include <stdio.h>      /* 0 から 99 までの整数の二乗を計算して出力 */
 
int main() {
    int n;
 
    for (n = 0; n < 100; n++) {
        printf("%d の二乗は %d です.\n", n, n*n);
    }
}

(2.5.3) n++ は「変数 n の値を 1 だけ増せ」, n-- は「変数 n の値を 1 だけ減らせ」の意味。

(2.5.4) for の働きは以下のとおり。

    for (文1; 式2; 文3) {     
        ...
    }
  • 文1(初期設定)を実行。(最初に一度だけ。)
  • 式2(継続条件)が成り立つかどうかチェックする。
    • 式2が真ならば中カッコの中を実行。 その後,文3(再初期化)を実行し,式2のチェックに戻る。
    • 式2が偽ならば,中カッコはとばして次へ進む。

※ 中カッコの中(ループ本体)が一度も実行されないこともありえる。

(2.5.5) おおまかに理解するなら, 「文1から始めて,式2である限り,毎回文3としながら, くり返す」となる。上の例では, 「n イコール 0 から始めて,n が 100 未満である限り, 毎回 n を 1 ずつ増しながらくり返す」となる。

(2.5.6)

#include <stdio.h>      /* 0 から 99 までの整数の足し算 */
 
int main () {
    int n, sum;
 
    sum = 0;
    for (n = 0; n < 100; n++) {
        sum = sum + n;
        printf("0 から %d まで足すと %d です.\n", n, sum);
    }
}

(2.5.7) for の継続条件で使える記号は、数学上の記号と少々異なる。

数学上の記号
C言語での記号 <><= >===!=
注意: 「=<」「=>」は不可。 「x = 0」,
0 < x < 8」は別の意味。

a+b < c+d」のように書くこともできる。

「かつ」は 「x > 0 && x < 8」, 「または」は 「x < 0 || x > 2」 のように書く。 「&」はシフトしながら 6のキーを、 「|」はシフトしながら のキーを打つ。 K&R2 では §2.6 を見よ。 「かつ」も「または」も同じ文字を二つ打つことに注意。 「&」「|」をひとつだけ書くと,別の意味になる。

(2.5.8) 上のプログラムについて, for 文だけをくわしく説明する。 まず初期設定 n = 0 を実行。 次に継続条件 n < 100 をチェックする。 ここでは 0 < 100 だから成り立っている。 よって中カッコの中を実行。 再初期化 n++ が実行されて n の値は 1 に変わる。 そして継続条件 n < 100 のチェックに戻る。 また成り立っているので中カッコの中を実行。 再初期化 n++ が実行されて n の値は 2 に変わる。 これをくり返してゆくと, n は 99 まで,1 ずつ増されてゆく。 99 のときも中カッコの中は実行されるが, 次に再初期化 n++ を行なうと n の値は 100 になる。 すると今度は継続条件 n < 100 が成り立たないので, 中カッコはとばして次へ進む。

(2.5.9) for の中かっこで囲まれた文は, タブ2つだけ字下げして(=右に寄せて)書く。 くわしくは §2.7, §2.8 を参照。 K&R2 では省略可能な中カッコを省略しているが, 私のやり方のように全てつけておくほうが間違いが少ない。

(2.5.10) 上のプログラムの 100 を別の数に変えて試せ。

(2.5.11) もう一つのプログラム例。

#include <stdio.h>

int main() {
    int n;

    printf("整数を入力してください.\n");
    scanf("%d", &n);

    for (      ; n != 0; n = n / 2) {
        printf("%d\n", n);
    }
}

このプログラムの for では, 初期設定がいらない。その場合は,このように,空白にしておく。 C言語による int 型の割り算は余りを切り捨てるので, 上のプログラムはいつか止まる。

(2.5.12) 練習:(2.5.6) のプログラムをもとに, 1 から n までの自然数の積 --- n の階乗 --- を出力するプログラムを書いてみよ。 sum に代わる変数名としては prod(product の略)がよかろう。

§2.6 プログラムの止め方

(2.6.1) ループの回数が多いプログラムを実行し, 「これは時間がかかりすぎだ,失敗!」と思うときがあろう。 プログラムの実行を中止してプロンプトに戻りたいときは Ctrl+C を押す。 途中でいったんとめて様子をみるときは Ctrl+S を押す。 任意のキーで再開。 (Ctrl+Q でなければ再開しないこともある。)

§2.7 中カッコの入れ子

(2.7.1) 中カッコが入れ子になった場合のインデントの(一つの)やり方は, 以下の通りである。

  1. {」を打ったらその直後で改行し, 次の行は前の行の(空白以外の)頭よりもタブ一つ分(ここでは空白四文字分) 下げて始める。 (ただし,コメントは「{」の右に書いてもよい。)
  2. }」を打つときは, それに対応する「{」を含む行の(空白以外の)頭の文字とそろえる。
  3. その他の行の頭は前の行とそろえる。

(2.7.2) これらの規則はすぐに慣れることができるし, これらを守ってさえいれば,「ある中カッコとペアになる中カッコをさがす」 ことや「ある文を囲む最小の中カッコをさがす」ことはきわめて容易である。 前にも言ったが, 自分流のインデントをすでに編み出している人はそれで構わない。 そうでない人はまずはこれを覚えよ。 また,プログラムを書き上げてから最後にインデントを整えるのではなく, きちんとインデントしながら書く習慣をつけるとよい。 そのほうが,すでに書いた部分をよりよく理解できるので, より整理された頭でプログラムを書き進めることができる。

(2.7.3) §2.8 のプログラム例でこれを確かめよ。

§2.8 二重ループ

(2.8.1) 例を示す。(%2d は,2文字分の幅で出力せよ,の意味である。)

#include <stdio.h>

int main() {
    int i, j;

    for (i = 9; i > 0; i--) {
        for (j = 9; j >= i; j--) {
            printf("%d * %d = %2d   ", i, j, i*j);
        }
        printf("\n");
    }
}

§2.9 練習問題

かけ算九九の表を画面に書くプログラムを書け。 次のようになればよい。

1 * 1 =  1   1 * 2 =  2  ...
2 * 1 =  2   2 * 2 =  4  ...
...

§2.10 発展問題

二つの自然数を入力するとそれらの最大公約数を出力するプログラムを書け。 (ヒント:エウクレイデースの互除法を用いよ。)


岩瀬順一