How does Array#sample work, if you pass a range as random numbers generator?
As here :
> [*1..10].sample(random: 1..3)
=>9
As per the doc, sample(random: rng), rng is not a Range Object, as you might thought. rng is a Random number generator.
The optional rng argument will be used as the random number generator.
a = (1..10).to_a
r = Random.new
r2 = r.dup
a1 = a.sample(random: r)
a2 = a.sample(random: r2)
a1 == a2 # => true
The point is #sample, takes its second argument as keyword argument. If we use something like foo: 12 or rng: (1..2), it will give ArgumentError: unknown keyword:. The optional argument will be acceptable only when you will supply it a value as random: <any random number generator>. Now, coming to your point :
r = 1..3
a1 = [*1..10].sample(random: r)
a2 = [*1..10].sample(random: r)
a1 == a2 # => false
When you are passing the second argument as random: r, r must be Random object, or an object that responds to #rand. Remember with the second argument, you are telling #sample, to use your random number generator instead of the default one it uses in absence of the optional argument.
Here is one custom implementation of a RNG :
ob = Object.new
def ob.to_int
5000
end
gen_to_int = proc do |max|
ob
end
class << gen_to_int
alias rand call
end
ary = (0...10000).to_a
ary.sample(random: gen_to_int) # => 5000
ary.sample(random: gen_to_int) # => 5000
ary.sample(random: gen_to_int) # => 5000
This will give you the understanding about #sample with the optional argument. Look for more examples from #test_sample_random.
Update
How does
Array#samplework, if you pass a range as random numbers generator?
To answer this, I would take a help of the TracePoint class.
trace = TracePoint.new(:c_call) do |tp|
p [tp.lineno, tp.defined_class, tp.method_id, tp.event]
end
trace.enable do
[1,2,3,4,5,66,4].sample(random: 1..3)
end
# >> [6, Array, :sample, :c_call]
# >> [6, Kernel, :rand, :c_call]
# >> [6, Kernel, :respond_to_missing?, :c_call]
So, from the above call stack, you can see - Array#sample method has been called. Now internally, Ruby called Kernel#rand on the Range object. Now, (1..3).respond_to? returned false(because #rand - it is a_private_ instance method for Range), that's why finally #respond_to_missing? method has been called to do the job.
rng in sample(random: rng) is actually a random number generator? Could you explain how?random... It will understand.