i'm attempting to understand why the following code behaves (to my knowledge) oddly.
[1] pry(main)> a = {}
=> {}
[2] pry(main)> a[1] = [[0,0]] * 7
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[3] pry(main)> a[2] = [[0,0]] * 7
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[4] pry(main)> a[1][2][0] = 3 # Should be one value changed, right?
=> 3
[5] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
What i thought should happen is that one value of the array in the hash a at key 1 at index 2 should change to 3, but instead all the first values of the entire array change to 3. What's going on here, what am I missing? Here's my Ruby version.
$ ruby -v
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-linux]
EDIT:
I also tried the following
[1] pry(main)> a = {}
=> {}
[2] pry(main)> a[1] = ([[0,0].dup].dup * 7).dup
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[3] pry(main)> a[2] = ([[0,0].dup].dup * 7).dup
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[4] pry(main)> a[1][2][0] = 3
=> 3
[5] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
[6] pry(main)> a = {}
=> {}
[7] pry(main)> a[1] = ([[0,0].clone].clone * 7).clone
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[8] pry(main)> a[2] = ([[0,0].clone].clone * 7).clone
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
[9] pry(main)> a
=> {1=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
[10] pry(main)> a[1][2][0] = 3
=> 3
[11] pry(main)> a
=> {1=>[[3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0], [3, 0]],
2=>[[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]}
Surely the values should be copies?
All the elements in a[1] refer to the same array.
[0,0] isn't deep-copied when you do [[0,0]] * 7.
Solution: a[1] = Array.new(7) { [0,0] } (thanks #Stefan!)
Here's how to diagnose this problem in the future:
object_id will tell you the "slot" where Ruby keeps an object. Objects should have unique IDs:
foo = []
bar = []
foo.object_id # => 70322472940660
bar.object_id # => 70322472940640
If they don't, though they might be different variable names, they're still pointing to the same object. That might be desirable in more advanced uses, but generally it's not something you want because changing one will change the other leading to confusion, gnashing teeth and possibly the return of "he who should not be named". For instance, we get fooled when we look at:
[[]] * 2 # => [[], []]
because it seems that we're getting back two arrays, which could be assigned using parallel assignment, like:
foo, bar = [[]] * 2
But, when we look at foo and bar to see where they're stored we can see the problem, they're both pointing to the same slot, and so changing one changes the other:
foo.object_id # => 70323794190700
bar.object_id # => 70323794190700
foo << 1
bar # => [1]
This is an ages old problem when writing code; You gotta learn about "pass by value" vs. "pass by reference" and when a language is doing one or the other otherwise this problem will occur. And then you have to learn how to tell the language to avoid the problem.
Related
I'm trying to write a generalized cartesian product, where input data of [n1, n2, ...ni] produces output data that is an array of [m1, m2, ...mi] for all mj such that 0 <= mj < nj. I understand the routine below would produce a somewhat folded version of that, but I'm trying to keep the example code as simple as possible. My immediate problem is that the second block variable (accumulator), which I understand is supposed to update for each iteration of the block, is not doing so:
#!/usr/bin/ruby
def gcp(dims)
first = dims.shift
dims.each_with_object((0...first).to_a) do |dim, v|
puts "\nv: #{v}, dim: #{dim}"
p v.product((0...dim).to_a)
end
end
gcp([3,2,4])
This produces the following output:
v: [0, 1, 2], dim: 2
[[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]
v: [0, 1, 2], dim: 4
[[0, 0], [0, 1], [0, 2], [0, 3], [1, 0], [1, 1], [1, 2], [1, 3], [2, 0], [2, 1], [2, 2], [2, 3]]
The p method is a passthrough, so the return value of the block should be [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]] on the first iteration, and that should be the value of v on the second iteration, unless I gravely misunderstand each_with_object.
Each iteration gets the same object, so you either need to mutate the object inside the block, or use reduce.
def gcp(dims)
first = dims.shift
dims.reduce((0...first).to_a) do |v, dim|
puts "\nv: #{v}, dim: #{dim}"
p v.product((0...dim).to_a)
end
end
gcp([3,2,4])
Results in:
v: [0, 1, 2], dim: 2
[[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]]
v: [[0, 0], [0, 1], [1, 0], [1, 1], [2, 0], [2, 1]], dim: 4
[[[0, 0], 0], [[0, 0], 1], [[0, 0], 2], [[0, 0], 3], [[0, 1], 0], [[0, 1], 1], [[0, 1], 2], [[0, 1], 3], [[1, 0], 0], [[1, 0], 1], [[1, 0], 2], [[1, 0], 3], [[1, 1], 0], [[1, 1], 1], [[1, 1], 2], [[1, 1], 3], [[2, 0], 0], [[2, 0], 1], [[2, 0], 2], [[2, 0], 3], [[2, 1], 0], [[2, 1], 1], [[2, 1], 2], [[2, 1], 3]]
I confess I don't fully understand the question, but I've addressed a similar problem that may explain why v is not being updated by your code.
Let's step through your code, returning the desired result rather than displaying it along the way.
dims = [3,2,4]
first = dims.shift
#=> 3
dims
#=> [2, 4] dims
The expression
dims.each_with_object((0...first).to_a) do |dim, v|
v.product((0...dim).to_a)
end
is effectively the same as
v = []
dims.each do |dim|
v.product((0...dim).to_a)
end
v #=> []
That v is still an empty array at the end should not be a surprise, as the value of v is not altered within the loop. The return value of v.product((0...dim).to_a) is shot out into space, never to be seen again. You need an assignment statement within the loop.
Now consider the following.
dims = [3,2,4]
v = []
dims.each do |n|
v << (0...n).to_a
end
v #=> [[0, 1, 2], [0, 1], [0, 1, 2, 3]]
(or v.push((0..n).to_a)). To use Enumerable#each_with_object we would modify the above code by removing the first (v = []) and last (v) statements, changing each to each_with_object([]) (the argument being the initial value of the object the method will return) and add a block variable v, which holds the object:
dims.each_with_object([]) do |n,v|
v << (0...n).to_a
end
#=> [[0, 1, 2], [0, 1], [0, 1, 2, 3]]
We can simplify this using Emumerable#map:
dims.map do |n|
(0...n).to_a
end
#=> [[0, 1, 2], [0, 1], [0, 1, 2, 3]]
Depending on your needs, you may prefer using Emumerable#flat_map:
dims.flat_map do |n|
(0...n).to_a
end
#=> [0, 1, 2, 0, 1, 0, 1, 2, 3]
Say I have the following input:
inp = [2, 9, 3]
I need output as all tuples in mixed counting, like this:
outp = [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], ..., [1, 8, 2]]
I know algorithm from Knuth vol 4a as direct loop solution, but I've heard ruby has some magic inside.
I am mostly C++ developer. My direct solution now looks like:
inparr = [2, 9, 3]
bmix = Array.new(inparr.size) { |i| 0 }
outp = Array.new
loop do
# some debug output
puts bmix.to_s
#visit next tuple
outp << bmix.clone
digit = inparr.size
while digit > 0 do
digit -= 1
if bmix[digit] + 1 < inparr[digit]
bmix[digit] += 1
break
end
bmix[digit] = 0
end
break if (bmix.select{|x| x != 0}.empty?)
end
How to rewrite it in several simple lines?
inp.
map { |i| (0...i).to_a }.
reduce(&:product).
map(&:flatten)
Used operations: Range, Enumerable#map, Enumerable#reduce, Array#product, Array#flatten.
You could use recursion.
def recurse(inp)
first, *rest = inp
rest.empty? ? [*0..first-1] : (0..first-1).flat_map do |e|
recurse(rest).map { |arr| [e, *arr] }
end
end
recurse [2, 4, 3]
#=> [[0, 0, 0], [0, 0, 1], [0, 0, 2],
# [0, 1, 0], [0, 1, 1], [0, 1, 2],
# [0, 2, 0], [0, 2, 1], [0, 2, 2],
# [0, 3, 0], [0, 3, 1], [0, 3, 2],
# [1, 0, 0], [1, 0, 1], [1, 0, 2],
# [1, 1, 0], [1, 1, 1], [1, 1, 2],
# [1, 2, 0], [1, 2, 1], [1, 2, 2],
# [1, 3, 0], [1, 3, 1], [1, 3, 2]]
If first, *rest = [2,4,3], then first #=> 2 and rest #=> [4,3].
See Enumerable#flat_map and Array#map. a ? b : c is called a ternery expression.
If e #=> 1 and arr #=> [2,1] then [e, *arr] #=> [1,2,1].
I will go to great lengths to avoid the use of Array#flatten. It's irrational, but to me it's an ugly method. That's usually possible using flat_map and/or the splat operator *.
Here's a mix of the 2 existing answers. It might be a bit more concise and readable:
head, *rest = inp.map{ |n| n.times.to_a }
head.product(*rest)
As an example:
inp = [2, 4, 3]
# => [2, 4, 3]
head, *rest = inp.map{ |n| n.times.to_a }
# => [[0, 1], [0, 1, 2, 3], [0, 1, 2]]
head.product(*rest)
# => [[0, 0, 0], [0, 0, 1], [0, 0, 2], [0, 1, 0], [0, 1, 1], [0, 1, 2], [0, 2, 0], [0, 2, 1], [0, 2, 2], [0, 3, 0], [0, 3, 1], [0, 3, 2], [1, 0, 0], [1, 0, 1], [1, 0, 2], [1, 1, 0], [1, 1, 1], [1, 1, 2], [1, 2, 0], [1, 2, 1], [1, 2, 2], [1, 3, 0], [1, 3, 1], [1, 3, 2]]
To create a one-dimensional array, I can write:
arr = Array.new(10) { |z| 0 }
#=> [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
But I want to know how to initialize an array in more dimensions, e.g.:
#=> [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
This is just an example. I am trying to understand the general case.
You can easily nest multiple Array.new calls:
Array.new(2, 0)
#=> [0, 0]
Array.new(3) { Array.new(2, 0) }
#=> [[0, 0], [0, 0], [0, 0]]
Array.new(4) { Array.new(3) { Array.new(2, 0) } }
#=> [[[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]]]
Note that the non-block version, i.e. Array.new(2, 0), should only be used for immutable objects, because the same object will be used for all elements. See the Common gotchas section for details.
To build an array of arbitrary dimensions, you could use recursion:
def multi_array(sizes, default = nil)
size, *remaining = sizes
if remaining.empty?
Array.new(size, default)
else
Array.new(size) { multi_array(remaining, default) }
end
end
multi_array([4, 3, 2], 0)
#=> [[[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]],
# [[0, 0], [0, 0], [0, 0]]]
You can also do it like:
Array.new(10, 0).each_slice(2).to_a
# => [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
5.times.map{Array.new(2, 0)}
# => [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
If by more dimensions you mean an array of array, you could try:
arr = Array.new(10){|z| z=Array.new(2,0)}
# => [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0,0]]
assuming that your second dimension is 2.
I am trying to understand the general case.
Array.new(n) # where n is natural number 1..n
#=>[nil, nil, nil]
This will create a 1-D Array i:e One dimension array.
Let extends further for a multi-dimensional array i:e Two dimension array .
In ruby, every method accepts a block.
Array.new(3) do
Array.new(3)
end
end
Same can be written In online (macro style) Array.new(3) { Array.new(3) }
[
[nil, nil, nil],
[nil, nil, nil],
[nil, nil, nil]
]
Further, it can extend to initialize each cell value.
Array.new(3) do
Array.new(3) do
0
end
end
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
One line micro style Array.new(3) { Array.new(3) { 0 } }
Let extends further three-dimension array.
Array.new(2) do
Array.new(3) do
Array.new(4) do
0
end
end
end
OR
The same can be written as micro-style syntax
Array.new(2) { Array.new(3) { Array.new(4) } {0} }
[
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]],
[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
]
Here's another way, though I feel the deep copy at every level could somehow be simplified:
def make_multi(dimensions, default)
dimensions.reverse.each_with_index.reduce([]) { |a,(d,i)|
Array.new(d) { i.zero? ? default : Marshal.load(Marshal.dump(a)) } }
end
arr = make_multi [4,3,2,2], 0
#=> [[[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]]]
arr[0][0][0][0] = 1
arr
#=> [[[[1, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]],
# [[[0, 0], [0, 0]], [[0, 0], [0, 0]], [[0, 0], [0, 0]]]]
arr = make_multi [4,3,2,2], {}
#=> [[[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]],
# [[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]],
# [[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]],
# [[[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]], [[{}, {}], [{}, {}]]]]
arr[0][0][0][0] = { a: 1 }
arr
#=> [[[[{:a=>1}, {}...] # all but the first are empty hashes
Try it:)
arr = Array.new(10, 0)
UPD: for multidimensional is:
arr = Array.new(10, Array.new(2, 0))
=> [[0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]]
Supposing we have an array with this shape: [2, 5]. The possible index combinations are:
[
[0, 0],
[0, 1],
[0, 2],
[0, 3],
[0, 4],
[1, 0],
[1, 1],
[1, 2],
[1, 3],
[1, 4]
]
If the array has n dimension(s), is there a simple way to generate the indices in Ruby?
This should work:
def coordinates(first, *others)
(0...first).to_a.product(*others.map { |to| (0...to).to_a })
end
coordinates(2, 5)
#=> [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4],
# [1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]
coordinates(4, 3, 3)
#=> [[0, 0, 0], [0, 0, 1], [0, 0, 2],
# [0, 1, 0], [0, 1, 1], [0, 1, 2],
# [0, 2, 0], [0, 2, 1], [0, 2, 2],
# [1, 0, 0], [1, 0, 1], [1, 0, 2],
# [1, 1, 0], [1, 1, 1], [1, 1, 2],
# [1, 2, 0], [1, 2, 1], [1, 2, 2],
# [2, 0, 0], [2, 0, 1], [2, 0, 2],
# [2, 1, 0], [2, 1, 1], [2, 1, 2],
# [2, 2, 0], [2, 2, 1], [2, 2, 2],
# [3, 0, 0], [3, 0, 1], [3, 0, 2],
# [3, 1, 0], [3, 1, 1], [3, 1, 2],
# [3, 2, 0], [3, 2, 1], [3, 2, 2]]
Here you go. It may not be pretty, but it seems to work.
def gen_indices(dimensions, solutions = [], current=[], level = 0)
if level < dimensions.length
dimensions[level].times do |i|
current << i
if level == dimensions.length - 1
solutions << current.clone
else
gen_indices(dimensions, solutions, current, level + 1)
end
current.pop
end
end
solutions
end
p gen_indices([4,3,2])
Not sure this is totally correct, but it should help you on the right track:
def combinations(dimensions)
dimensions.inject([]){|total, dimension|
dim = (0...dimension).to_a
total.empty? ? dim : total.product(dim)
}.map(&:flatten)
end
p combinations([2, 5]) #=> [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], [1, 2], [1, 3], [1, 4]]
p combinations([2, 2, 2]) #=> [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Try:
a = [[0,1],[0,1,2,3,4]]
res = a[0].product(a[1])
Code works, but feels very brute force, suggestions?
Goal of the code is to supply an array length, and then as fast as possible generate all possible unique binary combinations with that array length.
CODE:
class Array
def sequence(i = 0, *a)
return [a] if i == size
self[i].map {|x|
sequence(i+1, *(a + [x]))
}.inject([]) {|m, x| m + x}
end
end
[(0..1),(0..1),(0..1)].sequence
OUTPUTS:
[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
permutation and repeated_permutation are built in, so you can do:
def sequence(n)
[0, 1].repeated_permutation(n).to_a
end
p sequence(3) #=>[[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
"All unique binary combinations" with n bits is nothing but (0...2**n), so the only task is to efficiently convert from an integer to its binary representation, and the following is a solution that does not rely on string generation/manipulation:
def sequence(n)
ret = []
(2**n).times do |number|
ret << []
(n - 1).downto(0) do |bit|
ret.last << number[bit]
end
end
ret
end
sequence(3)
# => [[0, 0, 0], [0, 0, 1], [0, 1, 0], [0, 1, 1], [1, 0, 0], [1, 0, 1], [1, 1, 0], [1, 1, 1]]
Or, if you prefer a version more oriented on list operations, this is pretty much the same:
def sequence(n)
(0...2**n).map {|number|
(1..n).map {|bit|
number[n-bit]
}
}
end