トップ 最新 追記

ohai日誌

2003|12|
2004|01|02|03|04|05|06|07|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|06|08|10|11|
2008|01|02|03|04|05|07|09|
2009|01|02|
2013|06|12|
2014|01|02|03|04|06|09|10|12|
2015|04|
2016|09|
2018|02|

2006年08月03日

_ Rubyの深淵

いままでためこんだRubyのevalだのリフレクションだのの 知識をまとめようかなーと思っているんで すがいまいち気力がわきません。

そこでここに適当に垂れ流していこうと思います。

最初はブロックの話でも書きます。

_ 手続きオブジェクトとブロック

手続きオブジェクト(Procクラスのインスタンス)の持つ機能は いわゆるクロージャ、無名関数、lambdaと呼ばれるようなものです。 で、ブロックはこれのシンタックスシュガーと言えます。 細かいことを言うと単なるシンタックスシュガーではない点がある のですが、それは過去との互換性のためのようです。

Rubyの特徴の一つであるブロックの優れた点は見た目が優れている 所です。つまり手続き型系統の言語に見た目にうまくなじむという点です。

ブロックの一般的な使いかたは <URL:http://i.loveruby.net/w/RubyIteratorPattern.html> を見てください。

ブロックと手続きオブジェクトの相互変換

まずは手続きオブジェクトをブロックに変換します。 これはブロックとして渡したいオブジェクトを & で修飾して メソッド引数の最後に置けばよいです。つまり、

block = proc{ … }
foo.bar(1, 2, &block)

とします。 逆にメソッドに渡されたブロックを手続きオブジェクトに変換することもできます。 これは、メソッド定義の仮引数の最後の変数に&を付ければよいのです。 つまり、

def foo(a, b, &block)
  :
end

とした場合、

a.foo(1,3){|x| x+1}

とすると、変数blockにproc{|x| x+1}が渡されます。 ブロックを付けずにメソッドを呼びだした場合blockにはnilが代入されます。

これらの機能は、様々な面で役にたちます。特にメタ機能的なものを作るときに おおいに役に立つのですが、ここではもっと簡単な例を挙げます。

まずは、ブロックをほかのメソッドに転送する場合です。 Companyクラスは@employeesインスタンスを持っていて、Companyに each_employeeというメソッドを持たせたいとします。 このときは、

class Company
  def initialize
    @employees = …
  end

  def each_employee(&block)
    @employees.each(&block)
  end
end

とします。

次に、無限リストを作りましょう。

class Pair
  attr_reader :car

  def initialize(car, &cdrblock)
    @car = car
    @cdrblock = cdrblock
    @cdr = nil
    @cdr_initialized = false
  end

  def cdr
    unless @cdr_initialized
      @cdr = @cdrblock.call
      @cdr_initialized = true
    end
    return @cdr
  end

  def take(n)
    ary = []
    list = self
    n.times{ break if list == :term; ary << list.car; list = list.cdr }
    return ary
  end
end

ついでにzip_withも作ります。

def zip_with(xs, ys, &block)
  return :term if xs == :term || ys == :term
  Pair.new( block[xs.car, ys.car]){zip_with(xs.cdr, ys.cdr, &block)}
end

すると、フィボナッチ数列は以下のようになります。

fib = Pair.new(1){
  Pair.new(1){
    zip_with(fib, fib.cdr){|x,y| x+y}
  }
}

p fib.take(10)

こんなこともできます。

def cons(car, &cdr)
  Pair.new(car, &cdr)
end

fib2 = cons(1){ cons(1){ zip_with(fib2, fib2.cdr){|x,y| x+y}}}

このような芸当は yield だけでは無理でしょう。 また、ブロックの一つの効用として、遅延評価ができるというのもある わけです。

次回はsendとMethodクラスの話でも書きます。


2006年08月04日

_ __send__ と Methodクラス

今回は __send__ と Method クラスについてです。 senf、__send__はメソッド名を表す文字列もしくはシンボルで、任意のメソッドを 起動するメソッドです。 sendは例えばメールクラスを作ったときなどに使いたい名前でしょうから、 基本的には __send__ を使ったほうが良いでしょう。

"foo,bar,baz".__send__(:split, /,/) # => ["foo", "bar", "baz"]

