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.