わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

九九表生成プログラム

「資料を見てください.kuku.cのほうです.

/* kuku.c */

#include <stdio.h>

int main(void)
{
  int i, j;

  for (j = 1; j <= 9; j++) {
    for (i = 1; i <= 9; i++) {
      printf("%2d ", i * j);
    }
    printf("\n");
  }

  return 0;
}

先頭から見ていきますと,最初のコメント,次のinclude文,そしてmain関数と《{》まではいいですね.変数は《int i, j;》として,int型でiとjの2つを宣言しています.
次は,for文です.《for (j = 1; j <= 9; j++)》とあるので,変数jを使ったループです.
forの構文は『for (初期化; 条件; 増分)』でしたね.これに当てはめていくと,初期化ではjの値を1にします.条件は,j小なりイコール9ですので,jが9以下のあいだ,繰り返し処理を行います.増分は《j++》で,そのつどjに1を足し,その値を,変数jの新たな値とします.
これでどうなるかというと,jのループについては,まず〈j=1〉で1回,処理を行います.その4行あとの《}》まで行き着いたら,ループに戻って増分処理で,〈j=2〉になります.このとき《j <= 9》は『2小なりイコール9』という判定を行い,これは真ですので,また処理を行います.
と順々に進めていき,jの値が9だったときに《j++》で10になったら,そのときの《j <= 9》は『10小なりイコール9』は偽ということで,ループから抜けるというわけです.いいですね?
その『処理』ですが…
jのループのすぐ下は,変数iを使ったループです.変数がすべて,iになっていますが,それ以外は,先ほどのjのループとまったく同じです.ですので,初期化ではiの値を1にし,iが9以下のあいだ,繰り返し処理を行います.増分は《i++》で,そのつどiに1を足します.
では変数iとjは対等なのか,おんなじ扱いなのかというと,違うのです*1.2つのprintfを無視して,iとjの変化を追っていけば,その違いが見えてきます.
最初に《i = 1》の処理をした時点では,変数iの値は1,jの値も1です.そして中の反復処理1回分を終え,《i++》を行うと,iの値は増えて2となりますが,jの値は変わりません.
iとjの値をセットで見ると,〈i=2,j=1〉となります.順々に進めていくと,〈i=3,j=1〉,〈i=4,j=1〉となって…〈i=9,j=1〉の処理が終わってからi++によって,〈i=10,j=1〉となりますが,ここで《i <= 9》は『10小なりイコール9』で偽となり,iのfor文,すなわち内側のループを終えます.
外側のループの反復処理が1回,終わったときには,こんどは《j++》によって,jの値だけが1増え,iのほうは変わりません.だから〈i=10,j=1〉から〈i=10,j=2〉へと変化します.
そのとき《j <= 9》は真ですので,反復処理を最初からまた実行します.変数iを含むfor文で,初期化によって,iの値は1にセットされます.〈i=1,j=2〉となります.またしばらく,iの値だけが1ずつ増え,jの値は2から変化しないというループになります.
といったわけで,iとjの値の変化は,次のようになります.

  • 〈i=不明,j=1〉〈i=1,j=1〉〈i=2,j=1〉〈i=3,j=1〉……〈i=9,j=1〉そして〈i=10,j=1〉
  • 〈i=10,j=2〉〈i=1,j=2〉〈i=2,j=2〉〈i=3,j=2〉……〈i=9,j=2〉そして〈i=10,j=2〉
  • 〈i=10,j=3〉〈i=1,j=3〉〈i=2,j=3〉〈i=3,j=3〉……〈i=9,j=3〉そして〈i=10,j=3〉
  • ……
  • 〈i=10,j=9〉〈i=1,j=9〉〈i=2,j=9〉〈i=3,j=9〉……〈i=9,j=9〉そして〈i=10,j=9〉
  • そして〈i=10,j=10〉,おしまい.

『そして』と言ったのには,理由があります.forの増分や条件式を細かく見ていくと,『そして』のあとのように,iやjの値が10になるのですが,そうなった時点の値は,対応する反復処理では使用されません.内側のforループの中のprintfでは,〈i=10〉となっていることはありませんし,外側においても,〈j=10〉のときには,{ } 内の処理はなされません.
試験では例年,これよりも難しめの2重ループをもとに,変数iとjがどのように変化するかを解答してもらっていますが,そこには『ループ終了時の(ループ内の処理で使用されない)値は考慮しないものとする』という注意書きを入れています.
と,iとjの値をうまく,変化させるための2重ループを押さえたところで,中に書かれてある2つのprintfを見ていきましょう.
先にある《printf("%2d ", i * j);》は,iとjのかけ算をして,その結果を《"%2d "》によって出力します."%d" だったら10進数に変換されますが,間に2を入れて %2d と書けば,2桁未満であっても2桁とし,しかも右揃えにしてくれます.1*1(いんいちがいち)の結果は,スペースと1です.2*5(にごじゅう)になったら,1と0です.
dのあとの空白1文字は,次の数との分離のためです.これがなかったらどんな出力になるかを,言葉でいえるようになると,プログラミングをより深く,楽しむことができます.これは自習課題としましょう.
もう一つのprintfに移りますね.《printf("\n");》ですので,そこで改行せよという意味です.注意したいのは,いつ改行するかです.これはjの反復処理の内側にあり,iの反復処理の外側にあります.先ほどのiやjの変化の仕方と合わせて考えると,これは,iのループが終了し,jに1を加えるより前の時点で行われる,と言えます.
別の言い方をすると,jを固定して,iが1から9まで,あ,もう一つありますね,10まで変化したあとに,改行されるのです.
ここまで話したことを,スライドで確認しましょう.

矢印の右の出力ですが,実際のプログラムでは表の縦横の線,罫線(けいせん)って言うのですが,までは表示されません.改行も,目に見えません.もう一つ細かいことを言うと,改行の直前には,《"%2d "》のために1文字,空白が書かれますが,これも目に見えないものです.
一つ,吹き出しを設けましたが,最後の行の左から3番目,『27』と出力しているところでは,iは3,jは9です.他の九九のマスでも,iの値とjの値は求められますね.
最後に,知っておいてほしいことを.このプログラムでは,変数iとjを宣言していて,先に使っているのは,変数jのほうです.その後といいますか,内部に,変数iを使ったループとなっています.
このプログラムに限っていうと,《for (j = 1; j <= 9; j++) {》と《for (i = 1; i <= 9; i++) {》の2つの行をそっくりそのまま,入れ替えても,同じ出力になります.変数iとjの変化の仕方が交換されますが,中の計算は《i * j》ですので,かけ算の交換法則が,吸収してくれるのです.
それでも慣習として,こういうときはjを先,iを後に書きます.というのは,iがx軸に対応し,jがy軸に対応しているからです.y軸あるいは行列でいう行に着目して,左から,行列でいう列の一つひとつに対して処理をしていく際の基本形なのです」

なにこれ

前回の授業のプログラムの説明です.実際にほぼ上のように話しました.例題プログラムや画像(スライドの一部)は,制御文の授業で毎年使っているものです.
《……》はプログラムコードの断片で,〈……〉は変数の値です.《i=1》は,変数iに1を代入するという指示なのに対し,〈i=1〉は,その時点の変数iの値は1であることを意味します.

*1:変数iとjを同時に1ずつ増やしていきたければ,例えば,for (i = j = 1, i <= 9 && j <= 9; i++, j++)のように書きます.