などとして利用します。 このメソッドは「メソッド」もしくは「メソッド名」を抽象するために使えます。 利用法は多岐に渡りますが、ここでは例えばSymbol#to_procなどが挙げられるでしょう。

class Symbol
  def to_proc
    proc{|*args| reciever = args.shift; reciever.__send__(*args)}
  end
end

以前この日記で書いたSymbol#[]というかbindも 一つの例として挙げられます。 他にも例えばdrubyなどで使われます。他の利用方として、 次回にはdefine_methodと組み合わせる例も出す予定です。

またこれと関連するものにMethodクラスがあります。 これはC#のdelegateみたいなものです。メソッドとレシーバの組を持つ オブジェクトで、callメソッドでそのメソッドの呼出しができます。

m = "foo, bar, baz".method(:split)
m.call(/,/) # => ["foo", " bar", " baz"]
m.call(/ /) # => ["foo,", "bar,", "baz"]

とこのように利用できます。 手続きオブジェクトのほうが汎用的に利用できるため、こちらはあまり利用されません。 ただ、環境を保存しなくてよい分だけ手続きオブジェクトより生成に必要なコストは 小さいという利点があります。 また、to_procメソッドを持っているため、

filenames.all?{|filename| valid?(filename)}

filenames.all?(&method(:valid?))

などと書くこともできます。

他にもUnboundMethodというのがあるのですが、正直使ったことがありません。 メソッドを「名前」ではなく「実体」で持ち運びたいときに利用するのでしょう。

次回はdefine_method、Rubyの世界を大きく広げてくれるこのメソッドについて 書こうかと思います。

本日のツッコミ(全2件) [ツッコミを入れる]

_ babie [面白いです。勉強会でやってみませんか?]

_ ohai [じゃあそのうちにでも]


2006年08月06日

_ Module#define_method

Rubyでは通常メソッドは def を使って定義します。しかしそれ以外にもメソッドを クラスに追加する方法があります。それがdefine_methodです。 このメソッドは与えられたブロック(やMethod,Proc,UnboundMethodのインスタンス) をメソッドとして登録します。例えば以下のようにします。

define_method(:foo){|x,y| 2*x*y}

すると

foo(2,6) # => 24

というように使えます。

このメソッドを使う場合には、スコープに注意すべきです。

  • ブロックの中では、基本的に定義したクラスのインスタンスのコンテキストになる。
    • レシーバを省略したメソッド呼びだし
    • インスタンス変数
    • self
    • etc
  • ただし、ブロックの中から外のローカル変数が見える。

というようになっています。逆にこれがこのメソッドの利点でもあります。 define_methodを使うことで、以下のようなことができます。

  • メソッド名を動的に決めることができる
  • 外側のローカル変数を読み書きできる

1番目はevalを使うことで同じことができますが、2番目はdefine_methodで なければできません。

まずは、attr_accessorのようなものを作ってみましょう。 デフォルト値をあらかじめ指定できるようにしてみましょう。

class Module
  def attr_accsssor_default(name, val)
    iv_name = "@#{name}"
    attr_writer(name)
    define_method(name) do
      if instance_variables.include?(iv_name)
        instance_variable_get(iv_name)
      else
        val
      end
    end
  end
end

class A
  attr_accsssor_default :foo, "foo"
end

a = A.new
p a.instance_variables # => []
p a.foo # => "foo"
p a.instance_variables # => []
a.foo = 3 
p a.instance_variables # => ["@foo"]
p a.foo # => 3

ここでローカル変数(引数)valを読んでいます。instance_variables, instance_variable_get/setは名前そのままの機能です。

次はこれの変形版、インスタンス変数を使わないようにします。

class Module
  def attr_accsssor_default_(name, val)
    define_method("#{name}=") do |new_val|
      val = new_val
    end
    define_method(name) do
      val
    end
  end
end

class A
  attr_accsssor_default_ :bar, Math::E
end

a = A.new

p a.bar # => 2.71828182845905
a.bar = Math::PI 
p a.bar # => 3.14159265358979

ここではvalを読み書き両方しています。

__send__と組合せることでForwardableもどきを作ります。

