Как извлечь int из FsCheck.Gen.choose

Я новичок в F# и не вижу, как извлечь значение int из:

let autoInc = FsCheck.Gen.choose(1,999)

Компилятор говорит, что тип Gen<int>, но не могу получить int от этого! Мне нужно преобразовать его в десятичную, и оба типа не совместимы.

3 ответа

Решение

С точки зрения потребителя, вы можете использовать Gen.sample комбинатор, который, учитывая генератор (например, Gen.choose), возвращает вам несколько примеров значений.

Подпись Gen.sample является:

val sample : size:int -> n:int -> gn:Gen<'a> -> 'a list

(* `size` is the size of generated test data
   `n`    is the number of samples to be returned
   `gn`   is the generator (e.g. `Gen.choose` in this case) *)

Вы можете игнорировать size так как Gen.choose игнорирует его, так как его распределение равномерно, и делает что-то вроде:

let result = Gen.choose(1,999) |> Gen.sample 0 1 |> Seq.exactlyOne |> decimal

(* 0 is the `size` (gets ignored by Gen.choose)
   1 is the number of samples to be returned *)

result должно быть значением в закрытом интервале [1, 999], например, 897.

Привет, чтобы добавить к тому, что Никос уже сказал вам, вот как вы можете получить десятичное число от 1 до 999:

#r "FsCheck.dll"

open FsCheck

let decimalBetween1and999 : Gen<decimal> =
    Arb.generate |> Gen.suchThat (fun d -> d >= 1.0m && d <= 999.0m)

let sample () = 
    decimalBetween1and999
    |> Gen.sample 0 1 
    |> List.head 

теперь вы можете просто использовать sample () чтобы получить случайный десятичный обратно.

В случае, если вы просто хотите целые числа от 1 до 999, но конвертируйте их в decimal Вы можете просто сделать:

let decimalIntBetween1and999 : Gen<decimal> =
    Gen.choose (1,999)
    |> Gen.map decimal

let sampleInt () = 
    decimalIntBetween1and999
    |> Gen.sample 0 1 
    |> List.head 

что вы, вероятно, действительно хотите сделать вместо

Используйте это, чтобы написать вам несколько хороших типов и проверить свойства вроде этого (здесь мы используем Xunit в качестве тестовой среды и пакет FsCheck.Xunit:

open FsCheck
open FsCheck.Xunit

type DecTo999 = DecTo999 of decimal

type Generators = 
    static member DecTo999 =
        { new Arbitrary<DecTo999>() with
            override __.Generator = 
                Arb.generate 
                |> Gen.suchThat (fun d -> d >= 1.0m && d <= 999.0m)
                |> Gen.map DecTo999
        }

[<Arbitrary(typeof<Generators>)>]
module Tests =

  type Marker = class end

  [<Property>]
  let ``example property`` (DecTo999 d) =
    d > 1.0m

Gen<'a> это тип, который по существу абстрагирует функцию int -> 'a (фактический тип немного сложнее, но давайте пока проигнорируем). Эта функция является чистой, то есть, когда ей присваивается один и тот же int, вы каждый раз получаете один и тот же экземпляр back. Идея состоит в том, что FsCheck генерирует кучу случайных целых чисел, передает их Gen функция, случайные экземпляры типа 'a вы заинтересованы, и кормите их для теста.

Таким образом, вы не можете выйти из инт. У вас в руках есть функция, которая дает int, генерирует другой int.

Gen.sample как описано в другом ответе, по существу, просто передает последовательность случайных целочисленных значений в функцию и применяет ее к каждому, возвращая результаты.

Тот факт, что эта функция является чистой, важен, потому что она гарантирует воспроизводимость: если FsCheck находит значение, для которого тест не пройден, вы можете записать исходный int, который был передан Gen функция - повторный запуск теста с этим начальным значением гарантирует получение тех же значений, т. е. воспроизводит ошибку.

Другие вопросы по тегам