Pregunta sobre methods, hash, call, ruby – ¿Cómo llamo a un método que es un valor hash?

2

Anteriormente, pregunté acerca de una forma inteligente de ejecutar un método en una condición determinada "Ruby es una forma inteligente de ejecutar una función en una condición. "

Las soluciones y el tiempo de respuesta fueron excelentes, sin embargo, luego de la implementación, tener un hash de lambdas se pone feo con bastante rapidez. Así que empecé a experimentar.

El siguiente código funciona:

def a()
  puts "hello world"
end

some_hash = { 0 => a() }

some_hash[0]

Pero si envuelvo esto en una clase, deja de funcionar:

class A

  @a = { 0 => a()}

  def a()
    puts "hello world"
  end


  def b()
    @a[0]
  end

end

d = A.new()

d.b()

No puedo ver por qué debería dejar de funcionar, ¿alguien puede sugerir cómo hacerlo funcionar?

Tu respuesta

7   la respuesta
0

a = -> (string = "No se pasa una cadena") do

pone cuerda

fin

some_hash = {0 => a}

some_hash [0] .call ("Hello World")

some_hash [0] []

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

Soy bastante nuevo en el uso de devoluciones de llamada en Ruby y así es como me expliqué utilizando un ejemplo:

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

ese código no funciona. se ejecutaa en el momento en que se agrega al hash, no cuando se recupera del hash (pruébelo en irb).

No funciona en la clase porque no haya Método definido en la clase (eventualmente se define un métodoa en la instancia

Intenta usar lambdas como

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

en lugar

luego pase los nombres de los métodos como símbolos y use send o Symbol # to_proc. Ben Hughes
@Chuck sería idéntico, pero envolver el código en métodos y luego hacer referencia a ellos en el hash parece una solución más bella. user130076
No estoy seguro de lo que eso significa, lo siento! Sin embargo, usar el envío como sugeriste parece ideal. user130076
Eso es realmente lo que estaba tratando de evitar. El código que pondría en la lambda puede ser un poco desordenado, y tendré unos 15 elementos en el hash. Se desordena un poco el código. user130076
1

Bueno, la primera línea de tu clase llama a un método que aún no existe. Sin embargo, ni siquiera existirá después de que se cargue toda la clase, ya que eso sería una llamada al método de clase y solo habrá definido los métodos de instancia.

También tenga en cuenta que {0 => a ()} llamará al método a (), no creará una referencia al método a (). Si quisieras poner una función allí que no se evalúe hasta más tarde, deberías usar algún tipo de Lambda.

Ah, no me di cuenta de eso. Bueno, ¿hay alguna forma de lograr lo que estaba tratando de hacer? user130076
Puedes hacer algo como esto: @a = {0 => lambda {A.new.a ()}} Entonces deberías llamarlo con @a [0] .call Phil Kulak
2

Si realmente quieres crear este tipo de cosas, ¿por qué no envolver todos tus métodos en una clase como esta?

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

o, para usar tu ejemplo:

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

Otro error básico que cometió es que inicializó@a fuera de cualquier método de instancia - simplemente desnudo dentro de la definición deA. Este es un gran momento no-no, porque simplemente no funciona. Recuerde, en ruby, todo es un objeto, incluidas las clases, y el@ prefijo significa la variable de instancia de cualquier objeto que sea actualmente uno mismo. Dentro de las definiciones de un método de instancia,self Es una instancia de la clase. Pero fuera de eso, justo dentro de la definición de clase,self es el objeto de clase, por lo que definió una variable de instancia llamada@a para el objeto de claseA, que ninguna de las instancias deA Puede llegar directamente.

Ruby tiene una razón para este comportamiento (las variables de instancia de clase pueden ser realmente útiles si sabes lo que estás haciendo), pero esta es una técnica más avanzada.

En resumen, solo inicializar variables de instancia en elinitialize método.

5

En primer lugar, no estás poniendo un lambda en el hash. Estás poniendo el resultado de llamar.a() en el contexto actual.

Dada esta información, considera lo que significa el código en tu clase. El contexto de una definición de clase es la clase. Así que define un método de instancia llamadoay asigne una variable de instancia de clase al hash que contiene el resultado de la llamadaa en el contexto actual. El contexto actual es la clase A, y la clase A no tiene un método de clase llamadoa, entonces estás tratando de poner el resultado de un método inexistente allí. Luego en el método de instanciab, intenta acceder a una variable de instancia llamada@a - pero no hay uno. los@a definido en el contexto de la clase pertenece a la clase en sí, no a ninguna instancia en particular.

Entonces, primero que todo, si quieres un lambda, necesitas hacer un lambda. Segundo, debe ser claro acerca de la diferencia entre una clase y una instancia de esa clase.

Si desea hacer una lista de nombres de métodos para ser llamados en ciertas condiciones, puede hacerlo así:

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

  def a
    puts "Hello!"
  end

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

Esto define el hash de condiciones como un método de la clase (lo que facilita el acceso), y el hash simplemente contiene el nombre del método al que llamar en lugar de un lambda o algo así. Así que cuando llamasb(0), esosends mismo el mensaje contenido en A.conditions [0], que esa.

Preguntas relacionadas