配列、ハッシュ、ブロック、イテレータ
配列
まずは配列
a = [1, 3, 5, 7, 9] a[-1] # => 9 a[-2] # => 7 a[-99] # => nil
後ろから指定することもできる
インデックスを[start, count]で指定する。startからはじまるcount個のオブジェクトへの参照が返る
a = [1, 3, 5, 7, 9] a[1, 3] # => [3, 5, 7] a[3, 1] # => [7] a[-3, 2] # => [5, 7]
配列は範囲で参照することもできる。範囲は開始、終了の位置を二つまたは三つのピリオドで区切って指定する。
二つの場合は終了位置を含める。三つの場合は終了位置は含まれない。これいつも間違える。
a = [1, 3, 5, 7, 9] a[1..3] #=> [3, 5, 7] a[1...3] #=> [3, 5] a[-3..-1] #=> [5, 7, 9]
[]=で要素を代入できる
a = [1, 3, 5, 7, 9] a[1] = 'bat' ->[1, "bat", 5, 7, 9] a[3] = [9, 8] ->[1, "bat", 5, [9, 8], 9] a[6] = 99 ->[1, "bat", 5, [9, 8], 9, nil, 99]
[]=も同じく開始位置と長さを指定して代入できる。長さ0の場合、右辺が開始位置の前に代入される。
a = [1, 3, 5, 7, 9] a[2, 2] = 'cat' -> [1, 3, "cat", 9] a[2, 0] = 'dog' -> [1, 3, "dog", 9]
配列の便利なメソッドを使うことで、スタック、キュー等として扱うことができる。
以下はpush、popを使用することで配列の末尾に要素を追加、末尾の要素を削除できるのでスタックとして扱う。
stack = [] stack.push "red" stack.push "green" stack.push "blud" p stack puts stack.pop puts stack.pop puts stack.pop p stack
出力結果
["red", "green", "blue"] blue green red []
以下は先頭要素を削除するshiftとpushの組み合わせで配列をキューとして扱う
queue = [] queue.push = "red" queue.push = "green" puts queue.shift puts queue.shift
出力結果
red green
first、lastメソッドはそれぞれ先頭、末尾からn番目までのエントリを返す
array = [1, 2, 3, 4, 5, 6, 7] p array.first(4) p array.last(4)
出力結果
[1, 2, 3, 4] [4, 5, 6, 7]
ハッシュ
続いてハッシュ。以下のようにして作る。
h = { 'dog' => 'carine', 'cat' => 'feline', 'donkey' => 'azinine' } h.length #=> 3 h['dog'] #=> "carine" h[12] #=> 99 h # => {"dog" => "carine", "cat" =>'feline', "donkey" => "asinine", 12 => "dodecine"}
シンボルを使用する場合は以下のようになる
h = { dog: 'carine', cat: 'feline', donkey: 'asinine' }
ブロック
以下のように書く
some_array.each { |value| puts value * 3} sum = 0 other_array.each do |value| sum += value puts value / sum end
一行に収まる場合はブレース、複数行の場合はdo/end形式で書く。
イテレータ
Rubyのイテレータはコードブロックを呼び出せるメソッドのことを言う。メソッド内でyield文を使うことで、ブロックを呼び出すことができる。ブロックを抜けるとyield分の直後に制御が戻る。
def three_times yield yield yield end three_times { puts "Hello" }
出力結果
Hello Hello Hello
次はフィボナッチ数列を出力するプログラム。
def fib_up_to(max) i1, i2 = 1, 1 while i1 <= max yield i1 i1, i2 = i2, i1 + i2 end end fib_up_to(1000) { |f| print f, " "}
yield文に引数を与えることで、値をブロックに渡している。
次はArrayクラスのfindメソッド概要。ブロックからメソッドに値を返している。
class Array def find for i in 0 ...size value = self[i] return value if yield(value) end end end [1, 3, 5, 7, 9].find {|v| v*v > 30 } #=> 7
配列の各要素がブロックに渡され、ブロックがtrueを返すとfindメソッドはその値を返す。
これはなかなかJavaとかではできない。Arrayクラスが要素を調べる処理をしてくれるが、Javaだと自分のコードでその処理をかかないといけない。
eachは最も単純なイテレータで各要素に対してyield分を実行するだけ。
[1, 3, 5, 7, 9].each {|i| puts i}
出力結果
1 3 5 7 9
collect(map)はコレクションの各要素を受け取り、ブロックに渡す。ブロックから返された値を使って新しい配列を作る。
["H", "A", "L"].collect {|x| x.succ } #=> ["I", "B", "M"]
ブロックを通過した回数を記録したい場合は、each_with_indexメソッドを使用する。
f = file.open("testfile") f.each_with_index do |line, index| puts "Line #{index} is : #{line}" end f.close
Enumerableモジュールに定義されているinjectメソッドを使うとコレクションの各メンバの値を累算できる。
[1, 3, 5, 7].inject(0) {|sum, element| sum + element } #=> 16 [1, 3, 5, 7].inject(1) {|product, element| product * element} #=> 105
ブロックが最初に呼ばれるとsumにinjectの引数(0)が設定され、elementにコレクションの先頭要素が設定される。ブロックの2回目以降の呼び出しではsumに前回のブロック呼び出しで返された値が設定される。injectの最終値はブロックの最後の呼び出しで返された値になる。injectを引数なしで呼び出すとコレクションの先頭要素が初期値として使われ、繰り返しは2番目の要素から開始される。
[1, 3, 5, 7].inject {|sum, element| sum + element} # =>16 [1, 3, 5, 7].inject {|product, element| product * element} #=> 105
injectには各要素に適用するメソッドの名前を引数として与えることができる。
[1, 3, 5, 7].inject(:+) #=> 16 [1, 3, 5, 7].inject(:*) #=> 105