Open dwijnand opened 3 years ago
@dwijnand That would definitely be useful! I already added pick
to sbt quite a while ago.
def pick[A](n: Int, as: Seq[A]): Gen[Seq[A]] = {
if (n >= as.length) {
Gen.constant(as)
} else {
def go(m: Int, bs: Set[Int], cs: Set[Int]): Gen[Set[Int]] =
if (m == 0)
Gen.constant(cs)
else
Gen.element(bs.head, bs.tail.toList).flatMap(a => go(m - 1, bs - a, cs + a))
go(n, as.indices.toSet, Set())
.map(is => as.zipWithIndex.flatMap(a => if (is(a._2)) Seq(a._1) else Seq()))
}
}
Just on your version, I would definitely avoid relying on value equality, seems like a source of strange bugs and I think you can/should avoid. Definitely take my version with scepticism, there might be a much better/cleaner way.
The other consideration if the shrinking, it's one of those functions that the library version could possibly do a better job than what you get, but that requires a little more thought.
Looking at it again/now, the heavy use of flatMap
is a bit of a smell (it might not shrink well, but I'm not sure). It might be possible to generate a list of exactly n
booleans, "shuffle" (not implemented in scala, but is in haskell), map and zip and then take the true elements.
def pick[A](n: Int, as: Seq[A]): Gen[Seq[A]] = {
Gen.shuffle(List.fill(n, true) ++ List.fill(as.length - n, false)).map(bs =>
as.zip(bs).flatMap { case (a, b) => if (b) List(a) else Nil }
)
Having noticed it was missing, I implemented it forgetting the a ::
prepend (😳) causing someOf
to always return Nil... 😄 So I reckoned it's worth having something reasonably correct out the box.
(I also noticed there's no support for recursive definitions, IIRC. If I find the workaround I put together I'll open a separate issue on it.)
I developed these in order to port some ScalaCheck code. I think they would be handy to have: