プロキシオブジェクトの例で関数型プログラミングを理解
お金 | 仕事 | 勉強 | プライベート | 健康 | 心
プログラミング関連のコンテンツ
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。
- - 関連記事 -
- 無名クラスを継承するRubyの動的機能
- method_missing()とsend()でフック(処理を捕捉)
- obj.instance_eval(&block)とblock.call(obj)の違いを確認
- コードブロックを用いたサーバーとクライアントの例
- メソッド名とシグニチャを統一して美しいAPI設計
- mustメソッドで読みやすいテスト