Array members do not change

Hello. I am trying to generate a 10x6 matrix, which will contain 10
random permutations, each permutation with a length of 6 (by permutation
of length n I mean a set of non-duplicate numbers from 1 to n in any
order). Here is the code:

matrix = Array.new(10) { Array.new(6) }

matrix.map! do |line|
line.map! do |number|
while number.nil?
# rand makes numbers starting from 0, I need them to start from 1.
rnd = rand(6) + 1
# if number’s already in permutation I cannot put it again
number = rnd unless line.include? rnd
end
end
end

It seems what when I assign the “number” variable inside an inner block
(in my case, the while block), the value doesn’t change in the matrix.
If I eliminate the while block and just assign number, the value is
saved (but if I do that there could be duplicate numbers, and I can’t
have that in a permutation). Is it something I’m missing? I’m fairly new
to Ruby.

Thank you for your assistance.

First of all, here’s a way to do it with ruby’s in-built functions:

perm = (1..6).to_a.permutation.to_a
matrix = Array.new(10){perm.sample}

But note that this is a bad idea if you’re interested in larger
permutations (eg. 20 different numbers).

Your code has got some problem:

  • #map and #map! set each entry to the return value of the block, but
    the return value of the block is whatever the “while” loop returns,
    which is nil
  • it works, but there’s no reason to use #map! twice, you can just use
    #each and #map!, or #map! and #map
  • for the last number, there’s only one possibility, and generating
    random numbers until you get the missing one is wasteful

So the main problem is that you need to return the value at the end of
the block. Variables inside the block are local and always point to some
object. If you re-assign the variable name, you make it point to another
object, so the original one remains unchanged. However, if you use the
variable to modify the object passed to block, it changes. Observe the
following code:

a=%w[Hello world]
a.each{|x|x = x + x}
p a

This simply prints [“Hello”,“world”] because you’re re-assigning the
local variable to the newly created String object created by String#+
(x+x). Now consider this:

a=%w[Hello world]
a.each{|x|x << x}
p a

This prints [“HelloHello”,“worldworld”] because you’re modifying the
object to which the variable x points. The function String#<< adds
another string to the of it.

The reason why it works without the while loop is that the assignment
operator “=” actually returns the assigned value, not because the value
is assigned to the variable! So the following is equivalent:

[1,2,3,4].map{|x|x + 1}
[1,2,3,4].map{|x| x = x + 1}

The latter is bad style, though, as it is confusing. As a further
illustration, try running this code:

range = 2..4+n=3
puts range
puts n

In fact, the difference between Array#map and Array#map! is that the
former one creates a new array and the latter one modifies the original
array, which explains why “each” and then “map!” would work in your
original code.

Furthermore, you can just use rand(1…6) instead of rand(6)+1 and do the
generation inside the block passed to Array#new:

So if you wanted to do it manually without ruby’s in-built functions,
here’s how I’d do it:

matrix=Array.new(10) do
  numbers = [1,2,3,4,5,6]
  Array.new(6) do
    # take a random index from the array of available numbers
    idx = rand(numbers.size)
    # remove the number and return it
    numbers.delete_at(idx)
  end
end

Or shorter:

matrix=Array.new(10) do
  numbers = [1,2,3,4,5,6]
  Array.new(6) do
    # take a number, remove and return it
    numbers.delete(numbers.sample)
  end
end

Note that Array#delete and Array#delete_at return the entry that got
deleted.

Thanks a lot! I am really new to Ruby and I haven’t come across these
things until now! Now I understand my mistake.