Я не совсем уверен, возможно ли это в Ruby, но, надеюсь, есть простой способ сделать это. Я хочу объявить переменную, а потом узнать имя переменной. То есть для этого простого фрагмента:
foo = ["goo", "baz"]
Как мне вернуть имя массива (здесь "foo")? Если это действительно возможно, работает ли это с какой-либо переменной (например, скалярами, хешами и т. д.)?
Обновлено: вот что я в основном пытаюсь сделать. Я пишу сервер SOAP, который обертывает класс с тремя важными переменными, а код проверки по существу таков:
[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
}
Тогда мой вопрос: могу ли я заменить экземпляры param_name на foo, goo или bar? Все эти объекты представляют собой массивы, поэтому ответы, которые я получил до сих пор, похоже, не работают (за исключением реинжиниринга всего этого, ala ответ dbr)
Это не имеет отношения к вашему вопросу, но это не очень похоже на Ruby для проверки имени класса (Array). Вместо этого вы должны проверить ФУНКЦИОНАЛЬНОСТЬ класса, который вы хотите, чтобы он имел - в этом случае, вероятно, перечислимость.
@Max Cantor, так как лучше всего это проверить? Respond_to что ли?
Это очень плохо, мне нравится идея о том, что переменные знают свои имена. Как-то самореализуются

