variable method globales define def conversiones ruby variables

method - ruby variable scope



Acceda a las variables programáticamente por nombre en Ruby (11)

¿Qué pasa si das la vuelta a tu problema? En lugar de tratar de obtener nombres de variables, obtenga las variables de los nombres:

["foo", "goo", "bar"].each { |param_name| param = eval(param_name) if param.class != Array puts "#{param_name} wasn''t an Array. It was a/an #{param.class}" return "Error: #{param_name} wasn''t an Array" end }

Si existiera la posibilidad de que alguna de las variables no se definiera en absoluto (a diferencia de no ser una matriz), querría agregar "rescate nulo" al final de la línea "param = ..." para mantener la evaluación. de lanzar una excepción ...

No estoy del todo seguro de si esto es posible en Ruby, pero espero que haya una manera fácil de hacerlo. Quiero declarar una variable y luego averiguar el nombre de la variable. Es decir, para este simple fragmento de código:

foo = ["goo", "baz"]

¿Cómo puedo recuperar el nombre de la matriz (aquí, "foo")? Si es posible, ¿funciona esto en cualquier variable (por ejemplo, escalares, hash, etc.)?

Editar: esto es lo que básicamente estoy tratando de hacer. Estoy escribiendo un servidor SOAP que envuelve una clase con tres variables importantes, y el código de validación es esencialmente el siguiente:

[foo, goo, bar].each { |param| if param.class != Array puts "param_name wasn''t an Array. It was a/an #{param.class}" return "Error: param_name wasn''t an Array" end }

Mi pregunta es: ¿Puedo reemplazar las instancias de ''param_name'' con foo, goo o bar? Estos objetos son todos matrices, por lo que las respuestas que he recibido hasta ahora no parecen funcionar (a excepción de la reingeniería de toda la respuesta de ala dbr )


Foo es solo una ubicación para mantener un puntero a los datos. Los datos no tienen conocimiento de qué puntos. En los sistemas de Smalltalk podría pedirle a la VM todos los apuntadores a un objeto, pero eso solo le proporcionaría el objeto que contenía la variable foo, no foo. No hay una manera real de hacer referencia a una variable en Ruby. Como se menciona en una respuesta, aún puede colocar una etiqueta en los datos que hace referencia a su origen o similares, pero en general no es una buena referencia para la mayoría de los problemas. Puede usar un hash para recibir los valores en primer lugar, o usar un hash para pasarlo a su ciclo, de modo que conozca el nombre del argumento para fines de validación como en la respuesta de DBR.


Gran pregunta Entiendo completamente tu motivación. Permítanme comenzar señalando que hay ciertos tipos de objetos especiales que, bajo ciertas circunstancias, tienen conocimiento de la variable a la que han sido asignados. Estos objetos especiales son, por ej. Instancias de Module , instancias de Class e instancias de Struct :

Dog = Class.new Dog.name # Dog

El truco es que esto solo funciona cuando la variable a la que se realiza la asignación es una constante. (Todos sabemos que las constantes de Ruby no son más que variables emocionalmente sensibles.) Por lo tanto:

x = Module.new # creating an anonymous module x.name #=> nil # the module does not know that it has been assigned to x Animal = x # but will notice once we assign it to a constant x.name #=> "Animal"

Este comportamiento de los objetos conscientes de las variables que les han sido asignadas, comúnmente se llama magia constante (porque está limitado a las constantes). Pero esta magia constante altamente deseable solo funciona para ciertos objetos:

Rover = Dog.new Rover.name #=> raises NoMethodError

Afortunadamente, he escrito una joya y_support/name_magic , que se encarga de esto:

# first, gem install y_support require ''y_support/name_magic'' class Cat include NameMagic end

El hecho de que esto solo funcione con constantes (es decir, variables que comienzan con una letra mayúscula) no es una limitación tan grande. De hecho, le da libertad para nombrar o no nombrar sus objetos a voluntad:

tmp = Cat.new # nameless kitty tmp.name #=> nil Josie = tmp # by assigning to a constant, we name the kitty Josie tmp.name #=> :Josie

Desafortunadamente, esto no funcionará con los literales de arreglos, porque están construidos internamente sin utilizar el método #new , en el que se basa NameMagic . Por lo tanto, para lograr lo que desea, tendrá que subclase Array :

require ''y_support/name_magic'' class MyArr < Array include NameMagic end foo = MyArr.new ["goo", "baz"] # not named yet foo.name #=> nil Foo = foo # but assignment to a constant is noticed foo.name #=> :Foo # You can even list the instances MyArr.instances #=> [["goo", "baz"]] MyArr.instance_names #=> [:Foo] # Get an instance by name: MyArr.instance "Foo" #=> ["goo", "baz"] MyArr.instance :Foo #=> ["goo", "baz"] # Rename it: Foo.name = "Quux" Foo.name #=> :Quux # Or forget the name again: MyArr.forget :Quux Foo.name #=> nil # In addition, you can name the object upon creation even without assignment u = MyArr.new [1, 2], name: :Pair u.name #=> :Pair v = MyArr.new [1, 2, 3], ɴ: :Trinity v.name #=> :Trinity

