keys や values のデータ表現

p Hash 名.keys や、 p Hash 名.values で得られる表現は、配列であった。配列の入れ替えにおいて、要素を指定した入れ替えの方法は


配列.sort{
|A,B| 配列[A] <=> 配列[B]
}

であったことを思い出そう。 Hash 名.keys は配列として出力される。 配列の並びかえ要素は value すなわち Hash 名[key] である。 Hash 名.keys の要素を代入する変数を A, B と取って実行する。 A, B は並びかえ前後の要素を区別するものである。

Hash の作りかた

Hash.new で、Hash を新たに作る。 3 つの表示方法があった。key は文字列を取ることができる。

Hash 名 = Hash.new(既定値) 新たに Hash を定義するとき
Hash 名["key"] = "value" key に対して value が决まる
Hash 名 = {"key1" => "value1", "key2"=>"value2", ...} 対を並べて書く方法

一方、配列では、 Array.new で作ることができる。第 0 要素から始まる。

配列 = Array.new(全要素数,初期値) 新たに配列を定義するとき
配列[要素n] = "情報n" 情報がある第 n 要素に格納される
配列 = ["情報0","情報1",... , "情報n", ... ] 並べて書く方法

どちらにも初期値を設定することができた。空の要素が存在するときに、 初期値を自動的に入れて返す。5 教科の成績を配列 score にしまうとき、 欠席した場合得点は 0 なので、

score = Array.new(5,0) 配列要素の初期値は 0

と書いた。また、Hashでは、与えられた key に対して value が存在しないときに返す値を設定した。 例えば英和辞書機能 dic という Hashに対し、 登録されていない key が与えられても変換できない。このことを次のように表せる。

dic = Hash.new("不明な単語") # Hash の既定値は "不明な単語"

データの切り取りかた

データを切り取るには正規表現を用いた。データは、 *.dat あるいは *.txt というテキスト形式のデータで、 1 行には特定の情報が順に並べられている。同じ列には同じ属性のものが入る。 名簿などの場合、氏名と 5 教科の成績、などであった。 文字列は \S+, 空白文字列は \s+, 整数値は \d+ で切り取った。


