Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Evolution Of Enumerator

Evolution Of Enumerator

Akinori MUSHA

March 23, 2019
Tweet

More Decks by Akinori MUSHA

Other Decks in Programming

Transcript

  1. Evolution
    Of
    Enumerator
    Akinori Musha (@knu)
    Machimachi Inc.
    2019-03-23 Rails Developer Meetup 2019

    View Slide

  2. ࣗݾ঺հ

    View Slide

  3. ෢ऀ থل (@knu)
    • GitHub: https://github.com/knu
    • Twitter: https://twitter.com/knu
    • Docker Hub
    !

    View Slide

  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: ?

    View Slide

  5. ෢ऀ থل (@knu)
    • FreeBSD committer (Ports): [email protected]
    • Ruby committer: [email protected]
    • (Sorted)Setͷ࡞ऀ
    • Enumeratorͷ࡞ऀ
    • Rails: 4.1ʙ (2014)
    • Team Nokogiri, Team Huginn, …
    • A Father Of Two
    !"
    • Working at Machimachi Inc.

    View Slide

  6. झຯ
    • কع 㽪
    !"
    ΞϚޒஈ (ࢦ͢ɺ؍Δ)
    • Minecraft
    ⛏$
    • ϥϯν
    %
    (ौ୩ɺத໨ࠇɺֶܳେֶɺࣗ༝ٰ͕͋ͨΓ)
    • ΫϥϑτϏʔϧΛҿΉ: Craft beer
    • ԻָΛௌ͘
    '
    (Progressive, metal music)

    View Slide

  7. View Slide

  8. ϚνϚν͸ʮͻΒ͔Εͨɺͭͳ͕Γͷ͋
    Δ஍ҬࣾձΛͭ͘Δʯͱ͍͏ϛογϣϯ
    Λܝ͛ɺ஍Ҭͷ՝୊Λղܾ͢ΔͨΊͷࣾ
    ձΠϯϑϥΛ࡞ΔҰ؀ͱͯ͠ɺۙ͝ॴ
    SNSͱ஍Ҭ৘ใϙʔλϧαΠτΛӡӦ͠
    ͍ͯ·͢ɻ
    https://machimachi.com/
    ۙ͝ॴ͞Μͱɺ·ͪͷࢠҭͯ΍Φεεϝ
    ͷ͓ళɾපӃͳͲʹ͍ͭͯ৘ใަ׵͕Ͱ
    ͖ΔαʔϏεͰ͢ɻ1
    1 ͣͬͱ੍໊࣮Ͱ͕ͨ͠ɺ࠷ۙχοΫωʔϜͰؾܰʹࢀՃͰ͖ΔΑ͏ʹͳΓ·͠
    ͨɻ

    View Slide

  9. ϚνϚνΛࢧ͑Δٕज़
    • Ruby / Rails
    • React.js
    • PostgreSQL / PostGIS
    • GCP / GAE Flex Environment
    ٕज़ϒϩάΛॻ͍͍ͯ·͢ʂ
    https://tech.machimachi.com/
    We are hiring!

    View Slide

  10. EnumerableϞδϡʔϧͷྑ͞
    • ϝιουʹண໨ͨ͠ػೳ֦ுʢmix-inʣ
    • ஋Λྻڍ͢ΔeachϝιουΛprimitiveͱͯ͠༻͍ɺmap,
    select, to_aͳͲΛఆٛ͢Δ
    • each͑͞ఆٛͯ͠include Enumerable͢Ε͹ɺ੝Γͩ͘
    ͞Μͷػೳ͕෇Ճ͞ΕΔ
    • ͲΜͳίϨΫγϣϯ΋ಉ͡࢖͍উखͰ࢖͑Δ

    View Slide

  11. ಺෦ΠςϨʔλͱ
    ֎෦ΠςϨʔλ

    View Slide

  12. ֎෦ΠςϨʔλ (External Iterator)
    • ଟ͘ͷݴޠɾϥΠϒϥϦ͸ͪ͜Β͕جຊ
    • ܁Γฦ͠Λ੍ޚ͢Δ໾ׂ͕ίϨΫγϣϯΦϒδΣΫτ·ͨ͸ϝιουͷ֎෦ɺ
    ͭ·Γ࢖͏ଆʹ͋Δ
    • ࢖͏ଆ͕ࣗ༝ͳλΠϛϯάͰΧʔιϧΛਐΊΒΕΔͷͰɺॲཧͷࣗ༝౓͸ߴ͍
    • ίϨΫγϣϯΛ࢖͏ଆ͕ϧʔϓม਺Λ؅ཧ͢Δඞཁ͕͋Δ఺͸໘౗
    • forจͳͲͷ౶ҥߏจͰࢧԉ͢Δͷ͕௨ྫ
    • eachʹ͋ͨΔॲཧ͸forจɺmapʹ͋ͨΔॲཧ͸Ϧετ಺แදهɺͷΑ͏ʹಉ͡
    ܁Γฦ͠Ͱ΋ߏจͷ࢖͍෼͚͕ඞཁʹͳΔ (each: for, map: list comprehension)

    View Slide

  13. ಺෦ΠςϨʔλ (Internal Iterator)
    • Ruby͸ͪ͜Β͕جຊ
    • ܁Γฦ͠Λ੍ޚ͢Δ໾ׂ͕ίϨΫγϣϯΦϒδΣΫτ·ͨ͸ϝιουͷ಺෦ɺ
    ͭ·ΓΦϒδΣΫτଆʹ͋Δ
    • ࣮ମͱͯ͠͸ɺ܁Γฦ͠͝ͱʹ࣮ߦ͢Δؔ਺Λड͚औΔߴ֊ؔ਺
    • ཁૉ͝ͱͷίʔϧόοΫؔ਺Λॻ͚ͩ͘ͳͷͰɺఆܕͷϧʔϓॲཧͷهड़͸ͱ
    ͯ΋؆୯
    • ෳ਺ͷίϨΫγϣϯΛಉظతʹᢞΊΔͳͲ͸ۤखʢ͋Δ͍͸ແཧʣ
    • each΋map΋ಉ͡ߏจͰॻ͚Δ (each & map share a common construct)

    View Slide

  14. Rubyͷ಺෦ΠςϨʔλ
    Internal Iterator In Ruby
    • each + Enumerable ΛAPIͱͯ͠શ໘࠾༻͍ͯ͠Δ
    • ߴ֊ؔ਺ͱͯ͠ͷ಺෦ΠςϨʔλΛ࢖͍΍͍͢Α͏ɺϒϩοΫͱ͍͏ಛ
    घߏจΛ༻ҙ
    • break, next ͳͲϧʔϓʹదͨ͠δϟϯϓ໋ྩΛఏڙ
    • ͞Βʹ return Ͱ֎ଆͷϝιου͔Β΋ൈ͚ΒΕΔ
    • ݁Ռͱͯ͠ɺ֎෦ΠςϨʔλͱḮ৭ͳ͍ॲཧͷදݱྗ

    View Slide

  15. Rubyͷ֎෦ΠςϨʔλ
    External Iterator In Ruby
    • Ruby 1.8Ҏલ͸ඪ४తͳαϙʔτ͸ͳ͔ͬͨ
    • IOͳͲ͸ɺgets, eof?ͳͲͷʢ఻౷తͳʣ֎෦ΠςϨʔλܕ
    ͷAPI΋ఏڙ͍ͯ͠Δ
    • ഑ྻͳͲఴ͑ࣈΞΫηε͕Ͱ͖ΔίϨΫγϣϯͷ৔߹͸ɺࣗ
    ྗͰΠϯσοΫεม਺Λૢ࡞ͯ͠whileจͰճ͢ͳͲ

    View Slide

  16. ؔ࿈: Ruby 1.8ͷGenerator
    Ruby 1.8.1ʙ1.8.7ʹ͸ɺࢲ͕ॻ͍ͨ generator ͱ͍͏࣮ݧత
    ͳ֎෦ΠςϨʔλੜ੒ϥΠϒϥϦ͕ඪ४ఴ෇͞Ε͍ͯ·ͨ͠ɻ
    (2003-2008)

    View Slide

  17. ؔ࿈: Ruby 1.8ͷGenerator
    • Generator: converts an internal iterator to an external iteretor
    • ۩ମతʹ͸ɺEnumerableΦϒδΣΫτΛ౉͢ͱɺnextϝιουΛݺͿ౓ʹ͔ͦ͜Β
    ཁૉ͕ॱʹฦΔΦϒδΣΫτΛ࡞Δ
    • callcc (!)Λ࢖͍ɺ಺෦ΠςϨʔλͷ܁Γฦ͠ΛࢭΊͨΓ࠶։੍ͯ͠ޚΛୣ͏߽շͳ
    ࣮૷
    !
    • ޙʹThreadʹΑΔϦϥΠτ΋ࢼΈΒΕͨ
    • ͍ͣΕʹͤΑɺຖճ܁Γฦ͠ΛࢭΊͯ͸࠶։ ͢ΔͷͰΦʔόʔϔου͕͍͢͝
    • SyncEnumerator: GeneratorΛ࢖ͬͯෳ਺ͷ಺෦ΠςϨʔλΛಉظతʹ܁Γฦ͢ (lazyͳ
    zip)

    View Slide

  18. Generatorͷ࢖༻ྫ
    2ͭʢҎ্ʣͷίϨΫγϣϯΛಉظతʹϧʔϓॲཧͰ͖Δɻ
    require "generator"
    array1 = ["A", "B", "C", "D"]
    array2 = ["A", "B", "D", "C"]
    g1 = Generator.new(array1)
    g2 = Generator.new(array2)
    loop do
    e1 = g1.next
    e2 = g2.next
    # compare elements, etc.
    end

    View Slide

  19. eachͰ΋2ͭͷ഑ྻͷ֤ཁૉΛൺֱ͍ͨ͠
    ෳ਺ͷeach͸ಉظͰ͖ͳ͍
    !
    array1 = ["A", "B", "C", "D"]
    array2 = ["A", "B", "D", "C"]
    array1.each do |e1|
    array2.each do |e2|
    # gets all combinations
    end
    end

    View Slide

  20. eachͰ΋2ͭͷ഑ྻͷ֤ཁૉΛൺֱ͍ͨ͠
    ยํ͚ͩ಺෦ΠςϨʔλΛ࢖͍ɺ΋͏Ұํ͸ఴ͑ࣈΞΫηε͢Ε͹Մೳ
    array1 = ["A", "B", "C", "D"]
    array2 = ["A", "B", "D", "C"]
    array1.each_with_index do |e1, i|
    e2 = array2[i]
    # This is OK
    end
    ͨͩɺఴ͑ࣈΞΫηεͰ͖ͳ͍ΠςϨʔλಉ࢜ͷ৔߹͸͍ͬͨΜ഑ྻԽ͢ΔͳͲͷલॲཧ͕ඞཁɻ
    ·ͨɺঢ়گʹΑͬͯยํ͚ͩϙΠϯλΛਐΊΔɺͷΑ͏ͳॲཧ͸Φϑηοτͷ؅ཧ΋ඞཁʹͳ
    Δɻ

    View Slide

  21. ྻڍ͍Ζ͍Ζ

    View Slide

  22. ྻڍ͍ͨ͠΋ͷ͍Ζ͍Ζ
    Things To Enumerate
    ϋογϡʢHashʣͷ৔߹:
    • Ωʔͱ஋ͷ૊ʢpairʣ
    • Ωʔʢkeyʣ
    • ஋ʢvalueʣ

    View Slide

  23. ྻڍ୯Ґ͍Ζ͍Ζ
    Units For Iteration
    จࣈྻʢStringʣͷ৔߹:
    • ࿦ཧߦʢlineʣ
    • όΠτʢbyteʣ
    • ʮจࣈʯʢcharʣ
    • ίʔυϙΠϯτʢcodepointʣ
    • ॻهૉΫϥελʢgrapheme clusterʣ
    • ͦͷଞɺޠʢwordʣɺจʢsentenceʣɺஈམʢparagraphʣɺ෺ཧߦʢphysical lineʣͳͲ΋ߟ͑ΒΕ
    Δɻ

    View Slide

  24. Enumeratorͷ஀ੜ·Ͱ

    View Slide

  25. Enumerable͕ఏڙ͖ͯͨ͠໾ׂ
    • ಺෦ΠςϨʔλͷϑϨʔϜϫʔΫ (framework for internal
    iterators)
    • Ϋϥεʹinclude͢Δʢmix-inʣ
    • ͦͷΫϥεͷeachΛ֦ு͢Δ

    View Slide

  26. Mix-inͱͯ͠ͷݶք
    • includeͰ͸each͔֦͠ுͰ͖ͳ͍ʂ
    (Enumerable can only extend each)
    • each_byte΍each_keyͰ΋map΍select͍ͨ͠
    • ഑ྻ(Array)Խͯ͠΍Ε͹Ͱ͖Δ͚Ͳɺͨͱ͑͹findͷΑ͏ʹ1ͭݟ͔ͭͬ
    ͨΒ࢒Γ͸ཁΒͳ͍ɺͱ͍͏ͱ͖͸ඇޮ཰ (Intermediate arrays require
    memory)
    • ObjectSpace.each_objectͷΑ͏ʹϞδϡʔϧϝιου͕ΠςϨʔλɺ
    ͱ͍͏ྫ΋͋Δ (ObjectSpace is not an enumerable object, yet provides an
    iterator method)

    View Slide

  27. EachDelegatorͱ͍͏ൃ૝
    Ԭా ५͞Μ࡞ͷEachDelegator (2001)
    ࢦఆͨ͠ΠςϨʔλϝιουΛॻ͖׵͑ɺϒϩοΫͳ͠Ͱݺͼग़͞ΕͨΒEnumerableͳΦϒ
    δΣΫτΛฦ͢ɺʮϒϩοΫͳ͠eachʯͷઌߦఏҊ
    require "eachdelegator"
    class String
    each_delegator :each_byte
    end
    "͋".each_byte.each_with_index.collect { |byte, i|
    [i, "%02X" % byte]
    }
    #=> [[0, "E3"], [1, "81"], [2, "82"]]

    View Slide

  28. EnumeratorͷൃҊ
    • ͜ͷΞΠσΞʹײ໏Λड͚ͨࢲ͸ɺEnumeratorΛఏҊ (May 2001)
    • [ruby-list:29604] Importing eachdelegator.rb
    • [ruby-list:29624] Enumerator
    • [ruby-dev:13202] Re: [ruby-list:29672] Re: Enumerator
    • [ruby-dev:13259] Enumerator -- Round 2
    • Enumerable::EnumeratorΫϥεΛઃ͚ɺ
    Kernel#enum_with(:each_xxx) ͰͦͷΠϯελϯεΛฦ͢ͱ͍͏಺༰
    • each_xxx ϝιουΛࢧԉ͍ͨ͠

    View Slide

  29. Enumeratorͷ࠾༻
    Ruby 1.8.1ʹඪ४ϥΠϒϥϦͱͯ͠࠾༻ (Oct
    2003)
    • ͜ͷ࣌఺Ͱ͸ɺ·ͩrequire "enumerator"͕ඞཁͳҰϥΠϒϥϦ
    • ૊ΈࠐΈͰ͸ͳ͍ͷͰɺඪ४ΫϥεͷeachͰEnumerator͕ฦΔͱ͍
    ͏ػೳ΋ͳ͠
    • enum_with / each_for ͰϝιουΛࢦఆͯ͠࢖͏
    • ͜͜Ͱɺ܁Γฦ͠ॲཧҰൠΛ֦ு͢ΔػೳΛ2ͭಋೖͨ͠

    View Slide

  30. ܁Γฦ͠ͷ֦ு
    • ࢦఆݸ਺ͣͭͷεϥΠε (each_slice)
    • ࿈ଓཁૉͷ܁Γฦ͠ (each_cons)

    View Slide

  31. ࢦఆݸ਺ͣͭͷεϥΠε
    each_slice(n)Ͱnཁૉͣͭ·ͱΊͯ܁Γฦ͢
    # 100௨ͣͭλεΫԽ
    recipient_ids.each_slice(100) do |ids|
    MessageJob.perform_later(ids: ids)
    end

    View Slide

  32. ࿈ଓཁૉͷ܁Γฦ͠
    each_cons(n)Ͱʮ֤ཁૉ+ޙଓͷܭnཁૉʯʹ͍ͭͯ1ͭͣͭ
    ͣΒ͠ͳ͕Β܁Γฦ͢
    [1, 2, 3, 4, 5, 6].each_cons(3) do |es|
    p es
    end
    # [1, 2, 3]
    # [2, 3, 4]
    # [3, 4, 5]
    # [4, 5, 6]

    View Slide

  33. ࿈ଓཁૉͷ܁Γฦ͠
    • nilຒΊͤͣʹ (size - n + 1) ճ܁Γฦͯ͠ऴΘͬͯ͠·
    ͏ͷͰɺʮίϯςΩετ෇͖܁Γฦ͠ʯͱͯ͠࢖͏ʹ͸
    ͪΐͬͱ଍Γͳ͍
    • ͨͱ͑͹࠷ॳʹ [nil, 1, 2]ɺ࠷ޙʹ [5, 6, nil] Λ଍
    ͯ͘͠ΕΕ͹શཁૉͷલޙίϯςΩετ෇͖܁Γฦ͠ʹͳΔ
    ͷʹ…
    !
    Ruby 2.6ͷchainͰٹࡁ͞Ε·͢ʂʢޙड़ʣ

    View Slide

  34. Ruby 1.9ͷ։ൃ͕ਐΉ
    ෳ਺ͷେ͖ͳมߋΛؚΉRuby 1.9ͷ։ൃ͸͔ͳΓ೉ߤͨ͠
    • ΠϯλϓϦλͷࠩ͠ସ͑ʢYARVʣ
    • ਖ਼نදݱΤϯδϯͷมߋʢَं Onigurumaʣ
    • CSIํࣜͷM17Nಋೖ
    • ৽จ๏ ({ key: value }, -> {}) etc.

    View Slide

  35. ΠϯλϓϦλͷࠩ͠ସ͑ʢYARVʣ
    • VMͷಋೖ
    • OSͷωΠςΟϒεϨουΛ࢖͏Α͏ʹ (pthread)
    • ThreadΑΓܰྔͳίϧʔνϯػߏ (Fiber)

    View Slide

  36. ༨ஊ: ਖ਼نදݱΤϯδϯͷมߋ
    • ৽ͨʹॻ͖ى͜͞ΕͨΤϯδϯʮَंʯ(Oniguruma) by K.Kosako
    • ͦΕ·Ͱ͸GNU regexϥΠϒϥϦΛ(ຐ)վ଄ͯ͠࢖͍ͬͯͨ
    • Perl֦ுΛ͕Μ͹࣮ͬͯ૷͖͕ͯͨ͠ɺ֦ுੑʹ΋ݶք
    • ϥΠηϯε͕͚ͩ͜͜LGPLͩͬͨͷͰऔΓѻ͍ʹ஫ҙΛཁͨ͠
    • RubyͰ͸ະ࣮૷ͩͬͨPerlޓ׵ͷ௥ՃػೳΛ΄΅͢΂ͯඋ͑ɺBSDL
    Ͱɺ֦ுੑɺੑೳʹ΋༏Ε͍ͯͨ
    • ݱࡏ(2012-)͸ޙܧͷʮَӢʯ(Onigmo)ʹͳ͍ͬͯΔ

    View Slide

  37. ༨ஊ: CSIํࣜͷM17Nಋೖ
    • Code Set Independentํࣜ
    • ݸʑͷStringΦϒδΣΫτ͕ࣗ਎ͰΤϯίʔσΟϯά৘ใΛ࣋ͪɺෳ
    ਺ͷΤϯίʔσΟϯάͷจࣈྻσʔλ͕ࠞࡏͰ͖Δ
    • ͋·ΓଞͷݴޠʹྨΛݟͳ͍
    • ಺෦ΤϯίʔσΟϯά͸UTF-8ͷΈʹ౷Ұ͢Δͷ͕ʢ౰࣌Ͱ΋ʣେ੎
    • ͍͍ͤͥબ΂ͯ΋1ͭͷΈ
    • َंͷෳ਺ΤϯίʔσΟϯάରԠ෦͕ૅͱͳ͍ͬͯΔ

    View Slide

  38. Enumeratorͷ
    ૊ΈࠐΈԽͱ੒௕

    View Slide

  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͸ແࣄҾୀ
    "

    View Slide

  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)

    View Slide

  41. Iteration With A Memo Object
    ϧʔϓ಺ʢ͚ͩʣͰڞ༗Ͱ͖ΔΦϒδΣΫτΛ࣋ͪɺͦ͜ʹ৘
    ใΛू໿ͯ݁͠Ռ஋ͱͯ͠ಘ͍ͨ
    result = {}
    records.each do |record|
    result[record.id] = record
    end
    p result
    # એݴ/ॳظԽɺૢ࡞ɺࢀরͷ3εςοϓΛ·ͱΊ͍ͨ

    View Slide

  42. Iteration With A Memo Object
    inject/reduceΛ࢖͏ͱͰ͖Δ͕ɺϒϩοΫͷฦΓ஋Λҙࣝ͠
    ͳ͍ͱ͍͚ͳ͍
    result = records.inject({}) { |hash, record|
    hash[record.id] = record
    hash # ͋·Γ͜ΕΛॻ͖ͨ͘ͳ͍
    }

    View Slide

  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)

    View Slide

  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͸ΦϓγϣφϧҾ਺Ͱվߦʹ୅ΘΔ۠੾ΓจࣈΛࢦఆͰ͖Δ

    View Slide

  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

    View Slide

  46. Ruby 1.9ͷҰൠ޲͚ϦϦʔε
    ͦͯ͠ؾ͕෇͘ͱ…
    Ruby 1.9.1ϦϦʔε
    !
    (Jan 2009)
    • एׯෆຊҙͳ͕Βɺ each_with_object / with_object
    ͕ͦͷ໊લͰ֬ఆ͢Δ͜ͱʹ
    !
    • ௕͍ͳ͋ͱࢥ͏ਓ΋ଟ͍ͱࢥ͍·͕͢ɺ͜͏͍͏ࣄ৘Ͱ͠
    ͨ

    View Slide

  47. Enumeratorͷ
    ͞ΒͳΔൃల

    View Slide

  48. Enumeratorͷ͞ΒͳΔൃల
    • ஗ԆධՁ (Lazy Iterators)
    • ࣗ༝ͳྻڍ (Freehand Iterators)
    • ΠςϨʔλͷ࿈࠯ (Chaining Iterators)

    View Slide

  49. Lazy Iterators
    • find, any?, all?ͳͲɺ৚݅ʹ߹͏ʢ߹Θͳ͍ʣཁૉ͕ग़ݱͨ͠Βଧͪ੾Γɺͱ͍͏
    Enumerableͷϝιου͕৭ʑ͋Δ
    • map΍selectΛ࿈࠯ͤ͞Δͱ͖ɺຖճ഑ྻΛ࡞Δͷ͸ۭؒޮ཰͕ѱ͍
    • ू໿͍ͨ͠৔߹ɺೖΕ෺Λ༻ҙ͢Δͷ͸࠷ޙ͚ͩͰे෼ͳ͸ͣ
    • ͦ΋ͦ΋ɺϑΟϧλϦϯά͚͕ͩඞཁͰɺू໿ࣗମ͸ෆཁͳ͜ͱ΋ଟ͍
    • [Feature #4653] Enumerable#rude_map by shyouhei (May 2011)
    • [Feature #4890] Enumerable#lazy by yhara (Jun 2011)
    • Ruby 1.9.3ʹ͸ؒʹ߹Θͣ (Oct 2011)
    • Ruby 2.0ͰEnumerable#lazy͓ΑͼEnumerator::Lazy͕࠾༻ (Feb 2013)

    View Slide

  50. Freehand Iterators
    • ΠςϨʔλΛ࣮૷͢Δʹ͸ɺeachϝιουΛఆٛ͢Δɺ͕جຊ
    • ͦͷ৔ͰEnumerableͳΦϒδΣΫτΛ࡞ͬͯฦ͍ͨ͠ɺͱ͍͏ͱ͖ɺ
    Object.newͯ͠.extend(Enumerable) ͯ͠ಛҟϝιουeachΛੜ
    ΍͢ɺͱ͍͏ͷ͸͋·ΓΧδϡΞϧʹ΍Γͨ͘ͳ͍
    • EnumeratorΛ࡞ͬͨͱ͖ɺEnumerableͷ֦ுधཁͷύϯυϥͷശΛ։
    ͘͜ͱʹͳΔͱ༧ݟͨ͠
    "#
    • ԿͰ΋Ͱ͖Δ΋ͷ΋ඞཁͩΖ͏…ͱ Enumerator.new {} Λ͓·͚
    ͰೖΕͨ

    View Slide

  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

    View Slide

  52. Chaining Iterators
    ͱ͍͏Θ͚ͰɺҎԼͷཧ༝͔ΒҰճݟૹΒͤͯ΋Βͬͨ
    • ԋࢉࢠϝιουΛ͋·Γ҆қʹೖΕΔͱ͋ͱͰଞͷԋࢉࢠͱ
    ͷ੔߹ੑͰۤ࿑ɾޙչ͢Δ
    • Ϣʔεέʔε͕Ͳͷ͘Β͍͋Δ͔·ͩ໌Β͔Ͱͳ͍
    • ࠓ͸Enumerator.new {}Ͱखॻ͖ͯ͠Ͷ

    View Slide

  53. Chaining Iterators, A Decate Later
    • ͦͯ͠े೥ޙɺࠓ౓͸chainͱ͍͏໊લͰఏҊ͕དྷͨ
    [Feature #15144] Enumerator#chain (Sep 2018)
    by zverok
    • ͦͷࠒʹ͸ɺࢲࣗ਎ͷܦݧ͔Β΋࿈࠯ͷϢʔεέʔε͸۩
    ମతʹݟ͍͑ͯͨ
    !

    View Slide

  54. Chaining Iterators, A Decate Later
    • ૝ఆͰ͖ΔϢʔεέʔε
    • ྫ: ίϚϯυϥΠϯΦϓγϣϯͰͷࢦఆɺ؀ڥม਺ͷ஋ɺઃఆϑΝΠϧ಺ͷઃఆ
    ஋ɺͷॱʹݕࡧύεΛฦ͢
    • ྫ: ࠷৽ͷߋ৽σʔλΛAPI͔ΒҾͬுͬͯฦ͠ɺଓ͍ͯϩʔΧϧετϨʔδʹ
    Ωϟογϡ͞Εͨ෦෼Λฦ͠ɺ͞ΒʹͦΕҎલͷ΋ͷ͕ඞཁͳΒAPI͔ΒҾ͍ͯฦ͢
    • first(3), take_while, find ͳͲͰޮՌΛൃش
    • ୯७ʹɺ഑ྻΛ࡞Δ͜ͱͳ͘ෳ਺ͷετϦʔϜΛॱ࣍૸͍ࠪͨ͜͠ͱ͸࣮ࡍʹଟ͍
    • શ߲໨૸ࠪ͢Δ৔߹Ͱ΋ޮ཰త

    View Slide

  55. Chaining Iterators, A Decate Later
    • MatzΛަ͑ͨٞ࿦Ͱ΋ɺ Enumerator#+ ΛׂΓ౰ͯͯ΋໰
    ୊ͳ͔Ζ͏
    !
    ͱͳͬͨ
    • chain ͸ Enumerable ʹ͋ͬͯ΋͍͍ΑͶɺͱͳΓɺ͋Θ
    ͤͯRuby 2.6Ͱ࠾༻
    "

    View Slide

  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.

    View Slide

  57. Ruby 2.7Ҏ߱ͷ
    ల๬ͱ՝୊

    View Slide

  58. Ruby 2.7Ҏ߱ͷల๬ͱ՝୊
    • Enumerator::Yielder#to_proc
    • Enumerator.generate
    • Multi-Value Or Array?
    • Lazy: Back To Eager?

    View Slide

  59. Enumerator::Yielder#
    to_proc

    View Slide

  60. Enumerator::Yielder#to_proc
    • [Feature #15618] Enumerator::Yielder#to_proc (Feb 2019)
    by knu
    Enumerator#to_procΛ࣮૷͢Δ͜ͱͰ
    enum = Enumerator.new { |y|
    Dir.glob("*.rb") { |file|
    File.open(file) { |f| f.each_line(&y) }
    # File.open(file) { |f| f.each_line { |line| y << line } }
    }
    }
    ͱॻ͚ΔΑ͏ʹ͢ΔఏҊ
    • Ruby 2.7Ͱ࠾༻
    !

    View Slide

  61. Enumerator.generate

    View Slide

  62. Enumerator.generate
    • [Feature #14781] Enumerator.generate (May 2018)
    by zverok
    • Ұͭલͷ஋͔Β࣍ͷཁૉΛܾఆ͢Δ܁Γฦ͠
    • ਺ֶͰ͍͏ʮ઴ԽࣜʯʹΑΔ܁Γฦ͠ఆٛ
    • ϊʔυͷ parent ΛॱʹḷΔͱ͔
    • ϦτϥΠͷ଴ͪ࣌ؒΛഒʑʹ͍ͯ͘͠ͱ͔
    • ݕ౼ࣄ߲
    • ऴྃ৚݅ͷࢦఆํ๏ʢಛఆͷྫ֎ͰऴΘΔͷ͔ɺnilͰऴΘΔͷ͔ɺͦͷ৔߹nilΛؚΉͷ͔ʣ
    • 2ͭҎ্લ΋ࢀর͍ͨ͠ʁʢྫ: ϑΟϘφον਺ྻ… ͋·Γݱ࣮ʹ͸ͳ͍ʁʣ

    View Slide

  63. Multi-Value Or Array?

    View Slide

  64. Multi-Value Or Array?
    • Hash#each / map ͸ϖΞΛ1Ҿ਺Ͱ2ཁૉ഑ྻΛϒϩοΫʹ౉ͯ͘͠Δ
    [1] pry(main)> {foo: 1, bar: 2, baz: 3}.each{|x| p x}
    [:foo, 1]
    [:bar, 2]
    [:baz, 3]
    => {:foo=>1, :bar=>2, :baz=>3}
    [2] pry(main)> {foo: 1, bar: 2, baz: 3}.map{|x| p x}
    [:foo, 1]
    [:bar, 2]
    [:baz, 3]
    => [[:foo, 1], [:bar, 2], [:baz, 3]]

    View Slide

  65. Multi-Value Or Array?
    • Hash#select / reject ͸ϖΞΛ2Ҿ਺ͰϒϩοΫʹ౉ͯ͘͠Δ
    [3] pry(main)> {foo: 1, bar: 2, baz: 3}.reject{|x| p x}
    :foo
    :bar
    :baz
    => {}
    [4] pry(main)> {foo: 1, bar: 2, baz: 3}.select{|x| p x}
    :foo
    :bar
    :baz
    => {:foo=>1, :bar=>2, :baz=>3}

    View Slide

  66. Multi-Value Or Array?
    • ͜ͷҧ͍͸Ͳ͔͜Βʁ
    • Hash#each / map ͸ϖΞΛ2ཁૉ഑ྻ1ͭΛϒϩοΫʹ౉ͯ͘͠Δ
    (yield [key, value])
    • Hash#select / reject ͸ϖΞΛ2Ҿ਺ͰϒϩοΫʹ౉ͯ͘͠Δ
    (yield key, value)
    • Hash#select / reject ͸ɺRuby 1.8·Ͱ͸ArrayΛฦ͢ϝιου͕ͩͬͨɺ1.9͔
    ΒHashΛฦ͢Α͏ʹมߋ͞Εͨ
    • ͦͷͱ͖ʹɺҙਤ͔ͯ͠஌Β͔ͣ2Ҿ਺Ͱ౉͢Α͏ʹͳͬͨ
    • ࠓ͞Β௚ͤͳ͍ͱͯ͠ɺmulti-value㱻arrayͷ૬ޓม׵͸धཁ͕͋Δ͔΋

    View Slide

  67. Multi-Value Or Array?
    • λϓϧͷ֤ཁૉʹରͯ͠ɺͦΕͧΕϝιουΛద༻͢Δྫ
    require "csv"
    line = "10,foo,3.14"
    modifiers = [:to_i, :to_s, :to_f]
    # "10".send(:to_i), "foo".send(:to_s), "3.14".send(:to_f) Λظ଴…
    p CSV.parse_line(line).zip(modifiers).map(&:send)
    #=> no method name given (ArgumentError)
    • ࣮ࡍʹ͸ ["10", :to_i].send, … ͕࣮ߦ͞Εͯ͠·͏

    View Slide

  68. Multi-Value Or Array?
    ΋͠array→multi-valueʹม׵͢Δػೳ͕͋Ε͹…
    module Enumerable
    def each_splat
    return to_enum(__method__) unless block_given?
    each { |a| yield(*a) }
    end
    end

    View Slide

  69. Multi-Value Or Array?
    ͏·͍͘͘
    !
    require "csv"
    line = "10,foo,3.14"
    modifiers = [:to_i, :to_s, :to_f]
    p CSV.parse_line(line).zip(modifiers).each_splat.map(&:send)
    #=> [10, "foo", 3.14]

    View Slide

  70. ͪͳΈʹ…
    • CSVʹ͸convertersͱ͍͏͢͹Β͍͠ػೳ͕͋Γ·͢ʂ
    line = "10,foo,3.14"
    modifiers = [:to_i, :to_s, :to_f]
    p CSV.parse_line(line, converters: ->(value, field) {
    value.send(modifiers[field.index])
    })
    #=> [10, "foo", 3.14]
    • ͱ͍͏Θ͚Ͱઆಘʹ࢖͏ʹ͸ద͞ͳ͍ϢʔεέʔεͰͨ͠

    View Slide

  71. Lazy: Back To Eager?

    View Slide

  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 }
    #=> #:map>
    • lazyΛۦ࢖ͯ͠࡞ͬͨEnumeratorΛɺmap͢Δͱ഑ྻ͕ฦΔΑ͏ͳ;ͭ͏ͷEnumerableΦϒδΣΫτ
    ͱͯ͠ฦ͍ͨ͠

    View Slide

  73. Enumerator::Lazy#eager (Ҋ)
    ΋͠lazyͰͳ͍Enumeratorʹม׵͢Δػೳ͕͋Ε͹…
    class Enumerator::Lazy
    def eager
    Enumerator.new { |y|
    each { |x| y << x }
    }
    end
    end

    View Slide

  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]

    View Slide

  75. ·ͱΊ

    View Slide

  76. ·ͱΊ
    • ΈΜͳେ޷͖Enumerable͸ͲΜͲΜਐԽ͖ͯ͠·ͨ͠ɻ
    • EnumeratorͷಋೖʹΑΓɺ७ਮʹ܁Γฦࣗ͠ମͷݚڀ͕ਐΈ·ͨ͠ɻ
    • ࠓ΋ਐԽ͍ͯ͠·͢ʂ
    • ࠓޙ΋ɺݱ࣮ͷχʔζΛ౿·͑ͯʮ΄͍͠ʂʯػೳΛ֦ॆ͍͖ͯ͠·͢ʂ
    • ࠷ۙ͸RubyͷϦϦʔεαΠΫϧ͕ߴ଎Խ͍ͯ͠ΔͷͰɺ͋ͳͨ΋ཁ๬Λग़ͤ͹
    ೥຤ʹ͸۩ݱԽ͢Δ͔΋ʂ
    • ΫϦεϚεϓϨθϯτ
    !
    • ϚνϚνͰҰॹʹಇ͘஥ؒΛืू͍ͯ͠·͢ʂ

    View Slide