Domanda

Non sono del tutto sicuro che ciò sia possibile in Ruby, ma spero che ci sia un modo semplice per farlo.Voglio dichiarare una variabile e poi scoprire il nome della variabile.Cioè, per questo semplice frammento:

foo = ["goo", "baz"]

Come posso recuperare il nome dell'array (qui "foo")?Se è effettivamente possibile, funziona su qualsiasi variabile (ad esempio scalari, hash, ecc.)?

Modificare:Ecco quello che fondamentalmente sto cercando di fare.Sto scrivendo un server SOAP che racchiude una classe con tre variabili importanti e il codice di convalida è essenzialmente questo:

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

La mia domanda è allora:Posso sostituire le istanze di "param_name" con foo, goo o bar?Questi oggetti sono tutti array, quindi le risposte che ho ricevuto finora non sembrano funzionare (ad eccezione della riprogettazione del tutto ala la risposta di dbr)

È stato utile?

Soluzione

E se capovolgessi il tuo problema?Invece di provare a ottenere nomi dalle variabili, ottieni le variabili dai nomi:

["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 ci fosse la possibilità che una delle variabili non venga definita affatto (invece di non essere un array), dovresti aggiungere "rescue nil" alla fine della riga "param = ..." per mantenere la eval dal lanciare un'eccezione...

Altri suggerimenti

Devi riprogettare la tua soluzione.Anche se tu Potevo fallo (non puoi), la domanda semplicemente non ha una risposta ragionevole.

Immagina un metodo get_name.

a = 1
get_name(a)

Probabilmente tutti sarebbero d'accordo sul fatto che dovrebbe restituire "a"

b = a
get_name(b)

Dovrebbe restituire "b" o "a" o un array che li contiene entrambi?

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

Dovrebbe restituire "arg", "b" o "a"?

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

Dovrebbe restituire "arg", "b" o "a" o forse l'array di tutti loro?Anche se restituisse un array, quale sarebbe l'ordine e come saprei come interpretare i risultati?

La risposta a tutte le domande di cui sopra è "dipende dalla cosa particolare che voglio in quel momento". Non sono sicuro di come potresti risolvere questo problema per Ruby.

Sembra che tu stia cercando di risolvere un problema che ha una soluzione molto più semplice.

Perché non archiviare semplicemente i dati in un hash?Se fate..

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

..è quindi assolutamente banale ottenere il nome 'foo'.

Detto questo, non hai fornito alcun contesto al problema, quindi potrebbe esserci un motivo per cui non puoi farlo.

[modificare] Dopo i chiarimenti, capisco il problema, ma non credo che sia questo il problema..Con [foo, bar, bla] è equivalente a dire ['content 1', 'content 2', 'etc'].Il nome effettivo delle variabili è (o meglio, dovrebbe essere) del tutto irrilevante.Se il nome della variabile è importante, è proprio per questo che esistono gli hash.

Il problema non è l'iterazione su [foo, bar] ecc., è il problema fondamentale con il modo in cui il server SOAP restituisce i dati e/o il modo in cui stai tentando di utilizzarli.

La soluzione, direi, è fare in modo che il server SOAP restituisca gli hash o, poiché sai che ci saranno sempre tre elementi, non puoi fare qualcosa del tipo...

{"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, funziona anche con i metodi di istanza e, in base al tuo requisito specifico (quello che hai inserito nel commento), potresti fare questo:

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

Basta sostituire Fixnum con il controllo del tipo specifico.

Non conosco alcun modo per ottenere un nome di variabile locale.Ma puoi usare il file instance_variables metodo, questo restituirà un array di tutti i nomi delle variabili di istanza nell'oggetto.

Chiamata semplice:

object.instance_variables

O

self.instance_variables

per ottenere un array di tutti i nomi delle variabili di istanza.

Basandosi su joshmsmoore, qualcosa del genere probabilmente lo farebbe:

# 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

C'è Kernel::local_variables, ma non sono sicuro che funzionerà per le variabili locali di un metodo e non so se puoi manipolarlo in modo tale da fare ciò che desideri ottenere.

Ottima domanda.Capisco perfettamente la tua motivazione.Vorrei iniziare notando che ci sono alcuni tipi di oggetti speciali che, in determinate circostanze, hanno conoscenza della variabile a cui sono stati assegnati.Questi oggetti speciali sono ad es. Module istanze, Class istanze e Struct istanze:

Dog = Class.new
Dog.name # Dog

Il problema è che funziona solo quando la variabile su cui viene eseguito l'assegnazione è una costante.(Sappiamo tutti che le costanti di Ruby non sono altro che variabili emotivamente sensibili.) Quindi:

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"

Questo comportamento in cui gli oggetti sanno a quali variabili sono stati assegnati viene comunemente chiamato magia costante (perché è limitato alle costanti).Ma questo è altamente auspicabile magia costante funziona solo per determinati oggetti:

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

Fortunatamente, Ho scritto un gioiello y_support/name_magic, che si prende cura di questo per te:

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

class Cat
  include NameMagic
end

Il fatto che funzioni solo con costanti (es.variabili che iniziano con una lettera maiuscola) non è una grande limitazione.Ti dà infatti la libertà di nominare o non nominare i tuoi oggetti a tuo piacimento:

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

Sfortunatamente, questo non funzionerà con i valori letterali di array, perché sono costruiti internamente senza utilizzare #new metodo, su cui NameMagic fa affidamento.Pertanto, per ottenere ciò che desideri, dovrai creare una sottoclasse 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

Ho ottenuto il costante comportamento di imitazione della magia tramite cercando tutte le costanti in tutti gli spazi dei nomi dell'attuale spazio oggetti di Ruby.Ciò spreca una frazione di secondo, ma poiché la ricerca viene eseguita solo una volta, non vi è alcuna penalità nelle prestazioni una volta che l'oggetto ne individua il nome.In futuro, il team principale di Ruby ha promesso const_assigned gancio.

Non puoi, devi tornare al tavolo da disegno e riprogettare la tua soluzione.

Foo è solo una posizione in cui contenere un puntatore ai dati.I dati non hanno alcuna conoscenza di ciò che lo indica.Nei sistemi Smalltalk potresti chiedere alla VM tutti i puntatori a un oggetto, ma ciò ti darebbe solo l'oggetto che conteneva la variabile foo, non foo stesso.Non esiste un modo reale per fare riferimento a una variabile in Ruby.Come menzionato da una risposta, puoi comunque inserire un tag nei dati che faccia riferimento alla loro provenienza o qualcosa del genere, ma generalmente questo non è un buon approccio alla maggior parte dei problemi.Puoi utilizzare un hash per ricevere i valori in primo luogo oppure utilizzare un hash per passare al ciclo in modo da conoscere il nome dell'argomento a fini di convalida come nella risposta di DBR.

La cosa più vicina a una risposta reale alla tua domanda è utilizzare il metodo Enumerable each_with_index invece di ciascuno, in questo modo:

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

Ho rimosso l'istruzione return dal blocco che stavi passando a ciascuno/each_with_index perché non faceva/significava nulla.Ciascuno e ciascuno_con_indice restituiscono entrambi l'array su cui stavano operando.

C'è anche qualcosa sull'ambito nei blocchi degno di nota qui:se hai definito una variabile all'esterno del blocco, sarà disponibile al suo interno.In altre parole, potresti fare riferimento a foo, bar e baz direttamente all'interno del blocco.Non è vero il contrario:le variabili che crei per la prima volta all'interno del blocco non saranno disponibili al di fuori di esso.

Infine, la sintassi do/end è preferita per i blocchi multilinea, ma è semplicemente una questione di stile, sebbene sia universale nel codice Ruby di qualsiasi annata recente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top