I have a class that primarily implements some logic around a multi-dimensional array, which is essentially a grid of numbers. These numbers can swap positions, etc.. However, when they swap, other objects of the same class also appear to be modified. I'm not sure why.
I'm using instance variables to store the grid, so I don't understand why changes are apparently affecting other class members.
Here's a simplified example;
class TestGrid
attr_accessor :grid
#grid = []
def initialize(newgrid)
#grid = newgrid
end
def to_s
out = ""
#grid.each{|row|
out += row.join("|") + "\n"
}
out
end
def swap(x, y)
#grid[x[0]][x[1]], #grid[y[0]][y[1]] = #grid[y[0]][y[1]], #grid[x[0]][x[1]]
end
end
When we interact with a single instance in IRB, things look fine;
1.9.3-p385 :001 > require './TestGrid.rb'
=> true
1.9.3-p385 :002 > x = TestGrid.new([[1,2],[3,4]])
=> 1|2
3|4
1.9.3-p385 :003 > x.swap([0,1],[1,1])
=> [4, 2]
1.9.3-p385 :004 > puts x
1|4
3|2
=> nil
However, if I create a second instance by cloning or duping;
1.9.3-p385 :006 > x = TestGrid.new([[1,2],[3,4]])
=> 1|2
3|4
1.9.3-p385 :007 > y = x.clone
=> 1|2
3|4
1.9.3-p385 :008 > x.swap([0,1],[1,1])
=> [4, 2]
1.9.3-p385 :009 > puts x
1|4
3|2
=> nil
1.9.3-p385 :010 > puts y
1|4
3|2
=> nil
Why are my changes to x also being applied to y? From my understanding of Object#Clone, theses are supposed to be distinct instance, unrelated to each other. Their object ID's would seem to support that expectation;
1.9.3-p385 :012 > puts "#{x.object_id} #{y.object_id}"
70124426240320 70124426232820
For reference, I ended up creating an initialize_copy method which ensures the affected parameter is deep copied. I didn't really like the idea of Marshalling objects around just to copy an array deeply, so I decided on this instead.
def initialize_copy(original)
super
#grid = []
original.grid.each{|inner|
#grid << inner.dup
}
end
By default, dup and clone produce shallow copies of the objects they are invoked on. Meaning that x and y in your example still reference the same area of memory.
http://ruby-doc.org/core-2.0/Object.html#method-i-dup
http://ruby-doc.org/core-2.0/Object.html#method-i-clone
You can override them inside of your customized class to produce a deep copy in a different area of memory.
A common idiom in Ruby is to use the Marshal#load and Marshal#dump methods of the Object superclass to produce deep copies. (Note: these methods are normally used to serialize/deserialze objects).
def dup
new_grid = Marshal.load( Marshal.dump(#grid) )
new_grid
end
irb(main):007:0> x = TestGrid.new([[1,2],[3,4]])
=> 1|2
3|4
irb(main):008:0> y = x.dup
=> [[1, 2], [3, 4]]
irb(main):009:0> x.swap([0,1],[1,1])
=> [4, 2]
irb(main):010:0> puts x
1|4
3|2
=> nil
irb(main):011:0> y
=> [[1, 2], [3, 4]]
irb(main):012:0> puts y
1
2
3
4
=> nil
irb(main):013:0>
y remains the same after the swap.
Alternatively, create a new array, iterate through #grid and push its subarrays into the array.
def dup
new_grid = []
#grid.each do |g|
new_grid << g
end
new_grid
end
Related
How to implement proper 2D indexer with the following class? The following was my first shot
class MyArray
#init 2D array, set all elements at 0
def initialize(size)
#array = []
0.upto(size - 1) {|x|
#array[x] = []
0.upto(size - 1) {|y|
#array[x][y] = 0
}
}
end
def [](*args)
#array[args[0]][args[1]]
end
def []=(*args)
#array[args[0]][args[1]] = args[2]
end
end
and it works just fine for
test = MyArray.new(3)
test[1, 1] = 5
but I would like to make it working also for
test[1][1] = 5
which now gives a compilation error
in `[]': no implicit conversion from nil to integer (TypeError)
in the [] method.
I doubt there is any need for a class for that task in Ruby. create and index a 2d array is ruby is very simple :
1.9.3p194 :001 > a = Array.new(3){[]} #create a array with 3 rows
=> [[], [], []]
1.9.3p194 :002 > a[1][2]=3 #assignment
=> 3
1.9.3p194 :003 > a[1][2] #index
=> 3
1.9.3p194 :004 > a
=> [[], [nil, nil, 3], []]
I have a class which inherits from Array (in practice, it's just a mask for a multidimensional array).
I want to override its to_a method:
def to_a
self.each.with_index { |el, i| el.map {|j| j} }
end
but this messes things up: when I try to test my function:
it 'should be non destructive' do
a_board = Representation.new(#a_size)
a_clean_board = Representation.new(#a_size)
expect(a_board).to eq(a_clean_board)
# Try to modify a_board
arr = a_board.to_a
arr.pop
a_board.to_a.pop
# Check that it stayed equal to a_clean_board
expect(a_board).to eq(a_clean_board)
end
both calls to pop have side effects on the original board.
How can I avoid this?
Probably because it returns reference to the same object. To avoid this use map instead of each or use .dup at the end.
UPD
As I said, just use map. Like it's in functional programming where intentionally are no side-effects. Example:
class WrappedArr < Array
def to_a
map { |el| el.map {|el2| el2 } }
end
end
w_arr = WrappedArr.new([[1,2], [2,3]])
# => [[1, 2], [2, 3]]
2.0.0p247 :012 > w_arr.to_a.object_id # always different as it is different object
#=> 70318090081080
2.0.0p247 :013 > w_arr.to_a.object_id
# => 70318095088040
2.0.0p247 :014 > w_arr.to_a.object_id
# => 70318095081540
# final test
2.0.0p247 :015 > w_arr.to_a.pop
# => [2, 3]
2.0.0p247 :016 > w_arr
# => [[1, 2], [2, 3]]
Say you have the following Ruby hash,
hash = {:a => [[1, 100..300],
[2, 200..300]],
:b => [[1, 100..300],
[2, 301..400]]
}
and the following functions,
def overlaps?(range, range2)
range.include?(range2.begin) || range2.include?(range.begin)
end
def any_overlaps?(ranges)
# This calls to_proc on the symbol object; it's syntactically equivalent to
# ranges.sort_by {|r| r.begin}
ranges.sort_by(&:begin).each_cons(2).any? do |r1, r2|
overlaps?(r1, r2)
end
end
and it's your desire to, for each key in hash, test whether any range overlaps with any other. In hash above, I would expect hash[:a] to make me mad and hash[:b] to not.
How is this best implemented syntactically?
hash.each{|k, v| puts "#{k} #{any_overlaps?( v.map( &:last )) ? 'overlaps' : 'is ok'}."}
output:
a overlaps.
b is ok.
Here's another way to write any_overlaps:
def any_overlaps?(ranges)
(a = ranges.map { |r| [r.first, r.last] }.sort_by(&:first).flatten) != a.sort
end
any_overlaps? [(51..60),(11..20),(18..30),(0..10),(31..40)] # => true
any_overlaps? [(51..60),(11..20),(21..30),(0..10),(31..40)] # => false
Input : [1,2,2,3,4,2]
Output : Index of 2 = [1,2,5]
A method like this:
def indexes_of_occurrence(ary, occ)
indexes = []
ary.each_with_index do |item, i|
if item == occ
indexes << i
end
end
return indexes
end
Gives you the following:
irb(main):048:0> indexes_for_occurrence(a, 2)
=> [1, 2, 5]
irb(main):049:0> indexes_for_occurrence(a, 1)
=> [0]
irb(main):050:0> indexes_for_occurrence(a, 7)
=> []
I'm sure there's a way to do it a one liner (there always seems to be!) but this'll do the job.
A nice, single line, clean answer depends on what version of Ruby you are running. For 1.8:
require 'enumerator'
foo = [1,2,2,3,4,2]
foo.to_enum(:each_with_index).collect{|x,i| i if x == 2 }.compact
For 1.9:
foo = [1,2,2,3,4,2]
foo.collect.with_index {|x,i| i if x == 2}.compact
Easy with find_all:
[1,2,2,3,4,2].each_with_index.find_all{|val, i| val == 2}.map(&:last) # => [1, 2, 5]
Note: If using Ruby 1.8.6, you can require 'backports/1.8.7/enumerable/find_all'
Does anyone use tuples in Ruby? If so, how may one implement a tuple? Ruby hashes are nice and work almost as well, but I'd really like to see something like the Tuple class in Python, where you can use . notation to find the value for which you are looking. I'm wanting this so that I can create an implementation of D, similar to Dee for Python.
OpenStruct?
Brief example:
require 'ostruct'
person = OpenStruct.new
person.name = "John Smith"
person.age = 70
person.pension = 300
puts person.name # -> "John Smith"
puts person.age # -> 70
puts person.address # -> nil
Based on the fact that you talk about hashes and . notation I'm going to assume you mean a different kind of tuple than the (1. "a") sort. You're probably looking for the Struct class. eg:
Person = Struct.new(:name, :age)
me = Person.new
me.name = "Guy"
me.age = 30
While this isn't strictly a tuple (can't do dot notation of members), you can assign a list of variables from a list, which often will solve issues with ruby being pass-by-value when you are after a list of return values.
E.g.
:linenum > (a,b,c) = [1,2,3]
:linenum > a
=> 1
:linenum > b
=> 2
:linenum > c
=> 3
Arrays are cool to use as tuples because of destructuring
a = [[1,2], [2,3], [3,4]]
a.map {|a,b| a+b }
Struct give you convenient . accessors
Person = Struct.new(:first_name, :last_name)
ppl = Person.new('John', 'Connor')
ppl.first_name
ppl.last_name
You can get the convenience of both worlds with to_ary
Person = Struct.new(:first_name, :last_name) do
def to_ary
[first_name, last_name]
end
end
# =>
[
Person.new('John', 'Connor'),
Person.new('John', 'Conway')
].map { |a, b| a + ' ' + b }
# => ["John Connor", "John Conway"]
I'm the author of Gem for Ruby tuples.
You are provided with two classes:
Tuple in general
Pair in particular
You can initialize them in different ways:
Tuple.new(1, 2)
Tuple.new([1, 2])
Tuple(1, 2)
Tuple([1, 2])
Tuple[1, 2]
Both of the classes have some auxiliary methods:
length / arity - which returns number of values inside tuple
first / last / second (only pair) - which returns a corresponding elements
[] that gives you an access to a particular elements
You can mock the Scala tuples with this trick :
Tuple = Struct.new(:_1, :_2)
2.2.5 :003 > t = Tuple.new("a", "b")
=> #<struct Tuple _1="a", _2="b">
2.2.5 :004 > t._1
=> "a"
2.2.5 :005 > t._2
=> "b"
but here you can't have destructuring:
2.2.5 :012 > a, b = t
=> {:_1=>"a", :_2=>"b"}
2.2.5 :013 > a
=> {:_1=>"a", :_2=>"b"}
2.2.5 :014 > b
=> nil
But thanks to this trick : https://gist.github.com/stevecj/9ace6a70370f6d1a1511
destructuring will work:
2.2.5 :001 > Tuple = Struct.new(:_1, :_2)
=> Tuple
2.2.5 :002 > t = Tuple.new("a", "b")
=> #<struct Tuple _1="a", _2="b">
2.2.5 :003 > t._1
=> "a"
2.2.5 :004 > class Tuple ; def to_ary ; to_a ; end ; end
=> :to_ary
2.2.5 :005 > a, b = t
=> #<struct Tuple _1="a", _2="b">
2.2.5 :006 > a
=> "a"
2.2.5 :007 > b
=> "b"
You can do something similiar with destructuring:
def something((a, b))
a + b
end
p something([1, 2])
This prints out 3 as expected.