ようへいの日々精進XP

よかろうもん

2018 年 06 月 24 日 (日)

ジョギング

  • 嬉野の街を 45 分くらい
  • 宿泊していたホテルから椎葉山荘 に登って, お茶畑を周りに見ながら走った
  • 気持ち良かった

日課

  • お休み

今日のるびぃ ~ REx - Ruby Examination にチャレンジ (36) ~

REx - Ruby Examination の問題を自分なりにアレンジした上で 1 〜 3 問くらいずつ解いていく. 正直言ってかなり難しい. 尚, irb に動作確認環境は以下の通り.

$ ruby --version
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-linux]
$ irb --version
irb 0.9.6(09/06/30)

Enumerator クラス

以下のプログラムの __(1)__ に適切な内容を選択して実行すると, [97, 112, 112, 108, 101] と表示される. 期待した結果を得られるように正しい選択肢を選ぶ.

enum_char = Enumerator.new do |yielder|
  "apple".each_char do |chr|
    __(1)__
  end
end

array = enum_char.map do |chr|
  chr.ord
end

p array

以下, irb にて確認.

irb(main):001:0> enum_char = Enumerator.new do |yielder|
irb(main):002:1*   "apple".each_char do |chr|
irb(main):003:2*     yielder << chr
irb(main):004:2>   end
irb(main):005:1> end
=> #<Enumerator: #<Enumerator::Generator:0x00558ba63f9468>:each>
irb(main):006:0> 
irb(main):007:0* array = enum_char.map do |chr|
irb(main):008:1*   chr.ord
irb(main):009:1> end
=> [97, 112, 112, 108, 101]
irb(main):010:0> 
irb(main):011:0* p array
[97, 112, 112, 108, 101]
=> [97, 112, 112, 108, 101]

以下, 解説より抜粋.

  • map メソッドのブロックは Enumerator オブジェクトをレシーバーとした場合 Enumerator::Yielder オブジェクトとなり, 設問では変数 yielder を指す
  • Enumerator::Yielder を評価するには, << を呼び出す

Enumerator クラスをも少し突っ込んで (ドキュメントを参考に)

Enumerator クラスとは

  • each 以外のメソッドにも Enumerable の機能を提供するためのラッパークラス
  • Enumerator を生成するには Enumerator.new あるいは Object#to_enum, Object#enum_for を利用する

そもそも Enumerable クラスとは

  • 繰り返しを行なうクラスのための Mix-in
  • このモジュールのメソッドは全て each を用いて定義されているので, インクルードするクラスには each が定義されている必要がある

Enumerator.new について

new(obj, method = :each, *args) -> Enumerator

  • オブジェクト obj について, each の代わりに method という 名前のメソッドを使って繰り返すオブジェクトを生成して返す
  • args を指定すると, method の呼び出し時に渡される
irb(main):001:0> str = "xyz"
irb(main):003:0* enum = Enumerator.new(str, :each_byte)
(irb):3: warning: Enumerator.new without a block is deprecated; use Object#to_enum
=> #<Enumerator: "xyz":each_byte>
irb(main):004:0> enum.class
=> Enumerator
irb(main):007:0> enum.map {|b| '%02x' % b }
=> ["78", "79", "7a"]

new(size=nil) {|y| ... } -> Enumerator

  • Enumerator オブジェクトを生成して返す
  • 与えられたブロックは Enumerator::Yielder オブジェクトを引数 (以下の例では y が該当する) として実行される
  • 生成された Enumerator オブジェクトに対して each を呼ぶと, この生成時に指定されたブロック ((1..10).each{|i|...) を実行し, Yielder オブジェクトに対して << メソッドが呼ばれるたびに, each に渡されたブロックが繰り返される
  • new に渡されたブロックが終了した時点で each の繰り返しが終了し, このときのブロックの返り値が each の返り値となる
irb(main):001:0> enum = Enumerator.new{|y|
irb(main):002:1*   (1..10).each{|i|
irb(main):003:2*     y << i if i % 5 == 0
irb(main):004:2>   }
irb(main):005:1> }
=> #<Enumerator: #<Enumerator::Generator:0x0055620e1400f8>:each>
irb(main):006:0> enum.each{|i| p i }
5
10
=> 1..10
irb(main):007:0> fib = Enumerator.new { |y|
irb(main):008:1*   a = b = 1
irb(main):009:1>   loop {
irb(main):010:2*     y << a
irb(main):011:2>     a, b = b, a + b
irb(main):012:2>   }
irb(main):013:1> }
=> #<Enumerator: #<Enumerator::Generator:0x0055620e0dbec8>:each>
irb(main):014:0> p fib.take(10)
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

以下, Object#enum_forObject#to_enum について.

Object#enum_forObject#to_enum についても, Enumerator.new と同様に Enumerator オブジェクトを返す. 以下, ドキュメントより引用.

irb(main):011:0* enum = str.enum_for(:each_byte)
=> #<Enumerator: "xyz":each_byte>
irb(main):012:0> p(a = enum.map{|b| '%02x' % b })
["78", "79", "7a"]
=> ["78", "79", "7a"]
irb(main):013:0> enum = str.to_enum(:each_byte)
=> #<Enumerator: "xyz":each_byte>
irb(main):014:0> p(a = enum.map{|b| '%02x' % b })
["78", "79", "7a"]
=> ["78", "79", "7a"]

以下, ブロックを指定する場合.

irb(main):001:0> module Enumerable
irb(main):002:1>   def repeat(n)
irb(main):003:2>     raise ArgumentError, "#{n} is negative!" if n < 0
irb(main):004:2>     unless block_given?
irb(main):005:3>       # __method__ はここでは :repeat
irb(main):006:3*       return enum_for(__method__, n) do # return to_enum(__method__, n) でも同義
irb(main):007:4*         # size メソッドが nil でなければ size * n を返す。
irb(main):008:4*         sz = size
irb(main):009:4>         sz * n if sz
irb(main):010:4>       end
irb(main):011:3>     end
irb(main):012:2>     each do |*val|
irb(main):013:3*       n.times { yield *val }
irb(main):014:3>     end
irb(main):015:2>   end
irb(main):016:1> end
=> :repeat
irb(main):017:0> %i[hello world].repeat(2) { |w| puts w }
hello
hello
world
world
=> [:hello, :world]
irb(main):018:0> enum = (1..14).repeat(3)
=> #<Enumerator: 1..14:repeat(3)>
irb(main):019:0> enum.first(4)
=> [1, 1, 1, 2]
irb(main):020:0> enum.first(10)
=> [1, 1, 1, 2, 2, 2, 3, 3, 3, 4]
irb(main):021:0> enum.size
=> 42

フムフム.