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.

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:


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ą.


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.


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ś.


Przepraszam. Skopiuję część sformułowań i przetłumaczę, aby były specyficzne dla Rubiego. +1
— BuddyJoe
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.