課題
この課題では,所要時間の計算に使われる出発時刻と到着時刻について,次のことを仮定します.
- 時刻は24時間制にしたがって入力される.
- 出発と到着は同日であって,出発時刻より到着時刻の方が後である.
- 時刻は常に適切に指定される.おかしな時刻(ありえない時刻)は指定されない.
これらを満たさないデータが指定されたとしても,上の仮定のもとで処理してよいことにします.
課題ではメソッドを有効活用することを期待しています.
想定するプログラム実行例
プログラムのファイル名が仮に「elapsed_time.rb」であったとして, 想定される実行例を示します.
$ ruby elapsed_time.rb 出発時刻と到着時刻を指定して所要時間を計算します 出発時刻(HH:MM:SS): 10:30:56 ⏎ 到着時刻(HH:MM:SS): 12:43:15 ⏎ 所要時間は2時間12分19秒です $ ruby elapsed_time.rb 出発時刻と到着時刻を指定して所要時間を計算します 出発時刻(HH:MM:SS): 8:11:3 ⏎ 到着時刻(HH:MM:SS): 22:05:18 ⏎ 所要時間は13時間54分15秒です
プログラムヘッダ
次に示すコードをプログラムの先頭にコピーして入れておくようにして下さい(必要事項は記入して下さい).
=begin
所属:
氏名:
学生番号:
難易度(5段階評価):
感想など(任意)
=end
[参考] ヘッダの調整
EmacsでRubyファイルを新規作成したときに取り込まれるヘッダは次のファイルに記述されています.
HOME > .emacs.d > template > template.rb
このファイルを編集して保存すれば,次回からは(新たにEmacsを起動すれば),変更後のヘッダが自動的に取り込まれるようになります.
なお「.」から始まる名前のファイル,フォルダ(ディレクトリ)は, 通常は見えないようになっています. Emacsで新規ファイルを作成する画面で「Show hidden files」にチェックを入れれば,「.」から始まる名前のファイルとディレクトリも表示されるようになります.
☑ Show hidden files
プログラムの構成,サンプル
プログラム全体の構成は次のようになるでしょう.
1. メソッド定義(1つ以上) 2. メソッドを使った一連の処理 2-1 データ入力 2-2 計算 2-3 結果の表示
次にサンプルプログラムを示します. ブラウザの画面で開いたときに文字化けしてしまう場合には, ダウンロードしてEmacs等で開いてみて下さい.
データ入力のための処理
次のように書くことで,端末ウインドウに質問文を表示しつつ, キーボードから文字列を読み取ることができます.
# メッセージを端末のウインドウに表示して,キーボードから文字列を読み取る.
# 読み取った文字列はstrに代入する.
print "出発時刻(HH:MM:SS): "
str = gets()
この例の場合,読み取った文字列は「変数str」に代入されます. 変数名は自由に決めて構いません.
getsで読み取られるデータは文字列(キーボードで入力された文字の並び)です. 「HH:MM:SS」の形式で時刻が指定されたとすると, 次のようにして文字列データから「時」「分」「秒」の数値(整数データ)を得ることができます.
# サンプル時刻データ(10時30分56秒) str = "10:30:56" # strを':'で区切って,それぞれを整数に変換し(to_i),順にh,m,sに代入する h,m,s = str.split(':').map(&:to_i) # h, m, sの値を使って時刻をputsで表示する # 「10時30分56秒です」と表示される(最後に改行される) puts "#{h}時#{m}分#{s}秒です"
この処理は,splitで指定した文字列(この場合は':')で数字が区切られていないと想定通りには動作しません.
splitに指定する文字列については空白の有無も区別されます.
そこでたとえばsplit(' : ')と書いた場合には,
':'ではなくて' : 'によって区切ることになります.
そのように指定した場合は'10 : 30 : 56'のように空白を入れないと期待通りには区切られません.
[参考] 数字列から数値への変換
数字列は飽くまでも文字の並びです.数値とは区別されます.
x = '12' y = '34' z = x + y # z → '1234' (文字列同士の「+」は連結処理) a = x.to_i # xを整数に変換してaに代入 b = y.to_i # yを整数に変換してbに代入 c = a + b # c → 46
上に示した時刻データの入力では,文字列(HH:MM:SS)から切り出した数字列のそれぞれ(HH,MM,SS)を「to_i」で整数に変換しています.
「gets」でキーボードから取り込まれるのは常に文字列ですので,数値をデータとして取り込もうとする場合には注意が必要です. たとえば次のプログラムは動作しません.
print('税抜き価格を入力してください:') s = gets() # 次の処理はエラーになる(文字列に「floor」は適用できない) y = (s * 1.10).floor # floorは小数点以下の切り捨て処理 puts "税込価格は#{y}円です"(価格として数字列が入力されるとして)期待通り動作させるには,次のように「to_i」によって数字列を数値(整数)に変換する必要があります.
print('税抜き価格を入力してください:') s = gets() # OK(文字列sを整数に変換してから計算) y = (s.to_i * 1.10).floor # floorは小数点以下の切り捨て処理 puts "税込価格は#{y}円です"
文字列への式の値の埋め込み
上でも示したとおり,文字列("...")中に「#{式}」と記述すると, 「式」を評価した結果に置き換えられます. 「式」は「数値」でなくても構いません.
# キーボードから「名前」を読み込んで,「こんにちは」とあいさつする print "お名前を教えてください: " name = gets().chomp # chomp: 文字列の末尾に「改行」があれば,取り除く puts "こんにちは,#{name}さん!"
多重代入
Rubyでは,代入式の左辺に複数の変数,右辺に複数の式を指定して,それぞれの変数に対応する式の値を同時に代入できます. この仕組みを多重代入とよびます. 上に示した時刻の入力処理では,この多重代入を利用しています.
# サンプル時刻データ(10時30分56秒) str = "10:30:56" # strを':'で区切って,それぞれを整数に変換 # → 右辺の処理で整数が3つ得られて,多重代入によりh,m,sに順に代入される h,m,s = str.split(':').map(&:to_i)
次に多重代入の具体例を示します.
x,y = 1,5 # x = 1, y = 5 a,b,c = x+y,x-y,x*y # a = 6, b = -4, c = 5 z,w = 3,4 # z = 3, w = 4 z,w = w,z # zとwの値を交換(z=4,w=3になる)
ちなみに多重代入を使わない場合,上の例で示した2つの変数の値の交換はそれほど単純には実現できません. 次のようにしても値は正しく交換されません.
z = 3 w = 4 # zとwの値を交換??(z=4,w=4になってしまう) z = w # この時点でz=4 w = z # w=4となる
複数の値を返すメソッドの定義
Rubyでは複数の値を返すメソッドを定義できます. 次に2通りの例を示します.
def powers(x) y = x**2 # x2 z = x**3 # x3 return y,z # yとzを返す(returnを使う方法) end def more_powers(x,k) y = x**k # xk z = x**(k+1) # xk+1 w = x**(k+2) # xk+2 [y,z,w] # y,z,wを返す(「配列」として返す方法) end u,v = powers(5) # ==> u == 25, v == 125 a,b,c = more_powers(2,8) # ==> a == 256, b == 512, c == 1024
Tips
以下,課題に取り組むにあたって,知っておくとよさそうなtips(hint)を示します.
- pメソッドの活用
プログラム作成中には(意図せずして)さまざまな問題が発生するものです. 問題が起きたとき,もちろんプログラム自体をチェックすればいいのですが, 「怪しそうな」変数の値を確認してみることが,原因を絞り込むのに有用です. 変数の値を調べてみるのにRubyでは「pメソッド」あるいは「ppメソッド」が使えます. pメソッド,ppメソッドによって,指定されたデータの値を端末の画面に表示できます.x = 0; y = 1; z = 2 # # pメソッド,ppメソッドの利用例 # 「p」「pp」の後に空白を入れていることに注意 # 空白を入れないとエラーになる # p [x,y] # [0, 1] p [:check1, x-y, x-y > 0] # [:check1, -1, false] p [:check2, z-y, z-y > 0] # [:check2, 1, true] pp [:x,x,:y,y,:z,z] # [:x, 0,:y, 1, :z, 2]
上の例では,まだ授業では説明していない種類のデータ(配列,真偽値,Symbol)を用いていますが,この例を応用してpメソッドを使ってみることはできるでしょう. なおppメソッドは,pメソッドの拡張版のようなものです. 多くのデータを並べたときに,それらを「キレイに」表示します(pretty print). データが少ない場合にはp,ppに差はありません. - 同じ計算式を何度も書くのは避ける→変数を適切に活用する
- 変数に代入すればデータを再利用できる
- 変数で記述→プログラムが短くなる
- 何度も計算→計算処理のコストがかかる
- 即値(数字)で辻褄合わせするのは避ける→変数を適切に活用する
- 変数の値を変えると辻褄が合わなくなる
- プログラムに汎用性がなくなる
- 即値ではデータの意味が分かりにくい
- 関係性のあるデータは変数の関係式から導出する
- プログラムには適切にインデントを付ける(半角の空白で字下げする)
- プログラムの構造をインデントで視覚的に示す
→構造が見えやすくなって,間違いも見つけやすくなる - Emacsの場合,自動的にインデントが付けられる
インデントがおかしくなった?→上から順に各行で[TAB]
(手動でインデントを付けるのは避ける)
- プログラムの構造をインデントで視覚的に示す