Я создал генератор для создания списков int одинаковой длины и для проверки свойства zip и unzip. Запуская тест, я время от времени получаю ошибку
Error: System.ArgumentException: list2 is 1 element shorter than list1
но этого не должно происходить из-за моего генератора.
У меня три раза тест прошел на 100% и потом ошибка выше. Почему? Кажется, мой генератор не работает должным образом.
let samelength (x, y) =
List.length x = List.length y
let arbMyGen2 = Arb.filter samelength Arb.from<int list * int list>
type MyGenZ =
static member genZip() =
{
new Arbitrary<int list * int list>() with
override x.Generator = arbMyGen2 |> Arb.toGen
override x.Shrinker t = Seq.empty
}
let _ = Arb.register<MyGenZ>()
let pro_zip (xs: int list, ys: int list) =
(xs, ys) = List.unzip(List.zip xs ys)
|> Prop.collect (List.length xs = List.length ys)
do Check.Quick pro_zip
Не уверен, что это то, что вы подумали let pro_zip = Prop.forAll MyGenZ.genZip() (xs: int list, ys: int list) = List.unzip(List.zip xs ys)
, но я получаю сообщение об ошибке MyGenZ.genZip()
Ваш код, как написано, работает для меня. Поэтому я не уверен, что именно не так, но могу дать вам несколько полезных (надеюсь!) советов.
В первую очередь попробуйте не использовать механизм регистрации, а вместо этого использовать Prop.forAll
следующим образом:
let pro_zip =
Prop.forAll arbMyGen2 (fun (xs,ys) ->
(xs, ys) = List.unzip(List.zip xs ys)
|> Prop.collect (List.length xs))
do Check.Quick pro_zip
Обратите внимание, что я также изменил ваш вызов Prop.collect
для сбора длины списка (списков), что дает несколько более интересный результат. На самом деле ваше свойство уже проверяет, что списки имеют одинаковую длину (хотя и неявно), поэтому тест не будет выполнен с контрпримером, если это не так.
Arb.filter
преобразует существующий Arbitrary
(то есть фильтр генератора а также) в новый Arbitrary
. Другими словами, arbMyGen2
имеет функцию сжатия, которая будет работать (то есть возвращает только меньшие пары списков одинаковой длины), в то время как в genZip()
вы отбрасываете функцию сжатия. Хорошо бы просто написать
type MyGenZ =
static member genZip() = arbMyGen2
вместо.
В чем разница между регистрацией генератора и использованием Prop.forAll? Когда следует использовать первый метод, а когда второй? Что касается моего кода, я получаю сообщение об ошибке во время проверки длины списков. Это происходит, если я не выполняю код весь вместе в F# Interactive (я использую Monodevelop), если я сначала выполняю код без быстрой проверки, а затем выполняю его, я получаю эту ошибку. Может быть, это мой компьютер, я не знаю.
Prop.forAll предпочтительнее, потому что он понятнее (вы можете сказать по свойству, какой экземпляр произвольного объекта используется) и не подвержен тем же проблемам Arb.register
, которые вы только что испытали. Особенно в F# Interactive я бы постарался избежать метода регистрации, потому что каждый раз, когда вы выполняете часть кода, он компилируется в свой собственный (генерируемый компилятором) модуль и т. д., что может иметь странные побочные эффекты. Я думаю, что что-то вроде этого является причиной вашей проблемы.
Будет ли работать, если вы укажете свою собственность с помощью
Prop.forAll
?let pro_zip = Prop.forAll MyGenZ.genZip() (xs : int list, ys: int list) ...