2015年12月5日土曜日

Ruby でブロックを使ったメソッドを定義してみよう

Ruby Advent Calendar 2015 - Qiita の 12月5日の内容です。

Ruby のブロックとかについて書きます。

毎年アドベントカレンダーがある言語だし、同じような内容がたぶんあると思うんですけど、全部の内容を見直してかぶってないか確認するのとかしんどいので、ブロックのネタ書きます。

普通の使い方


みなさんもよく使うのは Array#each とか Range#each あたりでの使い方ですかね。
たとえば、 1 から 10 の数字を画面に表示するコードとかは下のような内容になります。

(1..10).each do |i|
  puts i
end

このときに使ってる do と end の間にある部分がブロックと言うヤツです。

わりとコイツは便利なんですが、組み込みのクラスや用意されたメソッドなどに対してのブロックは使えるけれど、自分でブロックを与えるメソッドを定義するということに慣れていないユーザが多いような気がするので、今日はその方法について書きます。

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


結論から言ってしまえば、  block_given? というメソッドと yield という式を使えば扱うことができます。
たとえば、整数について偶数か奇数の集合ということを意識したクラスを作ってみます。
名前は EvenOrOddNumbers とします。

class EvenOrOddNumbers << Array
end

ちょっと横着して EvenOrOddNumbers は Array のひとつ、という関係があるとしました。

偶数と奇数を判別する


さて、この EvenOrOddNumbers の Numbers に 偶数がふくまれているかを調べるメソッドと奇数がふくまれているかを調べるメソッドを追加してみましょう。

class EvenOrOddNumbers << Array
  def include_even?
    each do |i|
      return true if i.even?
    end
    false
  end
  def include_odd?
    each do |i|
      return true if i.odd?
    end
    false
  end
end

こんな感じで書くことができます。
利用例は以下のような感じです。


eoon = EvenOrOddNumbers.new
eoon << 1
eoon << 3
eoon << 5
p eoon
puts "include even: #{eoon.include_even?}"
puts "include odd:  #{eoon.include_odd?}"
puts
eoon = EvenOrOddNumbers.new
eoon << 2
eoon << 4
eoon << 6
p eoon
puts "include even: #{eoon.include_even?}"
puts "include odd:  #{eoon.include_odd?}"
puts
eoon = EvenOrOddNumbers.new
eoon << 1
eoon << 2
eoon << 3
p eoon
puts "include even: #{eoon.include_even?}"
puts "include odd:  #{eoon.include_odd?}"



実行結果はこんな感じになります。

[1, 3, 5]
include even: false
include odd:  true
[2, 4, 6]
include even: true
include odd:  false
[1, 2, 3]
include even: true
include odd:  true

ここまでは普通にブロックを使うとこんなメソッドが作れて便利ですよ、みたいな感じで別にブロックを渡すメソッドは作ってないです。

偶数のみや奇数のみを扱うメソッドを作ってみる


本題ですね。偶数のみの each や奇数のみの each を定義してみましょう。

普通はひとつの定義にまとめますが、ブログ的に面倒なので先ほどのクラス定義のあとに以下のコードを記述してもメソッド定義できるので、こんな感じに書いてみました。

class EvenOrOddNumbers
  def each_even(&block)
    each do |i|
      yield i if i.even?
    end
  end
  def each_odd(&block)
    each do |i|
      yield i if i.odd?
    end
  end
end

使い方はこんな感じです。

eoon = EvenOrOddNumbers.new
eoon << 1
eoon << 1
eoon << 2
eoon << 3
eoon << 5
eoon << 8
eoon << 13
puts "EvenOrOddNumbers: #{eoon}"
evens = []
eoon.each_even do |i|
  evens << i
end
odds = []
eoon.each_odd do |i|
  odds << i
end
puts "evens: #{evens}"
puts "odds:  #{odds}"

出力はこんな感じ。

EvenOrOddNumbers: [1, 1, 2, 3, 5, 8, 13]
evens: [2, 8]
odds:  [1, 1, 3, 5, 13]

上の定義の例ではブロックが渡されなかったときの挙動を考えていないのですが、ブロックが渡されなかったときの挙動を記述するには block_given? を使います。

class EvenOrOddNumbers
  def each_even(&block)
    raise 'No block given' unless block_given?
    each do |i|
      yield i if i.even?
    end
  end
  def each_odd(&block)
    raise 'No block given' unless block_given?
    each do |i|
      yield i if i.odd?
    end
  end
