ひがきの日記

2013-02-02

Ruby初級者向けレッスン 44回 ― ブロック ―

第56回 Ruby/Rails勉強会@関西での初級者向けレッスンのスライドを公開します。*1

f:id:mas-higa:20130205231822p:image

繰り返し

a = [0, 1, 2]

a.each do |i|
  puts i
end

a.each{|i| puts i}

# >> 0
# >> 1
# >> 2

f:id:mas-higa:20130204232616p:image

  • ブロックの代表的な使い方は繰り返し処理。
  • do と end で囲まれたもの、{ と } で囲まれたものがブロック。
    • 上のコードと下のコードは同じ処理をしている。
  • Array のように、たくさんのオブジェクトを持っていると、全てのオブジェクトに繰り返し同じ処理をしたいことがよくある。
  • | と | で囲まれた変数 i が Array の各要素を順に指す。

便利な例

a = [0, 1, 2, 3]        # => [0, 1, 2, 3]

a.map{|i| i * i}        # => [0, 1, 4, 9]
a.select{|i| i.even?}   # => [0, 2]
a.inject{|s, i| s + i}  # => 6
a.find{|i| i.odd?}      # => 1
a.all?{|i| i.even?}     # => false
a.any?{|i| i.even?}     # => true

f:id:mas-higa:20130204232617p:image

  • 初級者には、このスライドがおすすめ。

f:id:mas-higa:20130204232620p:image


ブロックを渡す

  • メソッドには、ブロックをひとつ渡せる。
  • ブロックをどう使うかは、メソッド次第。
    • 繰り返し
    • ハリウッドの原理
open('hello.txt')   # => #<File:hello.txt>
open('hello.txt'){|f| f.read}
                    # => "こんにちは\n"

f:id:mas-higa:20130204232618p:image

  • ブロックの役割りは、繰り返しだけではない。
  • ブロックの有無で動作を変えるメソッドがある。

ハリウッドの原理

# open('hello.txt'){|f| f.read}

begin
  f = open('hello.txt')
  f.read
ensure
  f.close unless f.nil?
end

f:id:mas-higa:20130204232619p:image

  • 処理のスケルトンメソッドで用意しておき、処理の一部をブロックで切り替える。
  • ブロック付きの 1行で書いた open メソッドは、ブロックなしで書くと、だいたいこんな感じ。
  • close の部分は f.close unless f.closed? かも。

ブロックのない open

f:id:mas-higa:20130206000309p:image

  • ファイルを扱う場合は、以下の処理が必要。
    1. open
    2. read/write など
    3. close
  • コードを書く際の負担が多い。

ブロック付き open

f:id:mas-higa:20130206000308p:image

  • open メソッドが忘れずに close してくれる。
  • コードを書く際の負担が少ない。

値を受け取る

f:id:mas-higa:20130204232621p:image


値を受け取る (2)

  • 受け取るか受け取らないかは、ブロック次第。
2.times{puts 'こんにちは'}
# >> こんにちは
# >> こんにちは

2.times{|i| puts i}
# >> 0
# >> 1

f:id:mas-higa:20130204232622p:image

  • 上の例は値を受け取っていない。
  • しかし times メソッドは値を渡してくれる。(下の例)
  • ブロックは必ずしも値を受け取る必要はない。

Hash の例

people = {matz: 47, dhh: 32}
            # => {:matz=>47, :dhh=>32}

people.each{|person| p person}
# >> [:matz, 47]
# >> [:dhh, 32]

f:id:mas-higa:20130204232623p:image

  • key と value のペアを受け取れる。
  • ふたつの値が、ひとつの Array オブジェクトに。

Hash の例 (2)

people = {matz: 47, dhh: 32}

people.each do |name, age|
  p "#{name}(#{age})"
end

# >> "matz(47)"
# >> "dhh(32)"

f:id:mas-higa:20130204232624p:image

  • ふたつの値を、それぞれ別の変数で受け取れる。
  • | と | の間に , で区切って変数を列挙する。

each_cons の例

midosuji = ["梅田", "淀屋橋", "本町", "心斎橋", "なんば"]

midosuji.each_cons(2){|path| p path}

# >> ["梅田", "淀屋橋"]
# >> ["淀屋橋", "本町"]
# >> ["本町", "心斎橋"]
# >> ["心斎橋", "なんば"]

f:id:mas-higa:20130204232625p:image


each_cons の例 (2)

midosuji.each_cons(2) do |from, to|
  p "#{from} - #{to}"
end

