2

I'm still learning FSCheck, and recently needed a fixed collection of unique strings.

This works, but I suspect there is a more efficient way.

Arb.generate<Set<NonEmptyString>> 
|> Gen.filter (fun s -> Set.count s > 9)
|> Gen.map (Seq.truncate 10) 

Is there?

OrdinaryOrange
  • 2,420
  • 1
  • 16
  • 25

1 Answers1

2

It's probably more efficient to build a set that you know contains exactly N strings, like this:

let genSet genItem size =
    let rec loop (items : Set<_>) =
        gen {
            if items.Count >= size then
                return items
            else
                let! item = genItem
                return! items.Add(item) |> loop
        }
    loop Set.empty

let genSetOfNonEmptyStringsOfSize10 =
    genSet
        Arb.generate<NonEmptyString>
        10

Note that genSet will build a set of any type, not just NonEmptyStrings.

Brian Berns
  • 15,499
  • 2
  • 30
  • 40
  • Thanks for the suggestion. Interestingly I just tested with Gen.Eval passing various sizes, and your implementation is faster up to 100, but after that takes more time. What is also interesting is that the lengths of NonEmptyString are better distributed in yours. In mine they are heavily skewed closer to 0. I'm realising that getting test case distributions right is hard... – OrdinaryOrange Jan 07 '22 at 02:39
  • Yes. Your implementation will have a bias towards strings that sort lower alphabetically, because you're always taking the first 10 items out of the set (instead of taking 10 arbitrary items). I'm not sure why mine would be slower for large sets, though. – Brian Berns Jan 07 '22 at 02:41
  • BTW, are you testing in Debug or Release build? Mine will definitely be slower for large sets if you have tail call optimization disabled (due to the deep recursion). – Brian Berns Jan 07 '22 at 02:43
  • Just a quick and dirty test in LinqPad. So possibly not a fair test ;) – OrdinaryOrange Jan 07 '22 at 02:53