SlideShare une entreprise Scribd logo
1  sur  16
Télécharger pour lire hors ligne
Enumerable#lazyについて

      2012/11/10
        cuzic
Ruby 2.0.0 について                        1
 Ruby の生誕20周年にリリース予定
     2013年2月24日 公式リリース予定
 新機能
     実はそれほど多くない
     100%互換を目指している
     大きいのは、パラメータ引数 と Module#prepend くらい?
 Enumerable#lazy
     Ruby 2.0.0 の新機能の1つ
     遅延評価っぽい配列演算を扱うことができるライブラリ
     Esoteric Lang や Ruby ISO 界隈で有名な @yhara さんの作




2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
Enumerable#lazy                                      2
  Enumerable#lazy を使うと、"遅延評価" される
  ふつうにやると、無限ループになっちゃってハマる
  Enumerable#lazy があれば、必要な分を1個ずつ処理する。
      無限個のリストでも問題なく処理できる。


■ 1,000 より大きな素数を 10個 出力する                 ■ 1,000 より大きな素数を 10個 出力する
  無限ループに陥る                                  最初の 10個だけが処理される
require 'prime'                           require 'prime'

infinity = 1.0/0                          infinity = 1.0/0
(1000..infinity).select do |i|            (1000..infinity).lazy.select do |i|
  i.prime?                                  i.prime?
end. # ここで、無限ループに陥る                       end.take(10). # 最初の10個だけを評価する
  take(10).each do |i|                      each do |i|
  puts i                                    puts i
end                                       end




  2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
Enumerable#lazy のベンリなところ                                        3
【Enumerable#lazy を使わなければ】                【Enumerable#lazy なら】
 (1) map などの処理ごとに配列を生成する                  (1) map などの処理ごとに、
   ・大量の配列生成をともなうので、                           Enumerator::Lazy オブジェクトを生成
     メモリ効率が悪い                               ・すべての要素を配列にしたりしない
 (2) 最終的に使われなくても、生成する                       ・大量のメモリを消費したりしない
   ・もったいない                                (2) 次に使う分だけ生成する
   ・下の例だと、10個目より後は不要                        ・エコ
 (3) 無限ループに陥る                               ・下の例だと、10個目より後は何もしない
   ・無限個の要素を処理しようとするので                     (3) 無限を無限のまま扱える
     無限ループになっちゃう。                           ・宇宙ヤバい

■ 1,000 より大きな素数を 10個                      ■ ".rb" ファイルを 10個 列挙
require 'prime'                           require 'find'

infinity = 1.0/0                          Find.find("/").lazy.select do |f|
(1000..infinity).lazy.select do |i|         File.file?(f) && f.end_with?(".rb")
  i.prime?                                end.take(10).each do |f|
end.take(10). # 最初の10個だけを評価する               puts f
  each do |i|                             end
  puts i
end

  2012/11/10 Scala/Clojure/Ruby勉強会    「Enumerable#lazy について」
Enumeratable#lazy の内部動作(1)                        4
 Enumerable#lazy は Enumerator::Lazy オブジェクトを返す
     Enumerator::Lazy は Enumerator のサブクラス
     Enumerator : 外部イテレータを実現するためのクラス
     内部では Fiber を使って実装されている
 Enumerator::Lazy は、Enumerable モジュールのメソッドを再定義
    map 、select、take などのメソッドの返り値は、Enumerator::Lazy オブジェクト
  ※ Enumerable モジュールで増えるメソッドは Enumerable#lazy だけ。


                          ここで、Enumerator::Lazy オブジェクトを生成。
infinity = 1.0/0
(1000..infinity).lazy.
                          1個ずつ処理する。
  select do |i|
  i.prime?                select や take などの Enumerable モジュールのメソッド
end.                      は、Enumerator::Lazy クラスでは
  take(10).each do |i|
  puts i                  1個ずつ処理する Enumerator::Lazy オブジェクト
end                       を返すように再定義されている。




2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
Enumerable#lazy の内部動作(2)                                       5
 Enumerable#lazy は最初の1個だけがんばって処理。残りは後回し。
     2個目以降については、後工程で必要になって初めて、前工程の処理を行う。



                                                inf = 1.0/0
                                                (1000..inf).lazy.select do |i|
【lazy Fiber】 (1000..inf).lazy
                                                  i.prime?
値を順に yield する Enumerable::Lazy オブジェクトを生成
                                                end.take(10). # 最初の10個だけ評価
次の値を yield して、 lazy Fiber は中断する。
                                                  each do |i|
                                                    puts i
     【select Fiber】 select {|i| i.prime?}         end
     素数である場合だけ yield する E::Lazy オブジェクト
     素数となる値が来るまで、親 Fiber に値を要求する。
     素数があれば、その値を yield して select Fiber は中断

          【take Fiber】 take(10)
          最初の 10個であれば yield する E::Lazy オブジェクト
          親 Fiber から得られた値を 10個目までは 子Fiber に
          そのまま yield し、take Fiber は処理を中断する。

               【each Fiber】 each {|i| puts i }
               親Fiber から受け取った値をそのまま 表示する。
               ループごとに親 Fiber に次の値を要求する。

 2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
