我正在使用NICTA的RNG库,我希望我的RNG在测试中具有确定性。所以,假设我只想让下面的代码始终正确。
Rng.chooseint(0, 5).run.unsafePerformIO == 3
但我找不到任何设置种子的方法,run
方法不接受任何参数。有人可以帮忙吗?
答案 0 :(得分:1)
嗯,this issue已经开放了将近一年,所以我认为你在现成的支持方面只是运气不好。
RngOp
不提供公共构造函数或折叠的事实使得定义自己不方便,但并非不可能:
import com.nicta.rng._
import java.util.Random
import scala.annotation.tailrec
class NextBitsRandom(seed: Long) extends Random(seed) {
def nextBits(bits: Int): Int = super.next(bits)
}
def runWithSeed[A](rng: Rng[A], seed: Long): A = {
@tailrec def loop(g: Rng[A], r: NextBitsRandom): A = g.resume match {
case RngTerm(a) => a
case RngCont(op) =>
val nop = op.store.map(s => RngOp.store(s.imap(r.nextBits))).getOrElse(op)
nop.seed.foreach(r.setSeed)
loop(nop.extract, r)
}
loop(rng, new NextBitsRandom(seed))
}
fold
方法可以让我们跳过store
和getOrElse
上那种笨重的地图,但这很好用:
scala> runWithSeed(Rng.chooseint(0, 5), 0L)
res0: Int = 4
scala> runWithSeed(Rng.chooseint(0, 5), 0L)
res1: Int = 4
scala> runWithSeed(Rng.chooseint(0, 5), 1L)
res2: Int = 3
scala> runWithSeed(Rng.alphanumerics(10), 101L)
res3: List[Char] = List(M, h, Z, H, t, M, Z, H, c)
scala> runWithSeed(Rng.alphanumerics(10), 1001L)
res4: List[Char] = List(N, X, K, O, 6, C, L)
你可以尝试提交类似这样的东西作为#20的修复 - 如果它在库中,实现会更清晰,因为你可以使用RngOp
构造函数。这是一个非常合理的想法(如果我没记错的话,以前版本的API确实支持它。)