プログラムを書く際の約束事

Ruby 言語でプログラムを作成する方法や、表示する方法、入力する方法を復習した。 とくに入出力との関係は以下のようになる。

唯一のキーワードをヒントに連想しよう

公益ルビ緒は、インターンシップで、 ある大手の問屋に受け入れてもらうことになった。 まずは農産物の部門に配属された。 伝票を作成する仕事を与えられた。 100g 当りの品物の価格は次のようなものである。

9 時までに届いた価格表
押し麦250
あわ180
ひえ130
きび210
クコの実230
そばの実170
10 時までに届いた価格表
白ごま150
黒ごま150
玄米300
七分付き400
白米650
干し椎茸620

取り扱う項目が次々に増えるので、 プログラムで伝票を作れるようにしたいと考えた。 商品にラベルや番号がとくにないので、Hash がよいのではと思い、 試してみることにした。 まずは データをプログラムに入力しよう。

入力したデータを取り出そう


#!/usr/koeki/bin/ruby
#coding: euc-jp

shop = Hash.new
shop["白ごま"] = 150
shop["黒ごま"] = 150
 :

入力したデータを取り出し、 顧客の要望の重量から取引する価格を出力する部分を付け加えよう。


#p shop p は確認のために使う実行文

STDERR.print("購入する品物:\n")
item = gets.chomp

STDERR.print("購入する重量:\n")
weight = gets.chomp.to_f

printf("%s\t(%5.0f グラム)\t%4.0f円\n",item, weight, shop[item] * weight / 100)

出来上がったら、実行して調べよう。


購入する品物:
きび
購入する重量:
250
きび(250 グラム)        525円

bc -l で合っているかどうか確認しよう。

一度にいくつかの品物を調べられるようにに変更するには、 どのようにしたらよいだろうか。

あるかないか

ルビ緒は、担当する品物の数が増えてきたので、 重量を入力する前に、入力した品物名が Hash 対の key として登録されているのか、 知りたくなった。

ハッシュ.key?(key)
文字列 key があるかどうか調べる
ハッシュ.value?(value)文字列 value があるかどうか調べる

返された値を調べるには、p 文を使うと便利である。


STDERR.print("購入する品物:\n")
item = gets.chomp

p shop.key?(item)

と付け加えてみた。 実行し、存在しない品物を入力されたときの反応を調べよう。


購入する品物:
にんじん
false
購入する重量:

この key? method を使って、品物が存在しないときに、 存在しないことを知らせるように変更することにした。 存在しないとき対に関する初期値については次に

value? を用いて、 単価が 150 円の品物があるかどうか調べるようにするにはどうしたらよいか。

対にないものが呼び出されたとき

Hash 対の value では初期値を設定することができる。 初期値には存在しない key が呼び出されたとき、 その代替となるものを入れておくとよい。 ルビ緒は、入力した品物が Hash 対の key でないとき、 取り扱っていない商品であることを知らせるようにした。

hash 名 = Hash(初期値) 存在しない key が呼び出さ れたときに初期値を代入

Hash 対が登録されていないときにこの初期値が呼び出される。


#p shop 

STDERR.print("購入する品物:\n")
item = gets.chomp
#p shop.key?(item)

if shop.key?(item) == false
printf("%sは%sです\n",item, shop[item])
else
STDERR.print("購入する重量:\n") weight = gets.chomp.to_f printf("%s(%5.0f グラム)\t%5.0f円\n", item, weight, shop[item] * weight / 100)
end

出来上がったら、実行して調べよう。

購入する品物:
にんじん
にんじんは非取扱商品です

初期値を設定しなくてもよい。 この場合、key が存在しないときには nil が返る。 nil とは、空っぽの、無効の、という意味である。

Hash の対を入力するには

ルビ緒は、その日の仕入の状況で、 取り扱う雑穀の種類や価格が変わることに気がついた。 そこで、雑穀のデータを入力するプログラムを作ることにする。 hash_pair.rb

実行してみよう。