Enumerator の復習                                         6
 Enumerator                            ■ Object#enum_for による Enumerator の生成

     Enumerable なオブジェクト                enum = "abc".enum_for(:each_byte)
                                       enum.each do |byte|
     を簡単に作れる                             puts byte
     ~Ruby 1.8.6 の標準添付ライブラリ            end
     Ruby 1.8.7 の組込ライブラリ
                                       ■ Enumerator.new による Enumerator の生成
     Enumerable::Enumerator
     Ruby 1.8.8~ の組込ライブラリ              str = "abc"
                                       enum = Enumerator.new(str, :each_byte)
     Enumerator
                                       enum.each do |byte|
     Ruby 1.9 で Generator の機能を           puts byte
     取り込み強化される                         end

■ Ruby 1.8 の添付ライブラリ Generator          ■ Enumerator.new (Ruby 1.9 feature )
g = Generator.new do |yielder|         enum = Enumerator.new do |yielder|
  "abc".each_byte do |byte|              "abc".each_byte do |byte|
    yielder.yield byte                     yielder << byte
  end                                    end
end                                    end
# callcc だから遅い                         # Fiber を使っているから速い
g.each do |byte|                       enum.each do |byte|
  puts byte                              puts byte
end                                    end

2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
Enumerator と Fiber                              7
  Enumerator は Fiber を活用するデザインパターンの1つ
      Fiber における難しい概念を隠ぺい
      Fiber.yield => Enumerator::Yielder#<< 、 Enumerator#each による列挙
  Enumerator を使いこなせれば、十分 Fiber のメリットを活用できる
  《参考: Fiber にできて、Enumerator でできないこと》
   親 Fiber から 子Fiber へのデータの送付(Fiber.yield の返り値の利用)
■ Enumerator の例(フィボナッチ数)                    ■ 《参考》 Fiberの例(フィボナッチ数)
fib = Enumerator.new do |yielder|           fib = Fiber.new do
  a = b = 1                                   a = b = 1
  loop do                                     loop do
                                                Fiber.yield a
    yielder << a
                                                a, b = b, a + b
    a, b = b, a + b                           end
  end                                       end
end
                                            def fib.each
fib.each do |i|                               loop do              これが必要。
                        Enumerator の
  break if i > 1000     内部実装まで
                                                yield fib.resume   だけど、これが
                                              end
  puts i
                        考えなくていい             end                    難しい。(汗)
end
                                            fib.each do |i|
                                              break if i > 1000
                                              puts i
                                            end
  2012/11/10 Scala/Clojure/Ruby勉強会     「Enumerable#lazy について」
Enumerator はベンリ                                    8
  Enumerator なら Enumerable の便利メソッドを使える。
      ブロック付メソッド呼び出しは単純なイテレーションならいいけど。。。
  Enumerator はオブジェクトなので、使い回しが簡単。
      引数にしたり、返り値にしたり、インスタンス変数に格納したり
      Enumerator オブジェクトの生成も慣れれば簡単。

■ Enumerator の例(フィボナッチ数)                  ■ 《参考》 ブロック付メソッド呼び出しの例
fib = Enumerator.new do |yielder|         def fibonacci
  a = b = 1                                 a = b = 1
  loop do                                   loop do
    yielder << a          慣れれば、               yield a
    a, b = b, a + b       カンタン                a, b = b, a + b
  end                                       end
end                                       end
fib.each do |i|                           fibonacci do |i|
  break if i > 1000                         break if i > 1000
  puts i                                                          単純なイテレーション
end                                         puts i                ならいいけど・・・。
                                          end
fib.each_cons(2) do |i, j|   Enumerable
  break if i > 1000                       prev = nil
                             の便利メソッド      fibonacci do |i|
  puts "#{i} #{j}"           を使い放題
end                                         break if i > 1000             面倒 ! !
                                            puts "#{prev} #{i}" if prev
                                            prev = i
                                          end
  2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
(おまけ) Enumerator の利用例
データ構造に対する巡回方法を Enumerator で抽象化可能
                                          1       2           3                  1     4           7
マトリックスに対して、
行 => 列 の順で列挙するか                           4       5           6                  2     5           8
列 => 行 の順で列挙するか
                                          7       8           9                  3     6           9

                                                                  /       ①

                                   /bin                /usr               /etc         /home
                                          ②                   ③                  ④             ⑤

 木構造に対して、                                 /usr/local
                                                       ⑥
                                                              /usr/lib
                                                                         ⑦
                                                                                     /home/cuzic
                                                                                       ⑧
 幅優先探索を行うか
 深さ優先探索を行うか
                                                                  /
                                                                         ①

                                   /bin                /usr               /etc         /home
                                          ②                   ③                  ⑥             ⑦
                                          /usr/local          /usr/lib               /home/cuzic
                                                   ④                  ⑤               ⑧
