応用理工学情報処理 3回目

2回目提出課題についてのコメント

(1)演算子の優先順位(左側が優先順位の高いもの)
括弧( )、 単項演算子(-1 &a)、乗除(* / %, 左から右)、関係演算子(< > <=, 左から右)、等価(== !=, 左から右)、論理AND(&&, 左から右)、論理OR(||, 左から右)、条件演算子(?:, 右から左)、代入演算子(=, 右から左)、コンマ演算子(, 左から右)
(-1 < 2 < 1)のような式は、結合規則(この場合は左から右)に従って評価されます。
この場合はまず(-1 < 2)が評価されてTrueを表す1となり、次に( 1 < 1)が評価されてFalseを表す0となります。数学における表記とは評価方法が異なる点にご注意下さい。
優先順位や結合規則を利用すると括弧を( )をあまり使わずに式を記述することが出来ますが、括弧( )を用いて演算順序を明白に示した方が分かりやすくなることが多いです。例えば、3/5*5は(3/5)*5と同じで結果は0となりますが、後者の方が理解しやすいため良い記述だと言えます。

(2)コンマ(,)演算子について
式を左から右に順次評価する演算子です。最後に評価した式の値が全体の値となります。前回の提出課題で、
if (12<=num,num<=12) のように記述したものがありましたが、これはif (num<=12)と記述するのと同じ結果となります。

(3)条件演算子(3項演算子、? :)について
条件演算子(3項演算子)は、x = A ? B : C; の形以外ではわかりにくくなるため使わない方が良いです。
a<=c?a:b?b:c という式では、まずb?b:c が評価され、bが0で無い(True)の場合には値がbとなり、次にa<=c?a:bが評価されます。括弧をつけて(a<=c)?a:(b?b:c)のように書くとわかりやすくはなりますが、単純な場合を除いて条件演算子の使用は控えましょう。

(4)int main(void)のvoidとコマンドライン引数について
C言語では、main関数がコマンドライン引数をとらない場合には明示的にmain(void)と記述します。C++言語では、この場合main()と記述するため、この点はC++と異なる点です。コマンドライン引数は、以下のプログラム(test.c)のように使用します。このプログラムは本授業後半の課題を含むため、紹介程度にとどめます。

 
#include <stdio.h>
int main(int argc, char* argv[])
{
    int n;
    for (n=0; n<argc; n++){
        printf("%s\n", argv[n]);
    }
    return (0);
}
このプログラムをコンパイル(実行ファイルをtest.exeとする)して、VS Code下側のTerminal画面にて、実行ファイルのあるホルダーにカレントホルダーを移動(cd Ex2のようにcdコマンドで移動)して、「./test first second third」と入力してEnterキーを押して実行すると、Terminal画面にfirst second thirdと表示されます。この、testの後に続くfirst second thirdがコマンドライン引数に対応します。
============================================

今日は、テキスト第4章に従って処理を繰り返し実行させる方法として、do文while文およびfor文を学びます。

(a) 繰り返し(1) do, while 

do文は、条件が偽でも必ず1回は処理を行うような場合に用います。典型的な利用例は、数値入力で値を0~10までに限る場合に正しい値が入力されるまで繰り返すような場合です。

while文は条件が偽ならば1回も処理を行わないような場合に使用します。

繰り返しの判定に用いる条件式は、if文と同じです。C言語では、x > 0 のような式も真であれば1、偽であれば0という値を持っていることに注意すること。

「1」 do文 テキストp.72, List4-1のプログラムを実行してdo文がどう機能するかを学びましょう。

/*
	読み込んだ整数値は奇数であるか偶数であるか(好きなだけ繰り返す)
*/

#include  <stdio.h>

int main(void)
{
	int	 retry;		/* 処理を続けるか */

	do {
		int	 no;	

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

		if (no % 2)
			puts("その数は奇数です。");
		else
			puts("その数は偶数です。");

		printf("もう一度?【Yes…0/No…9】:");
		scanf("%d", &retry);
	} while (retry == 0);

	return (0);
}

