Hatena::Grouprubyist

Going My Ruby Way このページをアンテナに追加 RSSフィード

Ruby ロゴ (C) Ruby Association LLC

2011年07月20日(水)

Proc のアクセサ

| 22:26 |  Proc のアクセサ - Going My Ruby Way を含むブックマーク はてなブックマーク -  Proc のアクセサ - Going My Ruby Way  Proc のアクセサ - Going My Ruby Way のブックマークコメント

(2011/07/22 Obsoleted by アクセサ定義 property - Going My Ruby Way - Rubyist)

--------

Proc のインスタンス変数のアクセサを、attr_accessor で定義すると

class C
  attr_accessor :handler
end

c = C.new
c.handler = proc do   # ここ!
  "hello"
end

c.handler.call

のようになり、「ここ!」のとこの '= proc ...' が嫌です。イテレータで渡したい。

c.handler do |...|
  ...
end

ので、proc の setter(兼getter) は以下のように書いてます。

def handler(&block)
  @handler = block || @handler
end

自分的にはパターンになっているのでモジュールにしました(思いつきで)。

module Accessor
  extend self
  def proc_accessor(name)
    module_eval %-  
      def #{name}(&block)
        @#{name} = block || @#{name}
      end
    -
  end
end

テスト。こんな感じで使います。

class C
  extend Accessor
  proc_accessor :handler
end

c = C.new

c.handler do |s|        # ブロックを与えると setter になる
  puts s + "!!"
end

c.handler.call("hello") # ブロックを与えないと getter

高階手続き Filter

| 20:01 |  高階手続き Filter - Going My Ruby Way を含むブックマーク はてなブックマーク -  高階手続き Filter - Going My Ruby Way  高階手続き Filter - Going My Ruby Way のブックマークコメント

Ruby ベストプラクティス』5章で紹介されていたのでメモ。

高階手続きの例として Filter という本の著者使っているパターンが載ってます。

以下のようなもの。

class Filter
  def initialize
    @constraints = []
  end

  def constraint(&block)
    @constraints << block
  end

  def to_proc
    -> e { @constraints.all? {|f| f.(e)} }
  end
end

if __FILE__ == $0
  filter = Filter.new
  filter.constraint {|x| x > 10 }
  filter.constraint {|x| x.even? }
  filter.constraint {|x| x % 3 == 0 }

  p (0..24).select(&filter) #=> [12,18,24]
end

----

参考書籍

memoize

| 18:19 |  memoize - Going My Ruby Way を含むブックマーク はてなブックマーク -  memoize - Going My Ruby Way  memoize - Going My Ruby Way のブックマークコメント

Ruby ベストプラクティス』5章で紹介されていたのでメモ。

James Gray さんが書いたmemoizeのモジュールです。

(公式なものは 本で URI も紹介されてないしググってもわからなかった)

memoizable.rb

module Memoizable
  def memoize(name, cache = Hash.new)
    original = "__unmemoized_#{name}__"

    ([Class, Module].include?(self.class) ? self : self.class).class_eval do
      alias_method original, name
      private      original
      define_method(name) { |*args| cache[args] ||= send(original, *args) }
    end
  end
end

以下の方の日記にも詳しく紹介されています。

-----

以下のようなスクリプトを書くと、サーバにアクセスに行くのは最初の1回だけです。

require 'open-uri'
require 'memoizable'

class C
  extend Memoizable

  def server_text
    open('http://localhost/mysite/hello.txt').read
  end

  memoize :server_text 
end

puts C.new.server_text   # ここだけ、サーバにアクセス
puts C.new.server_text
puts C.new.server_text

C.new.server_text でわかるとおり、キャッシュはクラスで行なわれます。

以下のようにすると、サーバへは 2回アクセスに行きます。

require 'open-uri'
require 'memoizable'

class C
  def server_text
    open('http://localhost/mysite/hello.txt').read
  end
end

c1 = C.new
c2 = C.new

class << c1
  extend Memoizable
  memoize :server_text 
end

puts c1.server_text   # ここで、サーバにアクセス
puts c1.server_text
puts c1.server_text
puts c1.server_text
puts c1.server_text

puts c2.server_text   # ここでも、サーバにアクセス

----

ところで、memoize は「メモイズ」で正解でしょうか。あるいは「メモアイズ」?

memorize(メモライズ)とは違うようです。

----

参考書籍

モジュール の extend self

| 18:06 |  モジュール の extend self - Going My Ruby Way を含むブックマーク はてなブックマーク -  モジュール の extend self - Going My Ruby Way  モジュール の extend self - Going My Ruby Way のブックマークコメント

Ruby ベストプラクティス』5章で紹介されていたのでメモ。

以下のような場合、M.bar が private なので呼び出せずエラーになる。

module M
  module_function
  def foo
    puts bar
  end

  private
  def bar
    "bar"
  end
end

M.foo

extend self した場合は呼び出せる。

module M
  extend self
  def foo
    puts bar
  end

  private
  def bar
    "bar"
  end
end

M.foo

M.bar は private のままである。

古いスクリプトをさぐったら、以下のようにしていた。extend self と同じ効果。

module M
  class << self
    def a
      b
    end

    private
    def b
      "hello"
    end
  end
end

p M.b

extend self の方が 1行少ないし、インデントも浅い。

----

参考書籍

lazy.rb の promise

| 12:00 |  lazy.rb の promise - Going My Ruby Way を含むブックマーク はてなブックマーク -  lazy.rb の promise - Going My Ruby Way  lazy.rb の promise - Going My Ruby Way のブックマークコメント

Ruby ベストプラクティス』5章で紹介されていた、lazy.rb の promise についてのメモです。

lazy.rb は遅延評価を実現します。

以下のようなクラスがあったとき、

class C
  def initialize(seed)
    @seed = seed
  end
  attr_accessor :seed
  attr_writer   :val

  def val
    @val ||= calc
  end

  def calc
    @seed * 10
  end
end

これを実行すると以下のようになります。

@val は C#val の初めての呼び出し時に評価されます。

c = C.new(3)
#puts c.val   # (コメントアウト)
c.seed = 6
puts c.val    # 60 と表示
c.seed = 9
puts c.val    # 60 と表示
c.val = 120
puts c.val    # 120 と表示
c = C.new(3)
puts c.val    # 30 と表示
c.seed = 6
puts c.val    # 30 と表示
c.seed = 9
puts c.val    # 30 と表示
c.val = 120
puts c.val    # 120 と表示

promise を使うと以下のように書き換えられます。

require 'lazy'

class C
  def initialize(seed)
    @seed = seed
    @val  = promise { calc }
  end
  attr_accessor :seed
  attr_accessor :val

  def calc
    @seed * 10
  end
end

実行結果は上と同じです。@val は遅延評価されます。

実際には promise のブロックが遅延評価されてます。こうも書けます。

require 'lazy'

class C
  def initialize(seed)
    @seed = seed
    @val  = promise { @seed * 10 }
  end
  attr_accessor :seed, :val
end

----

参考書籍