テーマ
最近読んだ本、最近観た映画、最近気になることのいずれかについて数行で熱く語る。
制限時間は5分。出席点は2点。提出要領は下記の通り。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:Mewによるメールの送り方はMewコマンドを参照
前回のプログラムは、getsメソッドを使ってファイルから1行ずつ読み込み特定の処理をするものであった。ファイルの指定は、ktermでプログラムを実行する際に
% ruby mining.rb regi.txt
のように指定した。これにより指定した任意のファイルを自由に読み込むことが可能になる。
一方で、このような指定方法にはデメリットもある。ファイル名を間違えたり、指定し忘れるとプログラムが正しく動かなくなる。また、常に同じファイルを用いるのであれば、いちいち指定するのが煩わしい。
前回はregi.txtを読み込んで分析をするプログラムであった。分析は目的により異なるため、同一のプログラムを継続的に使用しないこともある。それゆえプログラム実行時にファイル名を指定する方式であってもあまり問題にならない。一方、regi.txtを作成するプログラムを考えてみる。これはレジスターの履歴を単にファイルに出力するだけであり、なおかつデータは時系列で1つのファイルに蓄積されていくため、ファイル名は常に固定である。このような場合レジスターの電源を入れる(=プログラムを実行する)たびに販売履歴の出力先として毎回同じファイルを指定するとなるとなんとも面倒である。
こうした問題に対処するために準備されているのがopenメソッドである。
基本構造
open("ファイル","モード")do |変数| ファイルを読み込んで行う処理 end
ファイル:ファイル名を指定する。ファイルとプログラムは同一のディレクトリに置く。
モード:指定したファイルを開いて読み込むのか、指定したファイルを作成して書き込むのか、既存のファイルを開いて追加書き込みするのか等、モードを指定する。以下の6種類から選ぶ。
変数:printfやprintメソッドの表示先、getsメソッドの読み込み元を指定する際に使用する。任意の名前でよい。
register.rbを改良し、もう少し現実的なデータマイニングができるようにしてみよう。まずは年代を入力できるように改良してみよう(register_final.rb)。このプログラムでは、実行すると結果をディスプレイに表示するのではなく、ファイルに出力する。出力する際のファイル名はpos.outである。
#!/usr/koeki/bin/ruby product = [] #商品名 amount = [] #金額 sum = 0 #合計金額 item = 0 #配列のインデックス tax = 1.05 #消費税率(5%に固定) tc = 1 #transaction count(取引回数) open ("pos.out", "a") do |pos| #会計の繰り返し while true STDERR.print "いらっしゃいませ\n" STDERR.print "年代を入力(1:若年、2:中年、3:老年、9:終了):" age = STDIN.gets.chomp!.to_i if age == 9 then break end #商品名、金額の入力とお会計 while true STDERR.print "商品名を入力(終了はq):" product[item] = STDIN.gets.chomp! if product[item] == "q" then break end STDERR.print "金額を入力:" amount[item] = STDIN.gets.chomp!.to_i sum += amount[item] item += 1 end sum *= tax STDERR.print "\n" STDERR.printf ("お会計%d円になります\n", sum) STDERR.print "支払い金額を入力:" pay = STDIN.gets.chomp!.to_i STDERR.printf ("%d円お預かりいたします\n", pay) change = pay - sum STDERR.printf ("%d円のお返しになります\n", change) STDERR.print "どうもありがとうございました\n\n" #商品名、金額をファイルに書き出し i = 0 #配列のインデックス(2回目以降は0にリセット) while i < item pos.printf ("%d \t %d \t %s \t %d\n", tc, age, product[i], amount[i]) i += 1 end tc += 1 #取引回数を1増やす sum = 0 #合計をリセットする item = 0 #配列のインデックスをリセットする end end
このプログラムを実行した例を以下に示す。黄色の部分が実行中にキーボードから入力したものである。なお、ktermでの日本語入力の切り替えはShift-Space(シフトキーを押しながらスペースキー)である。
irsv{c106000}% ruby register_final.rb[Return] いらっしゃいませ 年代を入力(1:若年、2:中年、3:老年、9:終了):1 商品名を入力(終了はq):みかん 金額を入力:200 商品名を入力(終了はq):りんご 金額を入力:400 商品名を入力(終了はq):q お会計630円になります 支払い金額を入力:1000 1000円お預かりいたします 370円のお返しになります どうもありがとうございました いらっしゃいませ 年代を入力(1:若年、2:中年、3:老年、9:終了):3 商品名を入力(終了はq):卵 金額を入力:98 商品名を入力(終了はq):q お会計102円になります 支払い金額を入力:200 200円お預かりいたします 97円のお返しになります どうもありがとうございました いらっしゃいませ 年代を入力(1:若年、2:中年、3:老年、9:終了):9
プログラムの実行結果を見ると、なんとも中途半端な終わり方をしているように見える。このプログラムでは、詳細は後述するが、購入データをpos.outというファイルに出力している。では、pos.outにどのような情報が書き込まれているのかを見てみよう。
catはktermで使用するコマンドで、ファイル内を表示するものである。catの後ろに中味を見たいファイル名を指定し[Return]を押す。catと同じようにファイル内を表示するコマンドとしてlessコマンドがある。catコマンドは上下のスクロールができないのでファイル内のデータが多い場合には、上のほうはktermの画面の外に消えてしまい見ることができない。lessコマンドはkterm内で自由にスクロールをすることができる。lessコマンドを使用した場合、見終わったらqを押す。なお、ファイルを削除したい場合はrmコマンドを使用する。rm pos.out[Return]でpos.outが削除される。一旦削除したファイルはどんなことをしても復活することはできない。削除するときはよく確認すること。
irsv{c106000}% cat pos.out[Return] 1 1 みかん 200 1 1 りんご 400 2 3 卵 98
pos.outを見ると、1行につき、2つの数字、商品名、金額の4つのデータが書かれていることがわかる。最初の数字は取引回数を表し、1は最初のお客様、2は2番目のお客様ということになる。次の数字は年代で1は若年、3は老年となる。1人のお客様が2つの商品を購入するとデータは2行分になる。ここには書かれていないが5つ購入すれば5行分のデータになる。
なお、もう一度プログラムを実行し、適宜入力をして終了をしてから再度pos.outの中味を表示してみると、以下のようになる。
irsv{c106000}% cat pos.out[Return]
1 1 みかん 200
1 1 りんご 400
2 3 卵 98
1 2 大根 128
1 2 ほうれん草 100
1 2 たまねぎ 198
2 1 しょうゆ 398
2 1 さんま 58
3 3 卵 98
3 3 牛乳 128
3 3 食パン 100
3 3 大根 128
先ほどのデータに新しいデータが追加されていることがわかる。ただ一番左端の数字を見ると、2回目の通し番号がつながっておらず、また1から開始されているという問題がある。
このプログラムについて、まず全体的な構造を確認し、続けて詳細に見ていこう。このプログラムではwhile-endが3回登場している。そしてそれらをopen-endがさらに取り囲んでいる。まずは構造を図示してみよう。
open ("pos.out", "a") do |pos| (open-endで囲むことでこの間で行う処理の結果をファイルに出力できる)
while true STDERR.print "いらっしゃいませ\n" STDERR.print "年代を入力(1:若年、2:中年、3:老年、9:終了):" : (以下の2つのwhile-endを繰り返し行う) (これにより複数のお客さんに対応できるようになる)
while true STDERR.print "商品名を入力(終了はq):" : end (1人のお客さんについて商品名、金額の入力とお会計を行う)
while i < item pos.printf ("%d \t %d \t %s \t %d\n", ・・・・ : end (そのお客さんについて商品名、金額等をファイルに出力する)
end
end
この構造を前提に、新しく出てきた部分(プログラムの黄色の部分)について順に説明する。
openメソッドを使って、pos.outというファイルを開いている。開くときのモードはaであるため、追加書き込み専用となる。はじめてこのプログラムを実行した際には、pos.outというファイルは存在しないため、新規作成される。しかし、2回目以降はpos.outは存在していることから、データの末尾に追加で書き込みが行われる。なおdo |pos|のposはファイルに書き込みを行う際の変数として指定している(後述)。
openメソッドを使用してファイルを開いているため、printやprintfを使って何かを表示する場合、
という2種類の表示先が存在する。このため、どちらに表示するかを指定する必要が生じる。同様にgetsメソッドは値を読み込むメソッドであるが
という2種類の読み込み元がある。この場合も、どちらから読み込むのかを指定する必要がある。
キーボードからの入力を受け取る場合はSTDIN.getsのようにgetsの前にSTDINをつける。またディスプレイへの表示を行う場合はSTDERR.print, STDERR.printfのようにprintやprintfの前にSTDERRをつける。
通常のprintfにpos.がついている。このposは、openメソッドの行で指定した変数名である。変数名.メソッドとすると、そのメソッドはファイルに対して行われる。ここではpos.printfとしてファイルに出力している。このプログラムでは使用していないが、pos.printも同様にファイルに出力し、pos.getsはファイルから1行読み込むことになる。
ファイルに出力する内容は、後ろの()で指定したものであり、ここでは4種類となる。tcはお客さんの通し番号、ageは年代、productは商品名、amountは金額となる。tc、age、amountは数字であるため%dを使用している。productは文字であるため、書式制御文字に%sを使用して文字列を表示できるようにしている。
register_final.rbによりお客さんの購入データがファイルに出力できるようになったので、このデータを読み込んでデータマイニングを行うmining.rbもopenメソッドを使って書き直してみよう(mining_final.rb)。
#!/usr/koeki/bin/ruby #初期設定 name = [] #商品名を代入する配列 price = [] #金額を代入する配列 cost = [] #仕入額を代入する配列 profit = 0 #利益額を代入する配列 n = 0 #配列に使用するインデックス i = 0 #配列に使用するインデックス #商品名と金額の読み込みと利益額の計算 open ("regi.txt", "r") do |mining| while line = mining.gets if /\S+\s+\S+\s+(\S+)\s+(\d+)\s+(\d+)/ =~ line name[n] = $1 price[n] = $2.to_i cost[n] = $3.to_i profit += $2.to_i - $3.to_i n += 1 end end #個々の利益額の表示 STDERR.print "- 商品名 ------- 利益 --\n" STDERR.print "\n" while i < n STDERR.printf ("%-10s \t %3d円\n", name[i],price[i]-cost[i]) i += 1 end STDERR.print "-------------------------\n" STDERR.printf ("利益(合計) %5d円\n",profit) STDERR.printf ("利益(平均) %5d円\n",profit/n) end
mining.rbから変更した箇所は以下の通り。
前回はktermでプログラムを実行する際にregi.txtを指定していたが、今回はopenメソッドを使ってプログラム内で指定している。開く際のモードはrであり、読み込み専用となる。
openメソッドの行で指定した変数名を使い、変数.メソッドで、ファイルに対してそのメソッドを適用できる。今回は変数名をminingにしているので、mining.getsとすることで、regi.txtから1行ずつ読み込みlineに代入する。
その他、プログラム内の様々な箇所でSTDERRが使用されているが、これはディスプレイに結果を表示するために使用している。
以下の3種類の課題から実施できそうなものを選んで実施する。採点は以下の通り行う。
1番は既存のregister.rbにregister_final.rのb変更点を適用するものである。これまで作成してきたregister.rbはこの図の緑色と、青色の部分に相当する。オレンジ色の部分を追加して複数人のお会計が連続して行えるようにし、さらに赤色の部分を追加することで売り上げのデータをファイルに出力する必要がある。ファイルに出力するためには、青色の部分も適宜修正が必要となる。2番は、プログラムの中で1つ目のwhileに突入する前に、pos.outの中の一番最後のデータのtcがいくつであるかを調べておく必要がある。3番はmining_final.rbの改良だが、このプログラムはregi.txtを読み込むためのプログラムであるため、pos.outを読み込む形式に変更する必要がある。4番は1年生の後期で行ったhtmlの作り方を思い出してやってみること。
1番のみ・1番+2番・1番+2番+3番の場合
4番の場合
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする
Tips:Mewによるメールの送り方はMewコマンドを参照