(1) whileの条件判断は、retry == 0で行われ、この式が真(つまりretryが0である)である間はdoとwhileではさまれたブロックが繰り返し実行される。条件式は、( !retry)と書いてもこれと同じ意味を持つ。"!"は論理否定演算子で、!retryが0(つまり偽)であれば、( !!retry)は1となる。((!retry == 0)を ( !!retry)と書き換えて、プログラムが同じように動作することを確認せよ。

(2) { と }で囲まれた領域はブロック(複合文)と呼び、C言語では文法的に1つの文と同等に扱われる。このプログラムではnoというint型変数をブロックの先頭で宣言している。ブロックの内部で宣言された変数はそのブロック内だけで有効で、そのブロックの外部からは参照できないことに注意すること。

「2」 複合代入演算子、及びインクリメント演算子(p.78)

/*
	1から5までの和を求めて表示(複合代入演算子)
*/

#include  <stdio.h>

int main(void)
{
	int	 no = 1;
	int	 sum = 0;

	do {
		sum += no;			/* sumにnoを加える */
		no  += 1;			/* no に 1を加える この文は、no++; とも書ける*/
	} while (no <= 5);

	printf("1から5までを足した値は%dです。\n", sum);

	return (0);
}

(1) sum += no は sum = sum + no と全く同じ意味です。"+"の部分は、"+", "-", "*", "/" のどれでも使うことが出来ます。もちろん sum *= noのような式はsum=sum*noと書くことも出来ます。どちらの書き方を選ぶかは、どちらの方がプログラムが読みやすくなるかで決めれば良いことです。

(2) no += 1 はn++と書くことが出来ます。++をインクリメント演算子と呼びます。同様に、n -= 1 はn--と書くことが出来ます。これをデクリメント演算子と呼びます。

「3」 while文 テキストp.81, List4-5のプログラムでwhile文を学びましょう。

/*
	読み込んだ整数値を0までカウントダウン
*/

#include  <stdio.h>

int main(void)
{
	int	 no;
	
	printf("整数を入力してください:");
	scanf("%d", &no);

	while (no >= 0) {
		printf("%d ", no);
		no--;			/* noの値をデクリメントする(一つ減らす) */
	}
	putchar('\n');		/* 改行 */

	return (0);
}

(1) while文では繰り返しの条件判断を最初に行います。

(2) 関数putchar( )は文字を画面に出力します。putchar('\n')は改行文字を画面に出力する、つまり改行することです。

(3) シングルクォーテーションマークで囲んだ 'A' は文字定数を表します。ダブルクォーテーションマークで囲んだ"A"は文字列定数を表します。文字列定数では、文字列の終端を認識するために末尾に'\0'の記号が入りますが、文字定数には入りません。これらは違うものだということを頭に入れておいてください。

====== 演習問題 3A (p3a.c) ==============

while文を用いて、自分の学籍番号を逆順に以下のように表示するプログラムを作成せよ(int n = 202212345; のようにプログラム中で直接値を設定すること)。
(ヒント)202212345を整数型(int型)とすると、202212345 % 10は最下位の数字5となる。整数同士の割り算では小数点以下が切り捨てられるため、202212345 / 10 は最下位の数字5を除去した20221234となる。
------------------------------
学籍番号:202212345
学籍番号を逆順に並べる:543212202
-------------------------------
===============================

(b) 繰り返し(2) for

「4」 for文 for文はwhile文で良く出てくるパターンを1か所にまとめて記述したもので、繰り返しの構造がわかりやすいので良く用います。次の2つのプログラムは全く同じ処理を記述したものです。

以下のプログラムは、テキストp.90 List4-11です。

/*
	読み込んだ正の整数値までカウントアップ(for文)
*/

#include  <stdio.h>

int main(void)
{
	int	 i, no;
	
	printf("正の整数を入力してください:");
	scanf("%d", &no);

	for (i = 0; i <= no; i++)
		printf("%d ", i);
	putchar('\n');

	return (0);
}

(1) このプログラムのfor文の部分は以下のようにwhileを使って書くことも出来ます。しかしfor文を用いることによって繰り返しに関する部分を1箇所にまとめることが出来るのでプログラムが読みやすくなります。

i = 0;
while(i <= no) {
		printf("%d ", i);
		i++;
}

「5」多重ループ for文や前回学習したdo文、while文はいずれも自由に入れ子にすることができます。

/*
	for文の2重ループの例
	縦横に*を並べて正方形を描画(テキストのList4-17を参照)
*/

#include  <stdio.h>

int main(void)
{
	int	 i, j;
	int  width;
	puts("正方形を作りましょう。");	
	printf("幅:");
	scanf("%d", &width);

	for (i = 0; i <= width; i++){
		for (j = 0; j <= width; j++)
			putchar('*');
		putchar('\n');
	}
	
	return (0);
}
このように外側のループを制御する変数が内側のループと関係しないこともありますが、次の例のようにそれによって内側のループの回数を制御することも多くあります。
/*
	for文の2重ループの例 その2
	直角三角形(左下が直角)を表示(テキストのList4-18を参照)
*/

#include  <stdio.h>

int main(void)
{
	int	 i, j, ln;
	
	printf("何段ですか:");
	scanf("%d", &ln);

	for (i = 0; i <= ln; i++){
		for (j = 0; j <= i; j++)
			putchar('*');
		putchar('\n');
	}

	return (0);
}

「5」break文とcontinue文 繰り返し処理のループの中から強制的に抜けること(break文)や繰り返し処理の単位の末尾までスキップする(continue文)が出来ます。

以下のプログラムは、本日の最初のサンプルプログラムをbreak文を用いて書き直したものです。他に、continue文を用いて0以下の値が入力された場合にループの最後へスキップする処理を加えてあります。

/*
	読み込んだ整数値は奇数であるか偶数であるか(好きなだけ繰り返す)
	最初のプログラムをwhile(1)の無限ループとbreak文を用いて書き直したプログラム
*/

#include  <stdio.h>

int main(void)
{
	int	 retry;		/* 処理を続けるか */

	while (1) {
		int	 no;	

		printf("正の整数を入力してください:");
		scanf("%d", &no);
		if (no <= 0){
			printf("負の数は扱いません\n");
			continue;
		}

		if (no % 2)
			puts("その数は奇数です。");
		else
			puts("その数は偶数です。");

		printf("もう一度?【Yes…0/No…9】:");
		scanf("%d", &retry);
		if (retry != 0) break;
	} 

	return (0);
}

====== 演習問題 3B (p3b.c) ==============

教科書のList4-16を参考にして、下記の出力例の通りに九九の表を出力するプログラムを作成せよ。

(ヒント:最初の整数値をi、次の整数値をj とすると、1つの式は printf("%d×%d=%2d ", i, j, i*j);で出力できる。ここで、%2dは整数を2桁で表示するための書式指定子である(%2dの後に空白を1つ入れておくことを忘れないこと)。このプログラムは2重forループを用いる典型的なプログラムである。内側のforループは横方向の表示を行い、外側のforループは縦方向の表示を行う。)

1×1= 1 1×2= 2 1×3= 3 1×4= 4 1×5= 5 1×6= 6 1×7= 7  1×8= 8  1×9= 9
2×1= 2 2×2= 4 2×3= 6 2×4= 8 2×5=10 2×6=12 2×7=14 2×8=16 2×9=18
3×1= 3 3×2= 6 3×3= 9 3×4=12 3×5=15 3×6=18 3×7=21 3×8=24 3×9=27
4×1= 4…
5×1= 5…
6×1= …
7×1=…
8×1=…
9×1=…

====== 演習問題 3C (p3c.c) ==============

以下のコードは、C言語で0から99までの整数の乱数を生成するものです。

#include  <stdio.h>
#include  <stdlib.h>
#include  <time.h>
int main(void)
{
	int	 k;
	
	srand(time(NULL)); /* 乱数列を現在時刻で初期化 */
	k = rand()%100;
	printf("生成した乱数:%d\n", k);

	return (0);
}

このプログラムを参考にして、0から99の範囲で実行するたびに異なる値を持つ整数を生成し、その値を当てるゲームを作成して下さい。ただし、プログラムの構造は必ずwhile文の無限ループとbreak文を用いた以下の構造として下さい。

[0から99の範囲の整数を乱数を用いて1つ作成]
[入力回数を1にセット]
while(1){
	 [整数を入力];
   if (入力した値と乱数で作成した値が一致) break;
   [入力した値が乱数で作成した値と比較して大きいか小さいかを表示]
   [入力回数に1を加える]
}
[何回目で正解であったかを出力]
画面上には以下の出力例のように表示します。イタリックの太字はキーボードから入力した値を示します。ここでは3回目で数を当てていますが、当たらなければ何回でも繰り返すものとします。
---------出力例----------
0から99までの整数を当ててください。
何回目の入力で当たるでしょうか。

1回目の入力:51
もっと小さな数です。
2回目の入力:35
もっと大きな数です。
3回目の入力:43

大正解! 3回目で正解です。
======