module Forwardable_
  def def_delegator(accessor, *methods)
    methods.each do |m|
      define_method(m) do |*args|
        instance_variable_get(accessor).__send__(m, *args)
      end
    end
  end
end

class B
  extend Forwardable_
  def_delegator :@baz, :foo, :bar

  def initialize(baz)
    @baz = baz
  end
end

オブジェクトの特異クラスに対するdefine_method、define_singleton_methodを作ります。

class Object
  def define_singleton_method(name, &block)
    (class << self
       self
     end).module_eval{ define_method(name, &block) }
  end
end

特異クラス構文の中でselfが特異クラスオブジェクトになることを利用します。 また、define_methodはprivate methodなのでmodule_evalでそのあたりをを ごまかします。 このようにブロックをたらいまわしにすることでクラスに直接触れずに メソッドを定義することもできます。あまり濫用するとプログラムがわかり にくくなりそうなテクニックですね。

次回はまあ情報取得の類でもしましょう。

_ プログラミング言語について

プログラミング言語について議論するとき○○ができる/できないと いう議論はそれほど意味がありませんよね。簡単にできるか/否か、 わかりやすく書けるか/書けないかが問題になるべきですよね。


2006年08月08日

_ 変数/定数

今回は変数/定数関連です。

まずはインスタンス変数関連から

  • Object#instance_variables

    オブジェクトのインスタンス変数名を「文字列の配列で」返します。

  • Object#instance_variable_get
  • Object#instance_variable_set

    名前そのままです。privateメソッドではないのでクラスの外からでも 触り放題です。基本的にそういうことはやらないほうが良いのですが。

使用例は前回のattr_accessor_defaultなど。 「インスタンス変数名」をパラメータとして扱いたいときなどに使います。

  • Object#remove_instance_variable

    こういうものもあります。

次はクラス変数。おおよそインスタンス変数と同様。クラスオブジェクトは Classクラスのインスタンスで、ClassクラスはModuleクラスのサブクラスで あることに注意しましょう。

  • Module#class_variables
  • Module#class_variable_get
  • Module#class_variable_set

クラス変数をそもそもあまり使いませんし、使い道は少ないと思います。 インスタンス変数とあわせるために一通りあるのでしょう。

ローカル変数とグローバル変数には、

  • Kernel#local_variables
  • Kernel#global_variables

があります。irbやdebuggerくらいしか使い道はないでしょう。 Binding.of_callerみたいな代物と組合せればなにかが見えてくるのかも しれません。よくわかりませんけど。

定数は、同じようなものとして

  • Module.constants
  • Module#constants

上は呼び出したコンテキストにおける定数を、下はそのモジュールに属する 定数のリストを文字列の配列得ます。やはり使い道はあまりありません。 Module#constantsはRRBで使っていますがこれも特殊用途でしょう。 Module.constantsはさらに使い道が限られてきます。

定数関連はインスタンス変数と似たメソッドもあります。

  • Module#const_get
  • Module#const_set
  • Module#const_defined?
  • Module#remove_const

Module#remove_constを以下のように使うことで、警告を発さずに 定数を変更することができます。

module Bar
  Foo = 2
  remove_const(:Foo)
  Foo = 3
end

トップレベル定数はObjectクラスに定義されているので

class Object
  remove_const(:Foo)
end

などとなります。こんなことをしては定数の意味がなくなるので 基本的にはやってはいけません。ただこれにより既存のライブラリを 乗っ取るようなことができます。例えば後方互換性がないライブラリ を後方互換性があるように見せ掛けたりする等に使えます。

さらに邪悪なこととして、同じ名前を持つが実体が異なるクラスなどと いったものも作れます。

まあこのあたりバージョンが上がると変わりそうな話ではあります。

次回は、instance_evalかhook関連かの話をしようと思います。

_ Object/Module/Class/Kernel

この手のメソッドはおよそ Object/Module/Class/Kernel のいずれかに 属します。Rubyでより妙なことをしたければリファレンスの このあたりを良く読むと良いでしょう。

_ *_defined?

const_defined?はあってもinstance_variable_defined?はありませんね。


2006年08月12日

_ instance_eval, module_eval

