Pytanie w sprawie call, hash, methods, ruby – Jak wywołać metodę, która jest wartością skrótu?

2

Wcześniej pytałem o sprytny sposób na wykonanie metody w danych warunkach ”Ruby sprytny sposób na wykonanie funkcji pod warunkiem. ”

Rozwiązania i czas reakcji były świetne, jednak po wdrożeniu posiadanie hasha lambd szybko staje się brzydkie. Więc zacząłem eksperymentować.

Działa następujący kod:

def a()
  puts "hello world"
end

some_hash = { 0 => a() }

some_hash[0]

Ale jeśli zawijam to w klasie, przestaje działać:

class A

  @a = { 0 => a()}

  def a()
    puts "hello world"
  end


  def b()
    @a[0]
  end

end

d = A.new()

d.b()

Nie rozumiem, dlaczego powinien przestać działać, czy ktoś może zasugerować, jak to działa?

Twoja odpowiedź

7   odpowiedzi
1

Pierwsza linia w twojej klasie wywołuje metodę, która jeszcze nie istnieje. Nie będzie nawet istnieć po załadowaniu całej klasy, ponieważ byłoby to wywołanie metody klasy i zdefiniowałeś tylko metody instancji.

Należy również pamiętać, że {0 => a ()} wywoła metodę a (), a nie utworzy odwołania do metody a (). Jeśli chciałbyś umieścić tam funkcję, która nie zostanie poddana ewaluacji później, musiałbyś użyć jakiegoś Lambda.

Ach, nie zdawałem sobie z tego sprawy. Cóż, czy i tak można osiągnąć to, co próbowałem zrobić? user130076
Możesz zrobić coś takiego: @a = {0 => lambda {A.new.a ()}} Wtedy będziesz musiał wywołać go za pomocą @a [0] .call Phil Kulak
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 )
5

Przede wszystkim nie wstawiasz lambdy do skrótu. Stawiasz wynik powołaniaa() w obecnym kontekście.

Biorąc pod uwagę te informacje, zastanów się, co oznacza kod w twojej klasie. Kontekstem definicji klasy jest klasa. Zatem definiujesz metodę instancji o nazwieai przypisz zmienną instancji klasy do skrótu zawierającego wynik wywołaniaa w obecnym kontekście. Obecny kontekst to klasa A, a klasa A nie ma metody klasy zwaneja, więc próbujesz tam umieścić wynik nieistniejącej metody. Następnie w metodzie instancjib, próbujesz uzyskać dostęp do zmiennej instancji o nazwie@a - ale nie ma takiego. The@a zdefiniowane w kontekście klasy należy do samej klasy, a nie do konkretnej instancji.

Przede wszystkim, jeśli chcesz lambda, musisz utworzyć lambdę. Po drugie, musisz jasno określić różnicę między klasą a instancją tej klasy.

Jeśli chcesz utworzyć listę nazw metod, które mają być wywoływane na określonych warunkach, możesz to zrobić w ten sposób:

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

  def a
    puts "Hello!"
  end

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

Definiuje to hash warunków jako metodę klasy (ułatwiającą dostęp), a skrót zawiera jedynie nazwę metody do wywołania, a nie lambda lub coś podobnego. Więc kiedy dzwoniszb(0), tosends sam komunikat zawarty w A.conditions [0], który jesta.

1

Jestem całkiem nowy w używaniu wywołań zwrotnych w Rubim i tak wyjaśniłem to sobie używając przykładu:

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

Jeśli naprawdę chcesz po prostu ładnie to robić, czemu nie zawinąć wszystkich metod w takiej klasie:

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

lub, aby użyć swojego przykładu:

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

Innym podstawowym błędem, który popełniłeś, jest zainicjowanie@a poza jakąkolwiek metodą instancji - po prostu obnaża definicjęA. To wielki czas, nie, nie, bo to po prostu nie działa. Pamiętaj, że w rubinie wszystko jest obiektem, w tym klasami i@ przedrostek oznacza zmienną instancji dowolnego obiektu, który jest obecnie sobą. Wewnątrz definicji definicji instancji,self jest instancją klasy. Ale poza tym, tylko wewnątrz definicji klasy,self jest obiektem klasy - więc zdefiniowałeś zmienną instancji o nazwie@a dla obiektu klasyA, którego żaden z przykładówA może dostać się bezpośrednio.

Ruby ma powód takiego zachowania (zmienne instancji klasy mogą być naprawdę przydatne, jeśli wiesz, co robisz), ale jest to bardziej zaawansowana technika.

W skrócie, tylko inicjuj zmienne instancji winitialize metoda.

0

a = -> (string = „Brak ciągu”)

stawia łańcuch

koniec

some_hash = {0 => a}

some_hash [0] .call („Hello World”)

some_hash [0] []

7

ten kod nie działa. wykonuje sięa w tym czasie jest dodawany do skrótu, a nie kiedy jest pobierany z skrótu (spróbuj w irb).

Nie działa w klasie, ponieważ nie maa metoda zdefiniowana w klasie (ostatecznie definiujesz metodęa na instancji.

Spróbuj użyć takich lambd

{0 => lambda { puts "hello world" }} 

zamiast

@ Chuck to byłoby identyczne, ale zawijanie kodu w metodach, a następnie odwoływanie się do nich w haszy wydaje się piękniejszym rozwiązaniem. user130076
w zasadzie możesz stworzyć taką metodęself.add_condition(a, &b) na twojej klasie, która po prostu dodaje blok b do skrótu na poziomie klasy. Następnie możesz pobrać i wywołać blok jako metodę. W swoim ciele klasowym miałbyśadd_condition 0 { puts "hello world" } lub coś Ben Hughes
rozważyłeś stworzenie metody klasy, która mogłaby zająć blok i dodać go jako lambdę do twojego skrótu, a następnie po prostu użyć go jako potrzebnego w ciele klasy. Ben Hughes
Oto przykład tego, o czym mówi Ben (dodaj w A): def self.condition (cond, & block) @conditions || = {}; @ warunki [cond] = blok; koniec Chuck

Powiązane pytania