2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
Enumerable#lazy の実装                                             10
  Enumerable#lazy を使うと、Enumerable のメソッドの返り値が
  Enumerator::Lazy オブジェクトになる
  Enumerable モジュールに増やすメソッドは Enumerable#lazy だけ
module Enumerable                                  def select(&block)
  def lazy                                           Lazy.new(self){|yielder, val|
    Enumerator::Lazy.new(self)                         if block.call(val)
  end                                                    yielder << val
end                                                    end
                                                     }
class Enumerator                                   end
  class Lazy < Enumerator
    def initialize(obj, &block)
                                                   def take(n)
      super(){|yielder|
                                                     taken = 0
        begin
                                                     Lazy.new(self){|yielder, val|
          obj.each{|x|
            if block                                   if taken < n
              block.call(yielder, x)                     yielder << val
            else                                         taken += 1
              yielder << x                             else
            end                                          raise StopIteration
          }                                            end
        rescue StopIteration                         }
        end                                        end
      }                                       ……
    end                                     end

  2012/11/10 Scala/Clojure/Ruby勉強会     「Enumerable#lazy について」
Enumerable#lazy の活用例                                          11
    Enumerable#lazy を上手に活用すると生成、フィルタを順に
    処理するプログラムをメソッドチェーンで簡潔に記述できる。
require 'forwardable'                    def map &block
                                           @lazy = @lazy.map &block
class FizzBuzz                             self
  extend Forwardable                     end
  def_delegators :@lazy, :each           def fizzbuzz
                                           map do |i|
 def self.each &blk                            (i % 15 == 0) ? "FizzBuzz" : i
   fb = self.new                           end
   fb.fizzbuzz.fizz.buzz.                end
     take(30).each(&blk)                 def buzz
 end                                       map do |i|
                                               (i % 5 == 0) ? "Buzz" : i
 def initialize                            end
   inf = 1.0/0                           end
   @lazy = (1..inf).lazy                 def fizz
 end                                       map do |i|
                                               (i % 3 == 0) ? "Fizz" : i
 def take n                                end
   @lazy = @lazy.take n                  end
   self                                end
 end                                   FizzBuzz.each do |i|
                                         puts i
                                       end
2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
(おまけ)パイプライン処理                                               12
    Enumerator を活用すると、UNIX のパイプのように処理を書ける
        下記の例では本当にパイプ(|)で処理する例
        ついカッとなってやった。今は反省している。
    ふつうはメソッドチェーンで十分実用的
        フィルタ処理をオブジェクトにしたいときはこういう方法もベンリかも。
        Enumerator.new の書き方に精通していないと、分かりにくい。。。
            Enumerable#map とか使って、書けたら分かりやすいのにな。。。
      # Enumerable#lazy は無関係だけど。。。



class Pipeline <Enumerator
  attr_accessor :source                     def main
  def |(other)                                fb    = FizzBuzz.new(15, "FizzBuzz")
    other.source = self                       buzz = FizzBuzz.new( 5, "Buzz")
    other                                     fizz = FizzBuzz.new( 3, "Fizz")
  end
end                                           inf   = 1.0/0
                                              sink = Pipeline.new(1..inf)
class FizzBuzz <Pipeline                      pipe = sink | fb | buzz | fizz
  def initialize n, subst                     pipe.take(30).each{|i| puts i}
    super() do |y|
      @source.each do |i|                   end
        y << (i % n == 0) ? subst : i
      end                                   if $0 == __FILE__
    end                                       main
  end                                       end
end
2012/11/10 Scala/Clojure/Ruby勉強会        「Enumerable#lazy について」
(おまけ) もっと lazy に                                     13
      クロージャを活用すれば、もっと評価タイミングを遅延できる
      値ではなく、クロージャを要素とし、必要なときクロージャを評価する
      クロージャなら、memoize も簡単に実装可能
          同じ計算を2回以上しないようにできる。
     # 下記の例も Enumerable#lazy と関係ない。。。

# memoize しない場合。同じ計算を何度もする。               # memoize版。同じ計算は1度だけ。圧倒的に速い。
# すごい遅い。                                  def add a, b
                                            memo = nil
def add a, b                                lambda do
  lambda do                                   return memo if memo
    return a.() + b.()                        memo = a.() + b.()
  end                                         return memo
end                                         end
                                          end
fib = Enumerator.new do |yielder|
  a = b = lambda{ 1 }                     fib = Enumerator.new do |yielder|
  loop do                                   a = b = lambda{ 1 }
    yielder << a                            loop do
    a, b = b, add(a, b)                       yielder << a
  end                                         a, b = b, add(a, b)
end                                         end
                                          end
fib.take(36).each do |i|
  puts i.()                               fib.take(36).each do |i|
end                                         puts i.()
                                          end
  2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