ここでは ブロックを与えた場合の instance_eval と module_eval の挙動について話をします。直接の応用例という話は やりにくいのですが、ここで話をするコンテキストの概念を 理解することでより高度なRubyメタプログラミングが可能になります。

参考文献として 青木さんの Ruby Hacking guideの14章コンテキストの所を挙げておきます。この話は Ruby の実装を直接 見たほうがわかりやすい人もいると思います。

コンテキストとは

まず、コンテキストとは何かを簡単に説明しましょう。 プログラムではローカル変数/インスタンス変数/定数は同じ識別子のものが 複数ある可能性があります。ローカル変数はメソッド呼びだしごとに 必要なメモリ領域が用意されます。つまりプログラムの字面上は同じ ローカル変数の参照/代入も実は異なるものを取り扱っているという事態が発生 します。またインスタンス変数はインスタンスごとにそのメモリ領域が確保 されます。定数はクラス(オブジェクト)ごとなのでまあインスタンス変数と 似ていますね。 このように、プログラムのある部分でローカル変数/インスタンス変数/定数 を参照した場合、それは実際には「どの」変数なのかを解決する必要があります。 これを解決するための情報がコンテキストです。また、似たこととして、 「self」と書いた場合、それは実際にはどのオブジェクトを指すのか、 レシーバを省略したメソッド呼びだしは実際にはどのオブジェクトのメソッドを 呼びだすのか、といった問題があります。 また、あるメソッドが終了した際にどこに戻ればよいのか、という のもプログラムに直接書かれているわけでばありませんので、これを 解決する情報が必要です。これらもコンテキストに含めます。

つまり、コンテキストというのは、プログラム上における 「暗黙の情報」をあらわしていると言えます。

Rubyには

  • メソッド呼びだしコンテキスト
  • ローカル変数コンテキスト
  • インスタンスコンテキスト
  • クラスコンテキスト

などがあります。 これは、上の文献の

  • ruby_frame
  • ruby_scope, ruby_dyna_vars
  • rb_evalの第一引数
  • ruby_class

に対応しています。 で、ここで解説するのは、インスタンスコンテキストとクラスコンテキスト の2つについてです。ちなみにこれらの用語は私が勝手に作ったものです。

インスタンスコンテキスト

これは、結局は、 「self」というキーワードがどのオブジェクトを 指しているのかという情報です。 これと関連して、

  • インスタンス変数を参照した場合どのオブジェクトのインスタンス変数が 参照されるか
  • レシーバを省略したメソッド呼出しは実際にはどのオブジェクトのメソッドを 呼びだすのか

もこれによって解決されます。

クラスコンテキスト

これは、以下の2つの問題を解決するための情報です。

  • 定数の実体を探す
  • def foo(x, y); ..... end と書いた場合にどのクラス/モジュールにこのメソッドが 定義されるかという問題

Rubyの文法要素をコンテキストの概念で説明する

これらの概念を使うことで、Rubyの文法をすっきりと説明することができます。

クラス定義(class Foo .... )の場合、

  • ローカル変数コンテキストは新たに作られる
  • インスタンスコンテキストは新しいクラスのクラスオブジェクトを指す
  • クラスコンテキストは新しいクラスのクラスオブジェクトを指す

となります。クラスコンテキストは新しいクラスのクラスオブジェクトを指す ことによって、クラスにメソッドを定義できるようになるわけです。 モジュール定義の場合も同様です。

メソッド呼び出しの場合は、

  • ローカル変数コンテキストは新たに作られる
  • インスタンスコンテキストはレシーバを指す
  • クラスコンテキストはそのメソッドの属するクラスを指す

となります。つまり、

class A
  def foo
    p "foo"
    def bar
      p "bar"
    end
  end
end

とした場合、まず、クラスAにはfooが定義され、barはどこにも定義されません。 つまり、A.new.barとすると undefined method … というエラーがでます。 ここで、

A.new.foo

とすると、"foo"と表示されるわけですが、この結果、def bar .. end という文も実行されます。ここで クラスコンテキストはそのメソッドの属するクラス、つまりAを指しているわけですから、 結果Aにbarというメソッドが定義され、A.new.barとすると"bar"が表示されます。

module_eval, instance_eval

