情報と地域
2007.07.13

Back to index page



  1. 本日の作業内容

  2. ウォームアップ

    先週の作業を思い出してスクリプト実行のおさらいをしましょう.今回はプラッ トホームがWindowsに変わりますので,少しだけ作業手順が変わります.次のよ うにしてプログラム実行の復習をしましょう.

    • 秀丸エディタ

      今回はエディタとして「秀丸」を使います.別にWindows標準の「メモ帳」でも 構わないのですが,拡張子を .rb にすることが困難なので,自由度の ある秀丸にします.スタートメニュー他の手段で秀丸を起動して下さい.

    • コマンドプロンプト

      スクリプトを実行するコマンドを入力するのは「コマンドプロンプト」と呼ばれ るWindowsに標準で備わっているターミナルもどきです.「スタートメニュー」 の「全てのプログラム」から「アクセサリ」に移動して起動して下さい.

    • スクリプト作成

      printf "Hello, world!\n"
      

      のような簡単なもので良いので秀丸エディタに入力し,保存して下さい.保存す るときには前回と同じ様に ruby_script のようなディレクトリ(フォ ルダ)を作成 してからその中に保存しましょう.

    • コマンドによる実行

      コマンドプロンプトでディレクトリを移動してからコマンド入力します.例えば,

      > cd ruby_script

      のようにしてディレクトリを移動して

      > ruby hello.rb

      のようにして実行します.無事に出来ましたでしょうか.ここの感じを思い出せ れば以下の作業に移ります.

      上のコマンドの部分で > はWindowsにおけるコマンドプロンプトで す.その後にコマンドを入力しますので自分で > を入力する必要 はありません.

  3. 繰り返し

    先週の実習で数字を繰り返し画面に出力するスクリプト(プログラム)を作成しま した.単純作業を繰り返すのは人間には苦痛ですが,コンピュータは嫌がりませ ん.そこで,コンピュータに同じことを繰り返しさせるスクリプトを考えてみま しょう.

    for i in 0..20
      print "a"
    end
    

    上のスクリプトを試してみるとどうなりましたでしょうか."a"という文字がた くさん表示されましたか?全部で何個出たか確認してください.

    上の例はほとんど全てのプログラミング言語に用意されている for 文 (for ループ)と呼ばれているもののRuby版です.言語が違うと表記法 がそれぞれ違ってくるのですが,その違いはせいぜい「方言」程度のものなので 一つの言語の書式を覚えると他の言語のものもだいたい類推して分かるようにな ります.では,上記の文の解説をしましょう.

    for というのは英語の

    I have lived in Matsue for 4 years.

    に出てくる"for"のような「〜の間」という意味になります.すなわち,以下に 続く条件の間何かの作業をしなさい,というような意味です.そして,繰り返し 行う処理が何であるか,というか,どこまでの範囲の処理を繰り返すのか,を明 示するために for で示す繰り返しの構文の終わりを end で 示します.上の例文で言えば,

      print "a"
    

    の部分が繰り返される処理になります.

    例文ではその行を2文字分右にずらしています.これを字下げ(インデント)と言 います.Rubyはインデントがあろうが無かろうが動作に違いは出ません.しかし, インデントがある方がスクリプトを理解する上で分かりやすくなるので通常イン デントを行います.Rubyではインデントは2文字分が推奨されています.

    次に繰り返しの条件ですが

    for i in 0..20
    

    の赤字の部分が相当します.i というのは前回紹介した通りアルファ ベット小文字で始まるので「変数名」です.繰り返しの処理における回数指定く らいの意味しかないので複雑な名前を付けずにただ i とすることが多 いです.

    なぜ,i かは,正確には知らないのですが,integer(整数)も しくはindex(指数)から来ているはずです.

    後は,in という前置詞ですが,これは「予約語」として変数名に使用 できないように登録されています.in ということなので「〜の内」と いうことになります.何の内かというと,0から20という数字の範囲の内という ことになります.まとめると,「i という変数が0から始まって20にな るまでの間」

      print "a"
    

    の行を実行する」というスクリプトです.ここで問題です.i という 変数は値はいくつなんでしょうか.これが for 文の特徴なのですが, 先ほどの,

    for i in 0..20
    

    赤色の部分に重要なことが含まれています.この部分をより明確にするために, スクリプトを次のように変えてみましょう.

    for i in 0..20
      print i, " "
    end
    

    今度はどのような結果になったでしょうか.

    i という変数は,実は,繰り返しが行われるたびにその値が1ずつ増え ていくようになっています.なので,最初は範囲指定にあるように値が0だった のに,繰り返すたびに1ずつ増えていき,20になったときに繰り返し(ループ)が 終わるように設定されていたのですね.ですので print 文で変数 i を出力しなさいという命令がなされると,順に1ずつ増えていく値 を繰り返し表示します.

    かつて,自分の授業で for ループの学習用にフローチャートと対比さ せて動作するwebページを作成しました.興味があれば覗いてみて下さい.

    最後に,このスクリプトでは終了時に改行していないので,コマンドプロンプト がずれてしまっていますので,それを修正するために1行付け加えておきましょ う.以下の行を最後に足すともうちょっと見栄えがよくなるはずです.

    print "\n"
    

  4. 練習問題1

    先ほどのスクリプトを改造して1から19までの「奇数だけ」表示するスクリプト ができるでしょうか.試してみてください.

         解答例

  5. 多重のループ

    今回の目標はカレンダーを作ることなので数字を7つずつ行を変えて出力してみ ましょう.数字は今月は31日あるので1から31までとします.これを1行に7つず つ表示するスクリプトです.1行に7つ表示するということは7回繰り返す for ループを作る必要があります.例えば,次のようなものですね.

    for i in 1..7
      print i, " "
    end
    
    print "\n"
    

    7つの数字を表示することはできました.さて,次の行はどうしましょうか?今 度は8から14までです.こんな風にしてみますか?

    for i in 1..7
      print i, " "
    end
    
    print "\n"
    
    for i in 8..14
      print i, " "
    end
    
    print "\n"
    

    2行目もなんとかできました.この調子で3行目,4行目,5行目と行きますか?

    それはダメです!

    このように同じような処理を繰り返す場合に,人間の労力で実現するのは時間の 無駄です.単純作業はすべてコンピュータに押しつけましょう.そう,for ループの出番ですね.でも,

    for i in 1..7
      print i, " "
    end
    
    print "\n"
    

    のような複数行にわたるスクリプト,しかも,もともとが繰り返しだったものを どうやってさらに繰り返すのでしょうか.答えは,for ループのかた まりをもう一つの for ループの繰り返し実行する部分にしてしまうことです ね.例としては次のようになりますかね.

    for i in 0..4
      for j in 1..7
        print j + i * 7, " "
      end
    
      print "\n"
    end
    
    print "\n"
    

    なんだか,できたようなできていないような.数字が35まであるし…

    このようなループが入れ子になったものを「多重」ループと言います.実行され る順番は,まず変数 i が0にセットされ,j が1になります. 本文を実行した後,j が1増やされ,本文実行,…,で j が 7までくると,今度は i が1に増やされてからまた j が1か ら7まで変わり,j に関するループが終わるたびに i が増え ていき,i が4のループが終わると終わりです.

    インデントはループが多重になるとそれに応じて深くなってきます.それにより スクリプトがわかりやすくなります.

  6. フォーマット出力

    数字が35まであったことは今はちょっと忘れておきましょう.それよりも数字が 縦に揃っていなかったことの方に着目してください.先週ターミナルで cal コマンドを使ったときのように美しくそろった出力に変えることをま ず考えます.それには一桁の数字と二桁の数字が混在していることが問題 です.ですので,数字を必ず3桁分の幅で出力してしまうことを実現しましょ う.そのために「フォーマット」付きの出力命令である printf に変更します.次のスクリプトのようにしてみましょう.

    print " SU MO TU WE TH FR SA\n"
    
    for i in 0..4
      for j in 1..7
        printf "%3d", j + i * 7
      end
    
      print "\n"
    end
    
    print "\n"
    

    printf というのは初心者には難しい命令です.どこが難しいかという と %d のような「%記法」を使いこなさなければいけない点です. 次のスクリプトで%記法に慣れてしまいましょう.

    a = 12345
    b = 67890
    c = 123.0
    
    printf "a = %d\n", a
    printf "a = %d, b = %d\n", a, b
    printf "a = %10d, b = %10d\n", a, b
    printf "a/c = %d\n", a / c
    printf "a/c = %f\n", a / c
    printf "a/c = %1.1f\n", a / c
    

    d はdecimal,f はfloatの略で,それぞれ10進数,浮動小数 点数を意味します.他にも %s で文字列がありますし,数につい ても2進数(binary, %b),8進数(octal, %o),16進数 (hexdecimal, %x)が用意されていますので,簡単な進数変換にも 使えます.

    興味ある人は次のような進数変換を体験してみてください.

    printf "%b\n", 10
    printf "%o\n", 10
    printf "%x\n", 10
    printf "%d\n", 0xabc
    printf "%d\n", 01234
    printf "%d\n", 0b10101010
    

  7. 条件分岐

    さて,カレンダーに戻ります.一ヶ月の日数が31日という半端な日数なので1週 間の単位で区切るとうまくいきませんでした.なんとか31日で打ち切ることを考 えないといけません.そこでもし31日になったらそこから先は表示を止める,と いう処理ができるともうちょっといいかも知れませんね.そこでカレンダーもど きのスクリプトで31日が来たらループから「脱出」することを考えてみます.ス クリプトを次のように変えてみましょう.

    print " SU MO TU WE TH FR SA\n"
    
    for i in 0..4
      for j in 1..7
        printf "%3d", j + i * 7
    
        if j + i * 7 >= 31
          break
        end
      end
    
      print "\n"
    end
    
    print "\n"
    

    なんだか,いい感じになってきました.

    さて,ここで加えられた処理は次の部分です.

        if j + i * 7 >= 31
          break
        end
    

    if というのは英語の「もしも」ですので,これは「もし〜だったら… をする」という処理になると予想が付くと思います.条件部分の「もし〜だっ たら」が不等号で書かれている部分(青字部分)です.不等号の「≧」はキー ボードから入力できないので >= を使います.等号無しの > もありますし,逆向きの「小なり」もあります.等しいかどうか を見るのは == という等号二つを使います.等号一つは「代入」 操作に取られているので比較して等しいの方は区別するために記号を重ねま す.

    次は「…する」の方で,こちらは緑字で書いてある break だけです. break というのはそこで打ち切るという意味ですので,この処理 が実行されるとループを打ち切る,すなわち j に関するループを 中断して外にでます.それで31を出力した後,以後の数字はでなくなります.

    if を使うともっと違う処理もできます.

    print "         JULY\n\n"
    print " SU MO TU WE TH FR SA\n"
    
    for i in 1..31
      printf "%3d", i
    
      if i % 7 == 0
        printf "\n"
      end
    end
    
    print "\n"
    

    こちらはループが2重にはなっていません.また,先週学習した「剰余」演算子 の % が使われていますね.7で割ったあまりが0のときだけ改行文字を 出力します.

  8. 本当のカレンダー

    さあ,最後の仕上げです.見たい月のカレンダーを表示するスクリプトを作りま しょう.例えば,来月8月はどうでしょうか.8月1日は水曜日ですね.水曜日か ら始まるカレンダーを作るにはどうするのでしょうか.次のような準備が必要で すね.

    これだけではよくわかりませんが,それらを踏まえてとりあえず8月のカレンダー なら次のようにしてできるでしょう.

    print "          AUG\n\n"
    print " SU MO TU WE TH FR SA\n"
    
    sp = " "
    day = 3
    
    for i in 0..day-1
      printf "%3s", sp
    end
    
    for i in 1..31
      printf "%3d", i
    
      if i % 7 == (7 - day) % 7
        printf "\n"
      end
    end
    
    print "\n"
    

    最初の空白を埋めるためのスペースとして変数 sp を用意しました. その値はただのスペースですね.もう一つ曜日を表す変数 day を用意 しました.これは1日が何曜日かを示す数値を値として持ちます.日曜日を0とし て数えると水曜日は3ですので,3を入れています.そうすると改行が入るのは4 日,11日,18日,25日ですので,7で割ると4余る日です.条件分岐の部分にその 4を入れないで (7 - day) % 7 としたのは,何月にでも対応できるようなス クリプトへの準備です.さて,次はいよいよ今年のカレンダーですね,と,言い たいところですがもう一つ準備をしておきましょう.

  9. 配列

    配列はプログラミング学習初心者の「鬼門」です.ここを通り抜けられないと自 由なプログラミングは出来ません.難しくは無いのですが,とっつきにくいよう です.次のように使うということを知ってください.

    month = ["", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
    sum = [0, 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
    
    printf "%s", month[7]
    printf "%d",  sum[7]
    

    括弧( [ ] )の中にカンマで区切られた値の列があります.これらを順 番に参照できるように覚えているのが配列(array)です.ですので,例のように month[7] のように呼び出すと7番めの値が読み込まれます.ただし, 配列の「序数」は0から始まりますので0番目からかぞえて7番め(すなわち,要素 の個数としては8番め)の値は文字列の JUL ですので7月の名前が呼ば れます.もう一つの sum という名前の配列は引数として呼ばれた月の 1日がその年の何日目に当たるかを格納している配列です.すなわち sum[7] というと,7月1日はその年の182日目に当たるので182という数値を参照し ます.今年の元旦は月曜日で したので年初からの日数を割った余りが1であれば月曜日,2なら火曜,6で土曜, そして0であれば日曜日とい うことになります.

  10. 本日の実習

    今年の1月から12月までどの月のカレンダーでも表示できるようにするスクリプ トを作りましょう.ただし,月の与え方は次のような定数配列 ARGV を使うようにしてくだ さい.

    n = ARGV[0].to_i #最初についている引数を数値として変数 n に代入
    
    month = ["", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
    sum = [0, 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
    
    printf "%s", month[n]
    printf "%d",  sum[n]
    

    これは例えばスクリプトのファイル名を test.rb とすると

    > ruby test.rb 4

    のように使います.そうするとコマンドライン引数として与えられた数字の4が 数値として変数 n の値になります.これを活用して次の例に続けて スクリプトを作りましょう.

    n = ARGV[0].to_i
    
    month = ["", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
    sum = [0, 1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
    
    printf "          %s\n\n", month[n]
    printf " SU MO TU WE TH FR SA\n"
    
    
    

         解答例

    補足ですが,今回のように日付を扱うときにはRubyにはあらかじめ Date という「クラス」が用意されています.「オブジェクト指向」に精通して いる人はこの Date クラスのメソッドを呼び出すために,スクリプト の最初の方に,

    require 'date'
    

    として,メソッドを呼び出して使用するのが妥当でしょう.こんな感じでしょう か

  11. 宿題

    授業の最後に宿題の説明を行います.アナウンス に注意していてください.


目次ページに戻る