2009-07-01 8 views
2

以前は、特定の条件 "Ruby a clever way to execute a function on a condition"でメソッドを実行する巧妙な方法について質問しました。ハッシュ値であるメソッドをどのように呼び出すのですか?

解決策と応答時間は素晴らしかったが、実装時にlambdasのハッシュを持っていると非常に醜い。だから私は実験を始めました。

次のコードは動作します:

def a() 
    puts "hello world" 
end 

some_hash = { 0 => a() } 

some_hash[0] 

しかし、私はそれが動作を停止し、クラスでこれをラップする場合:

class A 

    @a = { 0 => a()} 

    def a() 
    puts "hello world" 
    end 


    def b() 
    @a[0] 
    end 

end 

d = A.new() 

d.b() 

それは動作を停止しなければならない理由を私は見ることができない、誰がどのように提案することができますそれを動作させるには?

答えて

8

そのコードは機能しません。ハッシュに追加された時点でaを実行し、ハッシュから取得した時点では実行しません(irbで試してみてください)。

クラスに定義されたa方法がないので、それは(あなたが最終的にインスタンス上でメソッドaを定義したクラスでは動作しません。

代わり
{0 => lambda { puts "hello world" }} 

のようなラムダを使用して、実際に試してみてください

+0

でそれを呼び出す必要がありますI避けようとしていた。私がラムダに入れるコードはちょっと混乱するかもしれません。そして、私はハッシュに約15要素を持っています。それはコードをちょっと混乱させます。 – Peter

+0

@Peter:コードはメソッド内にあるよりもラムダで扱いにくいですか? – Chuck

+1

次に、メソッド名をシンボルとして渡し、sendまたはSymbol#to_procを使用します。 –

1

あなたのクラスの最初の行は、まだ存在しないメソッドを呼び出します。クラス全体がロードされた後でさえ、クラスメソッドの呼び出しであり、インスタンスメソッドしか定義していないので、それは存在しません。

また、{0 => a()}はメソッドa()を呼び出し、メソッドa()への参照を作成しません。後で評価されない関数をそこに入れたいのであれば、何らかのラムダを使わなければならないでしょう。

+0

ああ、私はそれを実現しませんでした。さて、私がやろうとしていたことを達成するために、とにかくありますか? – Peter

+0

あなたはこのような何かを行うことができます: @a = {0 =>ラムダ{A.new.a()}} 次に、あなたが本当に何をだそれ@a [0]は.call –

4

最初に、ハムにラムダを入れないでください。a()を呼び出す結果を現在のコンテキストに入れています。

この情報があれば、クラス内のコードの意味を検討してください。クラス定義のコンテキストはクラスです。したがって、aというインスタンスメソッドを定義し、現在のコンテキストでaを呼び出した結果を含むハッシュにクラスインスタンス変数を割り当てます。現在のコンテキストはクラスAであり、クラスAにはaというクラスメソッドがないため、そこに存在しないメソッドの結果を入れようとしています。インスタンスメソッドbでは、インスタンス変数@aにアクセスしようとしますが、インスタンス変数は存在しません。クラスコンテキストに定義されている@aは、クラス自体に属し、特定のインスタンスには属しません。

まず、ラムダが必要な場合は、ラムダを作成する必要があります。次に、クラスとそのクラスのインスタンスの違いについて明確にする必要があります。

あなたが特定の条件で呼び出されるメソッド名のリストを作成したい場合、あなたはこのようにそれを行うことができます。

class A 
    def self.conditions() { 0 => :a } end 

    def a 
    puts "Hello!" 
    end 

    def b(arg) 
    send self.class.conditions[arg] 
    end 
end 

これは(それが簡単になったクラスのメソッドとして、ハッシュの条件を定義しますアクセスする)、ハッシュにはラムダなどのものではなく、呼び出すメソッドの名前だけが含まれています。したがって、b(0)を呼び出すと、send自体はA.conditions [0]に含まれるメッセージで、aです。

2

あなたは本当にただの事を上のかなりこの種、 はなぜこれほどのようなクラスで、すべてのメソッドをラップしないようにしたい場合:

# a container to store all your methods you want to use a hash to access 
class MethodHash 
    alias [] send 
    def one 
    puts "I'm one" 
    end 
    def two 
    puts "I'm two" 
    end 
end 

x = MethodHash.new 
x[:one] # prints "I'm one" 
x.two # prints "I'm one" 

や、あなたの例を使用する:

# a general purpose object that transforms a hash into calls on methods of some given object 
class DelegateHash 
    def initialize(target, method_hash) 
    @target = target 
    @method_hash = method_hash.dup 
    end 
    def [](k) 
    @target.send(@method_hash[k]) 
    end 
end 

class A 
    def initialize 
    @a = DelegateHash.new(self, { 0 => :a }) 
    end 
    def a() 
    puts "hello world" 
    end 
    def b() 
    @a[0] 
    end 
end 

x = A.new 
x.a #=> prints "hello world" 
x.b #=> prints "hello world" 

あなたが作成したもう1つの基本的なエラーは、Aの定義内にちょうど裸の のインスタンスメソッドの外に@aを初期化したことです。これはちょうどうまくいかず、それはうまくいきません。 ルビではすべてがクラスを含むオブジェクトであり、@という接頭辞はインスタンス のインスタンスであり、現在のオブジェクトはすべて自己のものであることを意味します。インスタンスメソッドの定義の中で、selfはクラスのインスタンス です。しかし、それ以外では、クラス定義内ではselfがクラスオブジェクトです。したがって、Aのインスタンスのどれも直接取得できないクラスオブジェクトのインスタンス変数@aをクラスオブジェクトAに定義しました。

Rubyにはこの動作の理由があります(クラスインスタンス変数はあなたが何をしているのか知っていれば本当に便利です)。これはもっと高度な技術です。

要するに、initializeメソッドでインスタンス変数を初期化するだけです。

0

= - >(文字列= "NO文字列が渡されていない")

は0 [ストリング

some_hash = {0 => A}

some_hashを置きますか] .call( "Hello World")

some_hash [0] []

3
table = { 
    :a => 'test', 
    :b => 12, 
    :c => lambda { "Hallo" }, 
    :d => def print(); "Hallo in test"; end 
} 

puts table[:a] 
puts table[:b] 
puts table[:c].call 
puts table[:d].send(:print) 
1

私はRubyでコールバックを使用することにはかなり新しいですし、これは私が例を使用して自分自身にそれを説明する方法である:

require 'logger' 
log = Logger.new('/var/tmp/log.out') 

def callit(severity, msg, myproc) 
    myproc.call(sev, msg) 
end 

lookup_severity = {} 
lookup_severity['info'] = Proc.new { |x| log.info(x) } 
lookup_severity['debug'] = Proc.new { |x| log.debug(x) } 

logit = Proc.new { |x,y| lookup_sev[x].call(y) } 

callit('info', "check4", logit) 
callit('debug', "check5", logit) 
関連する問題