Rubyの`method`メソッドを使いこなして楽しむ
ブロックしか受け取らない関数にメソッドを渡したいと思ったことはありませんか?あるいは、呼び出そうとしているメソッドが、オブジェクトのどのスーパークラスで壊されてしまったのかを突き止めたいことは?
こうしたことは、methodメソッドを使えば簡単に実現できます。うまく使いこなせば、依存関係への理解が深まり、何時間もかかるデバッグから解放され、コードをあるべき場所へ導けるようになります。
ラムダのように手軽に扱えるメソッド
Rubyには、ブロックやラムダを受け取るメソッドが数多くあります。しかし、ラムダと同じようには、メソッドを別のメソッドに直接渡すことはできません。まずmethodを使う必要があります。
irb(main):001:0> sin_method = Math.method(:sin)
=> #<Method: Math.sin>
irb(main):002:0> (1..10).map(&sin_method)
=> [0.8414709848078965, 0.9092974268256817, 0.1411200080598672, -0.7568024953079282, -0.9589242746631385, -0.27941549819892586, 0.6569865987187891, 0.9893582466233818, 0.4121184852417566, -0.5440211108893699]
ここで何が起きているのでしょうか?
最初の行では、Math.sinというメソッドをMethodオブジェクトに変換しています。Methodオブジェクトはto_procに応答するため、ラムダを使える場所ならどこでも同じように利用できます。
irb(main):004:0> sin_method = -> (x) { Math.sin(x) }
=> #<Proc:0x007fe9f90a9bd8@(irb):4 (lambda)>
irb(main):005:0> (1..10).map(&sin_method)
=> [0.8414709848078965, 0.9092974268256817, 0.1411200080598672, -0.7568024953079282, -0.9589242746631385, -0.27941549819892586, 0.6569865987187891, 0.9893582466233818, 0.4121184852417566, -0.5440211108893699]
もう一度最初の行を見てください。このラムダを使ったコードは、先ほどのmethodを使ったコードとまったく同じ結果になります。
コードの書き方によっては、ラムダで包むよりもMethodオブジェクトを取得するほうがずっと簡単な場合があります。「ラムダが必要なのに、手元にあるのはメソッドだけ」というときは、methodメソッドを思い出してください。
そのメソッドはどこから来たのか?
オブジェクトのメソッドを呼び出したら、予期しない動作をした——そんな経験はないでしょうか。誰かがオーバーライドしたか、モンキーパッチを当てたのかもしれません。どうやって突き止めればいいのでしょうか?
ソースコードを何時間もかけて掘り起こすこともできますが、Methodには調査を高速化できる2つのメソッドが用意されています。
呼び出しているメソッドがどのクラスで定義されているかを調べるには、ownerを使います。
irb(main):003:0> Task.new.method(:valid?).owner
=> ActiveRecord::Validations
さらに一歩踏み込んで、メソッドが正確にどこで定義されているかを知りたいときは、source_locationを使います。
irb(main):004:0> Task.new.method(:valid?).source_location
=> ["/usr/local/lib/ruby/gems/2.1.0/gems/activerecord-4.2.0.beta2/lib/active_record/validations.rb", 55]
source_locationは配列を返します。最初の要素はメソッドが定義されているファイルへのパス、2番目の要素は行番号です。これがわかれば、次にどこを見ればいいのか正確に把握できます。
ソースコードを読む(掘り起こす必要なし)
メソッドがどこで定義されているかがわかったら、次はそれがどのように定義されているのかを見たくなるでしょう。
Method単体ではそれはできません。しかしmethod_source gemをインストールすれば、多くのメソッドのソースコードをコンソールから直接確認できます。
irb(main):002:0> puts Task.new.method(:valid?).source
def valid?(context = nil)
context ||= (new_record? ? :create : :update)
output = super(context)
errors.empty? && output
end
=> nil
コメントまで見ることだってできます。
irb(main):003:0> puts Task.new.method(:valid?).comment
# Runs all the validations within the specified context. Returns +true+ if
# no errors are found, +false+ otherwise.
#
# Aliased as validate.
#
# If the argument is +false+ (default is +nil+), the context is set to <tt>:create</tt> if
# <tt>new_record?</tt> is +true+, and to <tt>:update</tt> if it is not.
#
# Validations with no <tt>:on</tt> option will run no matter the context. Validations with
# some <tt>:on</tt> option will only run in the specified context.
=> nil
かなり便利ですよね?ドキュメントがコンソールの中にあるようなものです!
イントロスペクションは素晴らしい
私のお気に入りのRubyクラスのいくつかは、コードの中からコード自身を検査できるクラスです。Class、Module、そしてMethodなどがその代表例です。これらを使えば、実行中のコードについて多くのことを学べるだけでなく、その場で動的に変更することも可能になります。これらのクラスをしっかり学び、APIを研究すれば、Rubyで驚くようなことができるようになるでしょう。
-
Rubyのtransposeメソッドで行を列に変換する方法
Rubyでグリッド状のデータ(多次元配列)を扱うときに便利なのが、Arrayクラスのtransposeメソッドです。この記事では、行と列を入れ替える「転置」の基本から、三目並べ(○×ゲーム)のような実践的な活用例までをわかりやすく解説します。 たとえば、3×3の正方形グリッドを多次元配列として持っているとしましょう。ここから「行を列に変換したい」という場面は意外とよくあります。 なぜそんなことが必要になるのでしょうか? 代表例が、古典的なゲームである三目並べです。 盤面をグリッドとして保存し、勝利判定を行うには、行・列・斜めのすべてをチェックしなければなりません。 ところが、グリッドを普通の配
-
メタプログラミングの隠れたコストとは?知っておくべき3つのデメリット
「メタプログラミング」という言葉は、とても高度で洗練された技術のように聞こえますが、果たして本当に良いものなのでしょうか? 結論から言えば、使いどころ次第で非常に有用です。しかし多くの開発者は、メタプログラミングの利用には一定のコストが伴うことを理解していません。 まずは認識を合わせるために… メタプログラミングとは何か? ここでは、メタプログラミングを以下のような手法全般と定義します。 コードの構造そのものを変更する(例:define_method) 文字列を、あたかも実際のRubyコードの一部であるかのように実行する(例:instance_eval) 何らかのイベントへの反応として動作す