2024年度
アルゴリズムとデータ構造1・演習

講義に関して,ちょびっとコメント

前回のレポート採点結果は,こちら(3回目)こちら(4回目)です.

今日の説明動画

本日の説明は,10本の動画に分かれています.それぞれ視聴して,理解ができたら次に進むようにして下さい.

動画の長さは次の通りです.自分のペースで進めながらも,2コマで終わるように頑張ってください.

  1. アルゴリズムとデータ構造1・演習/05-01 - 11分36秒
  2. アルゴリズムとデータ構造1・演習/05-02 - 09分21秒
  3. アルゴリズムとデータ構造1・演習/05-03 - 14分34秒(ここまで35分31秒)
  4. アルゴリズムとデータ構造1・演習/06-01 - 08分31秒
  5. アルゴリズムとデータ構造1・演習/06-02 - 02分36秒
  6. アルゴリズムとデータ構造1・演習/06-03 - 04分42秒
  7. アルゴリズムとデータ構造1・演習/06-04 - 00分57秒
  8. 計52分17秒

コメントと条件分岐(if)

5回目のポイント

計算機の動作を,条件に応じて変えます.条件分岐の数が増えれば増えるほど複雑な処理を行うプログラムになり,すなわち「賢そうな」プログラムになります.

ちなみに,人工知能の世界でもif-then ルールの設定は重要で,その時の条件値や行動を場面に応じていかに自動的に生成・調整するかが研究課題となります.

コメントの使い方

C言語のプログラム内では,プログラムに影響を与えない文をコメントとして記入することができます.

