4. 武者 晶紀 (@knu)
• 武者 (Musha)
• Kanji: martial/brave-one
• Meaning: samurai
• 晶紀 (Akinori)
• Kanji: shine/bright-age/century
• Meaning: (My parents' hope) Shine in the next (21st) century
• knu
• Meaning: ?
5. 武者 晶紀 (@knu)
• FreeBSD committer (Ports): knu@FreeBSD.org
• Ruby committer: knu@ruby-lang.org
• (Sorted)Setの作者
• Enumeratorの作者
• Rails: 4.1∼ (2014)
• Team Nokogiri, Team Huginn, …
• A Father Of Two
!"
• Working at Machimachi Inc.
39. Enumeratorの組み込み化
• ext/enumerator からトップレベルに移動し、デフォルトでロードされるよ
うに (2005)
• requireが不要に
!
• 組み込みクラスに「ブロックなしeach」が実装される
• Ruby 1.9のFiberを利用してnext, next?, rewindが実装される (Aug 2007)
ついにEnumeratorが外部イテレータ兼用に!
(Enumerator became usable as an external iterator)
• これでGeneratorは無事引退
"
40. Enumeratorの正式な組み込み化
• Ruby 1.9.0-devリリース
!
(Dec 2007)
• 1.8系にもバックポートされ私がして、Ruby 1.8.7リリース (Jun 2008)
やがて来る1.9.1とのギャップを減らしたかった
• 1.9.1での安定版リリースを控え、さらにここからEnumeratorの仕様詰めが進む…
• メモオブジェクトの付加機能
(Iteration with a memo object)
• インデックスの付加
(Iteration with an index number)
41. Iteration With A Memo Object
ループ内(だけ)で共有できるオブジェクトを持ち、そこに情
報を集約して結果値として得たい
result = {}
records.each do |record|
result[record.id] = record
end
p result
# 宣言/初期化、操作、参照の3ステップをまとめたい
42. Iteration With A Memo Object
inject/reduceを使うとできるが、ブロックの返り値を意識し
ないといけない
result = records.inject({}) { |hash, record|
hash[record.id] = record
hash # あまりこれを書きたくない
}
43. Iteration With A Memo Object
Enumerator#with_memoの名前で提案した (Jun 2008)
• [ruby-core:17084] Enumerable::Enumerator#with_memo
• 最低限の記述で実現できる
result = records.each.with_memo({}) { |record, hash|
hash[record.id] = record
}
• with_object, with_obj, withなどの案も添え、その後、introduceというかっこいい案
も出した
• しかしなかなか決めきれず、無難な each_with_object という名前で入れて寝かせること
にした
!
(To be continued)
44. Iteration With An Index Number
• each_with_indexはあるが、開始値が0固定なので、originを指定したい
[ruby-dev:37921] with_index_from
by mameさん (2009)
• 今までは自分でオフセットを足していた
file.each_with_index do |line, i|
print "#{i+1}: #{line}"
end
• each_with_indexに引数を足したかったが、each_with_indexに渡された引数はその
ままeachに渡るようになっている
• たとえばString#eachはオプショナル引数で改行に代わる区切り文字を指定できる
45. Iteration With An Index Number
• each_with_indexはそのままに、
Enumerator#with_indexだけ拡張することにして採用
(Feb 2009)
file.each.with_index(1) do |line, i|
print "#{i}: #{line}"
end
51. Chaining Iterators
Enumerator#+ がほしい、という要望が来たときも…
• [Feature #709] Enumerator#+ (Nov 2008)
by candlerb
「これで一応できるよ」
Enumerator.new { |y|
collection1.each { |x| y << x }
collection2.each { |x| y << x }
}.each do |x|
# Enumerates collection1, and then collection2
end
56. Example: chain + each_cons =
Contexual Iteretion
a = ["foo", "bar", "baz"]
[nil].chain(a).each_cons(2) do |prev, curr|
if prev.nil?
puts "#{curr} comes first."
else
puts "#{curr} comes after #{prev}."
end
end
# Output:
foo comes first.
bar comes after foo.
baz comes after bar.
72. Lazy: Back To Eager?
• 一度lazyにすると、普通の(eagerな)Enumeratorには戻せない
lazy = [1, 2, 3].lazy
# Expecting #each would create a normal Enumerator...
still_lazy = lazy.each
# Not: Enumerator::Lazy#each is overridden to return a Lazy
# Not usable as a normal Enumerator
p still_lazy.map { |x| x * 2 }
#=> #<Enumerator::Lazy: #<Enumerator::Lazy: [1, 2, 3]>:map>
• lazyを駆使して作ったEnumeratorを、mapすると配列が返るようなふつうのEnumerableオブジェクト
として返したい
74. Lazy: Back To Eager?
うまくいく
!
lazy = [1, 2, 3].lazy
eager = lazy.eager
# Usable as a normal enumerable object
p eager.map { |x| x * 2 }
#=> [2, 4, 6]