end

超適当なんですが、ブロックが渡されなかったとき、いきなり Exception を発生させるという処理を入れるとこんな感じです。

each という名前がつくメソッドだとブロックがなかったときの挙動というのは、どんなものか考えるのか面倒なんで、マネしちゃだめです。

所感


ブロックの扱いについて、複数の引数を受け取るブロック、たとえば Hash#each_with_index とかはどうやって作るか?とか考えると面白いと思います。

今日の内容はこんなところで。

っていうか、最後まで書いて気づいたけど、数年前に同じネタをアドベントカレンダーに書いた気がしてきた・・・まあ、気にしない方針で。

2015年に参加する Advent Calendar

Ruby と Smalltalk と Pyspa のカレンダーに参加しています。
今日、余裕あると思って Ruby と Smalltalk のカレンダーの内容を書くことになっているんだけど、わりと余裕なかった。

これから書きます。

2014年10月28日火曜日

@keitahaga 先生が Chrome for Android の脆弱性で貢献 x 2

更新が滞っていたため、二件もスタックしてしまいました。

WEDNESDAY, JULY 16, 2014


http://googlechromereleases.blogspot.jp/2014/07/chrome-for-android-update.html
Security fixes:
[$3000][352083] High CVE-2014-3159: Omnibox URL Spoofing (Android). Credit to Keita Haga.

WEDNESDAY, OCTOBER 8, 2014

This release also contains the following security fix: 
[$1500][406593] Medium CVE-2014-3201: Content spoofing with scrollbar. Credit to Keita Haga.


2013年9月19日木曜日

@keitahaga 先生が iOS 7 にも貢献

ハガ先生が指摘された脆弱性の修正が iOS 7 に取り込まれたようです!


APPLE-SA-2013-09-18-2 iOS 7
Safari
Available for: iPhone 4 and later,
iPod touch (5th generation) and later, iPad 2 and later
Impact: Visiting a malicious website may allow an arbitrary URL to
be displayed
Description: A URL bar spoofing issue existed in Mobile Safari. This
issue was addressed through improved URL tracking.
CVE-ID
CVE-2013-5152 : Keita Haga of keitahaga.com, Lukasz Pilorz of RBS

私も帰ったらハガ先生のおかげでまたひとつ安全になった iOS 7 にアップデートしてみたいと思います。

2013年5月27日月曜日

@keitahaga 先生の Yahoo!ブラウザー についての脆弱性の指摘が JVN に掲載されました

@keitahaga 先生の脆弱性報告が掲載されました!
以前に調整されたら掲載されるものがある、と伺っていた件だと思います。

JVN#31817913: Yahoo!ブラウザーにおけるアドレスバー偽装の脆弱性

謝辞
この脆弱性情報は、情報セキュリティ早期警戒パートナーシップに基づき下記の方が IPA に報告し、JPCERT/CC が開発者との調整を行いました。
報告者: keitahaga.com Keita Haga 氏
次は異なるタイプの脆弱性にも挑戦されるかもしれないコメントをいただきました。
期待は高まります!

2013年5月7日火曜日

@keitahaga 先生がアドレスバー偽装についての脆弱性を二件報告!


ハガ先生のなかでいまアツいのは Android のようです!

謝辞
この脆弱性情報は、情報セキュリティ早期警戒パートナーシップに基づき下記の方が IPA に報告し、JPCERT/CC が開発者との調整を行いました。
報告者: keitahaga.com Keita Haga 氏
Nice report.

2013年4月24日水曜日

@keitahaga 先生が Sleipnir の脆弱性を報告

オレたちのハガ先生。

Sleipnir for Windows におけるアドレスバー偽装の脆弱性

Sleipnir Mobile for Android において任意のエクステンション API が呼び出される脆弱性

この脆弱性情報は、情報セキュリティ早期警戒パートナーシップに基づき下記の方が IPA に報告し、JPCERT/CC が開発者との調整を行いました。
報告者: keitahaga.com Keita Haga 氏
 いま、 keitahaga.com を訪問して何もコンテンツがないことを確認しようと思ったら、コンテンツが存在して吹いた。
Hi, I'm Keita Haga, a hikikomori living in Kanagawa, Japan.
hgkn ...