ようへいの日々精進XP

よかろうもん

2018 年 06 月 11 日 (月)

ジョギング

  • 雨の為, お休み

日課

  • お休み

引き続き...体調が...

  • 肩がとても張って, 右側の頭痛が激しい

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

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)

lazy と take

以下のコードを実行するとどうなるか.

p (1..10).lazy.map{|num|
  num * 2
}.take(3).inject(0, &:+)

12 と表示される

以下, irb による動作確認.

irb(main):001:0> p (1..10).lazy.map{|num|
irb(main):002:1*   num * 2
irb(main):003:1> }.take(3).inject(0, &:+)
12
=> 12

以下, 解説より抜粋.

  • lazyEnumerator::Lazy クラスを返す
  • Enumerator::Lazy クラスは mapselect メソッドに遅延評価を提供する
  • take(3) が実行されると 1 から 3 まで map に渡されたものと判断され, inject に渡される

設問のコードから lazy を除いて実行してみる.

# lazy メソッド無し, map された結果が返る
irb(main):001:0> e = (1..10).map{|num|
irb(main):002:1*   num * 2
irb(main):003:1> }
=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
# take メソッドで Array クラスの変数 e から最初の 3 要素を取得する
irb(main):004:0> t = e.take(3)
=> [2, 4, 6]
# inject メソッドを利用して 3 要素の合計を計算している 
irb(main):005:0> t.inject(0, &:+)
=> 12

Enumerable#map (Enumerable#collect) メソッドについて. (ドキュメントより引用)

  • 各要素に対してブロックを評価した結果を全て含む配列を返す
  • ブロックを省略した場合, 上で説明した繰り返しを実行し, その結果として得られる配列を返すような Enumerator オブジェクトを返す
  • Enumerable#collect も同様の機能を提供する
irb(main):004:0> p [1, 2, 3].map {|n| n * 3 }
[3, 6, 9]
=> [3, 6, 9]
irb(main):006:0> a = [1, 2, 3].map
=> #<Enumerator: [1, 2, 3]:map>

Enumerable#select (Enumerable#find_all) メソッドについて. (ドキュメントより引用)

  • 各要素に対してブロックを評価した値が真であった要素を全て含む配列を返す
  • 真になる要素がひとつもなかった場合は空の配列を返す
  • ブロックを省略した場合, 各要素に対しブロックを評価して真になった値の配列を返すような Enumerator を返す
  • Enumerable#find_all も同様の機能を提供する
irb(main):001:0> (1..10).find_all
=> #<Enumerator: 1..10:find_all>
irb(main):002:0> (1..10).find_all { |i| i % 3 == 0 }
=> [3, 6, 9]
irb(main):003:0> [1,2,3,4,5].select
=> #<Enumerator: [1, 2, 3, 4, 5]:select>
irb(main):004:0> [1,2,3,4,5].select { |num| num.even? } 
=> [2, 4]

Enumerable#inject メソッドについて. (ドキュメントより引用)

  • リストのたたみこみ演算を行う
  • 最初に初期値 init と self の最初の要素を引数にブロックを実行する
  • 2 回目以降のループでは、前のブロックの実行結果と self の次の要素を引数に順次ブロックを実行する
  • 最後の要素まで繰り返して最後のブロックの実行結果を返す
  • 要素が存在しない場合は init を返す
  • 初期値 init を省略した場合はには, 最初に先頭の要素と 2 番目の要素をブロックに渡す
  • 要素が 1 つしかなければブロックを実行せずに最初の要素を返し, 要素がなければブロックを実行せずに nil を返す
# 合計の計算
irb(main):005:0> p [2, 3, 4, 5].inject {|result, item| result + item }
14
=> 14
# 自乗和を計算する. 初期値 (0) をセットする必要がある.
irb(main):006:0> p [2, 3, 4, 5].inject(0) {|result, item| result + item**2 }
54
=> 54

以下のように書いても同様の結果を得ることが出来る.

irb(main):007:0> result = 0
=> 0
irb(main):008:0> [1, 2, 3, 4, 5].each {|v| result += v }
=> [1, 2, 3, 4, 5]
irb(main):009:0> p result
15
=> 15
irb(main):010:0> p [1, 2, 3, 4, 5].inject(:+)
15
=> 15
irb(main):001:0> p ["b", "c", "d"].inject("abbccddde", :squeeze)
"abcde"
=> "abcde"

inject メソッドの応用

(0..10).inject([1, 1]) {|fib, i| fib << fib[i] + fib[i+1] }

以下, irb にて確認.

irb(main):001:0> (0..10).inject([1, 1]) {|fib, i| fib << fib[i] + fib[i+1] }
=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233]
  • inject を使って Ruby の Array や Enumerable の各種メソッドを再実装する
# Array クラスを継承する List クラスを作成, 同時に Array クラスのメソッド達を undef して利用出来なくしている
irb(main):001:0> class List < Array
irb(main):002:1>   undef_method *%w(inject map size at index select reject detect all? any? one? none? min max minmax take_while grep include? partition group_by count join assoc zip reverse values_at compact take flat_map product)
irb(main):003:1> end
=> List
# まずは inject メソッドを再実装
irb(main):005:0> class List < Array
irb(main):006:1>   def inject(m, &blk)
irb(main):007:2>     return m if empty?
irb(main):008:2>     (drop 1).inject( yield(m, first), &blk)
irb(main):009:2>   end
irb(main):010:1> end
=> :inject
irb(main):011:0> List[1,2,3,4,5].inject(0) { |m, x| m + x }
=> 15
# 続いて, map メソッドを再実装
irb(main):012:0> class List < Array
irb(main):013:1>   def map
irb(main):014:2>     inject([]) { |m, x| m << yield(x) }
irb(main):015:2>   end
irb(main):016:1> end
=> :map
irb(main):017:0> List[1,2,3,4,5].map { |x| x**2 }
=> [1, 4, 9, 16, 25]

こちらの記事を引用させて頂いた. とても面白かった.

フムフム.