まとめ                      14
 Enumerable#lazy
     @yhara さんの作品
     無限を無限のまま扱える
     次の1個だけ処理して、残りは後回し
         全部一気に処理するより、必要なメモリ量が少なくて済む
 Enumerator
     Enumerator::Lazy の内部実装で大活躍
     Enumerator は Fiber より分かりやすく、実用的。
         Fiber でやりたいことの大部分は Enumerator で十分可能
 発展的な使い方
   メソッドチェーンでパイプライン処理とか
   クロージャを使ってより遅延評価させたり。




2012/11/10 Scala/Clojure/Ruby勉強会   「Enumerable#lazy について」
15




ご清聴ありがとう
 ございました

Contenu connexe

Tendances

ゼロ幅スペースという悪夢
ゼロ幅スペースという悪夢ゼロ幅スペースという悪夢
ゼロ幅スペースという悪夢swamp Sawa
 
pythonワンライナーのすゝめ
pythonワンライナーのすゝめpythonワンライナーのすゝめ
pythonワンライナーのすゝめcocu_628496
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented ProgrammingScott Wlaschin
 
Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)
Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)
Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)Hiro H.
 
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法Yoshifumi Kawai
 
ChordアルゴリズムによるDHT入門
ChordアルゴリズムによるDHT入門ChordアルゴリズムによるDHT入門
ChordアルゴリズムによるDHT入門Hiroya Nagao
 
WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装
WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装
WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装tnoho
 
僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達Yusuke Wada
 
逆向工程入門
逆向工程入門逆向工程入門
逆向工程入門耀德 蔡
 
An other world awaits you
An other world awaits youAn other world awaits you
An other world awaits you信之 岩永
 
PHP AST 徹底解説
PHP AST 徹底解説PHP AST 徹底解説
PHP AST 徹底解説do_aki
 
プログラマのための線形代数再入門
プログラマのための線形代数再入門プログラマのための線形代数再入門
プログラマのための線形代数再入門Taketo Sano
 
Java EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jpJava EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jpNorito Agetsuma
 
MarkdownをBacklogのwikiに変換するPWA
MarkdownをBacklogのwikiに変換するPWAMarkdownをBacklogのwikiに変換するPWA
MarkdownをBacklogのwikiに変換するPWAYuhei FUJITA
 
JWTを使った簡易SSOで徐々にシステムをリニューアルしている話
JWTを使った簡易SSOで徐々にシステムをリニューアルしている話JWTを使った簡易SSOで徐々にシステムをリニューアルしている話
JWTを使った簡易SSOで徐々にシステムをリニューアルしている話Kazuyoshi Tsuchiya
 
最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)Akihiko Matuura
 
Sigreturn Oriented Programming
Sigreturn Oriented ProgrammingSigreturn Oriented Programming
Sigreturn Oriented ProgrammingAngel Boy
 
C/C++とWebAssemblyを利用したライブラリ開発
C/C++とWebAssemblyを利用したライブラリ開発C/C++とWebAssemblyを利用したライブラリ開発
C/C++とWebAssemblyを利用したライブラリ開発祐司 伊藤
 

Tendances (20)

ゼロ幅スペースという悪夢
ゼロ幅スペースという悪夢ゼロ幅スペースという悪夢
ゼロ幅スペースという悪夢
 
pythonワンライナーのすゝめ
pythonワンライナーのすゝめpythonワンライナーのすゝめ
pythonワンライナーのすゝめ
 
Railway Oriented Programming
Railway Oriented ProgrammingRailway Oriented Programming
Railway Oriented Programming
 
Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)
Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)
Linuxにて複数のコマンドを並列実行(同時実行数の制限付き)
 
Docker Tokyo
Docker TokyoDocker Tokyo
Docker Tokyo
 
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
ZeroFormatterに見るC#で最速のシリアライザを作成する100億の方法
 
ChordアルゴリズムによるDHT入門
ChordアルゴリズムによるDHT入門ChordアルゴリズムによるDHT入門
ChordアルゴリズムによるDHT入門
 
WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装
WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装
WebRTCを利用した遠隔リアルタイム映像処理フレームワークの実装
 
詳説WebAssembly
詳説WebAssembly詳説WebAssembly
詳説WebAssembly
 
僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達僕がつくった 70個のうちの48個のWebサービス達
僕がつくった 70個のうちの48個のWebサービス達
 
逆向工程入門
逆向工程入門逆向工程入門
逆向工程入門
 
An other world awaits you
An other world awaits youAn other world awaits you
An other world awaits you
 
PHP AST 徹底解説
PHP AST 徹底解説PHP AST 徹底解説
PHP AST 徹底解説
 
プログラマのための線形代数再入門
プログラマのための線形代数再入門プログラマのための線形代数再入門
プログラマのための線形代数再入門
 
Java EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jpJava EE パフォーマンスTips #glassfish_jp
Java EE パフォーマンスTips #glassfish_jp
 
MarkdownをBacklogのwikiに変換するPWA
MarkdownをBacklogのwikiに変換するPWAMarkdownをBacklogのwikiに変換するPWA
MarkdownをBacklogのwikiに変換するPWA
 
