如果将范围作为随机数生成器传递,Array#sample
如何工作?如下:
> [*1..10].sample(random: 1..3)
=>9
根据文档,sample(random: rng)
,rng
不是您可能认为的Range
对象。rng
是一个随机数生成器。
可选的rng参数将用作随机数生成器。
a = (1..10).to_a
r = Random.new
r2 = r.dup
a1 = a.sample(random: r)
a2 = a.sample(random: r2)
a1 == a2 # => true
重点是#sample
,将其第二个参数作为关键字参数。如果我们使用类似foo: 12
或rng: (1..2)
的东西,它将给出ArgumentError:unknown关键字:。只有当您将值提供为random: <any random number generator>
时,可选参数才可接受。现在,来谈谈你的观点:
r = 1..3
a1 = [*1..10].sample(random: r)
a2 = [*1..10].sample(random: r)
a1 == a2 # => false
当您将第二个参数作为random: r
传递时,r
必须是Random
对象,或者是响应#rand
的对象。请记住,对于第二个参数,您告诉#sample
使用随机数生成器,而不是在没有可选参数的情况下使用的默认生成器。
以下是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
这将使您了解带有可选参数的#sample
。从#test_sample_random
中查找更多示例。
更新
如果将范围作为随机数生成器传递,
Array#sample
是如何工作的?
要回答这个问题,我需要TracePoint
课程的帮助。
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]
因此,从上面的调用堆栈中,您可以看到-Array#sample
方法已经被调用。现在,Ruby在内部对Range
对象调用了Kernel#rand
。现在,(1..3).respond_to?
返回了false
(因为#rand
-它是Range
的a_private_instance方法),这就是为什么最终调用了#respond_to_missing?
方法来完成这项工作。