千葉大学普遍教育情報処理科目:自習用テキスト「プログラミング入門」

生物生産科学科 担当 國分 尚


<-- 第3章目次第5章 -->


4.Rubyプログラムの構造

  1. 文、数値、文字列、変数、代入
  2. データの型、あるいはオブジェクトの種類
  3. 配列とハッシュ
  4. 制御構造
  5. アルゴリズムからプログラムへ
  6. オブジェクトとメソッド

4−1.文、数値、文字列、変数、代入

Rubyプログラムは文の集まりである。他の多くのプログラミング言語と違い、Rubyの文は最後に何かの記号を付ける必要はない。ただし、複数の文を1行に書く時はセミコロンで区切る。


i = 1
a = 0; b = "text"
print "Hello world\n"
print 'Hello world\n'
c = 5 / 6
print c,"\n"
c = 5.0 / 6.0
print c,"\n"

この例でもわかるように数値はそのままの数字を書き、文字列は " " (ダブルクォーテーション)、または ' ' (シングルクォーテーション)でくくる。" " と ' ' には違った意味があるが、主に使うのは " " である。数値で気をつける必要があるのは整数と実数の違いで、小数点のない数値は整数として判断される。

練習3.上の例を実行して""と’’のちがい、小数点の有無に対する挙動に慣れること。また、上の割り算で、片方だけに小数点を付けるとどうなるか。

Rubyでは = は代入の記号であり、数学の等号とは異なる。

a = 5

は a という変数に 5 を代入している。二つの値が同じかどうかを調べるには==を使う。

a == 5

はaに代入されているのが5であれば真 (true)、そうでなければ偽 (false)である。

プログラミング言語で変数と呼ぶものは数学の変数とは異なる。簡単に言ってしまうとある値が入っている入れ物となる。(Rubyの場合、厳密には変数にはオブジェクトへの参照が入っている。)

RubyはCやPascal等と違って変数の型と言うものがなく、宣言をする必要もない。従ってプログラムに変数宣言部という特別な構造はない。しかし、次に出てくるローカル変数は初めに必ず代入をしないと使えないし(代入が暗示的な宣言になっている)、プログラムの見通しをよくするためにもあとで使う変数にプログラムの先頭であらかじめ初期値を代入しておくのは悪いことではない。

変数にはローカル変数、グローバル変数、インスタンス変数、定数の4種類があり、次のように名前の付け方で区別する。

ローカル変数
アルファベットの小文字で始まる
グローバル変数
$で始まる
インスタンス変数
@で始まる
定数
アルファベットの大文字で始まる

このうちグローバル変数は一般には使われない。また、インスタンス変数はオブジェクト指向プログラミング以外には使わない。定数は最初に一度だけ代入することができるが、その後は値を変えることはできない。

4−2.データの型、あるいはオブジェクトの種類

この章の初めへ

一般に、プログラミング言語では扱うデータが「型」と呼ばれる種類に厳密に分かれている。整数型、実数(浮動小数点)型、文字列型、論理型などがあり、変数も型が決まっている。例えば a という変数が整数型と指定されると整数以外のデータを入れようとするとエラーになる。

Rubyではこれらはオブジェクトの種類(クラス)と呼ぶ。例えば123という数字は他の言語では整数型のデータだが、Rubyでは整数クラスのオブジェクトである。RubyではすべてのオブジェクトがObjectという種類のオブジェクトとなっていて、このような言語を純粋なオブジェクト指向言語、C++やJavaなどをハイブリッドなオブジェクト指向言語などという。


a = 25
print a, "\n"
a = "twenty-five"
print a, "\n"

Rubyの特徴の1つに多倍長整数がある。普通のプログラミング言語では扱える整数の大きさには制限があり、±32767または0から65535であることが多い。しかし、Rubyでは整数の大きさはメモリの容量のみに制限され、例えば400の階乗のような大きな数も問題なく扱える。

練習4.次のプログラムfact.rbを作成し、実行してみる。


