@kyanny's blog

My thoughts, my life. Views/opinions are my own.

2章 配列とハッシュ

配列は「オブジェクトへの参照を順に並べたもの」各要素はオブジェクトへの参照、そのものではないのでオブジェクトを変更すると配列の要素も変更される。

cattle = "yahoo"
container = [cattle, cattle]
p container #=> ["yahoo", "yahoo"]
cattle[2..3] = "pp"
p container #=> ["yappo", "yappo"]

cattle = "google"
p container #=> ["yappo", "yappo"], ["google", "google"] にはならない

最後のところがよくわからないなぁ。 cattle って変数も "yahoo" っていう String オブジェクトへの参照にすぎなかったので、代入だと参照先がかわるだけで "yahoo" オブジェクトは無変更だから array も変わらないってことなのかな。

添え字でのアクセスについて。 -1 とかで後ろから数えてアクセスできるのも Perl と同じなんだけど、おやっと思ったのが、

array = [1, 2, 3]
p array[-1] #=> 3
p array[array.length-1] #=> 3
p array[-4] #=> nil, 配列の範囲外なので
p array[array.length-4] #=> 3, array.length - 4 => -1 なので
  • 1 と array.length - 1 は常に同じ意味になるとは限らないので注意しないといけないな。たぶんわざわざ後者のように書きたいことはあんまりないだろうから(添え字の数字じたいに重要な意味があるなら index とかいう名前の変数に代入するよね)常に -1 スタイルを使うようにしよう。

長さ付き添え字というのはなんとなくわかる。 substr @array, 0, 1 みたいなかんじか。範囲添え字ってのもなんとなくわかる。けど .. と ... で差があるのはすぐ忘れてしまいそう。

あと a[0] は a.(0) というメソッド呼び出しだという。そういうもんだと思っておく。でも っていうメソッドなんですと言われても正直ピンとこない。

添え字代入、これはおどろいた。配列の端じゃない要素が増えて引き延ばされたり、逆に複数取り除かれて詰まったりするようだ。これはぱっと見ても配列の要素がどう変化するかわかんないだろうなー。添え字がついてるときは注意して、よくわからなかったら実行してみること。

a = [1, 2]
p a #=> [1, 2]
a[0] = 3
p a #=> [3, 2]
a[4] = "4"
p a #=> [3, 2, nil, nil, "4"]
a[0, 3] = 'a', 'b', 'c'
p a #=> ["a", "b", "c", nil, "4"]
a[0, 3] = 'a', 'b', 'c', 'd'
p a #=> ["a", "b", "c", "d", nil, "4"]
a[1..2] = 1, 2
p a #=> ["a", 1, 2, "d", nil, "4"]
a[0, 2] = "?"
p a #=> ["?", 2, "d", nil, "4"]
a[0..2] = "A"
p a #=> ["A", nil, "4"]
a[-1] = "Z"
p a #=> ["A", nil, "Z"]

Array#include? ?が末尾についたメソッドは真偽値を返す、という慣習がある。同様に !が末尾についたメソッドは破壊的なメソッドで、 array.uniq! だと array の中身が変更される。 array.uniq だと array の中身は変更されない。結果新しい配列を返す。 ?, ! は文法上予約されているわけではない。 ?, ! で終わるからといって常に真偽値を返したり破壊的な操作をしたりするわけではない。

array = ["a", "b", "c"]
p array.length
p array.size
p array *= 2

p array.include? "c"
p array.sort
p array
p array == array.sort

p array.uniq
p array == array.uniq
array2 = array
p array.object_id  #=> 3268540
p array2.object_id #=> 3268540
p array2
array2.uniq!
p array2
p array == array2 #=> true
p array #=> ["a", "b", "c"]

array2 = array ってやったあとで array2 に破壊的メソッドを適用した場合の array の値に注目。「array2 は array の参照が代入されるから、代入したあとで array に破壊的メソッドを適用したら array2 も変わってしまうが、 array2 のほうを変更しても array には影響がない」なんてことはなくて、 object_id をとってみたら全く同じものだった。だからどっちをいじっても両方変更される。

ブロック付きメソッドとイテレータ。 each, map などはなんとなく使えそうな気がする。あと sort もでてきた。

array = ["73", "2", "5", "1999", "53"]
p array.sort #=> ["1999", "2", "5", "53", "73"]
p array.sort{|x,y| x.to_i <=> y.to_i} #=> ["2", "5", "53", "73", "1999"]

