p Hash 名.keys や、 p Hash 名.values で得られる表現は、配列であった。配列の入れ替えにおいて、要素を指定した入れ替えの方法は
配列.sort{
|A,B|
配列[A] <=> 配列[B]
}
であったことを思い出そう。 Hash 名.keys は配列として出力される。 配列の並びかえ要素は value すなわち Hash 名[key] である。 Hash 名.keys の要素を代入する変数を A, B と取って実行する。 A, B は並びかえ前後の要素を区別するものである。
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 言語の要といえよう。
each method を使って、要素を一つ一つ取り出すことができた。 Key を叩いて、value 配列の一つを取り出すときには、 Hash 名[key][要素番号] を指定すればよい。
obj 配列.each{
|反復変数|
反復変数を用いた実行文
}
each method で key を変数に入れると、value も Hash を使って表せる。 value 配列のある要素に対して sort を行い、変更後の配列を obj 配列とする場合
Hash 名.keys.sort{ |変更前の key 変数,変更後の key 変数| Hash 名[変更前の key 変数][要素番号] <=> Hash 名[変更後の key 変数][要素番号] }
として、key 配列を並べ替えることができる。
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年のこと」ってことね 稲穂がたくさん実ったということってなあに? 豊作のことかな 「豊作だね」は「稲穂がたくさん実ったということ」ってことね 今年は雨ってなあに?