gpt4 book ai didi

f# - 如何递归使用 FsCheck 生成器?

转载 作者:行者123 更新时间:2023-12-01 11:27:17 24 4
gpt4 key购买 nike

我使用 FsCheck 进行基于属性的测试,因此我为自定义类型定义了一组生成器。有些类型由其他类型组成,并且所有类型都有生成器。为字母数字类型定义了一个生成器后,我想为 RelativeUrl 类型定义一个生成器,而 RelativeUrl 是由斜杠符号分隔的 1-9 个字母数字值的列表。这是有效的定义(Alpanumeric 具有将其转换为 String 的“Value”属性):

static member RelativeUrl() =
Gen.listOfLength (System.Random().Next(1, 10)) <| Generators.Alphanumeric()
|> Gen.map (fun list -> String.Join("/", list |> List.map (fun x -> x.Value)) |> RelativeUrl)

尽管它非常简单,但我不喜欢使用 Random.Next 方法而不是使用 FsCheck 随机生成器。所以我试着像这样重新定义它:

static member RelativeUrl_1() =
Arb.generate<byte>
|> Gen.map int
|> Gen.suchThat (fun x -> x > 0 && x <= 10)
|> Gen.map (fun length -> Gen.listOfLength length <| Generators.Alphanumeric())
|> Gen.map (fun list -> String.Join("/", list))

编译器接受它,但实际上它是错误的:最后一条语句中的“列表”不是字母数字值的列表,而是 Gen。下一次尝试:

static member RelativeUrl() =
Arb.generate<byte>
|> Gen.map int
|> Gen.suchThat (fun x -> x > 0 && x <= 10)
|> Gen.map (fun length -> Gen.listOfLength length <| Generators.Alphanumeric())
|> Gen.map (fun list -> list |> Gen.map (fun elem -> String.Join("/", elem |> List.map (fun x -> x.Value)) |> RelativeUrl))

但这也不起作用:我取回的是 RelativeUrl 的 Gen of Gen,而不是 RelativeUrl 的 Gen。那么在不同级别组合生成器的正确方法是什么?

最佳答案

Gen.map有签名(f: 'a -> 'b) -> Gen<'a> -> Gen<'b> - 也就是说,它需要一个来自 'a 的函数至 'b , 然后是 Gen<'a> , 并返回 Gen<'b> .人们可能会认为它是将给定函数“应用”到给定生成器“内部”的内容。

但是您在 map 中提供的功能事实上,电话是int -> Gen<Alphanumeric list> - 也就是说,它返回的不是一些 'b , 但更具体地说 Gen<'b> , 所以整个表达式的结果变为 Gen<Gen<Alphanumeric list>> .这就是为什么 Gen<Alphanumeric list>在下一个 map 中显示为输入.一切由设计决定。

你真正想要的操作通常叫做bind .这样的函数会有一个签名 (f: 'a -> Gen<'b>) -> Gen<'a> -> Gen<'b> .也就是说,它需要一个产生另一个 Gen 的函数。 ,不是裸值。

不幸的是,由于某种原因,Gen不公开 bind像这样。它作为 gen computation expression builder 的一部分提供或 operator >>= (这是代表 bind 的事实上的标准运算符)。

鉴于上述解释,您可以这样改写您的定义:

static member RelativeUrl_1() =
Arb.generate<int>
|> Gen.suchThat (fun x -> x > 0 && x <= 10)
>>= (fun length -> Gen.listOfLength length <| Generators.Alphanumeric())
|> Gen.map (fun list -> String.Join("/", list))

您也可以考虑使用计算表达式来构建生成器。不幸的是,没有 wheregen 定义表达式生成器,所以你仍然必须使用 suchThat过滤。不过好在有一个特殊的功能Gen.choose用于生成给定范围内的值:

static member RelativeUrl_1() =
gen {
// let! length = Arb.generate<int> |> Gen.suchThat (fun l -> l > 0 && l <= 10)
let! length = Gen.choose (1, 10)
let! list = Gen.listOfLength length <| Generators.Alphanumeric()
return String.Join ("/", list)
}

关于f# - 如何递归使用 FsCheck 生成器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36334533/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com