JWTを使った簡易SSOで徐々にシステムをリニューアルしている話
JWTを使った簡易SSOで徐々にシステムをリニューアルしている話JWTを使った簡易SSOで徐々にシステムをリニューアルしている話
JWTを使った簡易SSOで徐々にシステムをリニューアルしている話
 
最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)最新C++事情 C++14-C++20 (2018年10月)
最新C++事情 C++14-C++20 (2018年10月)
 
Sigreturn Oriented Programming
Sigreturn Oriented ProgrammingSigreturn Oriented Programming
Sigreturn Oriented Programming
 
C/C++とWebAssemblyを利用したライブラリ開発
C/C++とWebAssemblyを利用したライブラリ開発C/C++とWebAssemblyを利用したライブラリ開発
C/C++とWebAssemblyを利用したライブラリ開発
 

Plus de Tomoya Kawanishi

ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例Tomoya Kawanishi
 
エンジニア転職のノウハウ
エンジニア転職のノウハウエンジニア転職のノウハウ
エンジニア転職のノウハウTomoya Kawanishi
 
Ruby の文字列について
Ruby の文字列についてRuby の文字列について
Ruby の文字列についてTomoya Kawanishi
 
Ruby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてRuby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてTomoya Kawanishi
 
Ruby初心者からよく質問されること
Ruby初心者からよく質問されることRuby初心者からよく質問されること
Ruby初心者からよく質問されることTomoya Kawanishi
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler についてTomoya Kawanishi
 
Ruby の正規表現について
Ruby の正規表現についてRuby の正規表現について
Ruby の正規表現についてTomoya Kawanishi
 
Ruby での外部コマンドの実行について
Ruby での外部コマンドの実行についてRuby での外部コマンドの実行について
Ruby での外部コマンドの実行についてTomoya Kawanishi
 
Ruby のワンライナーについて
Ruby のワンライナーについてRuby のワンライナーについて
Ruby のワンライナーについてTomoya Kawanishi
 
AWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことAWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことTomoya Kawanishi
 
PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選Tomoya Kawanishi
 
HTTPと Webクローリングについて
HTTPと WebクローリングについてHTTPと Webクローリングについて
HTTPと WebクローリングについてTomoya Kawanishi
 
Active record query interface
Active record query interfaceActive record query interface
Active record query interfaceTomoya Kawanishi
 
Active Support のコア拡張機能について
Active Support のコア拡張機能についてActive Support のコア拡張機能について
Active Support のコア拡張機能についてTomoya Kawanishi
 
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーTomoya Kawanishi
 
RubyのDir、File、IO について
RubyのDir、File、IO についてRubyのDir、File、IO について
RubyのDir、File、IO についてTomoya Kawanishi
 
Thread の利用事例紹介
Thread の利用事例紹介Thread の利用事例紹介
Thread の利用事例紹介Tomoya Kawanishi
 
Ruby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてRuby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてTomoya Kawanishi
 

Plus de Tomoya Kawanishi (20)

英単語の覚え方
英単語の覚え方英単語の覚え方
英単語の覚え方
 
ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例ENECHANGE社での Scout APM 利用事例
ENECHANGE社での Scout APM 利用事例
 
エンジニア転職のノウハウ
エンジニア転職のノウハウエンジニア転職のノウハウ
エンジニア転職のノウハウ
 
Ruby の文字列について
Ruby の文字列についてRuby の文字列について
Ruby の文字列について
 
Ruby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構についてRuby on Rails のキャッシュ機構について
Ruby on Rails のキャッシュ機構について
 
Ruby初心者からよく質問されること
Ruby初心者からよく質問されることRuby初心者からよく質問されること
Ruby初心者からよく質問されること
 
RubyGems と Bundler について
RubyGems と Bundler についてRubyGems と Bundler について
RubyGems と Bundler について
 
Ruby の正規表現について
Ruby の正規表現についてRuby の正規表現について
Ruby の正規表現について
 
Ruby での外部コマンドの実行について
Ruby での外部コマンドの実行についてRuby での外部コマンドの実行について
Ruby での外部コマンドの実行について
 
Ruby のワンライナーについて
Ruby のワンライナーについてRuby のワンライナーについて
Ruby のワンライナーについて
 
AWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったことAWS のコスト管理をちゃんとしたくてやったこと
AWS のコスト管理をちゃんとしたくてやったこと
 
PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選PostgreSQL のイケてるテクニック7選
PostgreSQL のイケてるテクニック7選
 
HTTPと Webクローリングについて
HTTPと WebクローリングについてHTTPと Webクローリングについて
HTTPと Webクローリングについて
 
Rake
RakeRake
Rake
 
Active record query interface
Active record query interfaceActive record query interface
Active record query interface
 
Active Support のコア拡張機能について
Active Support のコア拡張機能についてActive Support のコア拡張機能について
Active Support のコア拡張機能について
 
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナーRuby ビジネス創出展 Ruby初心者向けプログラミングセミナー
Ruby ビジネス創出展 Ruby初心者向けプログラミングセミナー
 
