Rubyでメソッドの名前を使用して文字列からメソッドを呼び出す


156

ここで彼らが話していることをどのように行うことができますか?

オブジェクトに対して関数をどのように実行しますか?そして、どのようにグローバル機能を実行しますか(言及された投稿のjetxeeの回答を参照してください)?

コード例:

event_name = "load"

def load()
  puts "load() function was executed."
end

def row_changed()
  puts "row_changed() function was executed."
end 

#something here to see that event_name = "load" and run load()

更新: グローバルメソッドにどのように到達しますか?または私のグローバル機能?

私はこの追加の行を試しました

puts methods

リストされていない場合は、loadおよびrow_change。

回答:


231

オブジェクトの関数を直接呼び出すには

a = [2, 2, 3]
a.send("length")
# or
a.public_send("length")

期待通り3を返します

またはモジュール関数の場合

FileUtils.send('pwd')
# or
FileUtils.public_send(:pwd)

ローカルで定義されたメソッド

def load()
    puts "load() function was executed."
end

send('load')
# or
public_send('load')

ドキュメンテーション:


3
+1効果があります。これは馬鹿げたフォローアップかもしれませんが、なぜC:\ ruby​​ \ lib \ ruby​​ \ 1.8 \ fileutils.rbのRubyソースでsendという単語が見つからないのですか?そこに送信機能があると思いました。
BuddyJoe

1
私はそれが内部で何をしているのか知りたいと思った。
BuddyJoe 2009

それはオブジェクトで定義されています-ruby-doc.org/core/classes/Object.html#M000332利息の値としてランダムな関数を選びました。
Colin Gravill

1
興味深いのは、答えを2回読んで完全に理解する前に、FileUtils.send( "load")を実行して関数を実行したからです。「グローバル」で関数を作成してこれを正しく理解した場合、ルートオブジェクトにメソッドを追加していますか?か否か?
BuddyJoe 2009

12
ソースを検索してくれてありがとう!:)
Colin Gravill

34

3つの方法:send/ call/ eval-およびそのベンチマーク

典型的な呼び出し(参照用):

s= "hi man"
s.length #=> 6

使用する send

s.send(:length) #=> 6

使用する call

method_object = s.method(:length) 
p method_object.call #=> 6

使用する eval

eval "s.length" #=> 6

 

ベンチマーク

require "benchmark" 
test = "hi man" 
m = test.method(:length) 
n = 100000 
Benchmark.bmbm {|x| 
  x.report("call") { n.times { m.call } } 
  x.report("send") { n.times { test.send(:length) } } 
  x.report("eval") { n.times { eval "test.length" } } 
} 

...ご覧のとおり、メソッドオブジェクトのインスタンス化は、メソッドを呼び出す最も速い動的な方法です。また、evalの使用がいかに遅いかに注意してください。

#######################################
#####   The results
#######################################
#Rehearsal ----------------------------------------
#call   0.050000   0.020000   0.070000 (  0.077915)
#send   0.080000   0.000000   0.080000 (  0.086071)
#eval   0.360000   0.040000   0.400000 (  0.405647)
#------------------------------- total: 0.550000sec

#          user     system      total        real
#call   0.050000   0.020000   0.070000 (  0.072041)
#send   0.070000   0.000000   0.070000 (  0.077674)
#eval   0.370000   0.020000   0.390000 (  0.399442)

クレジットは、3つのメソッドについてもう少し詳しく説明し、メソッドが存在するかどうかを確認する方法も示すこのブログ投稿に移動します。


私はこれを見つけていたところ、カバーされていないことに気づきました。やりたかったらどうしますClass.send("classVariable") = 5か?エラーが発生します。それを回避する方法はありますか?同じことが使用に当てはまりますClass.method("classVariable").call() = 5
thesecretmaster

send呼び出しで引数を送信する場合は、このClassName.send( "method_name"、arg1、arg2)などを使用します
Aleem

真の時間を正確に表すためcallに、メソッドオブジェクト(m.test.method(:length))をインスタンス化することをベンチマークに含めるべきではありませんか?使用callする場合、メソッドオブジェクトを毎回インスタンス化する可能性があります。
ジョシュア・ピンター

ブログ投稿のリンクは無効です。
Joshua Pinter 2017

33

これを使って:

> a = "my_string"
> meth = a.method("size")
> meth.call() # call the size method
=> 9

シンプルでしょ?

グローバルについては、Rubyの方法はmethodsメソッドを使用して検索することだと思います。


meth = a.method?これはすでに関数を呼び出していませんか?メソッドが何かを返す場合はどうなりますか?
user1735921 2015年

メソッドが存在しない場合の"my_string".method('blah') #=> NameError: undefined method 参考までに:クラスの問題String'
Joshua Pinter

3

個人的には、関数参照にハッシュを設定し、その文字列をハッシュのインデックスとして使用します。次に、そのパラメーターを使用して関数参照を呼び出します。これには、呼び出したくないものを間違った文字列で呼び出せないという利点があります。もう1つの方法は、基本的evalに文字列を使用することです。こんなことしないで。

PSは、何かにリンクするのではなく、怠惰ではなく、実際に質問全体を入力してください。


ごめんなさい。一部の表現をコピーして翻訳し、Ruby固有にします。+1
バディジョー2009
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.