4

How does Array#sample work, if you pass a range as random numbers generator? As here :

> [*1..10].sample(random: 1..3)
=>9

1 Answer 1

6

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#sample work, 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.

Sign up to request clarification or add additional context in comments.

13 Comments

So this method checks if rng in sample(random: rng) is actually a random number generator? Could you explain how?
@zerozero7 How means ? I gave you examples.. Still not convinced.. :-)
How - in terms how the method knows, that supplied value is indeed a random number generator, not a range object for example. I understand your examples, I didn't mean to be rude. Just curious how the method actually omits optional argument.
Through the key random... It will understand.
Nice. Looks good. I'd upvote, but I did that already and can't upvote twice. ;-)
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.