プログラム2.fact.rb(まつもと・石塚, 1999)


def fact(n)
  return 1 if n ==0  # 0の階乗は1である
  f = 1
  while n>0
    f *= n  # f = f * nと同じ
    n -= 1  # n = n - 1と同じ
  end
  return f
end

print fact(ARGV[0].to_i), "\n"

ファイルを作成後、


keyaki% ruby fact.rb 10
3628800

と10の階乗が正しく計算されるのを確認したら


keyaki% ruby fact.rb 400

のようにすると400の階乗が出力される。この値は宇宙の直径と電子の直径の比よりも遥かに大きい。

4−3.配列とハッシュ

この章の初めへ

プログラミング言語で配列と呼ぶものも数学の配列とは異なる。簡単に言ってしまうと番号がついた変数のことで、この番号を別の変数を使って指定することで読み書きできるようになっている。

配列は a[0] とか youbi[d] の様な形をしている。

最初の例はaと言う名前の配列の0番目の要素、次の例はyoubiという名前の配列のd番目の要素と言うことで、dの値によって違う要素を指定できる。配列は下に出てくる繰り返しと組み合わせて使うことが多い。


プログラム3.dayofweek.rb


youbi = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
print "Today is ",youbi[Time.now.wday],".\n"
  #Time.now.wdayは今日が日曜なら0、金曜なら5という整数である

ハッシュ(連想配列)は配列と似ているが、要素を番号で指定するのでなく名前で指定するようになっている。使い方がわかると大変有効なものだが、ここでは紹介だけにとどめる。例えば p_rec["name"] というのはp_recという名前のハッシュのnameという要素を示す。


プログラム4.meibo.rb


def printPerson(p_rec)
  print "学籍番号: ", p_rec["id"],"\n"
  print "名前: ", p_rec["name"],"\n"
  print "出身地: ", p_rec["pob"],"\n"
  print "性別: "
  if p_rec["sex"] = "M"
    print "男\n"
  elsif p_rec["sex"] = "F"
    print "女\n"
  else
    print "性別データエラー\n"
  end
end

meibo=[]  # 空の配列を作る
meibo[0] = {"id" => "05H1111X", "name" => "田中一郎", "pob" => "新潟県", "sex" => "M"}  # 配列の0番目のデータとして{}内のハッシュを指定
meibo[1] = {"id" => "05H1112P", "name" => "新井素子", "pob" => "埼玉県", "sex" => "F"}
for person in meibo
  printPerson(person)
end

などという使い方が考えられる。いちいちこんなことをしなくてもid、name、age、sexという変数を使ったらいいのではないかと考える人は鋭い。ぜひともRubyプログラミングの参考書や一般的なプログラミングの本を調べて理由を考えてください。

4−4.制御構造

この章の初めへ

A. 文の連続

連続した文は上から順番に実行する。上のdaikei.rb等を参照。

B. 条件分岐

ある条件によって処理を変えたい時に使う。最も簡単には

if 条件
 条件に当てはまる時の処理
end

のように書く。もっと一般的には

if 条件1
 条件1に該当する時の処理
elsif 条件2
 条件2に該当する時の処理
...
elsif 条件n
 条件nに該当する時の処理
else
 どの条件にも当てはまらない時の処理
end

と言う形になる。この時に注意が必要なのは、条件1が成立した場合、その時の処理が終わったあとにはendの次の文を実行し、elsif以下の条件に当てはまるかどうかの判断は行わない、ということである。条件1に関わりなく条件2の判断をしたい場合は

if 条件1
 条件1に該当する時の処理
end
if 条件2
 条件2に該当する時の処理
end

と書く必要がある。


プログラム5.greeting.rb


hh = Time.now.hour  #Time.now.hourは現在時刻の時の部分
if hh >= 4 and hh <= 11
  print "おはようございます\n"
elsif hh > 11 and hh <= 18
  print "こんにちは\n"
elsif hh > 18
  print "こんばんは\n"
else
  print "こんな時間に起きていると体を壊しますよ\n"