# >> "梅田 - 淀屋橋"
# >> "淀屋橋 - 本町"
# >> "本町 - 心斎橋"
# >> "心斎橋 - なんば"

f:id:mas-higa:20130204232626p:image

  • ふたつの値を、ふたつの変数で受け取る。

each_cons の例 (3)

a = [*0..3]     # => [0, 1, 2, 3]

a.each_cons(3){|i| p i}
# >> [0, 1, 2]
# >> [1, 2, 3]

a.each_cons(3){|i, j| p [i, j]}
# >> [0, 1]
# >> [1, 2]

f:id:mas-higa:20130204232627p:image

  • 繰り返しの回数は、どちらも 2回。
  • みっつの値を、ひとつの変数で受け取ると Array オブジェクトになる。(上)
  • みっつの値を、ふたつの変数で受け取ると、みっつ目の値が受け取れない。(下)

おかしいな? と思ったら

p unknowns.first
            # >> [1, ["matz", 47]]

unknowns.each do |id, (name, age)|
  id        # => 1
  name      # => "matz"
  age       # => 47
end

f:id:mas-higa:20130204232628p:image

  • 要素を、ひとつ取り出して見る。
  • 例えば、こんなふうに表示されたら、値は幾つか?
    • 答えは、ふたつ! *2
      1. 数値 (1)
      2. Array
  • みっつの変数で受け取るには Array の中の Array を ( ) で表記する。

ブロックを受け取るメソッド

  • こんな感じで呼びたい
monta{puts 'block!'}

# >> block!
# >> block!
# >> 大切なことなので

f:id:mas-higa:20130204232629p:image

  • monta というメソッドを作る。
    • ブロックを 2回評価して、
    • 最後に '大切なことなので' と出力する。

ブロックを受け取る方法は、ふたつある。


ブロックを受け取る

def monta
  yield
  yield
  puts '大切なことなので'
end

f:id:mas-higa:20130204232630p:image

  • yield メソッドで受け取ったブロックを評価する。

ブロックを受け取る (2)

def monta &block
  block.call
  block.call
  puts '大切なことなので'
end

f:id:mas-higa:20130204232631p:image

  • 引数でブロックを受け取る。
  • call メソッドでブロックを評価する。

値を渡す

  • monta メソッドの仕様を変更。
  • ブロックに '大切なことなので' という文字列を渡す。
def monta
  yield '大切なことなので'
  yield '大切なことなので'
end

monta{|i| puts "#{i} block!"}

# >> 大切なことなので block!
# >> 大切なことなので block!

f:id:mas-higa:20130204232632p:image


値を渡す (2)

  • また monta メソッドの仕様を変更。
  • ブロックに '大切な', 'ことなので' という、ふたつの文字列を渡す。
def monta &block
  block.call '大切な', 'ことなので'
  block.call ['大切な', 'ことなので']
end

monta{|i| puts "#{i} block!"}

# >> 大切な block!
# >> ["大切な", "ことなので"] block!

f:id:mas-higa:20130204232633p:image

  • block.call に、ふたつの文字列を渡すだけだと……
    • ブロックがひとつの変数で待ち受けていると、ふたつ目の文字列が受け取ってもらえない。
  • ふたつ以上の値を渡すときは、Array のオブジェクトにして渡す。

ブロックは Proc

block = Proc.new do |i, j|
  puts "#{i}#{j} block!"
end

monta &block

# >> 大切なことなので block!
# >> 大切なことなので block!

f:id:mas-higa:20130204232634p:image

  • あらかじめブロックだけ生成しておける。
  • メソッドに渡す際には & を付ける。

Q&A

Q1
ブロックをふたつ渡せないの?
A1
& なしで普通の引数としてなら渡せます。
Q2
さっきから EmacsRuby のコード実行してるけど、それなに?
A2
るびきちさんが作られた rcodetools を使ってます。Ruby を起動して実行結果を # => の後に埋め込んでくれます。(コード補完もできます) コメントなので、結果の埋め込まれたコードは、そのまま保存・実行できます。
Q3
どんなキーバインドなの?
A3
えっ、それ重要?

*1:スライドだけ欲しい人は直接どうぞ http://higaki-it.jp/ruby/56/slide.pdf

*2:こんなデータ構造が Array で渡ってきたら、データ構造の設計が間違ってる。

mas-higamas-higa 2013/02/06 00:12 とり急ぎ公開しました。
演習問題については、いましばらくお待ちください。
なるはやで公開します。

mas-higamas-higa 2013/02/09 20:24 演習問題の解答例を公開しました。
トラックバックをご覧ください。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

リンク元