本日の価格を入力してください
** 終了は Ctrl-D
取扱商品名:押し麦
100グラムあたりの価格:250
取扱商品名:あわ
100グラムあたりの価格:180
取扱商品名:ひえ
100グラムあたりの価格:130
取扱商品名:きび
100グラムあたりの価格:210
取扱商品名:クコの実
100グラムあたりの価格:230
取扱商品名:そばの実
100グラムあたりの価格:170
取扱商品名:入力終了 
{"きび"=>210, "そばの実"=>170, "押し麦"=>250, "ひえ"=>130, "クコの実"=>230, "あわ"=>180}

同じ穀物を 2 回、異なる値段で入力した場合、 登録されたデータがどのように表示されるのか、調べてみよう。

Hash の対を表示するには

Hash の対を入力する方法は分かった。 全ての Hash の対をきれいに出力し、 項目の一覧表を作ろう。

for キー名,  値 in ハッシュ配列名
行う操作
x end

for 文は、繰り返すときに使う。 hash_pair.rb を以下のように変更しよう。


# p shop shop に入っているデータを全て見せよ

for item, price in shop 
printf("%s\t%d\n", item, price)
end

入力日の日付をつけて、 一覧表をファイルに出力するプログラムに変更せよ。

each を使って並び換え

for 文を使うかわりに、 each 文を使ってもよい。


配列.each{ 第 0 要素から順に取り出して変数に代入
|配列の要素をしまう変数| 配列の要素を使用して行う動作
}

例えば、 hash_pair.rb で、次のように書いてみよう。


shop.keys.each{|item| shop.keys で keys の並んだ配列
printf("%s\t%d\n", item, shop[item]) この配列の要素一つ一つを
} 仮引数 item に代入する

keys method あるいは values method を使うと、 Hash 配列に含まれる key や value を配列に埋め込ませることができる。

p shop.valueshash_pair.rb に埋め込んだ結果は何か。 また、 p shop.keys ではどうか。

配列の入れ替えを行うには、sort method を使えば良かった。 reverse method も組み合わせて、 小さな順、大きな順ではそれぞれ以下のように並べ替えることができた。

p shop.values.sorthash_pair.rb に埋め込んだ結果は何か。 また、p shop.values.sort.reverse ではどうか。

printf 文によりきれいに出力される。 またその出力順は文字コード順であることが分かる。

何が入っているのか

プログラムを実行しても、 エラーが出ずに行を越え、ときには終了してしまうことがある。または、 何度も実行させるべき while 文を通過していないと思われることがある。 あるいは while 文から抜けられないこともある。 その他にも if 文で通過すべきなのに通過しないまま終了することがある。 何行目で間違った実行文を書いているのか知るためには、 表示させる実行文で、中の変数を表示させると便利である。 print文, printf 文でもよいが puts 文, p 文なども使うことができる。 prints.rb

#!/usr/koeki/bin/ruby
print("表示 \n")       

puts("表示") 改行文字を含んで表示する

test_string = "表示"                 
printf("%s\n", test_string) format して表示する

p test_string        変数に入っている中身をそのまま表示

test_array = ["表","示"] 

puts test_array   改行文字を含んで要素を一つずつ表示する
p test_array      配列に入っている中身をそのまま表示

test_hash = {"表" => "示", "ひょう" => "じ"}

puts test_hash test_hash の key と値を並べて表示
p test_hash    test_hash の中身をそのまま表示

実行してみると、

%./prints.rb[˜/Ruby]
表示 
表示
表示 
"表示"
表
示
["表", "示"]
表示ひょうじ
{"表"=>"示", "ひょう"=>"じ"}

p 文, puts 文は、試作品を作っている最中に使うことが多い。 キーボードを叩く文字数が少ないのでよく使われる。 ただし 他人には分りにくい。 提出する作品では print 文, printf 文を使う。

覚える順番は違うけれど答えは正しい

計算機は自分の教えた順番どおりに 覚えてくれない ことを理解しよう。