そして、この2つのメソッドはつまりブロック内の 外側と内側でインスタンスコンテキストとクラスコンテキストを切り替えるため に存在するというわけです。

Module#module_evalは インスタンスコンテキストとクラスコンテキストを両方 レシーバオブジェクトに切り替えます。

Object#instance_evalは インスタンスコンテキストをレシーバオブジェクトに、 クラスコンテキストをレシーバオブジェクトの特異クラスオブジェクトに、 それぞれ切り替えます。

つまり、

class B
end

としたとき、

B.module_eval do
  def foo; p "foo"; end
end

とすると、fooはBのインスタンスメソッドになり。

B.instance_eval do
  def bar; p "bar"; end
end

とすると、fooはBのクラスメソッドになります。

簡単な応用例

instance_eval の簡単な応用として、プライベートメソッドを外から呼びだす ことができます。

a.instance_eval{ foo(1, x, y) }

などと書けば、fooがプライベートメソッドでも呼びだすことができます。

まとめ

このように、コンテキストを意識することで、Rubyのこのあたりの謎の挙動を 理解することが可能になるわけです。

次回は *_eval で文字列を与える場合の話の予定です。

本日のツッコミ(全1件) [ツッコミを入れる]

_ とむとむ [コンテキスト難しいですね。 module_evalはインスタンスメソッドで instance_evalはクラスメソッ..]


2006年08月13日

_ PS2の某RPG

始めから終わりまで100時間かかるそうです…。 今のComputer RPGは確実に衰退に近づいている、とか思ってしまいました。


2006年08月14日

_ 和歌山

現在実家にいます。 しかし京都とはすごしやすさが違いますね。 夕方以降は圧倒的に涼しいです。

_ 三角形言語

Lisp, ML, Haskell といった言語の見た目の異様さを 説明する言葉です。 もとは吉田君が言っていた話なんですが。

これを克服しなければが関数型言語の普及は覚束無いんでしょうね。

そういう言語を作りたいなあ、とここ一年くらい思っていて、 いろいろ思考実験もしているんですが難しいですね。


2006年08月20日

_ クラス階層関連

今回は特異クラスとクラスの継承関係についてです。 予告と内容が違いますがご容赦を。

参考文献はRHGの第4章。 今回の話もRubyのソースコードを見たほうがわかりやすいかもしれません。

クラスと特異クラス

Rubyのすべてのオブジェクトは何らかのクラスのインスタンスです。

class A < Object
  def foo
    puts "foo"
  end
end
a = A.new

とすると、aが指すオブジェクトはクラスAのインスンタンスです。 つまりRubyの任意のオブジェクトはそれ自身が属するクラスの 情報を持っていなければなりません。これを取りだすのが Object#classです。

さて、Rubyには特異メソッドというある1つのオブジェクトのみが 利用できるメソッドがあります。

def a.bar
  puts "bar"
end

とすると、aが指すオブジェクト固有のメソッドが定義されます。 しかし、Rubyではメソッドはクラス/モジュールに対し定義 されるはずのものです。つまりaが指すオブジェクト固有の クラスが必要になります。これが特異クラスです。 実際の実装としては、特異メソッド定義時に Aをスーパークラスとする新しいクラスを作成して aのクラスをその新しく作ったクラスに変更します。

しかし、こうしてもa.classはAを返します。 Object#classは特異クラスをスキップするようになっているからです。 Rubyの思想として特異クラスはあまり使うべきでないと考えられていて、 特異クラスはわざと使いにくくなっています。 ただそうはいっても何らかアクセス方法がないと不便な場合があります。 そのため、特異クラス構文と呼ばれるものが(裏道的なものとして) あります。

class << a
  :
end

とすることでaが指すオブジェクトの特異クラスに直接触れることができます。 よって、

class Object
  def singleton_class
    class << self
      self
    end
  end
end

とObject#singleton_classを定義すれば、特異クラスを直接取りだすこと もできるようになります。

includeとextend

Module#includeとObject#extendの実装は実はおよそ以下の通りになっています。 リファレンスマニュアルのModule#append_featuresの項も参考にしてください。

