ジョギング
日課
- おやすみ
博多デート
- お中元やら炊飯器, トースター, バリカン等電化製品を買い揃えたりした
今日のるびぃ ~ だいぶん古いけど, 「Ruby 2.0 最速入門」という記事を写経する (1) ~
だいぶん古い WEB+DB Press Vol.73 の「一歩先ゆく Ruby」というコーナーで「Ruby 2.0 最速入門」という記事が試験にあたって参考になりそうだったので写経する. 尚, 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::Lazy
Enumerator::Lazy とは, 遅延リストを手軽に導入し, 操作する為の仕組み.
irb(main):001:0> require 'date' => true irb(main):002:0> def each_fridays irb(main):003:1> friday = Date.new(2018, 6, 29) irb(main):004:1> loop do irb(main):005:2* yield friday irb(main):006:2> friday += 7 irb(main):007:2> end irb(main):008:1> end => :each_fridays irb(main):009:0> fridays = enum_for(:each_fridays) => #<Enumerator: main:each_fridays>
上記のように enum_for
を利用することで, each_fridays
から Enumerator オブジェクトを生成することが出来る. 以下のように Enumerable#first
を使うことで, 最初の 5 つの要素だけを取り出すことが出来る.
irb(main):013:0> puts fridays.first(5) 2018-06-29 2018-07-06 2018-07-13 2018-07-20 2018-07-27 => nil
Enumerable#map
や Enumerable#select
を使って絞り込み等を行う場合, Object#each_friday
ではいつまで経っても処理が終わらないことになる. これは map
や select
等のメソッドが, 配列全ての要素に対して
処理を行う前提となっている為である.
irb(main):014:0> fridays.select {|d| d.day == 13 }.first(5) ... ループ ...
この状態を解決する為, Enumerator::Lazy
を利用する.
irb(main):016:0> puts fridays.lazy.select {|d| d.day == 13 }.first(5) 2018-07-13 2019-09-13 2019-12-13 2020-03-13 2020-11-13 => nil
Enumerator::Lazy
を利用することで, その各要素を実際に計算する前に map
や select
等の配列に対する操作をメソッドチェインで書き連ねていくことが出来る. 尚, 操作した結果は Enumerator::Lazy
となる為, 要素を取り出す為には force
メソッドを呼び出す必要がある.
irb(main):020:0> puts fridays.lazy.select {|d| d.day == 13 }.take(5).force 2018-07-13 2019-09-13 2019-12-13 2020-03-13 2020-11-13 => nil
もしくは, 先述のように first
や inject
等の, 要素を利用せざる得ないメソッドを呼び出すことで自動的に各要素が計算される.
irb(main):021:0> puts fridays.lazy.select {|d| d.day == 13 }.first(5) 2018-07-13 2019-09-13 2019-12-13 2020-03-13 2020-11-13 => nil
巨大なログファイルの中から一部の文字列を検索したい場合にも lazy
を利用することで, 巨大な中間ファイルを用意することなく検索結果を取り出すことが出来る.
file = open('/path/to/large.log').each_line.lazy file.select {|line| line ~= /FooBar/ }.each {|line| print line }
Refinements
- ある特定の状況下でのみ, 特定のクラスメソッド定義の変更を適用した状態出来る
- モンキーパッチによるメソッドの再定義は, 影響がグローバルに及んでしまう可能性がある
以下のような単位変換のメソッドがあった場合.
class Numeric def inch self * 2.54 end end
以下のように利用可能.
irb(main):022:0> class Numeric irb(main):023:1> def inch irb(main):024:2> self * 2.54 irb(main):025:2> end irb(main):026:1> end => :inch irb(main):027:0> 1.inch => 2.54 irb(main):028:0> 2.inch => 5.08
但し, この inch メソッドが他の同名メソッドに影響を与える可能性があるので, 局所的にこの inch メソッドを利用出来るようにする.
module InchAvailable refine Numeric do def inch self * 2.54 end alias inches inch end end
irb で実行してみる.
irb(main):001:0> module InchAvailable irb(main):002:1> refine Numeric do irb(main):003:2* def inch irb(main):004:3> self * 2.54 irb(main):005:3> end irb(main):006:2> alias inches inch irb(main):007:2> end irb(main):008:1> end => #<refinement:Numeric@InchAvailable> # using メソッドで InchAvailable を有効化 irb(main):009:0> using InchAvailable; 1.inch => 2.54 # using メソッドで InchAvailable を有効化 irb(main):010:0> using InchAvailable; 2.inch => 5.08 # using していない場合には undefined method になる irb(main):011:0> 1.inch NoMethodError: undefined method `inch' for 1:Fixnum ... 略 ...
フムフム.