if /(\S+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/ =˜ 変数
   配列や Hash への分解
end

配列では、氏名は name, 成績は japanese, english, ... に入れることにすると


name[0] = $1
japanese[0] = $2
english[0] = $3
:

Hash では、データが


白米      5000
玄米      1000
黒ごま     200

であるとき、

shop[$1] = $2

とプログラムでデータを処理する。 value はそのままでは文字列として処理した。

一つずつ取り出すには / 並べ替えを詳しく行うには

1 つの答えを取り出したり、並べ替えたりすることができた。また、 その表示方法も工夫することができた。

一つずつ取り出すには

each method を使うと、一つずつ取り出して動作を行った。


配列.each{
|配列の要素を代入する内部変数| 内部変数に入った文字列について実行したい動作
}

指定して並べ替えるには

sort method は {} でどこを並べ替えるのか指定することができた。


配列.sort{|変数 A, 変数 B|
変数 A を使って並べ替えに使用する部分を表す <=> 変数 B を使って並べ替えに使用する部分を表す
}
変数 A, B は並べ替え前と後の要素を代入する変数 <=> 並べ替え演算子

中身には何が入っているのか

Method を組み合わせると一度に実行することが可能だが、 p 文などを使用しながら、 何が行われているかを調べつつ組み合わせることが大事である。 小さな method で大きな仕事ができる Ruby 言語の要といえよう。

Value に配列が入る場合の処理

each method を使って、要素を一つ一つ取り出すことができた。 Key を叩いて、value 配列の一つを取り出すときには、 Hash 名[key][要素番号] を指定すればよい。


obj 配列.each{
|反復変数| 反復変数を用いた実行文
}

Value の配列のある要素に対して入れ替えを行うには

each method で key を変数に入れると、value も Hash を使って表せる。 value 配列のある要素に対して sort を行い、変更後の配列を obj 配列とする場合

Hash 名.keys.sort{
|変更前の key 変数,変更後の key 変数|
    Hash 名[変更前の key 変数][要素番号] <=> Hash 名[変更後の key 変数][要素番号]
}

として、key 配列を並べ替えることができる。

Hash の中の Hash

Hash の中に Hashを入れることも可能である。


key_1 といえば value_1
value_1 を key_2 とし、key_2 といえば value_2
value_2 を key_3 とし、key_3 といえば value_3
:

わらしべ長者という昔話がある。 (参考)

貧乏な男が転んだ。
転んだらわらをつかんだ。
アブが飛んできた。
アブがうるさいのでわらにくくりつけた。
わらにくくるとおもちゃになった。
おもちゃになったわらを持って歩いた。
泣いている赤ん坊が欲しがった。
赤ん坊は泣きやんだ。
泣きやんだ礼としてみかんをもらった。
喉の喝いた旅人がみかんを欲しがった。
喉をうるおした礼として反物をもらった。
侍が倒れた馬と反物を交換したがった。
反物と倒れた馬を交換した。
倒れた馬を休ませた。
馬は元気になった。
元気な馬を欲しがる庄屋がいた。
庄屋は旅に出た。
庄屋は旅に出るため屋敷の留守を頼んだ。
庄屋は戻って来なかった。
貧乏な男は庄屋になった。

一つの出来事に対して一つの出来事が発生している。 数学の証明に使う三段論法もこの方法である。 また、数学の証明などによく使われる三段論法もこのパターンである。

「人間は皆死ぬ。ソクラテスは人間である。故にソクラテスは死ぬ。」

まず、大前提、小前提、最後に結論が来る。hash で表すと、

{人間 => 死ぬ, ソクラテス => 人間}

よって、「人間」が key と value に含まれているので、 これらをつなげて、

{ソクラテス => 死ぬ}

となる。 実際に作ってみよう

三段論法

三段論法をプログラムにしてみよう。 hash_socrates.rb

同じ文字列が key と value にあるかどうか調べている。 ある場合には三段論法を実行している。

hash 名.key?("文字列") key に "文字列" が存在するかどうか
hash 名.value?("文字列") value に "文字列" が存在するかどうか

存在すれば true, しなければ false を返す

わらしべ長者

さきほどのわらしべ長者をプログラムで表してみよう。

hash_strawrich.rb

結論は key, value の中で、 重複していない文字列によって構成される。

自分でも三段論法を考え、プログラムを作成し、結論を導け。

ねずみの嫁入り

ねずみの嫁入りという昔話がある。 (参考)

ある庄屋のお蔵に、ねずみが暮していた。
ねずみはとうとう美しい娘ねずみを授かった。
年頃になったので、娘のために日本一の婿を探しに旅に出た。
日本一の婿おてんとうさまだと考え、おてんとうさまに会いに行った。
おてんとうさまおてんとうさまより偉いものがあるという。
おてんとうさまより偉いのは、おてんとうさま隠す雲であるという。
そこで日本一の婿はだと考え、に会いに行った。より偉いものがあるという。より偉いのは、吹きはらう風であるという。
そこで日本一の婿はだと考え、に会いに行った。より偉いものがあるという。より偉いのは、風を遮るお蔵の壁であるという。
そこで日本一の婿はお蔵の壁だと考え、お蔵の壁に会いに行った。
お蔵の壁お蔵の壁より偉いものがあるという。
お蔵の壁より偉いのは、お蔵の壁をかじるねずみであるという。
そこで日本一の婿はねずみだと考え、ねずみを婿とした。

「こぶた、たぬき、きつね、ねこ」 のような key と value がぐるぐる回るパターンである。

{こぶた => たぬき, たぬき => きつね, きつね => ねこ}

それでは実際に key と value を比較していき、物語を進行させるように してみよう。hash_mouse.rb

value の値を key に代入し、ある key になったところで止まる。

人工無脳

Hash の「A といえば B」という性質を使って、 あたかも計算機がものごとを理解したかのようにふるまうようすが、 次のようなプログラム例で理解できる。 hash_chatterbot.rb


#!/usr/koeki/bin/ruby -Ke

vocab = Hash.new

printf("何か教えてよ\n")
line = gets.chomp

if /^(\S+)(は|が|と)(\S+)(ね*|だ|よ)/ =˜ line
       topic = $1  
       keyword = $3
end

while true
STDERR.printf("%sってなあに?\n",topic) line = gets.chomp! if /(\S+)(が|の|で|を|だ|も|こと|の)/ =˜ line
vocab[$1] = topic vocab.each{|pair| if topic == pair[1]
if vocab[topic] == nil
break
else printf("「%s」は「%s」ってことね\n",
vocab[topic],pair[1])
end
end } topic = keyword keyword = $1
end
end

同じ value を持った場合に、keyword をつなげ文章にする。 同じ value を持たなければ、その value について定義を訊ねる構造になっている。


何か教えてよ 
今年は豊作だね
今年ってなあに?
2011年のことだよ
豊作だねってなあに?
稲穂がたくさん実ったということだよ
2011年のことってなあに?
今年は雨が少なかったね今年」は「2011年のこと」ってことね
稲穂がたくさん実ったということってなあに?
豊作のことかな豊作だね」は「稲穂がたくさん実ったということ」ってことね
今年は雨ってなあに?