Экземпляр класса с именами, определенными строкой - PullRequest
0 голосов
/ 11 октября 2019

В 'pure ruby' (не rails), для класса:

class Person
end

... и массива строк:

people_names = ['John', 'Jane', 'Barbara', 'Bob']

как мне создать экземпляр Person класс, с каждой переменной экземпляра, названной одним из элементов в моем массиве?

John = Person.new
Jane = Person.new
Barbara = Person.new
Bob = Person.new

Ответы [ 3 ]

0 голосов
/ 11 октября 2019

Довольно неясно, что вы на самом деле хотите здесь, поскольку идентификатор в ruby, начинающийся с заглавной буквы в Ruby, является константой.

John = Person.new
Jane = Person.new
Barbara = Person.new
Bob = Person.new

Вы можете динамически назначать константы с помощью Module#const_set.

module MyModule
  ['John', 'Jane', 'Barbara', 'Bob'].each do |name|
    const_set(name, Person.new)
  end
end

# this imports the constants into Main which is the global scope
include MyModule

John
=> #<Person:0x007f973586a618>

Переменные экземпляра с другой стороны используют @ sigil. Вы можете динамически назначать переменные экземпляра с помощью instance_variable_set :

['John', 'Jane', 'Barbara', 'Bob'].map(&:downcase).each do |name|
  instance_variable_set("@#{name}", Person.new)
end

@john       
# => #<Person:0x007f9734089530>

Хотя вы можете объявить переменную экземпляра с именем @John, это нарушает соглашения языка.

Локальные переменныена самом деле не может быть определено динамически. Вы можете изменять существующие переменные только через eval и binding.local_variable_set.

def foo
  a = 1
  bind = binding
  bind.local_variable_set(:a, 2) # set existing local variable `a'
  bind.local_variable_set(:b, 3) # create new local variable `b'
                                 # `b' exists only in binding

  p bind.local_variable_get(:a)  #=> 2
  p bind.local_variable_get(:b)  #=> 3
  p a                            #=> 2
  p b                            #=> NameError
end
0 голосов
/ 11 октября 2019

Вы, конечно, можете это сделать, хотя, как говорит Брэндон, это, вероятно, не очень хорошая идея. Вот как это сделать:

people_names.each { |name| eval("#{name} = Person.new")}

eval принимает строку, переданную в качестве аргумента, и выполняет ее как строку кода. Таким образом, вы используете each, чтобы сделать это один раз для каждого члена вашего массива.

Вы можете зайти в Google "eval", чтобы увидеть любое количество статей о том, почему это зло. Многие из них уходят от метапрограммирования (eval - пример метапрограммирования) в целом и дают некоторые хорошие замечания. У меня есть немного более умеренный подход к метапрограммированию, чем этот (attr_accessor, в конце концов, тоже пример метапрограммирования, и люди все время его используют), но вы, конечно, можете написать какой-то очень запутанный код, используя eval.

Заметьте также, что, как отмечали несколько авторов, при использовании заглавных букв в ваших строках вы определяете их как константы. Вы можете изменить значение константы в Ruby, но вы будете получать предупреждение каждый раз, когда будете это делать.

0 голосов
/ 11 октября 2019

Я уверен, что у Руби есть какие-то средства для вас, чтобы динамически определять константы, но я не буду беспокоиться об этом, потому что это почти на 100% похоже на то, что вы на самом деле не хотите делать. Кажется, вы хотите каким-то образом связать «имя» с экземпляром класса. Это именно то, для чего Hash.

people_names = ['John', 'Jane', 'Barbara', 'Bob']
people = people_names.each_with_object({}) do |name, ppl|
  ppl[name] = Person.new(name)
end
people['John'].name # => 'John'
people['Jane'].name # => 'Jane'

Почему я говорю, что вы просите, вероятно, не то, что вы хотите? Потому что использование метапрограммирования для динамического создания и динамического чтения из локальных переменных / констант / переменных экземпляра, как правило, не одобряется при профессиональном развитии. Возможно, для ваших собственных проектов, для экспериментов. Тем не менее, для любого проекта в составе команды, когда вы начинаете использовать функции метапрограммирования, чтобы динамически добавлять эти значения и ссылаться на них (возможно, напрямую, возможно, косвенно позже), все хорошо, но когда вы пытаетесь выяснить, что происходит, выпочти никогда не будет в состоянии выяснить, откуда эти вещи определены / исходят, если массив с динамическими именами не будет жестко закодирован. И если это жестко запрограммировано, почему вы не можете просто создать константы / переменные / цели непосредственно в коде? Это значительно лучше, чем динамически делать это.

# this is a fake API to demonstrate
# let's assume this is what you want
PEOPLE_NAMES = ['John', 'Jane']
PEOPLE_NAMES.each do |name|
  const_def(name, Person.new)
end

get_person('Jane').do_something # maps to const_get('Jane').do_something
get_person(PEOPLE_NAMES[0]).do_something
John.do_something

Если вы хотите, чтобы вышесказанное, почему вы не можете просто сделать:

John = Person.new
Jane = Person.new

John.do_something

Последнее выглядит намного яснее, все еще может бытьдинамически просматривается, но имеет жестко заданное определение, которое можно легко использовать при отладке.

Это моя рекомендация и ответ. Я уверен, что ты не хочешь делать то, что просишь. Hash полностью соответствует потребностям, которые вы желаете, он интенсивно используется для подобных целей и тесно связан с ним, я рекомендую вам попробовать и заставить это работать в соответствии с вашими потребностями, а затем попытаться выяснить, как решить проблему, которую вы специально хотите получитьответ тоже.

РЕДАКТИРОВАТЬ

Как действительно забавное дополнение, вы можете сделать несколько действительно крутых динамических вещей с Hash, которые здесь не приводят ктонны путаницы, если вам не удалось скрыть, откуда взялся хэш. Но вы можете сделать что-то вроде:

people = Hash.new { |h, k| h[k] = Person.new(k) }
# right now, people contains no actual people
people['John'].name # => 'John'
# now people contains one Person instance

Это круто по двум причинам: 1) Вам не нужно иметь список для генерации хеша, поэтому, если вы получите имена после создания хеша, это нормально, вы можетепросто добавьте их, указав имя пользователя и 2) Будучи ленивым, он будет использовать только ту память, которая вам нужна. Если вы предварительно загрузили хеш со всеми четырьмя людьми, а затем получили доступ к данным только от двух людей, вы потратили впустую пространство, необходимое для неиспользованных экземпляров 2 Person, так что это позволяет вам использовать только столько, сколько вам нужно, и в противном случае предлагает вам все то же самоевыгоды.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...