RubyのDir、File、IO について
RubyのDir、File、IO についてRubyのDir、File、IO について
RubyのDir、File、IO について
 
Thread の利用事例紹介
Thread の利用事例紹介Thread の利用事例紹介
Thread の利用事例紹介
 
Ruby の制御構造とリテラルについて
Ruby の制御構造とリテラルについてRuby の制御構造とリテラルについて
Ruby の制御構造とリテラルについて
 

Dernier

SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものですiPride Co., Ltd.
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdftaisei2219
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A surveyToru Tamaki
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNetToru Tamaki
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...Toru Tamaki
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Yuma Ohgami
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Danieldanielhu54
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムsugiuralab
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略Ryo Sasaki
 

Dernier (9)

SOPを理解する 2024/04/19 の勉強会で発表されたものです
SOPを理解する       2024/04/19 の勉強会で発表されたものですSOPを理解する       2024/04/19 の勉強会で発表されたものです
SOPを理解する 2024/04/19 の勉強会で発表されたものです
 
TSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdfTSAL operation mechanism and circuit diagram.pdf
TSAL operation mechanism and circuit diagram.pdf
 
論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey論文紹介:Semantic segmentation using Vision Transformers: A survey
論文紹介:Semantic segmentation using Vision Transformers: A survey
 
論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet論文紹介:Automated Classification of Model Errors on ImageNet
論文紹介:Automated Classification of Model Errors on ImageNet
 
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
論文紹介:Content-Aware Token Sharing for Efficient Semantic Segmentation With Vis...
 
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
Open Source UN-Conference 2024 Kawagoe - 独自OS「DaisyOS GB」の紹介
 
Postman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By DanielPostman LT Fukuoka_Quick Prototype_By Daniel
Postman LT Fukuoka_Quick Prototype_By Daniel
 
スマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システムスマートフォンを用いた新生児あやし動作の教示システム
スマートフォンを用いた新生児あやし動作の教示システム
 
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
[DevOpsDays Tokyo 2024] 〜デジタルとアナログのはざまに〜 スマートビルディング爆速開発を支える 自動化テスト戦略
 

