In the following Ruby code I have two methods d100_in_detect
and d100_out_detect
which return an Array
of unique elements (numbers for simplicity) contained in ary
according to the result of d100
.
def d100
1 + ( rand 100 )
end
def d100_in_detect( ary )
choice = [ ]
100.times do
choice.push ary.detect { |el| d100 <= el }
end
choice.uniq.sort
end
def d100_out_detect( ary )
choice = [ ]
numbers = [ ]
100.times do
numbers.push d100
end
numbers.each do |i|
choice.push ary.detect { |el| i <= el }
end
choice.uniq.sort
end
As you can see the difference between the two methods is that in the first d100
is called inside detect
's block, while in the second 100 random numbers are stored in numbers
Array and then are used as it happens in d100_in_detect
.
Let's suppose I call the two methods as follows
ary = [ ]
50.times do |i|
ary.push i * 5
end
puts '# IN DETECT #'
print d100_in_detect ary
puts
puts '# OUT DETECT #'
puts d100_out_detect ary
puts
A typical output is the following.
# IN DETECT #
[ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55 ]
# OUT DETECT #
[ 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100 ]
I can't figure out why the two methods return such different results.
Is there any implication in calling d100
method in detect
's block?
Right.
First thing I did was to modify your sample script:
def d100_in_detect( ary )
choice = [ ]
numbers = []
100.times do
var = d100
numbers << var
choice.push ary.detect { |el| var <= el }
end
puts numbers.inspect
choice.uniq.sort
end
def d100_out_detect( ary )
choice = [ ]
numbers = [ ]
100.times do
numbers.push d100
end
puts numbers.inspect
numbers.each do |i|
choice.push ary.detect { |el| i <= el }
end
choice.uniq.sort
end
As you can see, all I did was assign the result of the d100 to a temporary variable to see what was happening....and the 'bug' went away! My return values were suddenly identical. Hmm.
Then, it occurred to me exactly what was going on. When you 'cache' the variable (as you do in the second example), you guarantee you have a spread of 100 numbers.
When you iterate through the block, for each number in the block, you execute the d100 again. Hence, the first one has vastly more calls than the second one...but you also require the randomly generated number at the time that number was called to be greater than the number (whereas, if you randomly generate 100 with 2, you can guarantee it will hit the 100 at some point). This drastically bias's your script towards the lower numbers!
For an example, run:
@called_count = 0
def d100
@called_count += 1
1 + ( rand 100 )
end
def d100_in_detect( ary )
choice = [ ]
numbers = []
100.times do
choice.push ary.detect { |el| d100 <= el }
end
puts @called_count.inspect
@called_count = 0
choice.uniq.sort
end
def d100_out_detect( ary )
choice = [ ]
numbers = [ ]
100.times do
numbers.push d100
end
puts @called_count.inspect
@called_count = 0
numbers.each do |i|
choice.push ary.detect { |el| i <= el }
end
choice.uniq.sort
end
I get
# IN DETECT #
691
# OUT DETECT #
100
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With