Create  FrontPage  Index  Search  Changes  RSS  Login

『初めてのRuby』余った切れ端

Error! Please edit this page again.

RD syntax error: line 28:
...ここで、モジュール(({A })) の定数と、モジュール(({B}))の定数はどちらも(({CONST}))という名前だが別個の定数である。メソッドも同じく。つまり、モジュールは名前空間を分割する役割を持っている。 ...
                                   ^^^

Original document

001:   
002:   ((<『初めてのRuby』|LearningRuby>))の素材として書いてはみたものの結局使ってない文章の断片です。
003:   * 納めるべきうまい場所が本の中に見つからなかったもの
004:   * 自説を考えたものの、自分でこれはどうかと思ったもの
005:   * もっと良い説明を思いついたもの
006:   * 締め切り後に思いついたもの
007:   
008:   このページは基本的に余った切れ端の寄せ集めなので、ここだけ読んでも文脈が分かりなかったりして意味が通じない場合もあるでしょう。「もっと良い説明を思いついたので本には採用しなかった」ものもある訳なので、本を買ってね。
009:   
010:   {{hr}}
011:   
012:   == モジュール (8章補稿)
013:   Rubyにおけるモジュールとは、(インスタンス)メソッド、クラスメソッド、定数、クラス変数を束ねた寄せ集めである。こうしたいろいろなものを包んでカテゴリ分けに利用するという意味では「モジュール」という名前は分かりやすいものであると思う。
014:   
015:   === 名前空間の分離
016:    module A
017:      CONST = :A
018:      def instance_method1; end
019:      def self.class_method1; end
020:    end
021:    
022:    module B
023:      CONST = :B
024:      def instance_method1; end
025:      def self.class_method1; end
026:    end
027:   
028:   ここで、モジュール(({A}))の定数と、モジュール(({B}))の定数はどちらも(({CONST}))という名前だが別個の定数である。メソッドも同じく。つまり、モジュールは名前空間を分割する役割を持っている。
029:   
030:   これがどうして嬉しいかというと、名前空間が分割されていることによって名前の衝突を回避できるからである。例えば、対数を計算するメソッドと、アプリケーション内のイベントを記録するメソッドはどちらも(({log}))という名前になりそうである。すると名前が「かぶって」困ったことになる。ところが、前者は(({Math}))モジュールに含まれているから名前が衝突しないで済んでいる。
031:    def log(message)
032:      $stderr.puts message
033:    end
034:    
035:    log("something wrong")  #=> something wrong
036:    Math.log(3)             #=> 1.09861228866811
037:   
038:   モジュールに含まれるクラスメソッド((-実際にはモジュールメソッドとも呼ぶ-))を呼び出すには、このようにモジュール自身をレシーバとして呼び出してやればよい。また、モジュール内の定数を参照するには(({A::CONST}))のようにスコープ解決演算子を付けてやればよい。このあたりは既に触れたクラスの場合と同じである。
039:   
040:   === クラスはモジュール
041:   モジュールとクラスは似ている。というよりも、クラスはモジュールの一種なのである。実際クラスも(インスタンス)メソッド、クラスメソッド、定数、クラス変数を包んで外部と区別する役割を果たす。この性質はモジュールと同じだ。クラスはモジュールの特殊例である。
042:   
043:   どこが特殊かというと、クラスには自身をインスタンス化する能力がある。一般のモジュールにはそれがない。オブジェクト指向におけるクラスとは大まかに言って「新しいオブジェクトの設計図」であろう。Rubyにおけるクラスとは「設計図として機能することを意図して作られているモジュール」なのである。
044:   
045:   === インスタンスメソッド
046:   さて、ここで妙なことに気がついて欲しい。モジュールはインスタンスメソッドを包むことができる。だが、モジュールはクラスとは異なりインスタンス化能力を持たない。だから、モジュールのインスタンスというのは存在しないと思われる。インスタンスがないなら、このインスタンスメソッドは何に使うのか?
047:    module M
048:      CONST = :M
049:      def instance_method_a
050:        p self
051:      end
052:      def self.module_method_b
053:        p self
054:      end
055:    end
056:   言い換えるとこういうことだ。定義できるからにはこの(({instance_method_a}))に使い道があるはずだが、(({instance_method_a}))を呼び出すときのレシーバとは何者か?
057:   
058:   モジュール(({M}))、という回答は外れである。それはモジュールメソッドのほうだ。
059:    M.module_method_b   #=> M
060:   正解は、「他のクラスにトッピングして使う」だ。
061:   
062:   === include
063:   モジュールが束ねているメソッドや定数や諸々を、まとめてクラスに取り込むことができる。これをRubyの用語ではクラスがモジュールを(({include}))するという。取り込むと、モジュールが持っていた定数はそのクラスの定数であるかのように使えるようになる。インスタンスメソッドも同様だ。
064:    class C
065:      include M
066:      def refer_const
067:        p CONST
068:      end
069:    end
070:    c = C.new
071:    c.refer_const          #=> :M
072:    c.instance_method_a    #=> #<C:0x650054>
073:   
074:   つまり、モジュールというのは単に名前空間を分離しておく他に、幾つかの定数やメソッドを束ねてまとめて(({include}))するための単位としても使える。例えば、2章で触れた(({Enumerable}))モジュールは「(({each}))イテレータを持っているクラスにトッピングすると便利なメソッドの一揃い」である。
075:   
076:   なお、こういうモジュールの使い方を((*mix-in*))と呼ぶ。モジュールの中身をクラスに混ぜる、という感じなんだろうか。語源は「アイスクリームに載せるためのクッキー片やナッツ」だそうで、まさにトッピングである。
077:   
078:   === 継承としてのinclude、includeとしての継承
079:   モジュールを(({include}))すると、クラスはモジュールが束ねていた定数やインスタンスメソッドを受け継ぐ。つまり、これは継承である。こうした性質はクラスがクラスを継承するときと同じである。ただし、Rubyは単一継承だから1つのクラスはクラスを1つしか継承できない。一方、モジュールはいくつでも(({include}))できる。
080:   
081:   この意味でもクラスが特殊なモジュールであるという構造が成り立っている
082:   * クラスはモジュールを(({include}))して機能を取り込むことができる
083:   * クラスを(({include}))するのは(({class Hoge < Huga}))のように特別な書き方をする
084:     * これを継承と呼ぶ
085:     * 継承は1つしかできない
086:   
087:   確かに、クラス継承も「機能をトッピング」と呼べないことはない。ただ、クラスがクラスを継承するというのは単に機能を引き継ぐだけではなくてオブジェクト指向設計における色々を含意するからかなり特殊なもの((-単なる機能トッピングとクラス継承の意味論上の違いがはっきり意識されていなかった時代、「差分プログラミング」が喧伝されて破綻したクラス設計に基づく多重継承地獄があったのだよね。Rubyを含む現代のオブジェクト指向言語の多くが単一継承しか許していないのはその反省によるものだ-))だが。
088:   
089:   === モジュール関数
090:   レシーバの情報を利用しないメソッドを関数的メソッドと呼ぶのであった。レシーバを使わないのだから、レシーバが何であっても関係ない。例えば(({Math::cos}))がそうだ。
091:   
092:   レシーバが何であっても関係ないから、こういうメソッドは名前空間内で使ってもいいし、「混ぜ込んで」から使っても同じである。
093:    Math.cos 3.14    #=> -0.99999873172754          (1)
094:   
095:    class Point
096:      include Math
097:      def initialize(arg, theta)
098:        @arg = arg; @theta = theta
099:      end
100:      def descartes
101:        return [@arg*cos(@theta), @arg*sin(@theta)]  # (2)
102:      end
103:    end
104:    point = Point.new(1, 3.14)
105:    point.descartes    #=> [-0.99999873172754, 0.00159265291648683]
106:   
107:   ん? またここで奇妙なことに気がついて欲しい。(1)はモジュールに含まれているクラスメソッドだが、(2)で使っているのは(({include}))によって(({Point}))クラスに受け継がれたインスタンスメソッドだ。
108:   
109:   実は、(({Math}))モジュールの(({cos}))メソッドにはインスタンスメソッド版とクラスメソッド版があって、実装は共有している。このようにインスタンスメソッドとしてもクラスメソッドとしても呼べる関数的メソッドを((*モジュール関数*))と呼ぶ。
110:   
111:   モジュール関数を定義するには(({Module#module_function}))メソッドを使う。このメソッドにメソッド名をシンボルまたは文字列で渡すと、そのインスタンスメソッドをモジュール関数にしてくれる。
112:    module M
113:      def sum(a, b)
114:        return a + b
115:      end
116:      module_function :sum
117:    end
118:    
119:    M.sum(1,1)            #=> 2
120:    class Hoge
121:      include M
122:      def print_sum(x,y)
123:        p sum(x,y)
124:      end
125:    end
126:    hoge = Hoge.new
127:    hoge.print_sum(1,1)   #=> 2
128:   
129:   といっても、モジュール関数が大量にある場合は大量に(({module_function}))メソッドの呼び出しを書くのは(たとえメタプログラミングで多少楽をしても)大変だ。そこで、最近はこんな簡略法もある。
130:    module M
131:      def module_function_1; end
132:      def module_function_2; end
133:      def module_function_3; end
134:      def module_function_4; end
135:      def module_function_5; end
136:      def module_function_6; end
137:      def module_function_7; end
138:      def module_function_8; end
139:      def module_function_9; end
140:      
141:      extend self
142:    end
143:   
144:   (({Object#extend}))メソッドは、レシーバの特異クラスに対してモジュールを(({include}))するメソッドであるる従って、レシーバであるオブジェクトだけが引数のモジュールで機能拡張される。
145:   
146:   さて、これは(({module_function}))を使った場合と殆ど同じ((-全く同じではない-))効果を産むのだが、何故だろうか。そのあたりは8章の「特異メソッドと特異クラス」の節を良く読めば分かるはずなので、読者への宿題とする。
147:   
148:   
149:   {{hr}}
150:   
151:   == Rubyの構文 (6章の余り)
152:   これまで、コード例に登場する個々の式の文法規則について細かに説明することはしませんでした。本章ではこの細かい点を掘り下げていきます。
153:   
154:   実際のところ、確かに(({a = 1+1}))みたいな文は読者のみなさんには通じるわけですから構わないですよね。不安な式があった場合は本章を読み直した後にもう一度前の部分を読み返すと良いでしょう。
155:   これがLISP風に
156:   (({(setq! a (+ 1 1))}))だったり(({(let (a (+ 1 1)) ....)}))だったりしたら通じるかどうか分かりませんが、(({a = 1+1}))ならBASICあるいはFORTRAN以来の伝統の力で通じることでしょう。LISP風のパワフルなS式ではなく、敢えて文化的に主流なALGOL風構文を採用した。それでいながらLISPの文化で生まれたいくつかの特長をうまく取り込んだ。これがRubyの長所なのです。
157:   
158:   {{hr}}
159:   
160:   == alias (7章 削除項目)
161:   alias文によってメソッドに「別名」を付けることができます。メソッド本体の定義は共有したままで別の名前でもメソッドを呼び出せるようにします。
162:   
163:   例えば、(({Array#length}))メソッドには(({Array#size}))という別名があります。
164:    [1, 2, 3].size    #=> 3
165:   一般に、メソッド(({foo}))に(({bar}))という別名を付けたい場合は
166:    alias bar foo
167:   と記述します。
168:   
169:   :Column: collectとmap
170:     ((<Rubyist Magazine|URL:http://jp.rubyist.net/magazine/?0014-Hotlinks#f08>))によれば、Rubyistには(({collect}))派と(({map}))派があり両者は血で血を洗う熾烈な抗争をしているということになっています。両者はaliasの関係で、好みが分かれるためです。これまで(({map}))しか紹介しなかったことから分かるように、MATHEMATICAの原体験を受けた筆者は(({map}))派です。一方、
171:   
172:     このように、Rubyのライブラリには多数のエイリアスが含まれています。どの名前を使うかはおおよそ好みの問題です。
173:   
174:   {{hr}}
175:   
176:   == 初期値 (6章 余り)
177:   :Column: 初期値
178:     インスタンス変数とグローバル変数は、代入されたことがなくても参照することができます。ローカル変数・クラス変数・定数は最初に代入されたときに定義され、それ以前に参照しようとすると(({NameError}))を生じるのとは挙動が異なっています。未初期化状態のインスタンス変数やグローバル変数を評価すると、値は(({nil}))になります。
179:   
180:     未初期化状態のインスタンス変数にアクセスするのはRubyプログラムでは普通のことです。たとえば、インスタンス変数の節で触れた(({Counter}))クラスのプログラム例を書き直すと次のようになります。
181:      class Counter
182:        def count
183:          @count ||= 0
184:        end
185:        def increment
186:          count += 1
187:        end
188:      end
189:     
190:     なぜ、挙動が異なっているのでしょうか。ローカル変数なども同様に未初期化状態の参照を許した方が便利ではないでしょうか。しかし、もしそうなっていたとしたらタイプミスによるエラーの発見が困難になることでしょう。変数を参照しようとしたときにタイプミスしたとして、それを新しい未初期化の変数への参照だと解釈されたら困るのではないでしょうか。このような場合にはいち早く例外で知らせてくれた方が親切です。
191:     
192:     では、逆にインスタンス変数はなぜそうなっていないのでしょうか。筆者の考えによれば、ローカル変数などに比べてスコープが広く定数などにくらべて利用頻度が高いからです。メソッドの呼び出し順序はそのメソッドを書いている時点では予測できません。ですので、メソッド内でインスタンス変数を参照する前に確実に初期化しておくには、初期化メソッド(({initialize}))に記しておくしかないでしょう。しかし、すべてのメソッドで用いられるすべての変数を(({initialize}))に並べて初期化するのでしょうか。これは保守性に欠けるやりかたです。変数が増えれば(({inititalize}))の見通しは悪くなります。しかも、8章で触れるようにRubyでは既存のクラスを自由に拡張できます。つまり、どのようなインスタンス変数が利用されるかは、実は(({initialize}))を書いている時点では予測不能なのです。グローバル変数も同様です。これはインスタンス変数よりも更に広範囲のコードからアクセスされます。
193:     
194:     Ruby文法は妥協と折衷、損益判断により構成されています。Ruby文法がなぜAであるかを調べると、いつも「Bにするだけの価値があるか」という点が浮かび上がってきます。変数は基本的に代入による初期化を必須にする方針の一方で、インスタンス変数とグローバル変数についてはアクセス可能なコード範囲が広すぎて初期化を強制するには弊害が大きすぎたのだと考えられます。
195:   
196:   {{hr}}
197:   
198:   == メソッド名 (7章 余り)
199:   メソッド名には「ASCII記号」をのぞく任意の印刷可能文字、またはアンダースコア「(({_}))」を用いることができます。ただし、ASCII数字で開始することはできません。例えば、「(({fooAndBar}))」「(({foo_and_bar}))」「(({このメソッドを実行しないでください}))」などは文法上正当なメソッド名です。一方、「(({foo=bar}))」「(({foo[]}))」「(({1st]}))」「(({@ruby-lang.org}))」などは正当なメソッド名ではありません。
200:   
201:   このような名前のメソッドを定義したり呼び出したりしようとすると構文エラーを生じます。正確に言うと、(({Module#define_method}))のような方法で通常の構文解析を回避すればNUL文字を含まないどんな名前でもメソッド名にできます。けれども、通常の方法では呼び出せないので意味は薄いでしょう。HpricotライブラリがXPath式の表現に用いている(({filter[text()=]})), (({filter[@=]}))メソッドは筆者が知る唯一の有意義な例です。
202:   
203:   また、メソッド名に非ASCII文字((-例えば日本語文字-))を用いる際には注意が必要です。Ruby処理系にソースコードの文字エンコーディングを正しく認識させないと構文エラーを引き起こすことでしょう。詳細は4章を参照してください。
204:   
205:   == リフレクション (8章補稿)
206:   再帰的(reflective)なプログラミング((-リフレクション(refelction)-))はRubyにおいてはごく普通のことです。つまり、熟練したRubyプログラマは、数値や文字列を操作するのと同じように当たり前にクラスやメソッドを操作します。ですから、それを黒魔術と呼ぶには抵抗があります。
207:   
208:   しかしながら、再帰的プログラミングと黒魔術の組み合わせは最高に効果的でもあります。黒魔術との相性の良さのために、ここで並べて紹介したいと思います。
209:   
210:   === send
211:   (({send}))メソッドは、レシーバに任意のメッセージを送るメソッドです。(({method_missing}))をオーバーライドする際には(({send}))が便利です。
212:   
213:    class SimpleDelegator
214:      def initialize(target); @target = target end
215:      def method_missing(message, *args)
216:        target.send(message, *args)
217:      end
218:    end
219:   
220:   (({Object#send}))は、メソッド名を表す文字列またはシンボルを引数に取ります。また、追加でそのメソッドに渡す引数列も付けることができます。つまり、(({send}))を使えば自由なルールで呼び出すべきメソッド名を算出して、呼び出しを行うことができるわけです。
221:   
222:   === クラスの定義
223:   通常の(({class}))式だけでもかなり多くのことができます。これについては1章や8章で既に触れました。(({class}))式は実行時に新しいクラスを定義する式です。代入や算術計算と殆ど代わりのないRubyの式です。そのため、親クラスを状況に応じて定義し分けることも可能ですし、イテレータで制御することすら可能です。
224:   
225:   この他に、(({Class.new}))によって匿名のクラスを作成することができます。
226:    c = Class.new do
227:      def foo; :foo end
228:    end                   #=> #<Class:0x4ac68> 名前のないクラス
229:    obj = c.new           #=> #<#<Class:0x4ac68>:0x3a480> そのインスタンス
230:    obj.foo               #=> :foo
231:   
232:   Rubyにおいてはクラスも(({Class}))クラスに属するオブジェクトに過ぎません。ですから、通常のオブジェクトを操作するようにクラスとそのもとのオブジェクトシステム自体を操作できるのです。((-オブジェクトシステムの語彙によってオブジェクトシステムを操作する、というのがこの類の手法が「再帰的(reflective)」と呼ばれる由縁です。-))
233:   
234:   === メソッドの定義
235:   クラスと同様に、メソッド定義にも対応する再帰的APIが存在します。それが(({Module#define_method}))です。
236:    class Foo
237:      define_method(:bar) do
238:        p :bar
239:      end
240:    end
241:    foo = Foo.new
242:    foo.bar        #=> :bar
243:   (({define_method}))はブロックを受け取ると、そのブロックをメソッド本体として新しいメソッドを定義します。この他に、(({Proc}))オブジェクトや(({UnboundMethod}))オブジェクトを引数として渡し、メソッド本体として利用させることもできます。
244:   
245:   それにしても、この例は(({def}))式と大差ないのであまり面白くありませんね。そこで、こんなことをしてみましょう。
246:    def make_criable(target, message)
247:      (class << target; self end).send(:define_method, :cry) do
248:        puts "Flow my tears! #{message}"
249:      end
250:    end
251:   
252:    message = "the Policeman said"
253:    the_man = Object.new
254:    make_criable(the_man, message)
255:    the_man.cry            #=> Flow my tears! the Policeman said
256:   
257:   (({make_criable}))は、新しい特異メソッドを与えるメソッドです。(({the_man}))は、(({make_criable}))によって(({cry}))メソッドを獲得しました。
258:   
259:   ここで、(({define_method}))の存在意義が発揮されています。(({cry}))メソッドのメソッド本体を作成するに当たり、外側の(({message}))引数を用いているのです。これは普通の(({def}))式ではできない芸当です。
260:   
261:   こうして、「コンテキストを保持するメソッド」を作成できます。7章で触れたように、通常のメソッドはクロージャーではありません。一方、ブロックや(({Proc}))オブジェクトは作成されたときのコンテキストを保持しています。これらはクロージャーです。そして、(({define_method}))メソッドにブロックを渡せば、ブロックが参照しているの外部コンテキストは保持したまま、それをメソッドに変換できます。
262:   
263:   ですから、もしもコンテキストが保持している(({message}))オブジェクトが変更されれば(({cry}))メソッドの挙動も変化するはずです。
264:   
265:    message['Policeman'] = 'Yapoo'
266:    the_man.cry            #=> Flow my tears! the Yapoo said
267:   
268:   :Column: 特異クラスの取り出し
269:     (({class << target; self end}))は、オブジェクト(({target}))の特異クラスを取得するためのイディオムです。
270:     
271:     実は、オブジェクトから対応する特異クラスを取り出すというメソッドは提供されていないのですね。特異クラス周辺の黒魔術的テクニックをむやみに乱用することがないようにという戒めです。けれどもこうした抜け道はあって、筆者のような「黒魔術」愛好家の間では広く知られています。「好ましくないプログラミングスタイルは難しくなるように。ただし、望むならば不可能ではないように」というRuby設計上の原則から導かれています。
Last modified:2008/07/02 06:10:30
Keyword(s):
References:
This page is frozen.