Logré el comportamiento constante de imitación de la magia buscando todas las constantes en todos los espacios de nombres del espacio de objetos de Ruby actual. Esto desperdicia una fracción de segundo, pero como la búsqueda se realiza solo una vez, no existe una penalización de rendimiento una vez que el objeto adivine su nombre. En el futuro, el equipo central de Ruby ha prometido el const_assigned .


Lo más parecido a una respuesta real a su pregunta es usar el método Enumerable each_with_index en lugar de cada uno, así:

my_array = [foo, baz, bar] my_array.each_with_index do |item, index| if item.class != Array puts "#{my_array[index]} wasn''t an Array. It was a/an #{item.class}" end end

Eliminé la declaración de devolución del bloque que estaba pasando a cada / each_with_index porque no significaba nada. Each y each_with_index devuelven la matriz en la que estaban operando.

También hay algo sobre el alcance en los bloques que vale la pena mencionar aquí: si ha definido una variable fuera del bloque, estará disponible dentro de él. En otras palabras, puede consultar foo, bar y baz directamente dentro del bloque. Lo contrario no es cierto: las variables que cree por primera vez dentro del bloque no estarán disponibles fuera de él.

Finalmente, la sintaxis do / end es preferible para bloques de varias líneas, pero eso es simplemente una cuestión de estilo, aunque es universal en el código ruby ​​de cualquier añada reciente.


Necesita volver a diseñar su solución. Incluso si pudieras hacerlo (no puedes), la pregunta simplemente no tiene una respuesta razonable.

Imagine un método get_name.

a = 1 get_name(a)

Todos probablemente podrían estar de acuerdo en que esto debería devolver ''a''

b = a get_name(b)

¿Debería devolver ''b'', o ''a'', o una matriz que contenga ambos?

[b,a].each do |arg| get_name(arg) end

¿Debería devolver ''arg'', ''b'' o ''a''?

def do_stuff( arg ) get_name(arg) do do_stuff(b)

¿Debería devolver ''arg'', ''b'' o ''a'', o tal vez la matriz de todos ellos? Incluso si devolviera una matriz, ¿cuál sería el orden y cómo sabría cómo interpretar los resultados?

La respuesta a todas las preguntas anteriores es "Depende de lo particular que deseo en ese momento". No estoy seguro de cómo podrías resolver ese problema para Ruby.


No puede, necesita regresar al diseño y rediseñar su solución.


No sé de ninguna manera para obtener un nombre de variable local. Pero puede usar el método instance_variables , esto devolverá una matriz de todos los nombres de variables de instancia en el objeto.

Llamada simple:

object.instance_variables

o

self.instance_variables

para obtener una matriz de todos los nombres de variables de instancia.


OK, también funciona en métodos de instancia y, en función de sus requisitos específicos (el que coloca en el comentario), puede hacer esto:

local_variables.each do |var| puts var if (eval(var).class != Fixnum) end

Simplemente reemplace Fixnum con su comprobación de tipo específico.


Parece que estás tratando de resolver un problema que tiene una solución mucho más fácil ...

¿Por qué no simplemente almacenar los datos en un hash? Si lo haces..

data_container = {''foo'' => [''goo'', ''baz'']}

... entonces es absolutamente trivial obtener el nombre ''foo''.

Dicho esto, no has dado ningún contexto al problema, por lo que puede haber una razón por la que no puedes hacer esto ...

[edit] Después de la aclaración, veo el problema, pero no creo que este sea el problema. Con [foo, bar, bla], es equivalente a decir [''content 1'', ''content 2'', ''etc''] El nombre real de las variables es (o más bien, debería ser) completamente irrelevante. Si el nombre de la variable es importante, es exactamente por eso que existen hashes.

El problema no es iterar sobre [foo, bar], etc., es el problema fundamental de cómo el servidor SOAP está retomando los datos y / o cómo está tratando de usarlos.

La solución, diría yo, es hacer que el servidor SOAP devuelva hashes, o, ya que sabes que siempre habrá tres elementos, ¿no puedes hacer algo como ...

{"foo" => foo, "goo" => goo, "bar"=>bar}.each do |param_name, param| if param.class != Array puts "#{param_name} wasn''t an Array. It was a/an #{param.class}" puts "Error: #{param_name} wasn''t an Array" end end


Sobre la base de joshmsmoore , algo como esto probablemente lo haría:

# Returns the first instance variable whose value == x # Returns nil if no name maps to the given value def instance_variable_name_for(x) self.instance_variables.find do |var| x == self.instance_variable_get(var) end end


Kernel::local_variables , pero no estoy seguro de que esto funcione para los vars locales de un método, y no sé si puedes manipularlo de tal manera que puedas hacer lo que deseas lograr.