Pregunta

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 sencillo fragmento:

foo = ["goo", "baz"]

¿Cómo puedo recuperar el nombre de la matriz (aquí, "foo")?Si es realmente posible, ¿funciona esto con alguna variable (por ejemplo, escalares, hashes, 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 este:

  [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 entonces:¿Puedo reemplazar las instancias de 'param_name' con foo, goo o bar?Todos estos objetos son matrices, por lo que las respuestas que he recibido hasta ahora no parecen funcionar (con la excepción de la reingeniería de todo). la respuesta de dbr)

¿Fue útil?

Solución

¿Qué pasa si le das la vuelta a tu problema?En lugar de intentar 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 una de las variables no estuviera definida en absoluto (en lugar de no ser una matriz), querrá agregar "rescue nil" al final de la línea "param = ..." para mantener la evaluación. de lanzar una excepción...

Otros consejos

Necesita rediseñar su solución.Incluso si tú podría hazlo (no puedes), la pregunta simplemente no tiene una respuesta razonable.

Imagine un método get_name.

a = 1
get_name(a)

Probablemente todos estén de acuerdo en que esto debería devolver 'a'

b = a
get_name(b)

¿Debería devolver 'b', '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 interpretar los resultados?

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

Parece que estás intentando resolver un problema que tiene una solución mucho más sencilla.

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

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

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

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

[editar] Después de la aclaración, veo el problema, pero no creo que sea ese.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 los hashes.

El problema no es iterar sobre [foo, bar], etc., es el problema fundamental con cómo el servidor SOAP devuelve los datos y/o cómo intenta usarlos.

Yo diría que la solución es hacer que el servidor SOAP devuelva hashes o, como 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

Bien, también funciona en métodos de instancia y, según su requisito específico (el que puso en el comentario), puede hacer esto:

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

Solo reemplaza Fixnum con su tipo específico de verificación.

No conozco ninguna forma de obtener el nombre de una variable local.Pero puedes usar el instance_variables método, esto devolverá una matriz de todos los nombres de variables de instancia en el objeto.

llamada sencilla:

object.instance_variables

o

self.instance_variables

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

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

hay Kernel::local_variables, pero no estoy seguro de que esto funcione para las variables locales de un método, y no sé si puedes manipularlo de tal manera que haga lo que deseas lograr.

Gran pregunta.Entiendo perfectamente 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 ejemplo. Module instancias, Class instancias y Struct instancias:

Dog = Class.new
Dog.name # Dog

El problema es que esto funciona sólo 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 sabiendo a qué variables se les ha asignado, se llama comúnmente magia constante (porque está limitado a constantes).Pero esto es muy deseable. magia constante sólo 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 por usted:

 # first, gem install y_support
require 'y_support/name_magic'

class Cat
  include NameMagic
end

El hecho de que esto sólo funcione con constantes (es decir.variables que comienzan con mayúscula) no es una limitación tan grande.De hecho, te da libertad para nombrar o no nombrar tus 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 literales de matriz, porque se construyen internamente sin usar #new método, en el que NameMagic depende.Por lo tanto, para lograr lo que deseas, tendrás que subclasificar 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 constante comportamiento de imitación de magia al buscando todas las constantes en todos los espacios de nombres del espacio de objetos Ruby actual.Esto desperdicia una fracción de segundo, pero como la búsqueda se realiza sólo una vez, no hay penalización en el rendimiento una vez que el objeto descubre su nombre.En el futuro, el equipo central de Ruby ha prometido const_assigned gancho.

No puede, necesita volver a la mesa de dibujo y rediseñar su solución.

Foo es solo una ubicación para contener un puntero a los datos.Los datos no tienen conocimiento de lo que apuntan a ellos.En los sistemas Smalltalk, podría pedirle a la máquina virtual todos los punteros a un objeto, pero eso solo le daría el objeto que contenía la variable foo, no foo en sí.No existe una forma 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 haga referencia a su origen o algo así, pero en general, ese no es un buen enfoque para la mayoría de los problemas.Puede usar un hash para recibir los valores en primer lugar, o usar un hash para pasar a su bucle para saber el nombre del argumento con fines de validación como en la respuesta de DBR.

Lo más parecido a una respuesta real a su pregunta es utilizar el método enumerable each_with_index en lugar de each, de esta manera:

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 each/each_with_index porque no hacía ni significaba nada.Cada uno y cada_with_index devuelven la matriz en la que estaban operando.

También hay algo sobre el alcance en los bloques que vale la pena señalar aquí:Si ha definido una variable fuera del bloque, estará disponible dentro de él.En otras palabras, podrías referirte a 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, se prefiere la sintaxis do/end 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 época reciente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top