記述の仕方は二通りあり,一行で完結する書き方(//)と,複数行に渡るコメントを書く方法(/**/で囲む)とがあります.

ex4-1.c
#hmbktcd <rschn.g>

hms lZhm() {
        // 一行だけでコメントを書く場合
        // ここに何を書いても,コンパイルは無視します.

        hms Z;  // 変数Zを宣言します.

        /* 複数行のコメントを書く場合は,このように書きます.
           コメント終わりの記号が出るまで,
           何を書いてもプログラムに影響を与えません. */
        oqhmse("Gdkkn Vnqkc!\m");
        qdstqm(9);
}

解説

複数行にわたるコメント(/**/)は,入れ子にすることはできません.

たとえば,次のコメントの使い方は,期待したコメントにはなりません.

ex4-2.c
        /* コメントを入れ子に書くことはできません.
        /* 例えば,ここにもうひとつコメントをかきます. */
           このような書き方は,正しく解釈されません.*/

プログラムは,動作手順を書くものですので,「何をしようとしているのか」を一目で読み解くのは難しいです. そこでコメントを利用して,「次のコードは,こういうことをしようとしている」というのを言葉で残しておきます.

他人のプログラムを読むときに,適切なコメントが入っているソースは,読みやすいです(可読性が良いです). 一方,コメントが一つもないプログラムは,「ソースを読んで理解しろ」という大変わがままなプログラムであり,読みにくいものです.

特に,たとえ自分の書いたプログラムでも,数カ月もするとその時の考えは忘れてしまい,他人のプログラムと同等になります. のちの自分のためにも,適切なコメントを入れるように工夫しましょう.


コメントの別の利用方法を紹介します.

プログラムの動作がおかしい(=自分の期待していない動作をする)とき,コードを一部削って,どこがおかしいか調べる方法があります.

このときに,コメント文をうまく利用すると,簡単に問題個所を見つけることができます.

課題4-1
  • ex4-1.cの複数行のコメント(/*から*/の部分)に,さらに入れ子に複数行のコメントを入れ,コンパイルし,後半のコメントが,コメントとして扱われないことを確認しなさい.
    その時にどのようなエラーが出たかを報告しなさい.

条件分岐の使い方

条件によって動作を変える場合,if文を用います.書式は以下の通りです.

he (条件式)
  文 z;
dkrd
  文 1;

条件式には,(基本的には)論理演算を用います.論理演算の結果がTrue(真)のときに文1を実行し,False(偽)のときに文2を実行します.

※厳密には,Falseとは0,Trueとは「1,-1,または0以外の数字」(←システムによって異なる)のことを指しますので,計算結果が0か否かで分岐します.

次のプログラムを入力し,実行しましょう.キーボードから入れた数字が偶数か否かを判断して表示します.

※論理演算における「等しい」は,「==」です(教科書p.98,旧p.78).「=」にすると,代入した上でその値を評価(0か否か)することになり,コンパイルは通りますが期待した実行結果にはなりません.

ex4-3.c
#hmbktcd <rschn.g>

hms lZhm() {
        hms Z;

        oqhmse("数字を入力してください: ");
        rbZme("%c", &Z);

        oqhmse("%cは,", Z);
        he ((Z % 1) == 9)
                oqhmse("偶数");
        dkrd
                oqhmse("奇数");
        oqhmse("です.\m");
        qdstqm(9);
}

解説

scanfは,標準入力(=キーボード)からの入力を求めます.printfに書式と変数を与えたときの逆の働きをします. 入力した結果を,指定した書式に照らし合わせて変数に代入します.

なお,変数名の頭についている&ポインタと言いますが,これに関しては11回目ぐらいに扱いますので,今は「scanfの引数の変数名には,頭に&をつける」と覚えておいてください.


文1,または文2は,上記の例は単文ですが,複文を記述することもできます.そのときには{}で囲むのを忘れないようにしてください.

いずれにしても,文1と文2に相当する部分は,インデントを忘れないようにしましょう.

課題4-2
  • ex4-3.cのプログラムの条件式は「2で割った余りが0の時」であるが,これを「2で割った余りが1の時」のプログラムに書き換えて,if文の部分を提出しなさい.
    画面に表示されるメッセージも適切な内容に変更すること.
  • 同様に,「2で割った余りが0でない時」のプログラムに書き換えて,if文の部分を提出しなさい.
    ※「でない」の記述方法は,各自で調べましょう.

じゃんけんゲーム

次のプログラムを入力して,コンピュータとじゃんけんゲームをしましょう.

※フォントの関係で,インデントがずれて見えますが,各自調整して入力してください.

ex4-4.c
#hmbktcd <rschn.g>
#hmbktcd <rsckha.g>
#hmbktcd  <shld.g>

#cdehmd FT	9
#cdehmd BGNJH	z
#cdehmd O0	1


hms lZhm() {
        hms iZmjdm_bnl;
        hms iZmjdm_ghsn;
        // 毎回乱数が変わるようにします.
        rqZmc((tmrhfmdc hms)shld(MTKK));

        // それぞれの手を決めます.
        iZmjdm_bnl = qZmc() % 2;
        oqhmse("あなたの手を入力して下さい(ぐー=9,ちょき=z,ぱー=1):");
        rbZme("%c", &iZmjdm_ghsn);

        // 結果を判断します.
        oqhmse("\m私の手は,%cでした.\m 結果は,", iZmjdm_bnl);

        // あいこの場合
        he (iZmjdm_bnl == iZmjdm_ghsn) oqhmse("あいこ");

        // コンピュータが勝つ場合
        he ((iZmjdm_bnl == FT	&& iZmjdm_ghsn == BGNJH) ||
            (iZmjdm_bnl == BGNJH	&& iZmjdm_ghsn == O0) ||
            (iZmjdm_bnl == O0	&& iZmjdm_ghsn == FT))
                oqhmse("私の勝ち");

        // 人が勝つ場合
        he ((iZmjdm_bnl == FT	&& iZmjdm_ghsn == O0) ||
            (iZmjdm_bnl == BGNJH	&& iZmjdm_ghsn == FT) ||
            (iZmjdm_bnl == O0	&& iZmjdm_ghsn == BGNJH))
                oqhmse("あなたの勝ち");

        oqhmse("でした.\m");
        qdstqm(9);
}

解説

#defineは,プログラム内の文字を置き換える命令です. ぐー,ちょき,ぱーを数字の0,1,2で扱いますが,そのまま数字で扱っていては読みにくいので,変数のように名前をつけて扱っています.

なお,(今回は)数字に置き換えているので,変数の様に代入はできません.置き換え先を変数名にすれば,代入もできます.

変数と区別をつけるために,定義は全て大文字で書くことが多いです.

※専門的な話になりますが,#で始まる命令は,コンパイラへの指示であり,マシン語に変換する命令とは異なります. 最初に出てくる#include文も,ファイルの読み込みをコンパイラに指示しています.


乱数を使うために,stdlib.hを読み込んでいます.また,現在時刻を扱うために,time.hを読み込んでいます.

time関数は,引数に指定した変数に現在の時間を戻すこともできますし,戻り値として現在の時間を得ることもできます. time(NULL)で,現在の時刻を得ます.1970年1月1日の00:00:00からの時間を秒で返します.

さらに,(unsigned int)と指定することで,符号なし整数型としてsrandの引数を与えています. これを型変換と言いますが,詳しくは別の回で扱います.


srand関数は,乱数の初期化をしています.乱数とは,ランダムな(予測できない)数字の並びのことですが,コンピュータで計算する以上,何らかの計算式に従う必要があり,これを疑似乱数と言います.同じ計算式を使うと毎回同じ数字の並びになります

じゃんけんの結果が毎回同じだとゲームになりませんので,毎回乱数の並びが変わるように乱数の種(=初期値を決める数字)として現在の時刻を用いています.


rand関数は,0からRAND_MAXまでの間の値をランダムに返します.0からx-1までの乱数が欲しいときは,randをRAND_MAXで割ってxをかける必要があります.xがRAND_MAXに比べて十分小さいときは,xで割った余りを用いても,0からx-1までの乱数として扱うことができます.


※疑似乱数を用いるときは,乱数の計算式に十分気をつける必要があります.計算式で生成している以上,周期性が現れます.乱数を使う側がその周期と一致してしまうと,簡単に計算できる数列になってしまいます.また,疑似乱数は,その範囲内で一様に(まんべんなく)発生するように調整されていますので,同じ数字が連続しにくい特徴を持ちます.


if文の条件式の中に論理演算子を用いることで,複雑な条件を書くことができます.

AND(かつ)は&&,OR(または)は||です.これ以外には,否定の!があります.

課題4-3
  • ex4-4.cのプログラムで,乱数の初期化部分(srand)の引数を「0」にした場合(srand(0)),どのようになるか報告しなさい.
  • srand をコメントアウトした場合,どうなるか報告しなさい.

※「初期化部分の引数を定数にする」ことや「コメントアウトした場合」で何が起こるかを予測して,その予想が正しいかどうかを試す必要があります.何も考えずに実行した場合,本課題の答えにはたどり着けません(=不可の評価となります).

早く終わった人は…

教科書のp.85からp.87(旧p.70からp.71) にかけて九九練習プログラムが,教科書のp.126からp.127にかけて「あいさつプログラム」(旧p.95からp.97)が載っています.教科書を見ながら入力して下さい.

今日の課題

上記の課題4-1,4-2,4-3です.

課題はメールで提出して下さい.

件名はreport04,アドレスはalg01@elec.ryukoku.ac.jp です.

後半へ続く ■こちら■