プロキシオブジェクトの例で関数型プログラミングを理解

スポンサーリンク
スポンサーリンク
ライフスタイル関連のコンテンツ
お金 | 仕事 | 勉強 | プライベート | 健康 |
プログラミング関連のコンテンツ
C言語/C++入門 | Ruby入門 | Python入門 | プログラミング全般

=追記(2010/07/23)
Rubyベストプラクティスのひとり読書。理解が難しかった部分を中心にまとめています。
書籍から引用しすぎと思われたページ、理解したページなどを削除して、当初より公開ページを減らしました。すいません。
読書のまとめ記事は難しいと感じたので、今後はブログで読書のまとめは書かないでおきます。差し障りがあれば現存するページも削除致しますので予めご了承下さい。
=追記ここまで

スポンサーリンク

String#lines
文字列を行の配列を生成するEnumeratorに変換します。

プロキシオブジェクトの実装

p137からのコードを理解するために、コードにコメントを多めに入れてみました。

# Lazy::Promiseの実装、コメント多めでコード説明
# Ruby1.9で動作
 
module NaiveLazy
  class Promise < BasicObject
    
    def initialize(&computation)
      @computation = computation
    end
 
    def __result__
      if @computation
        @result       = @computation.call
        @computation  = nil
      end
      
      @result # &computationブロックを実行した結果を返す
    end
 
    def inspect
      if @computation
        "#<NaiveLazy::Promise computation=#{ @computation.inspect }>"
      else
        @result.inspect
      end
    end
 
    def respond_to?(message)
      message = message.to_sym
      [:__result__, :inspect].include?(message) ||
        # __result__メソッドにより返されたオブジェクト@resultがmessageメソッドを持っているか調査
        __result__.respond_to? message
    end
 
    def method_missing(*a, &b)
      # promise.some_function を promise.__result__.some_function と解釈
      __result__.__send__(*a, &b)
    end
 
  end
end
 
# >>cell.width
# => #<Lazy::~
# >>cell.width + 10   #=> cellオブジェクトには+メソッドがないのでmethod_missingでフック
# => 114
 
# { 3 }のブロックが&computationに入る
num = NaiveLazy::Promise.new { 3 }
# +メソッドがmethod_missingでフックされる。num.+(100) → 3.+(100)と解釈
puts num + 100
puts num
# __result__ が 3(Fixnumオブジェクト) を返しtrue
puts num.respond_to?(:times)
# classメソッドがmethod_missingでフックされる。num.class → 3.class と解釈
puts num.class

Ruby1.9で実行
遅延評価
Rubyのコードブロックは遅延評価
lazy.rb

mapのナイーブな実装例

可変状態を最小限にして副作用を減らす

ステートフルとは以前の状態を保持することをいう。
ステートレスとは以前の状態を保持しないことをいう。
ステートフル arr << elem 破壊的 (可変状態、状態保持) ステートレス arr + [elem] 非破壊的(不変状態、状態未保持)=> 再帰処理でステートレスなコードになる

往々にしてRubyでは再帰は反復より実行速度が遅くなる。
Rubyのメソッドディスパッチのコストが高いため。
injectメソッドで反復を使ったステートレスなmapを作れる。

p143~p145で以下三種類のmap実装のメソッドによるベンチマークが比較されている。
反復+ステートレスじゃない => 1番速い
再帰+ステートレス => 最も遅い
反復+ステートレス => 2番目に速い

反復+ステートレスは、要素を配列に追加するたびに新しいオブジェクトを作る必要があるために遅い。
副作用を避けるということは、可変状態(破壊的)を完全に避けることとは違う。

オブジェクトを別のオブジェクトに変換するときに副作用を避ける単純な方法は、新しいオブジェクトを作って、元のオブジェクトをイテレートしながら、必要な状態変換を実行して、新しいオブジェクトにその状態を入れること
Array#+ のように、操作のたびに新しいオブジェクトを作ることにより、Rubyでステートレスなコードを書ける
再帰はパフォーマンス犠牲の恐れ
オブジェクトをたくさん作りすぎてもパフォーマンス犠牲の恐れ

モジュールによるコード整理

module_function だとプライベート関数が使えない。
extend self にするとOK。

スポンサーリンク
 
スポンサーリンク