Enumerable lazy について

  • 1. Enumerable#lazyについて 2012/11/10 cuzic
  • 2. Ruby 2.0.0 について 1 Ruby の生誕20周年にリリース予定 2013年2月24日 公式リリース予定 新機能 実はそれほど多くない 100%互換を目指している 大きいのは、パラメータ引数 と Module#prepend くらい? Enumerable#lazy Ruby 2.0.0 の新機能の1つ 遅延評価っぽい配列演算を扱うことができるライブラリ Esoteric Lang や Ruby ISO 界隈で有名な @yhara さんの作 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 3. Enumerable#lazy 2 Enumerable#lazy を使うと、"遅延評価" される ふつうにやると、無限ループになっちゃってハマる Enumerable#lazy があれば、必要な分を1個ずつ処理する。 無限個のリストでも問題なく処理できる。 ■ 1,000 より大きな素数を 10個 出力する ■ 1,000 より大きな素数を 10個 出力する 無限ループに陥る 最初の 10個だけが処理される require 'prime' require 'prime' infinity = 1.0/0 infinity = 1.0/0 (1000..infinity).select do |i| (1000..infinity).lazy.select do |i| i.prime? i.prime? end. # ここで、無限ループに陥る end.take(10). # 最初の10個だけを評価する take(10).each do |i| each do |i| puts i puts i end end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 4. Enumerable#lazy のベンリなところ 3 【Enumerable#lazy を使わなければ】 【Enumerable#lazy なら】 (1) map などの処理ごとに配列を生成する (1) map などの処理ごとに、 ・大量の配列生成をともなうので、 Enumerator::Lazy オブジェクトを生成 メモリ効率が悪い ・すべての要素を配列にしたりしない (2) 最終的に使われなくても、生成する ・大量のメモリを消費したりしない ・もったいない (2) 次に使う分だけ生成する ・下の例だと、10個目より後は不要 ・エコ (3) 無限ループに陥る ・下の例だと、10個目より後は何もしない ・無限個の要素を処理しようとするので (3) 無限を無限のまま扱える 無限ループになっちゃう。 ・宇宙ヤバい ■ 1,000 より大きな素数を 10個 ■ ".rb" ファイルを 10個 列挙 require 'prime' require 'find' infinity = 1.0/0 Find.find("/").lazy.select do |f| (1000..infinity).lazy.select do |i| File.file?(f) && f.end_with?(".rb") i.prime? end.take(10).each do |f| end.take(10). # 最初の10個だけを評価する puts f each do |i| end puts i end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 5. Enumeratable#lazy の内部動作(1) 4 Enumerable#lazy は Enumerator::Lazy オブジェクトを返す Enumerator::Lazy は Enumerator のサブクラス Enumerator : 外部イテレータを実現するためのクラス 内部では Fiber を使って実装されている Enumerator::Lazy は、Enumerable モジュールのメソッドを再定義 map 、select、take などのメソッドの返り値は、Enumerator::Lazy オブジェクト ※ Enumerable モジュールで増えるメソッドは Enumerable#lazy だけ。 ここで、Enumerator::Lazy オブジェクトを生成。 infinity = 1.0/0 (1000..infinity).lazy. 1個ずつ処理する。 select do |i| i.prime? select や take などの Enumerable モジュールのメソッド end. は、Enumerator::Lazy クラスでは take(10).each do |i| puts i 1個ずつ処理する Enumerator::Lazy オブジェクト end を返すように再定義されている。 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 6. Enumerable#lazy の内部動作(2) 5 Enumerable#lazy は最初の1個だけがんばって処理。残りは後回し。 2個目以降については、後工程で必要になって初めて、前工程の処理を行う。 inf = 1.0/0 (1000..inf).lazy.select do |i| 【lazy Fiber】 (1000..inf).lazy i.prime? 値を順に yield する Enumerable::Lazy オブジェクトを生成 end.take(10). # 最初の10個だけ評価 次の値を yield して、 lazy Fiber は中断する。 each do |i| puts i 【select Fiber】 select {|i| i.prime?} end 素数である場合だけ yield する E::Lazy オブジェクト 素数となる値が来るまで、親 Fiber に値を要求する。 素数があれば、その値を yield して select Fiber は中断 【take Fiber】 take(10) 最初の 10個であれば yield する E::Lazy オブジェクト 親 Fiber から得られた値を 10個目までは 子Fiber に そのまま yield し、take Fiber は処理を中断する。 【each Fiber】 each {|i| puts i } 親Fiber から受け取った値をそのまま 表示する。 ループごとに親 Fiber に次の値を要求する。 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 7. Enumerator の復習 6 Enumerator ■ Object#enum_for による Enumerator の生成 Enumerable なオブジェクト enum = "abc".enum_for(:each_byte) enum.each do |byte| を簡単に作れる puts byte ~Ruby 1.8.6 の標準添付ライブラリ end Ruby 1.8.7 の組込ライブラリ ■ Enumerator.new による Enumerator の生成 Enumerable::Enumerator Ruby 1.8.8~ の組込ライブラリ str = "abc" enum = Enumerator.new(str, :each_byte) Enumerator enum.each do |byte| Ruby 1.9 で Generator の機能を puts byte 取り込み強化される end ■ Ruby 1.8 の添付ライブラリ Generator ■ Enumerator.new (Ruby 1.9 feature ) g = Generator.new do |yielder| enum = Enumerator.new do |yielder| "abc".each_byte do |byte| "abc".each_byte do |byte| yielder.yield byte yielder << byte end end end end # callcc だから遅い # Fiber を使っているから速い g.each do |byte| enum.each do |byte| puts byte puts byte end end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 8. Enumerator と Fiber 7 Enumerator は Fiber を活用するデザインパターンの1つ Fiber における難しい概念を隠ぺい Fiber.yield => Enumerator::Yielder#<< 、 Enumerator#each による列挙 Enumerator を使いこなせれば、十分 Fiber のメリットを活用できる 《参考: Fiber にできて、Enumerator でできないこと》 親 Fiber から 子Fiber へのデータの送付(Fiber.yield の返り値の利用) ■ Enumerator の例(フィボナッチ数) ■ 《参考》 Fiberの例(フィボナッチ数) fib = Enumerator.new do |yielder| fib = Fiber.new do a = b = 1 a = b = 1 loop do loop do Fiber.yield a yielder << a a, b = b, a + b a, b = b, a + b end end end end def fib.each fib.each do |i| loop do これが必要。 Enumerator の break if i > 1000 内部実装まで yield fib.resume だけど、これが end puts i 考えなくていい end 難しい。(汗) end fib.each do |i| break if i > 1000 puts i end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 9. Enumerator はベンリ 8 Enumerator なら Enumerable の便利メソッドを使える。 ブロック付メソッド呼び出しは単純なイテレーションならいいけど。。。 Enumerator はオブジェクトなので、使い回しが簡単。 引数にしたり、返り値にしたり、インスタンス変数に格納したり Enumerator オブジェクトの生成も慣れれば簡単。 ■ Enumerator の例(フィボナッチ数) ■ 《参考》 ブロック付メソッド呼び出しの例 fib = Enumerator.new do |yielder| def fibonacci a = b = 1 a = b = 1 loop do loop do yielder << a 慣れれば、 yield a a, b = b, a + b カンタン a, b = b, a + b end end end end fib.each do |i| fibonacci do |i| break if i > 1000 break if i > 1000 puts i 単純なイテレーション end puts i ならいいけど・・・。 end fib.each_cons(2) do |i, j| Enumerable break if i > 1000 prev = nil の便利メソッド fibonacci do |i| puts "#{i} #{j}" を使い放題 end break if i > 1000 面倒 ! ! puts "#{prev} #{i}" if prev prev = i end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 10. (おまけ) Enumerator の利用例 データ構造に対する巡回方法を Enumerator で抽象化可能 1 2 3 1 4 7 マトリックスに対して、 行 => 列 の順で列挙するか 4 5 6 2 5 8 列 => 行 の順で列挙するか 7 8 9 3 6 9 / ① /bin /usr /etc /home ② ③ ④ ⑤ 木構造に対して、 /usr/local ⑥ /usr/lib ⑦ /home/cuzic ⑧ 幅優先探索を行うか 深さ優先探索を行うか / ① /bin /usr /etc /home ② ③ ⑥ ⑦ /usr/local /usr/lib /home/cuzic ④ ⑤ ⑧ 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 11. Enumerable#lazy の実装 10 Enumerable#lazy を使うと、Enumerable のメソッドの返り値が Enumerator::Lazy オブジェクトになる Enumerable モジュールに増やすメソッドは Enumerable#lazy だけ module Enumerable def select(&block) def lazy Lazy.new(self){|yielder, val| Enumerator::Lazy.new(self) if block.call(val) end yielder << val end end } class Enumerator end class Lazy < Enumerator def initialize(obj, &block) def take(n) super(){|yielder| taken = 0 begin Lazy.new(self){|yielder, val| obj.each{|x| if block if taken < n block.call(yielder, x) yielder << val else taken += 1 yielder << x else end raise StopIteration } end rescue StopIteration } end end } …… end end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 12. Enumerable#lazy の活用例 11 Enumerable#lazy を上手に活用すると生成、フィルタを順に 処理するプログラムをメソッドチェーンで簡潔に記述できる。 require 'forwardable' def map &block @lazy = @lazy.map &block class FizzBuzz self extend Forwardable end def_delegators :@lazy, :each def fizzbuzz map do |i| def self.each &blk (i % 15 == 0) ? "FizzBuzz" : i fb = self.new end fb.fizzbuzz.fizz.buzz. end take(30).each(&blk) def buzz end map do |i| (i % 5 == 0) ? "Buzz" : i def initialize end inf = 1.0/0 end @lazy = (1..inf).lazy def fizz end map do |i| (i % 3 == 0) ? "Fizz" : i def take n end @lazy = @lazy.take n end self end end FizzBuzz.each do |i| puts i end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 13. (おまけ)パイプライン処理 12 Enumerator を活用すると、UNIX のパイプのように処理を書ける 下記の例では本当にパイプ(|)で処理する例 ついカッとなってやった。今は反省している。 ふつうはメソッドチェーンで十分実用的 フィルタ処理をオブジェクトにしたいときはこういう方法もベンリかも。 Enumerator.new の書き方に精通していないと、分かりにくい。。。 Enumerable#map とか使って、書けたら分かりやすいのにな。。。 # Enumerable#lazy は無関係だけど。。。 class Pipeline <Enumerator attr_accessor :source def main def |(other) fb = FizzBuzz.new(15, "FizzBuzz") other.source = self buzz = FizzBuzz.new( 5, "Buzz") other fizz = FizzBuzz.new( 3, "Fizz") end end inf = 1.0/0 sink = Pipeline.new(1..inf) class FizzBuzz <Pipeline pipe = sink | fb | buzz | fizz def initialize n, subst pipe.take(30).each{|i| puts i} super() do |y| @source.each do |i| end y << (i % n == 0) ? subst : i end if $0 == __FILE__ end main end end end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 14. (おまけ) もっと lazy に 13 クロージャを活用すれば、もっと評価タイミングを遅延できる 値ではなく、クロージャを要素とし、必要なときクロージャを評価する クロージャなら、memoize も簡単に実装可能 同じ計算を2回以上しないようにできる。 # 下記の例も Enumerable#lazy と関係ない。。。 # memoize しない場合。同じ計算を何度もする。 # memoize版。同じ計算は1度だけ。圧倒的に速い。 # すごい遅い。 def add a, b memo = nil def add a, b lambda do lambda do return memo if memo return a.() + b.() memo = a.() + b.() end return memo end end end fib = Enumerator.new do |yielder| a = b = lambda{ 1 } fib = Enumerator.new do |yielder| loop do a = b = lambda{ 1 } yielder << a loop do a, b = b, add(a, b) yielder << a end a, b = b, add(a, b) end end end fib.take(36).each do |i| puts i.() fib.take(36).each do |i| end puts i.() end 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」
  • 15. まとめ 14 Enumerable#lazy @yhara さんの作品 無限を無限のまま扱える 次の1個だけ処理して、残りは後回し 全部一気に処理するより、必要なメモリ量が少なくて済む Enumerator Enumerator::Lazy の内部実装で大活躍 Enumerator は Fiber より分かりやすく、実用的。 Fiber でやりたいことの大部分は Enumerator で十分可能 発展的な使い方 メソッドチェーンでパイプライン処理とか クロージャを使ってより遅延評価させたり。 2012/11/10 Scala/Clojure/Ruby勉強会 「Enumerable#lazy について」