1

Consider this contrived example:

# Dispatch on value of fruit_kind:

TYPE_A = :apple
TYPE_B = :banana
TYPE_C = :cherry

eating_method = nil

case fruit_kind
  # Methods to use for different kinds of fruit (assume these are
  #  already defined)
  when TYPE_A then eating_method = bite
  when TYPE_B then eating_method = peel
  when TYPE_C then eating_method = om_nom_nom
end

Now I'd like to invoke the target of eating_method with some arguments:

# Doesn't work; this tries to invoke a method called "eating_method",
# not the reference I defined earlier.
eating_method(some_fruit)

What's the right way to do this in Ruby?

4

4 回答 4

6

Use send. Send takes the function name, so use symbols:

case fruit_kind
  # Methods to use for different kinds of fruit (assume these are
  #  already defined)
  when TYPE_A then eating_method = :bite
  when TYPE_B then eating_method = :peel
  when TYPE_C then eating_method = :om_nom_nom
end

send(eating_method, some_fruit)

EDIT:

By the way, did you know you can make that case a little prettier by doing something like this:

eating_method = case fruit_kind
  # Methods to use for different kinds of fruit (assume these are
  #  already defined)
  when TYPE_A then :bite
  when TYPE_B then :peel
  when TYPE_C then :om_nom_nom
  else nil
end

Or, as Sii mentions, use a hash instead:

fruit_methods = {:apple => :bite, :banana => :peel, :cherry => :om_nom_nom}
send(fruit_methods[fruit_kind], some_fruit)    
于 2009-04-30T14:03:07.473 回答
1

In Ruby you can use classes and methods as if they are values without too much work, thus you can reduce the amount you actually have to manage in your example to a single definition of Class -> Method and use a generalized algorithm to work with that definition.

Suppose each of your types implement the methods you specify, e.g. Apple.bite(), Banana.peel(), and Cherry.om_nom_nom() are all defined. Also suppose that fruit is an instance of fruit_kind You can manage the mapping in one place and use a generalized method to do all of the Class specific method evaluation like so:

fruit_table = {Apple => :bite, 
               Banana => :peel,
               Cherry => :om_nom_nom}
eating_method = fruit.method(fruit_table[fruit.type])
eating_method.call

Note that the reference eating_method is an instance of a method object specific to the instance of fruit. You could pull the table definition out to be a class variable, but you would want to evaluate fruit.method in the context of each time that you are deciding which function to call on an instance you are passed.

于 2009-04-30T14:28:12.873 回答
0

The wording confuses me greatly.

You probably want Object#send though.

于 2009-04-30T14:03:43.073 回答
0

The Ruby object model lets you call methods dynamically using the Object#send method which takes a symbol as the method you want to call.

So if you were to have a class called FruitEater you could send the eating method as:


f = FruitEater.new

# Dispatch on value of fruit_kind:

TYPE_A = :apple
TYPE_B = :banana
TYPE_C = :cherry

eating_method = nil

case fruit_kind
  # Methods to use for different kinds of fruit (assume these are
  #  already defined)
  when TYPE_A then eating_method = :bite
  when TYPE_B then eating_method = :peel
  when TYPE_C then eating_method = :om_nom_nom
end

f.send(eating_method)
于 2009-04-30T14:38:59.867 回答