Pergunta

Não tenho certeza se isso é possível em Ruby, mas espero que haja uma maneira fácil de fazer isso.Quero declarar uma variável e depois descobrir o nome da variável.Ou seja, para este trecho simples:

foo = ["goo", "baz"]

Como posso recuperar o nome do array (aqui, “foo”)?Se for realmente possível, isso funciona em qualquer variável (por exemplo, escalares, hashes, etc.)?

Editar:Aqui está o que basicamente estou tentando fazer.Estou escrevendo um servidor SOAP que envolve uma classe com três variáveis ​​importantes, e o código de validação é essencialmente 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
      }

Minha pergunta é então:Posso substituir as instâncias de 'param_name' por foo, goo ou bar?Esses objetos são todos Arrays, então as respostas que recebi até agora não parecem funcionar (com exceção da reengenharia de tudo, ala resposta do dbr)

Foi útil?

Solução

E se você reverter seu problema?Em vez de tentar obter nomes de variáveis, obtenha as variáveis ​​a partir dos nomes:

["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
  }

Se houvesse uma chance de uma das variáveis ​​não ser definida (em vez de não ser uma matriz), você gostaria de adicionar "rescue nil" ao final da linha "param = ..." para manter a avaliação de lançar uma exceção ...

Outras dicas

Você precisa reestruturar sua solução.Mesmo se você poderia faça isso (você não pode), a pergunta simplesmente não tem uma resposta razoável.

Imagine um método get_name.

a = 1
get_name(a)

Todos provavelmente concordariam que isso deveria retornar 'a'

b = a
get_name(b)

Deveria retornar 'b' ou 'a' ou um array contendo ambos?

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

Deveria retornar 'arg', 'b' ou 'a' ?

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

Deveria retornar 'arg', 'b' ou 'a', ou talvez o array de todos eles?Mesmo que retornasse um array, qual seria a ordem e como eu saberia como interpretar os resultados?

A resposta para todas as perguntas acima é "depende da coisa em particular que eu quero na época". Não tenho certeza de como você pode resolver esse problema para Ruby.

Parece que você está tentando resolver um problema que tem uma solução muito mais fácil.

Por que não apenas armazenar os dados em um hash?Se você fizer..

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

..é então totalmente trivial obter o nome 'foo'.

Dito isto, você não forneceu nenhum contexto para o problema, então pode haver um motivo para você não poder fazer isso.

[editar] Após esclarecimentos, vejo o problema, mas não creio que seja esse o problema.Com [foo, bar, bla], é equivalente a dizer ['content 1', 'content 2', 'etc'].O nome real das variáveis ​​é (ou melhor, deveria ser) totalmente irrelevante.Se o nome da variável é importante, é exatamente por isso que existem hashes.

O problema não é iterar [foo, bar] etc, é o problema fundamental de como o servidor SOAP está recuperando os dados e/ou como você está tentando usá-los.

A solução, eu diria, é fazer com que o servidor SOAP retorne hashes ou, como você sabe que sempre haverá três elementos, você não pode fazer 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

OK, também funciona em métodos de instância e, com base no seu requisito específico (aquele que você colocou no comentário), você poderia fazer o seguinte:

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

Basta substituir Fixnum com sua verificação de tipo específica.

Não conheço nenhuma maneira de obter um nome de variável local.Mas você pode usar o instance_variables método, isso retornará uma matriz de todos os nomes de variáveis ​​​​de instância no objeto.

Chamada simples:

object.instance_variables

ou

self.instance_variables

para obter uma matriz de todos os nomes de variáveis ​​de instância.

Construindo em joshmsmoore, algo assim provavelmente funcionaria:

# 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, mas não tenho certeza se isso funcionará para os vars locais de um método e não sei se você pode manipulá-lo de forma a fazer o que deseja alcançar.

Ótima pergunta.Entendo perfeitamente sua motivação.Deixe-me começar observando que existem certos tipos de objetos especiais que, sob certas circunstâncias, têm conhecimento da variável à qual foram atribuídos.Esses objetos especiais são, por exemplo. Module instâncias, Class instâncias e Struct instâncias:

Dog = Class.new
Dog.name # Dog

O problema é que isso funciona apenas quando a variável à qual a atribuição é realizada é uma constante.(Todos nós sabemos que as constantes Ruby nada mais são do que variáveis ​​emocionalmente sensíveis.) Assim:

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 comportamento de os objetos saberem a quais variáveis ​​foram atribuídas é comumente chamado magia constante (porque é limitado a constantes).Mas isso é altamente desejável magia constante só funciona para determinados objetos:

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

Felizmente, Eu escrevi uma jóia y_support/name_magic, que cuida disso para você:

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

class Cat
  include NameMagic
end

O fato de que isso só funciona com constantes (ou seja.variáveis ​​começando com letra maiúscula) não é uma limitação tão grande.Na verdade, isso lhe dá liberdade para nomear ou não seus objetos à vontade:

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

Infelizmente, isso não funcionará com literais de array, porque eles são construídos internamente sem usar #new método, no qual NameMagic depende.Portanto, para conseguir o que deseja, você terá que subclassificar 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

Eu alcancei o comportamento constante de imitação de magia ao pesquisando todas as constantes em todos os namespaces do espaço de objeto Ruby atual.Isso desperdiça uma fração de segundo, mas como a pesquisa é realizada apenas uma vez, não há penalidade no desempenho quando o objeto descobre seu nome.No futuro, a equipe principal do Ruby prometeu const_assigned gancho.

Você não pode, você precisa voltar à prancheta e reprojetar sua solução.

Foo é apenas um local para armazenar um ponteiro para os dados.Os dados não têm conhecimento do que aponta para eles.Em sistemas Smalltalk, você poderia solicitar à VM todos os ponteiros para um objeto, mas isso forneceria apenas o objeto que continha a variável foo, e não o próprio foo.Não existe uma maneira real de fazer referência a uma variável em Ruby.Conforme mencionado por uma resposta, você ainda pode colocar uma tag nos dados que faça referência à origem ou algo assim, mas geralmente essa não é uma boa abordagem para a maioria dos problemas.Você pode usar um hash para receber os valores em primeiro lugar ou usar um hash para passar para o seu loop, para saber o nome do argumento para fins de validação, como na resposta do DBR.

A coisa mais próxima de uma resposta real para sua pergunta é usar o método Enumerable each_with_index em vez de each, assim:

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

Eu removi a instrução return do bloco que você estava passando para each/each_with_index porque ela não significava nada.Cada e each_with_index retornam a matriz na qual estavam operando.

Também há algo sobre o escopo em blocos que vale a pena observar aqui:se você definiu uma variável fora do bloco, ela estará disponível dentro dele.Em outras palavras, você poderia consultar foo, bar e baz diretamente dentro do bloco.O inverso não é verdadeiro:variáveis ​​que você cria pela primeira vez dentro do bloco não estarão disponíveis fora dele.

Finalmente, a sintaxe do/end é preferida para blocos multilinhas, mas isso é simplesmente uma questão de estilo, embora seja universal em código Ruby de qualquer versão recente.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top