end

夜中の1時頃このプログラムを実行して「大きなお世話だ」と言いたくなる人は適当に改造すること。

C. 繰り返し

ある条件が成立している間、同じ処理を繰り返す必要がしばしばある。これを行うのが繰り返しで、ループとも呼ぶ。Rubyは繰り返しの書き方が何通りもあるが、基本は次のようになる。

while 条件
 条件が成立している時の処理
 条件の成立に影響を与える処理
end

よくあるプログラム上の間違い(bug,バグと呼ぶ)は繰り返しの処理の中で条件の成立に影響を与える処理を書き忘れることである。この場合運が悪いと繰り返しの中から抜け出せなくなる。これを無限ループと呼び、最も頻繁に出くわすバグの一つである。

文章ではよくわからないので例を挙げると


プログラム6.sum10.rb


s = 0; i = 1
while i <= 10
  s = s + i
  i = i + 1  #カウンタを増やす
end
print s,"\n"

これは1から10までの和を求めるごくつまらないプログラムである。ところが、ここでカウンタを増やす文を書き忘れてしまうとプログラムはいつまでたっても終了しない。(このような事態に陥った場合はC-cをタイプするとプログラムを中止できる。)繰り返しの部分がこのように単純な場合はまず問題ないが、繰り返しの処理が複雑になってくると間違いが入り込む確率も上がることに注意する。

D. サブルーチン(メソッド)

まとまりのある処理を値だけ変えて何度も行う必要があるとき、またはその処理を状況に応じて実行したりしなかったりする場合、一般にサブルーチン(または手続き、関数)と言うものを使う。Rubyの場合これをメソッドとよび、単なるサブルーチンとは性質が異なるが、オブジェクト指向プログラミングを意識しない限り普通のプログラミング言語のサブルーチンと違いはない。これまでの例題の中にも既に出て来ていて、プログラム2,fact.rbの中の

def fact(n)
  ....
end

の部分がメソッドである。10の階乗でも400の階乗でもアルゴリズムは同じで、与える値を変える仕組みさえあればよい。これを行うのがメソッドである。

メソッドはdef ... で定義した部分では実行されず、他の部分から呼び出された時に初めて実行される。

4−5.アルゴリズムからプログラムへ

この章の初めへ

ここまでの情報をふまえてアルゴリズム3をRubyプログラムに書き直してみる。プログラミングではまず部品を完成させてから全体を作る方法と、全体の流れを作っておいてから細かい部分を作っていく方法があるが、今回の場合は後者は成立しないので(素数かどうかの判断ができないとプログラムが永久に終了しない)、まず素数の判定部分をメソッドとして作成し、それをテストしてみる。


プロブラム7.test_sosuu.rb


def sosuu?(x)
# 素数の判定を行う
# 入力条件: xは3以上の奇数
# 出力条件: 素数ならtrue、素数でなければfalseを返す
  i = 3
  while (i <= x/3) and (x % i != 0)  #x % i はxをiで割った余り
    i = i + 2  # i += 2 とも書ける
  end
  if i > x/3
    # 成立すればxは素数(割り切れた場合にはiは必ずx/3以下である)
    return true
  else
    return false
  end
end

print "n="
n=STDIN.gets.to_i
if sosuu?(n)
  print n," is a prime number.\n"
else
  print n," is not a prime number.\n"
end

練習5.test_sosuu.rbを実行して11が素数であり、9はそうでないことを確認し、そのほかのいくつかの数について素数の判定をする。このアルゴリズムは3以上の奇数について正しく働くが、偶数に対して判断するとどうなるか。

素数の判断に間違いがなければ、全体のプログラムに進む。これは次のようになる。ただし、全員が一斉に1000番目の素数を求めようとするとホストに負荷がかかりすぎる可能性があるので、100番目に変更してある。(一人だけで実行した時は1000番目で約4秒かかった。単純計算で100人同時だと400秒かかることになる。)このような場合、この1000とか100とかの数を後から簡単に変更できるようにプログラムを書くことは非常に重要である。下の例ではたった1か所を変更するだけでいいことに注意する。