Я не знаю, как получить имя локальной переменной. Но вы можете использовать метод instance_variables, он вернет массив всех имен переменных экземпляра в объекте.
Простой звонок:
object.instance_variables
или же
self.instance_variables
чтобы получить массив имен всех переменных экземпляра.
Спасибо, но это не особо помогает. Конкретная проблема, с которой я столкнулся, заключается в том, что у меня есть три переменные, которые я хочу перебрать, и если все они не относятся к определенному типу данных, я хочу распечатать, что это не так, и напечатать имя этой переменной. Спасибо хоть!
Крис: не могли бы вы перефразировать свой актуальный вопрос выше, чтобы отразить этот вариант использования? Я все еще не совсем понимаю, какова ваша цель.
Основываясь на Джошмсмур, вероятно, что-то вроде этого:
# 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, но я не уверен, что это сработает для локальных переменных метода, и я не знаю, что вы можете манипулировать им таким образом, чтобы делать то, что вы хотите достичь.
Конечно, он работает с local_variables, но помните, что local_variables для метода включает только те, которые были определены внутри метода.
Хорошо, он также работает в методах экземпляра, и, в зависимости от вашего конкретного требования (того, которое вы указали в комментарии), вы можете сделать это:
local_variables.each do |var|
puts var if (eval(var).class != Fixnum)
end
Просто замените Fixnum на проверку вашего конкретного типа.
Проблема, с которой я сталкиваюсь, заключается в том, что вы выполняете команду put var. При выполнении команды put на массив выводится содержимое массива, а не имя массива. Мой текущий код выше похож на ваш (хотя я не использовал eval).
Похоже, вы пытаетесь решить проблему, у которой есть гораздо более простое решение.
Почему бы просто не хранить данные в хэше? Если вы это сделаете ..
data_container = {'foo' => ['goo', 'baz']}
... тогда получить имя "foo" совершенно тривиально.
Тем не менее, вы не указали контекст проблемы, поэтому может быть причина, по которой вы не можете этого сделать ...
[редактировать] После разъяснения я вижу проблему, но не думаю, что это проблема .. С [foo, bar, bla] это эквивалентно ['content 1', 'content 2', 'etc']. Фактическое имя переменных (или, скорее, должно быть) совершенно неактуально. Если имя переменной важно, именно поэтому хеши существуют.
Проблема не в повторении [foo, bar] и т. д., Это фундаментальная проблема в том, как сервер SOAP сохраняет данные и / или как вы пытаетесь их использовать.
Решение, я бы сказал, состоит в том, чтобы либо заставить сервер SOAP возвращать хэши, либо, поскольку вы знаете, что всегда будет три элемента, не можете ли вы сделать что-то вроде ..
{"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
Это возможно, но теперь количество повторений в моем коде теряет всякую выгоду, и я бы с таким же успехом разбил свой код на N сегментов, по одному для каждого массива (а затем жестко закодировал имя массива)
Вы не можете, вам нужно вернуться к чертежной доске и заново спроектировать свое решение.
Вот чего я боялся. Я просто надеялся, что, поскольку я новичок в Ruby, возможно, будет какое-то простое имя метода, о котором я еще не слышал.
Вам необходимо изменить архитектуру вашего решения. Даже если вы мог сделаете это (вы не можете), у этого вопроса просто нет разумного ответа.
Представьте себе метод get_name.
a = 1
get_name(a)
Вероятно, все согласились бы, что это должно вернуть «а»
b = a
get_name(b)
Должен ли он возвращать «b», «a» или массив, содержащий и то, и другое?
[b,a].each do |arg|
get_name(arg)
end
Должен ли он возвращать arg, b или a?
def do_stuff( arg )
get_name(arg)
do
do_stuff(b)
Должен ли он возвращать 'arg', 'b' или 'a', или, может быть, массив всех из них? Даже если бы он вернул массив, в каком бы порядке и как мне узнать, как интерпретировать результаты?
Ответ на все вышеперечисленные вопросы: «Это зависит от того, что я хочу в данный момент». Я не уверен, как вы могли бы решить эту проблему для Ruby.
Определенно все действительные баллы (отсюда и голос за). Но посмотрите ответ Гленна, он определенно заслуживает внимания.
На самом деле я рассматривал эту проблему с точки зрения языка, который изначально был разработан, чтобы рассматривать «переменные» как отдельный тип. Итак, в операторе a = 5 ; b = a есть два «переменных» объекта и одно «атомарное» значение (5). В этой системе b.name => 'b' и b.value => 5. a.name => 'a' и a.value => 5. b = a - это метод = на b, который присваивает значение RHS. a достаточно умен, чтобы знать, что следует назначать ценить, а не указатель a.
Я думаю, что язык, поддерживающий это, имел бы другой механизм для поиска родительской области видимости и, возможно, получения оттуда нового имени. Разве это не было бы весело?
Что, если вы решите проблему? Вместо того, чтобы пытаться получить имена из переменных, получите переменные из имен:
["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
}
Если бы была вероятность того, что одна из переменных вообще не была определена (в отличие от того, что она не является массивом), вам нужно было бы добавить «rescue nil» в конец строки «param = ...», чтобы сохранить eval от выброса исключения ...
Есть ли здесь альтернатива использованию eval.
Foo - это только место для хранения указателя на данные. Данные не знают, что на это указывает. В системах Smalltalk вы можете запросить у виртуальной машины все указатели на объект, но это даст вам только объект, содержащий переменную foo, а не сам foo. В Ruby нет реального способа ссылаться на vaiable. Как упоминалось в одном из ответов, вы все равно можете разместить тег в данных, который ссылается на то, откуда он пришел или что-то в этом роде, но в целом это не очень подходит для большинства проблем. Вы можете использовать хеш для получения значений в первую очередь или использовать хеш для передачи в свой цикл, чтобы вы знали имя аргумента для целей проверки, как в ответе DBR.
Наиболее близким к реальному ответу на ваш вопрос является использование метода Enumerable each_with_index вместо each, таким образом:
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
Я удалил оператор return из блока, который вы передавали в each / each_with_index, потому что он ничего не значил. Каждый и each_with_index возвращают массив, с которым они работали.
Здесь также стоит отметить кое-что об области видимости в блоках: если вы определили переменную вне блока, она будет доступна внутри него. Другими словами, вы можете ссылаться на foo, bar и baz прямо внутри блока. Обратное неверно: переменные, которые вы создаете впервые внутри блока, не будут доступны за его пределами.
Наконец, синтаксис do / end предпочтительнее для многострочных блоков, но это просто вопрос стиля, хотя он универсален в рубиновом коде любого недавнего выпуска.
Отличный вопрос. Я полностью понимаю вашу мотивацию. Позвольте мне начать с того, что отметим, что существуют определенные виды специальных объектов, которым при определенных обстоятельствах известна переменная, которой они были присвоены. Эти специальные объекты, например. Экземпляры Module, экземпляры Class и экземпляры Struct:
Dog = Class.new
Dog.name # Dog
Загвоздка в том, что это работает только тогда, когда переменная, которой выполняется присвоение, является константой. (Все мы знаем, что константы Ruby - это не что иное, как эмоционально чувствительные переменные.) Таким образом:
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"
Такое поведение объектов, знающих, каким переменным они назначены, обычно называется постоянная магия (поскольку оно ограничено константами). Но этот очень желательный постоянная магия работает только для определенных объектов:
Rover = Dog.new
Rover.name #=> raises NoMethodError
К счастью, Я написал жемчужину y_support/name_magic позаботится об этом за вас:
# first, gem install y_support
require 'y_support/name_magic'
class Cat
include NameMagic
end
Тот факт, что это работает только с константами (т.е. переменными, начинающимися с заглавной буквы), не является таким большим ограничением. Фактически, это дает вам свободу называть или не называть ваши объекты по желанию:
tmp = Cat.new # nameless kitty
tmp.name #=> nil
Josie = tmp # by assigning to a constant, we name the kitty Josie
tmp.name #=> :Josie
К сожалению, это не будет работать с литералами массива, потому что они создаются внутри без использования метода #new, на который полагается NameMagic. Следовательно, чтобы достичь того, чего вы хотите, вам нужно создать подкласс 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
Я добился постоянного поведения, имитирующего магию, с помощью поиск всех констант во всех пространствах имен текущего пространства объектов Ruby. Это тратит впустую долю секунды, но поскольку поиск выполняется только один раз, производительность не снижается после того, как объект узнает свое имя. В будущем основная команда Ruby обещал const_assigned крючок.
Если бы я мог это понять, это мог бы быть действительно интересный вопрос. Здесь нам нужен дополнительный контекст. Когда вам нужно, но вы не знаете имя переменной? Можем ли мы сказать «почему» или два назад - какую проблему вы пытаетесь решить?