class Module
  def include(*modules)
    raise if modules.any?{|mod| mod.instance_of?(Module)}
    modules.reverse_each |mod|
      mod.append_features(self)
      mod.included(self)
    end
  end

  def append_features(class_or_mod)
    include_module(class_or_mod)
  end

  def included(class_or_mod)
  end

  def extend_object(object)
    include_module(object.singleton_method)
  end

  def extended(object)
  end

  # includeの本体、rb_include_module
  # 実際にはこのメソッドはRubyから直接触れることはできない
  def include_module(class_or_mod)
    class_or_modの継承チェインにselfとselfにincludeされている
    モジュールをを追加する
    ただしすでにincludeされているものは無視される
  end
end

class Object
  def extend(*modules)
    raise if modules.any?{|mod| mod.instance_of?(Module)}
    modules.reverse_each |mod|
      mod.extend_object(self)
      mod.extended(self)
    end
  end
end

このようになっているため、あるモジュールのappend_featuresや extend_objectをオーバーライドすることで、includeやextendの操作を 根本的に変更してしまうことができます。例えば、

module A
  def self.append_features(class_or_mod)
  end
end

とすれば、このモジュールはincludeしても何も起きないモジュール になります。

module A
  def self.append_features(class_or_mod)
    raise "module A cannot be included"
  end
end

とするとincludeした瞬間に例外が発生するようになります。 これによって「includeできないモジュール」を作ることができます。

また、includedやextendexのほうをオーバーライドすることで、 includeの動作に+αの機能を加えることができます。 includeの動作を根本的に変更したい場合はあまりないでしょうから 通常はこちらを使うことが多いでしょう。

一例として、Singletonモジュールの簡易的な実装を見てみましょう。 マルチスレッドの問題やエラーチェック等はとりあえず無視します。

module Singleton
  # Singletonオブジェクトがcloneやdupで複製できるのはおかしいので
  # 定義を上書きする。
  def clone
    raise "can't clone"
  end
  def dup
    raise "can't dup"
  end

  # includeしたときに呼びだされる。
  # klassはインクルードするクラス
  def self.included(klass)
    class <<klass
      # newを外から呼びだせなくする
      private :new

      # Singletonオブジェクトを保管しておく変数
      _instance = nil
      # klass.instanceを定義する
      define_method(:instance) do
        # 最初にこのメソッドが呼ばれたときにはnewを呼びだして
        # インスタンスを生成する。二回目以降は生成したオブジェクトを
        # そのまま返す。
        _instance ||= new
      end
    end
  end
end

とりあえずこれだけです。

class A
  include Singleton
end

とすると、 A.new でエラー(NoMethodError: private method `new' called for A:Class) が出、A.instanceでインスタンスが得られます。 A.instanceは何度呼んでも同じオブジェクトが得られます。これは 実際にirbなどで試してください。

また、別の例として、モジュールをincludeしたときにクラスメソッドも 定義されるようにする方法を示します。 Rubyではクラスメソッドを継承するために継承時にクラスの特異クラス間に 特別な継承関係を作っています(詳しくは上の参考資料を見てください)。 しかしモジュールに関してはそのような特殊なことはしていません。 そのためincludeではクラスメソッドは継承されません。そこで なんらかの細工をする必要があります。 一つの方法として、 定義したいクラスメソッドを別モジュールに定義して includeしたときにそれをextendするというやりかたがあります。 これはRailsで使われている方法です。 おおよそ以下の通りにすれば良いです。

module A
  def foo
    p "foo"
  end

  def self.included(klass)
    klass.extend(A_ClassMethods)
  end

  module A_ClassMethods
    def bar
      p "bar"
    end
  end
end

これで、

class B
  include A
end

とすると

B.new.foo
B.bar

などとできます。

注意として、通同ではじmoduleを2回includeしても2度目以降は何も起きないのですが、 includedやappend_featuresをオーバーロードした場合これが成立しなくなります。

以上はモジュールをincludeする話でしたが、その他にも情報収集のためのメソッド として Module#ancestors, Module#included_modules, Module#include?などが あります。ancestorsは継承チェインを配列で得る機能です。ごちゃごちゃと includeしたり継承したりしたクラスをデバッグしたりするときに役にたつでしょうか。 あとのメソッドもおおよそ名前の通りです。 リファレンスのclass Moduleを見てください。


トップ 最新 追記