プログラム8.sosuu.rb


def sosuu?(x)
  i = 3
  while (i <= x/3) and (x % i != 0)
    i = i + 2
  end
  if i > x/3
    return true
  else
    return false
  end
end
  
n = 3
c = 2
last = 2
target = 100
while c <= target
  if sosuu?(n)  # nは素数か?
    last = n
    c = c + 1  # c += 1 とも書ける
  end
  n = n + 2  # n += 2 とも書ける
end
print target, "th prime is ", last, ".\n"

練習6.sosuu.rbを実行して100番目の素数が541になることを確認する。また、プログラムを変更して何通りかのn番目の素数を求める。

練習7.sosuu.rbを変更してキーボードから任意の自然数を入力し(プログラム1.daikei.rbプログラム7.test_sosuu.rb参照)、n番目の素数を求めることができるようにする。または、プログラム2.fact.rbのやり方を参考にしてコマンドラインからnを指定するようにしてもよい。1番目(2)と2番目(3)の素数についても動くだろうか。

練習8.上の練習7のプログラムを変更し、目的の素数だけでなく、途中で1番目の素数からn番目まですべての値をc: lastの形で出力すること。出力例を示す。

出力例
1: 2
1th prime is 2.

1: 2
2: 3
3: 5
4: 7
5: 11
6: 13
6th prime is 13.

練習9.上の出力例で"1th prime is 2."となっているが、これでは大学生の作ったプログラムの出力としては恥ずかしいのでここも変更したい。targetを10で割った余り (target % 10) が1、2、3、それ以外の場合分けをすればよいだろう。(少し考えればこれでも十分でないことはわかるだろう。外国語の数字の扱いは大変である。ヒント:11はどうなる?)

4−6.オブジェクトとメソッド

この章の初めへ

オブジェクト指向プログラミングにおいてオブジェクトとは簡単に言うとデータとそれを操作するアルゴリズム(メソッド)をまとめたものである。オブジェクトは自分自身にどんなデータが格納されていて、それをどう扱うかを「知って」いる。例えばプログラム1の中にSTDIN.getsという表現が出てくるが、これはSTDINというオブジェクトのgetsというメソッドを使うことを意味し、「STDINにgetsメッセージを送る」と表現する。STDINは標準入力といわれるIOクラスの特別なオブジェクトであり、プログラムが使われた状況によってキーボードであったりファイルであったりするが、STDINオブジェクトはこれを知っていて、getsというメッセージをもらうと自分自身のおかれている状況に従って適切な処理を行い、呼び出したプログラムに1行分のデータを答えとして送り返す。

また、jouhen.to_fはjouhenという変数が示すオブジェクトにto_fというメッセージを送っている。プログラム1の場合はjouhenにはキーボードから入力した文字列がオブジェクトとして入っているので、文字列に対するto_fメッセージということになり、文字列オブジェクトは自分自身を数値に変換した値を答える。

目次

  1. アルゴリズムとプログラム
  2. プログラミング言語
  3. Rubyプログラミング入門
    1. 環境を整える
    2. エディタの使い方
    3. Hello World!
    4. 台形の面積を求める
    5. Rubyプログラムの実行
  4. Rubyプログラムの構造
    1. 文、数値、文字列、変数、代入
    2. データの型、あるいはオブジェクトの種類
    3. 配列とハッシュ
    4. 制御構造
    5. アルゴリズムからプログラムへ
    6. オブジェクトとメソッド
  5. ファイル処理と正規表現
    1. Genbankの出力を加工する
    2. ファイルからデータを読み込む
    3. ファイルにデータを書き込む
    4. 正規表現
  6. その他
  7. 参考文献
  8. 用語集
  9. 練習問題の解答例
  10. 索引

情報処理のトップへ戻る


2004年5月14日作成、2006年4月18日更新

國分 尚
hkokubun@faculty.chiba-u.jp