Created
July 25, 2013 14:24
-
-
Save flarnie/6080184 to your computer and use it in GitHub Desktop.
A demo of Ruby passing by reference and the way it can mess up duplication of nested objects.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#the outer object | |
class Pen | |
attr_accessor :animals | |
def initialize | |
# Animals can be in | |
# one of three rows | |
# in the pen. | |
@animals = [[], | |
[], | |
[]] | |
end | |
def add_animal(animal, row) | |
@animals[row] << animal | |
end | |
end | |
#the inner object type | |
class Animal | |
attr_reader :name, :sounds | |
def initialize(name) | |
@name = name | |
@sounds = [] | |
end | |
def new_sound(sound) | |
@sounds << sound | |
end | |
def to_s | |
str = " #{name}:" | |
sounds.each { |sound| str += " #{sound}" } | |
str | |
end | |
end | |
#make our first pen with cows that go "moo" | |
moo_pen = Pen.new | |
moo_cow = Animal.new("cow") | |
moo_cow.new_sound("moo") | |
moo_pen.add_animal(moo_cow, 0) | |
# Now we've added a cow to row 1 of our pen. | |
puts moo_pen.animals | |
#returns | |
# cow: moo | |
# What if we want to copy moo_pen and | |
# teach all the copy_cows to talk | |
# without making the moo_pen cows | |
# any smarter? | |
# FIRST we need a dup method | |
# which we will first do | |
# the WRONG WAY: | |
# Make a 'deep dup' method which | |
# works on nested arrays. | |
module MakeCopies | |
def deep_dup(array) | |
new_arr = [] | |
array.each do |item| | |
if item.is_a?(Array) | |
new_arr << item.dup | |
else | |
new_arr << item | |
end | |
end | |
new_arr | |
end | |
end | |
# Then use it to dup the animals array. | |
class Pen | |
include MakeCopies | |
def dup | |
new_pen = Pen.new | |
#attempt to 'deep-dup' the animals | |
old_animals = animals | |
new_animals = deep_dup(old_animals) | |
new_pen.animals = new_animals | |
new_pen | |
end | |
end | |
# Finally, let's copy our moo_pen | |
# and teach the cows to talk. | |
experimental_pen = moo_pen.dup | |
experimental_pen.animals.each do |row| | |
row.each do |animal| | |
# Teach it to talk | |
animal.new_sound("Hello") | |
end | |
end | |
# Test the copy pen to see how it's animals sound: | |
puts experimental_pen.animals | |
# Returns | |
# cow: moo Hello | |
# BUT look at our original moo_pen: | |
puts moo_pen.animals | |
# Returns | |
# cow: moo Hello | |
# Do you see what happened? | |
# Both pens have a talking cow | |
# Because it is still the same cow. | |
# Ruby is referencing the same place | |
# in memory, which was altered | |
# when we added the sound "Hello". | |
# I'll post again about the possible solutions | |
# to properly duping nested objects, which | |
# my partner and I did eventually figure out. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment