本記事はRuby Advent Calendar jp: 2011の参加記事です。いえーい。本日は五日め……?
スタートの12/1はこのAdvent Calendarの主催者にして中学生Rubyコミッターことそらはー(@sora_h)のCod.note - Coffeescripting with Ruby、12/2はudzura (Uchio Kondo)さんのblog.udzura.jp - almost-sinatra.rb の深淵を覗くでした。
3日めと4日めは……?
12/3のRubySpec の走らせ方と、12/4のRuby の力じゃ!いまこそ Capybara を使え!!!も公開されてました。
本記事中の「Rubyなメソッドについて」 訂正があります。no6vさんありがたうございました。あとでたぶん補足記事書きます。
凡例
基本的にはRubyは1.9.3のirb
で、Pythonは2.7.2でipython
を使って実行してます(python
の対話環境とは出力が若干異ります)。掲載するコードの先頭にプロンプト(>>> など)は付けず、強調したい式の返り値(函数・メソッド呼び出し)に#=> を付けて表示します。
コードの見ためを比較してみよう
# ruby def foo() return "foooo" end foo() #=> "foooo"
# python def foo(): return "foooo" foo() #=> "foooo"
うん、そっくり。
ここはRubyとPythonで似たやうに書けるポイント…なのだけれど、RubyとPythonではコードの意味がまったく異る。つまり、Rubyではメソッドを、Pythonでは函数を宣言してゐる。Rubyのメソッドを函数のやうに使ふことはできるが、メソッドとは函数の別名ではないし、Pythonのメソッドは、これとは別にある。
また、メソッドと函数との差とは別に、foo
とだけ書いたときにRubyとPythonの挙動の違ひが顕れる。
# ruby foo #=> "foooo"
# python foo #=> <function __main__.foo>
これはRubyのメソッド呼び出しとPythonの函数呼び出しの記法の差異である。
Rubyのメソッドはfoo
と書いてもfoo()
と書いてもメソッドを呼び出せる。
一方で、Pythonでは<function __main__.foo>と表示されてしまってゐる。この表示はPythonのデバッグ用出力で、この函数を呼び出すにはfoo()
と書くしかない。
なんだかそれでもRubyではdef
を多用してるひとが多い気がするな……?
メソッドと函数は同じ…?
さて、とりあへず函数とメソッドは同じやうなものだと假定して、こんな問題を考へてみる。str_to_func
といふ函数/メソッドを作ってみよう。この函数/メソッドは文字列型の_str
と、コールバック函数(相当の)_func
といふ二つの引数をとる。そして、_func
を_str
で適用した値を返す。
C言語で書くとこんな感じ…?
#include <stdio.h> #include <stdlib.h> int str_to_func(const char *_str, int (*_cb) (const char *)){ return _cb(_str); } int myint(const char *_str){ return atoi(_str); } int main(void){ int n = 0; n = str_to_func("1234", myint); printf("%d\n", n); return 0; }
さて、これをPythonで書くとこんな感じになる。
# python def str_to_func(_str, _cb): return _cb(_str) def myint(obj): return int(obj) str_to_func("1234", myint) #=> 1234
うむ、これは自然に書ける。さて、これをRubyで書くとどうなるか。
# ruby def str_to_func(str,cb) cb.call(str) end def myint(obj) obj.to_i end str_to_int("1234",method(:myint)) #=> 1234
Cよりは短いけどPythonよりは素直じゃない気がするぞ……?
Rubyなメソッド
この章の内容には不正確な点、あるいは限られた状況下のみで成立する内容になってゐます。ご注意の上でお読みください。
初めに言った通り、Rubyにおけるdef
キーワードは、メソッドを定義する。メソッドであるからには、それは何らかのオブジェクトに属してゐるはずである。
# ruby def foo "#{self}: foooo" end def print_self p foo end class A; end a = A.new print_self() A.print_self() a.print_self()
このコードはirb
での実行時のみ、この記事の記述通りに動作します。コードをファイルに保存して実行すると、14行めでNoMethodErrorが発生して、動作が停止します。あるいはend
とclass A; end
の間の行にpublic :foo, :print_self
と追記することで動作させることもできます。
このコードはいままで紹介してきたコードと違ってdef hoge()
の形式ではないが、Rubyでは假引数がない場合は()
を省くことができる。上までのPythonと見ためを近しくするために付けてゐたが、以下は付けないこととする。ただ、假引数をとらない場合において()
のあるなしで意味が変化することはない。
self
は変数であるが、これはメソッドのオブジェクト自身を指す。pはメソッドで、値(オブジェクト)のデバッグ用出力を行ふ。このスクリプトを実行した結果、次のやうな出力を期待する。
"main: foooo" "A: foooo" "#<A:0x00000102017888>: foooo"
この記事の目的は構文の差からRubyとPythonの差を見出すことなので(それが根幹に位置する問題でありながら)、オブジェクト指向についての説明は省くことにする*1
このコードから確認できることを簡潔にまとめると、トップレベル(class
やmodule
で階層構造になってゐない箇所)でメソッドを定義すると、便宜上main
オブジェクトのメソッドとして定義される。次に、トップレベルのメソッドは他のクラスのクラスの特異メソッドとしても、インスタンスメソッドとしても定義されてゐる。どうしてこんな現象が起こるのかは……長くなるのでRubyソースコード完全解説参照。具体的には第4章 クラスとモジュールをどうぞ。
トップレベルのメソッドが継承される、までは合ってゐるのですが、ruby
で実行する際にはトップレベルで定義されたメソッドはObject
クラスのprivate
メソッドとなります。
ではPythonでは…?
Rubyのdef
は函数ではなくメソッドを作ると説明した。メソッドとはオブジェクト指向あってこその概念である。ところでPythonもRubyと同様にオブジェクト指向機能を持ってゐる。では、Pythonにはメソッドはあるのか?
――ある!
# python class A(object): def foo(self): return "foooo" a = A() a.foo() #=> "foooo" a.foo #=> <bound method A.foo of <__main__.A object at 0x10176c050>>
Pythonにおけるメソッドはクラスの中で定義された函数のことで、必ずレシーバを第一引数にとる。レシーバとは、この場合はインスタンスのことである。
# python class X(object): def myself(self): return self def bar(self, _str): return "bar: {str}".format(str=_str) x = X() x.bar("hogehoge") x2 = x.myself() x == x2 #=> True
Rubyのdef
がメソッドしか定義できないのに対して、Pythonは函数もメソッドも同様に定義することができる。Pythonのメソッドは定義するときには、self
のぶん、引数を一つ多く書く必要がある。
Rubyに慣れてゐるとself
がよけいなぶんでPythonがめんどくさく見えるかもしれないが、僕がどちらが決定的に優れてゐるとは思はない。一長一短がある。
そしてもうひとつRubyとPython
# python def x(): def y(): return "yyyyy" return y x() #=> ???
先の「メソッドと函数は同じ…?」の動きから考へれば、このx()
の出力がどうなるのか想像がつくだろうか。できれば対話環境(python, ipython)で実際に動かしてみてほしい。
続いてRuby。
# ruby def x def y return "yyyyy" end return method(:y) end x() #=> ???
さてさて、この結果はどうなるか。これもどうなるか想像してから対話環境(irb)で動かしてみてほしい。
補足しておくと、Rubyのメソッドはオブジェクトではない。しかし、Methodクラスといふものを作ることはできる。
# ruby def hoge: "hogehoge" end h1 = hoge #=> "hogehoge" # メソッドではなく、メソッド呼び出しの結果(返り値)が代入される h2 = method(:hoge) #=> #<Method: Object#hoge> # Method オブジェクトが代入される h2.call #=> "hogehoge" # Method オブジェクトのcallメソッドを呼び出すことで、元のメソッドが呼び出される
……と、こんなことをつらつら書いてるうちに12/5が終ってしまふので、こんなところでおひらきにしたいと思ひます。
これを機に
みたいなところを考へてもらって、いろいろ調べて知っていただけたら幸せに思ひます。
この文章を読んでそんな意欲が残ったひとは、最後に紹介したmethod
メソッドとMethod
クラスが本当は一体何者なのかを考へてもらふと、きっとおもしろいんじゃないかと。
関連記事
さて、次のRuby Advent Calendarは…?
Ruby Advent Calendar jp: 2011の6日めは、Asakusa.rbの@sakuroさんです。おそらくこちらの/var/log/messagesに掲載されるのではないかと。それではよろしくお願ひします。
*1:ほんとはくどい説明を書きかけてたのだけれど、あまりに本筋から外れるのでオミットしました