これはいくら

項目がキーワード (key) となり、何か値 (value) を出すときには、 Hash を使うと便利である。 hash_shop.rb

データを入力し、その内容を確認するときには、p 文が使える。

p Hash 名Hash の中身を確認する

実行してみると、Hash のもう一つの書き表し方も分かる。

%./hash_shop.rb
{"白米"=>130, "七分付き"=>40, "きび"=>210, "そばの実"=>170, 
"押し麦"=>250, "玄米"=>30, "黒ごま"=>150, "干し椎茸"=>500, "ひえ"=>130, 
"クコの実"=>230, "あわ"=>180, "白ごま"=>150}

中括弧で Hash の集合全体を表す。全ての key と value の対について

key_n => value_n (n=1,2,..)
で関係を示し、対の区切りは , を使う。

Hash 名["key_1"] = "value_1"
Hash 名["key_2"] = "value_2"
:
1 行に 1 つの組み合わせ後ろに次々に対を足していく
Hash 名 = {"key_1" => "value_1", "key_2" => "value_2", ...}
いくつも 1 行で書き込む方法

入力した順番と同じ ではない。 これは、 計算機が情報をしまうときに、メモリの空いている場所にしまい、 呼び出されたときはメモリの頭から読み込んで結果を出力するためである。 (目で確認したとおり)計算機は正確に覚えているので、 計算機が覚える 順番を気にする必要はない

key の並び換え

その日の単価が最も高い穀物を割り出し、 Web で公告を出すように上司から頼まれている。 この性質を使って、ルビ緒は、 価格の入れ替えを行い価格の高いものをリストアップしながら出力するように、 プログラムを変更しようとを考えた。まずは文字列コード順表示を利用してみよう。


print("本日の高値の穀物について\n")
shop.values.sort.reverse.each{|item|
    printf("%s\t%d\n", item, shop[item])
}

これでは結果を得られない。 shop.values.sort.reverse だけで得られる配列は確かに value の大きな順になっているが、 each method で指定したいものは hash 配列の key である。 shop.keys の配列が、 shop.values.sort の並べ替え順に並ぶようにしたい。 そこで、sort に詳しく指定をし、 value で入れ替えをするように、指定をすればよい。

配列.sort{
|もとの配列の要素を代入する変数,並べ替え後の配列要素を代入する内部変数| 並び替えに使用したい個所の指定 <=> 並べ替え後の該当個所
何を使って並べ替えるかを
もとの配列と並べ変え後の配列の要素を代入する変数を使って表す
}

もとの配列と並べ替え後の配列の要素を代入する内部変数は {} の中だけで使われる。 内部変数は名前を好きにつけてよい。 よって今の場合、これらの変数として prim, res を使うとすると、 prim, resshopkey をとりだし、 prim と res に代入する。 その value は shop[key] と を代入することによって 得ることができる。shop[prim]shop[res] の値について <=> で入れ替えを行う。これで value での入れ替えができる。


