Генераторы FsCheck путем выбора из пула возможностей

Есть ли способ сгенерировать строку в FsCheck, выбрав только один элемент из каждого списка строк и затем объединив результат?

Я просто полностью застрял и не могу понять это. Я просмотрел документы и github для чего-то подобного. И большую часть информации о FsCheck я прочитал на сайте FSharpForFunAndProfit.

Это что-то вроде того, о чем я бы подумал:

let rand = System.Random()
let randInt max = rand.Next(0, max)

let selectLetter (string: string) = 
    let whichLettersIndex = String.length string |> randInt
    string.Substring(whichLettersIndex, 1)

let generateOddlySpelledWord listOfStrings = 
    List.map selectLetter listOfStrings
    |> String.concat ""

let usingGenerateOddlySpelledWord =
    generateOddlySpelledWord ["zZ"; "oO0Ò"; "eEê"]

Это должно сгенерировать что-то вроде «Z0ê» или «zÒE».


person Thomas Sobieck    schedule 01.03.2015    source источник
comment
Напишите нужную функцию, которая генерирует string seq, а затем примените Gen.elements.   -  person Mauricio Scheffer    schedule 01.03.2015


Ответы (1)


Это делает то, что вы хотите?

open FsCheck

let createGenerators (l : string seq) =
    l |> Seq.map Gen.elements |> Seq.toList

type OddlySpelledWords =
    static member String() =
        ["zZ"; "oO0Ò"; "eEê"]
        |> createGenerators
        |> Gen.sequence
        |> Gen.map (List.map string >> String.concat "")
        |> Arb.fromGen

Специальный тест:

open FsCheck.Xunit

[<Property(Arbitrary = [| typeof<OddlySpelledWords> |])>]
let test (s : string) =
    printfn "%s" s

Выход (усеченный):

  z0ê
  ZÒe
  ZOe
  zoê
  ZÒe
  zoê
  Z0e
  zoê
  z0ê
  ZOe
  zÒê
  z0E
  zoe

Пояснение

Функция createGenerators имеет тип seq string -> Gen<char> list и создает Gen из каждой строки, используя Gen.elements, потому что строка также является char seq; Gen.elements создает Gen, который будет выбирать одно из этих char значений из каждой строки.

Затем он использует Gen.sequence для преобразования Gen<char> list в Gen <char list>, а затем отображает оттуда.


Кстати, вы также можете встроить createGenerators:

type OddlySpelledWords =
    static member String() =
        ["zZ"; "oO0Ò"; "eEê"]
        |> List.map Gen.elements
        |> Gen.sequence
        |> Gen.map (List.map string >> String.concat "")
        |> Arb.fromGen
person Mark Seemann    schedule 01.03.2015
comment
+1 Отличный ответ, а также хорошо продуманный (как показано из его правок)! – Как насчет использования List.reduce (+) вместо String.concat ""? - person Nikos Baxevanis; 02.03.2015
comment
Проблема с List.reduce в том, что он хрупкий, поэтому я стараюсь его избегать. Попробуйте это, если не верите мне: List.empty<string> |> List.reduce (+). - person Mark Seemann; 02.03.2015
comment
+1 Действительно, из любопытства List.empty<string> |> List.reduce (+) выдает исключение ArgumentException, говорящее, что входной список пуст. - person Nikos Baxevanis; 02.03.2015