数値に変換してからのソートはよく使いそうなので、イディオムというかんじでこのまま覚えてしまおう。 x, y で受け取って x.to_i <=> y.to_i か。

選択、 Array#select というのがでてきた。 Perl でいう grep だなこれは。

class Person
    attr_accessor "name"
    attr_accessor "grade"
end

p1 = Person.new
p1.name = "Ichiro"
p1.grade = 1

p2 = Person.new
p2.name = "Jiro"
p2. grade = 2

p3 = Person.new
p3.name = "Saburo"
p3.grade = 3

students = [p1, p2, p3]
selected_students = students.select{|person| person.grade == 3}
selected_students.each do |person|
    puts "#{person.name} was selected." #=> Saburo was selected.
end

なんかずいぶん長ったらしくなったけど。

あと do ... end と { ... } の結合強度の違い、使い分けについてなど。返り値を利用する場合は {} を使う、とかいくつか慣習があるらしい。あんまり気にせず、 do ... end を多く使って慣れるようにしよう。例のコードはなるべく実際に入力して実行しているので、そうやって読み書きしてるうちになんとなくどこで使い分けるものなのか慣れてくると思う。


ちょっと疲れたのでこのへんで休憩・・・一応2章は全部読んだけど、ハッシュと Enumerable についてのメモは明日にしちゃおうかな。 Enumerable はよくわからんかった(モジュールとか)

続き。ハッシュから。ハッシュは key => val, key => val, ... という風に表記する。 Perl と同じだ。常に {} を使うので読みやすそう。あと => は , でもいいので key, val, key, val, ... でも可。最後の val のうしろの , は省略してもいい。 key, val は任意の式を指定できる。意味ないけどこんな風にもできる。

hash = {
    "String".class => "c".upcase + "l" + "a" + "s"*2,
}

p hash

シンボルをキーとするハッシュの場合、 Ruby1.9 からはちょっと省略した記法が使えるらしい、、シンボルはあとで出てくるのでそこで。

あと、ハッシュリテラルの {} とブロック付きメソッドの呼び出し obj.method {} は相性が悪いらしく、どっちがどっちかでごっちゃになって意図したとおりに動かないことがあるらしい。ハッシュよりもブロックのほうが優先して解釈されてしまうのかな。なのでメソッド呼び出しの引数としてハッシュを渡す場合は {} を省略できるらしい。こういうこと。

def hoge (args)
    p args
end

hoge :foo => "FOO", :bar => "BAR" #=> {:foo=>"FOO", :bar=>"BAR"}
#hoge {:foo => "FOO", :bar => "BAR"} #=> syntax error

この表記方法はよく出てきそうだし、書くときにも syntax error で悩まずに済むみたいだから、覚えよう。シンボルのように : が先頭につく識別子って Perl では見なかったから目立つし、見分けられそうだ。

ハッシュの添え字は、参照も代入も配列に比べるとシンプル。キーと値が対応してるだけ。存在しないキーを指定して参照した場合は nil が返る。 hash.key?, hash.fetch というメソッドもある。 hash.key? は Perl の exists $hash{key} みたいなものか。 hash.fetch は Template Toolkit の hash.item('key') みたいなものかな。

ハッシュの比較もできる。 == と equal? の挙動の違いは文字列や配列のときと同じ。

hash1 = { "a" => 1, "b" => 2 }
hash2 = { "a" => 1, "b" => 2 }

p hash1 == hash2
p hash1 == { "b" => 2, "a" => 1 }
p hash1 == { "a" => 9, "b" => 2 }
p hash1 == { "z" => 1, "b" => 2 }
p hash1 == { "a" => 1, "b" => 2, "c" => 3 }
p hash1.equal? hash2 #=> false

ハッシュのメソッドは使いながら覚えていけるだろう。 keys, size, each あたり。

Enumerable モジュールでてきた。モジュールってのの意味がわからなかったので先に8章のモジュールのページを少し読んでしまった。クラスのようだけどクラスではないものなんだな、くらいの浅い理解で済ませておく。「each から導出可能な~」とか「他のメソッドをベースに導出したいこともあるので~」という説明がピンとこない。 Enumerable::Enumerator の enum_for とか、どういうときに必要でどういうときにありがたいものなのか見当がつかないな。まぁ、そのうち使うんだろうし、そのうちわかるんだろうと・・・。 Enumerable をうまく読めないのもとっつきづらく感じる一因だと思う。 say させてみたら「イナムェーボォ」と発音してた。