Wywołanie metody z łańcucha o nazwie metody w języku Ruby

157

Jak mogę zrobić to, o czym oni tutaj mówią , ale w Rubim?

Jak wykonałbyś funkcję na obiekcie? i jak wykonałbyś funkcję globalną (zobacz odpowiedź jetxee we wspomnianym poście)?

PRZYKŁADOWY KOD:

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()

AKTUALIZACJA: Jak dostać się do metod globalnych? czy moje funkcje globalne?

Wypróbowałem tę dodatkową linię

puts methods

oraz load i row_change, jeśli nie są wymienione.

BuddyJoe
źródło

Odpowiedzi:

232

Do wywoływania funkcji bezpośrednio na obiekcie

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

która zwraca 3 zgodnie z oczekiwaniami

lub dla funkcji modułu

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

i lokalnie zdefiniowana metoda

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

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

Dokumentacja:

Colin Gravill
źródło
3
+1 To działa. To może być głupia kontynuacja ... ale dlaczego nie mogę znaleźć słowa wysłanego w źródle Ruby w - C: \ ruby ​​\ lib \ ruby ​​\ 1.8 \ fileutils.rb? Pomyślałem, że znajdę tam funkcję wysyłania.
BuddyJoe,
1
Byłem ciekawy, co robi pod maską.
BuddyJoe,
Jest zdefiniowany na obiekcie - ruby-doc.org/core/classes/Object.html#M000332 Wybrałem losową funkcję dla wartości odsetek.
Colin Gravill,
1
Interesujące, ponieważ zanim dwukrotnie przeczytałem twoją odpowiedź i w pełni ją zrozumiałem, uruchomiłem FileUtils.send ("load") i wykonałem moją funkcję. więc jeśli rozumiem to poprawnie, tworząc funkcje w trybie „globalnym”, czy dodam metody do obiektu głównego? albo nie?
BuddyJoe,
12
Dobrze, że szukałeś rzeczy w źródle! :)
Colin Gravill,
34

Trzy sposoby: send/ call/ eval- i ich punkty odniesienia

Typowe wywołanie (w celach informacyjnych):

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

Za pomocą send

s.send(:length) #=> 6

Za pomocą call

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

Za pomocą eval

eval "s.length" #=> 6

 

Benchmarki

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" } } 
} 

... jak widać, tworzenie instancji obiektu metody jest najszybszym dynamicznym sposobem wywoływania metody. Zwróć też uwagę, jak wolno jest używać 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)

Podziękowania dla tego wpisu na blogu, który zawiera nieco więcej informacji na temat trzech metod, a także pokazuje, jak sprawdzić, czy metody istnieją.

cwd
źródło
Właśnie to znalazłem i zauważyłem coś, co nie było objęte. Co bym zrobił, gdybym chciał Class.send("classVariable") = 5? To powoduje błąd. Czy można to obejść? To samo dotyczy używaniaClass.method("classVariable").call() = 5
tajemniczego mistrza
jeśli chcesz wysłać jakiś argument za pomocą funkcji send, użyj czegoś takiego jak ta ClassName.send ("nazwa_metody", arg1, arg2)
Aleem
Czy Twój punkt odniesienia nie powinien calluwzględniać tworzenia wystąpienia metody object ( m.test.method(:length)) w celu dokładnego odzwierciedlenia czasu rzeczywistego? Podczas korzystania calljesteś prawdopodobnie będzie instancję obiektu metodę za każdym razem.
Joshua Pinter
Przy okazji, link do posta na blogu jest martwy.
Joshua Pinter
33

Użyj tego:

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

Proste, prawda?

Jeśli chodzi o globalne , myślę, że sposób w Ruby polegałby na przeszukaniu go za pomocą methodsmetody.

Geo
źródło
met = a.method? czy to nie jest już wywołanie funkcji? co jeśli metoda coś zwróci?
user1735921
Do Twojej wiadomości, gdy metoda nie istnieje: "my_string".method('blah') #=> NameError: undefined method bla 'dla klasyString'
Joshua Pinter
3

Osobiście ustawiłbym hash do funkcji referencji, a następnie użyłbym ciągu jako indeksu do skrótu. Następnie wywołujesz odwołanie do funkcji z jej parametrami. Ma to tę zaletę, że nie pozwala złemu łańcuchowi wywołać czegoś, czego nie chcesz wywołać. Innym sposobem jest w zasadzie evalsznurek. Nie rób tego.

PS nie bądź leniwy i faktycznie wpisz całe swoje pytanie, zamiast tworzyć link do czegoś.

dlamblin
źródło
Przepraszam. Skopiuję część sformułowań i przetłumaczę, aby były specyficzne dla Rubiego. +1
BuddyJoe