RuboCop 的 safe autocorrect 改变固定种子的采样结果,保留 random 调用
带固定随机生成器的 shuffle 后取前缀,不保证与 sample 有相同序列。RuboCop 1.90.0 保留这类调用的自动修正边界,避免破坏可重放结果。
shuffle(random: ...).first(n) 被 RuboCop 自动改成 sample(n, random: ...) 后,结果仍然是合法元素,却不再符合固定 seed 的预期序列。这种差异会影响快照、可重放抽样或依赖固定排列的业务逻辑。
seed = 1234
items = (1..100).to_a
before = items.shuffle(random: Random.new(seed)).first(5)
after = items.sample(5, random: Random.new(seed))
两边都从同一个 seed 开始,但不能要求它们输出相同元素和顺序。打乱整个数组再取前缀,与直接抽取若干元素,采用不同的算法,也可能消耗不同数量的随机数。传入同一个随机生成器,只固定各自算法的执行序列,没有建立两个算法之间的等价关系。
RuboCop 1.89.0 的 Style/Sample 在带 random: 参数的调用上仍提供自动修正。1.90.0 包含保护:shuffle 带参数时可以继续给出风格提示,但不执行这条替换。不是关闭随机性,也不是保证每个 seed 都一定产生不同结果;需要保护的是原程序对确定序列的约定。
更新现有依赖约束并确认锁文件中的版本:
bundle update rubocop
bundle exec rubocop --version
bundle exec rubocop --only Style/Sample example.rb
如果必须保留旧工具,可以局部禁用该检查:
# rubocop:disable Style/Sample
selected = items.shuffle(random: Random.new(seed)).first(5)
# rubocop:enable Style/Sample
已经接受自动修正的代码,应从原提交恢复抽样表达式;不要为了适配新输出而直接重录所有快照。先确认应用需要的是“任意均匀抽样”,还是“给定输入和 seed 得到既定序列”。前者可能允许主动迁移算法,后者需要保持旧行为。
验证时为两条表达式各建一个新的 Random,避免前一次调用推进状态污染比较。记录使用的 Ruby 版本、输入数组、seed、取样数量与完整结果,并检查连续两次调用的序列。如果应用共享一个生成器,还要检查后续消费随机数的代码;仅第一批元素一致不足以证明迁移等价。