shop.keys.sort{
|prim,res| shop[prim] <=> shop[res]
}
.reverse.each{ :

と変更すればよい。 配列の入れ替えについて

value は配列でもよい

Hash にしまえるのは変数だけではなく、 配列もしまうことができる。 ルビ緒は、各卸売先の 1 年間の黒ごまの出荷量を調べることにした。 四半期のデータは次のようになった。 sesame.dat

このデータを使ってプログラム hash_sesame.rb で出荷量を調べる。 Hash 名を sesame とし、データを変数 quarter で取得して、 それが求める分解になっているかどうか調べ、 そうであるならば、 Hash に分解したデータを配列としてしまうプログラムである。

実行してみると、 何も実行されていないかのような プログラムになる。

p 文を実行し、 この配列に入っている文字の種類は何か、調べよ。 とくに四則演算が可能であるか調べ、 そうでない場合はどこを変更すべきか述べよ。

整数値が使われているので、文字列を整数値に変換する to_i method を使用して、p 文を実行した結果、例えば

%./hash_sesame.rb sesame.dat
{"みがざスーパー"=>[20, 4, 4, 5], 
 "さけでん商店"=>[6, 2, 3, 4], 
 "まづやま酒店"=>[2, 1, 3, 1], 
 "づるおが米商店"=>[10, 3, 4]}

と表示されれば、四則演算が可能になる。データを計算し、 総量を求めるには、この Hash の中の配列を計算 すればよい。

key を叩いて value の一部を取り出すには

key を 1 つずつ叩いて、value を取り出すには for 文を使った。 4 半期の出荷量を入れた配列の名前を仮に perquarter とする。 総出荷量は 4 半期全てを足した量であるので、 それを変数 total に代入し、 小売店ごとの出荷量を得る。

 :
print("# # # 前年度の各小売店への出荷量 # # #\n")
for customer, perquarter in sesame
  total = perquarter[0] + perquarter[1] + perquarter[2] + perquarter[3]
  printf("%s\t%10d[kg]\n", customer, total)
end

実行すると、


# # # 前年度の各小売店への出荷量 # # #
みがざスーパー          33[kg]
さけでん商店            15[kg]
まづやま酒店             7[kg]
づるおが米商店          22[kg]

となる。

四半期ごとの総売り上げを調べるようにするには、 どのようなプログラムを組めばよいだろうか。

value の一部を使ってデータを入れ替えるには

第 2 期(4--6 月期)の黒ごまの売上げが低いことに気づいた。 ルビ緒は、売上げの低い小売店を割り出すことを頼まれた。 each method を使えば、 各小売り店の情報を取り出すことができる。 しかし、第 2 期のみ取り出す方法は分からない。 そこで、p 文を使って、使用したい key と value を表示させることにした。


print("第 2 期の売上げについて\n")
sesame.keys.each{
|retail|
p retail, sesame[retail]
}

p 文の実行結果を調べてみる。


第 2 期の売上げについて
"みがざスーパー"
[20, 4, 4, 5]
"さけでん商店"
[6, 2, 3, 4]
"まづやま酒店"
[2, 1, 3, 1]
"づるおが米商店"
[10, 3, 4, 5]

と表示される。value sesame[retail] は 配列である。そこで、第 2 期は、 sesame[retail][1] という成分で取り出せるのではないかと考えた。


p retail, sesame[retail][1]

この方法で配列の成分だけを取り出すことができるようになる。 この 成分を使って入れ替えをしたい

要素の一部分を使って入れ替えをするには

次に、sesame.keys 配列に sort をかけたい。 単なる sort method だけでは、key 部分での sort を行ってしまう。 よって、sort を value 配列の一部で行うように、指定する必要がある。


第 2 期の売上げについて
"まづやま酒店"
1
"さけでん商店"
2
"づるおが米商店"
3
"みがざスーパー"
4

と並べられたら、p 文を printf 文に変更する。 printf 文でより分りやすく表示させよう。(解答例)


print("第 2 期の売上げについて\n")
sesame.keys.sort{
  |second,lowest|
  sesame[second][1] <=>sesame[lowest][1]
}.each{
|retail|
    printf("%s\t%d\n", retail, sesame[retail][1])
}

構造は value 変数の並べ替えのときと同じであるが、 配列の一部分での入れ替えであるため、成分を指定している。


Hash.keys.sort{
|並び替え前の key 変数, 並び替え後の key 変数| Hash[並び替え前の key 変数][要素番号] <=> hash 配列[並び替え後の key 変数][要素番号]
}

Hash の中身を知るには

以下のような method が、Hashの情報を確かめるのに役に立つ。

# hash配列.key?("文字列")     # key に "文字列" が存在するかどうか
# hash配列.values?("文字列")  # value に "文字列" が存在するかどうか
#                             # 存在すれば true, しなければ false を返す

また、Hash に次々と情報を入力することができる。 すなわち Hash は

A といえば B, B といえば C, ...

という発想の展開をしまうことができる。 日本語での別名は連想配列である。