Ruby: Как мне вызвать функцию через ссылку на объект? - PullRequest
1 голос
/ 30 апреля 2009

Рассмотрим этот надуманный пример:

# 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

Теперь я бы хотел вызвать цель eating_method с некоторыми аргументами:

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

Какой правильный способ сделать это в Ruby?

Ответы [ 4 ]

6 голосов
/ 30 апреля 2009

Используйте send. Send принимает имя функции, поэтому используйте символы:

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:

Кстати, знаете ли вы, что вы можете сделать это case немного красивее, сделав что-то вроде этого:

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

Или, как упоминает Sii, вместо этого используйте хеш:

fruit_methods = {:apple => :bite, :banana => :peel, :cherry => :om_nom_nom}
send(fruit_methods[fruit_kind], some_fruit)    
1 голос
/ 30 апреля 2009

В Ruby вы можете использовать классы и методы, как если бы они были значениями без особой работы, таким образом, вы можете уменьшить объем, которым вы фактически должны управлять в своем примере, до единого определения Class -> Method и использовать обобщенный алгоритм для работать с этим определением.

Предположим, что каждый из ваших типов реализует указанные вами методы, например, Apple.bite (), Banana.peel () и Cherry.om_nom_nom () определены. Также предположим, что fruit является экземпляром fruit_kind . Вы можете управлять отображением в одном месте и использовать обобщенный метод для выполнения всей оценки метода, относящейся к классу, например:

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

Обратите внимание, что ссылка eat_method является экземпляром объекта метода, определенного для экземпляра fruit. Вы можете извлечь определение таблицы как переменную класса, но вы захотите оценивать fruit.method в контексте каждого решения о том, какую функцию вызывать в передаваемом вами экземпляре.

0 голосов
/ 30 апреля 2009

Объектная модель Ruby позволяет вам динамически вызывать методы, используя метод Object#send, который принимает символ в качестве метода, который вы хотите вызвать.

Так что, если бы у вас был класс FruitEater, вы могли бы отправить метод приема пищи как:


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)
0 голосов
/ 30 апреля 2009

Формулировка меня сильно смущает.

Вы, вероятно, хотите Object # send хотя.

...