Почему нет краткого определения новых хэшей в работе Ruby (все они ссылаются на один и тот же объект) - PullRequest
1 голос
/ 24 сентября 2011

Мне нужно установить количество хэшей, и я не хотел перечислять по одному на строку, как это

a = Hash.new
b = Hash.new

Я также новичок, что кроме Fixnums, я не мог сделать это

a = b = Hash.new

потому что и a, и b ссылаются на один и тот же объект. Что я мог, так это

a, b, = Hash.new, Hash.new 

если бы у меня была связка, казалось бы, я тоже мог бы сделать это

a, b = [Hash.new] * 2

это работает для строк, но для хэшей они все еще ссылаются на один и тот же объект, несмотря на тот факт, что

[Hash.new, Hash.new] == [Hash.new] * 2

и первые работы.

См. Пример кода ниже, единственное сообщение об ошибке - «сломан хэш умножения». Просто любопытно, почему это так.

a, b, c = [String.new] * 3
a = "hi"
puts "string broken" unless b == ""

puts "not equivalent" unless [Hash.new, Hash.new, Hash.new] == [Hash.new] * 3

a, b, c = [Hash.new, Hash.new, Hash.new]
a['hi'] = :test
puts "normal hash broken" unless b == {}

a, b, c = [Hash.new] * 3
a['hi'] = :test
puts "multiplication hash broken" unless b == {} 

Ответы [ 2 ]

4 голосов
/ 24 сентября 2011

В ответ на исходный вопрос простым способом инициализации нескольких копий было бы использование Array.new(size) {|index| block } варианта Array.new

a, b = Array.new(2) { Hash.new }
a, b, c = Array.new(3) { Hash.new }
# ... and so on

.В дополнение к путанице в назначениях, другая кажущаяся проблема с оригиналом заключается в том, что, возможно, вы ошибаетесь, когда == сравнивает ссылки на объекты Hash и String.Просто чтобы быть ясно, это не так.

# Hashes are considered equivalent if they have the same keys/values (or none at all)
hash1, hash2 = {}, {}
hash1 == hash1 #=> true
hash1 == hash2 #=> true

# so of course
[Hash.new, Hash.new] == [Hash.new] * 2 #=> true

# however
different_hashes = [Hash.new, Hash.new] 
same_hash_twice  = [Hash.new] * 2
different_hashes == same_hash_twice #=> true
different_hashes.map(&:object_id) == same_hash_twice.map(&:object_id) #=> false
3 голосов
/ 24 сентября 2011

Я так понимаю.[String.new] * 3 создает , а не создает три String объекта.Он создает one и создает трехэлементный массив, в котором каждый элемент указывает на один и тот же объект.

Причина, по которой вы не видите "поврежденную строку", заключается в том, что у вас есть a присвоено новое значение.Таким образом, после строки a = "hi", a ссылается на новый объект String ("hi"), в то время как b и c все еще ссылаются на тот же исходный объект ("").

То же самое происходит с [Hash.new] * 3;но на этот раз вы не назначаете никаких переменных.Скорее, вы изменяете один Hash объект, добавляя ключ / значение [hi, :test] (через a['hi'] = :test).На этом этапе вы изменили один объект, на который ссылаются a, b и c.

Вот пример искусственного кода, чтобы сделать это более конкретным:

class Thing
  attr_accessor :value
  def initialize(value)
    @value = value
  end
end

# a, b, and c all refer to the same Thing object
a, b, c = [Thing.new(0)] * 3

# Here we *modify* that object
a.value = 5

# Verify b refers to the same object as a -- outputs "5"
puts b.value

# Now *assign* a to a NEW Thing object
a = Thing.new(10)

# Verify a and b now refer to different objects -- outputs "10, 5"
puts "#{a.value}, #{b.value}"

Имеет ли это смысл?


Обновление : я не гуру по Ruby, так что может быть более здравый способ сделать это.Но если вы хотите иметь возможность использовать синтаксис, похожий на умножение, для инициализации массива с множеством различных объектов, вы можете рассмотреть этот подход: создать массив из lambdas , а затем вызвать их все, используя map.

Вот что я имею в виду:

def call_all(lambdas)
  lambdas.map{ |f| f.call }
end

a, b, c = call_all([lambda{Hash.new}] * 3)

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

x, y, z = call_all([lambda{rand(100)}] * 3)

# This should output 3 random (probably different) numbers
puts "#{x}, #{y}, #{z}"

Обновление 2 : мне нравится подход numbers1311407, использующий Array#new намного лучше.

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