f# - 为相同长度的整数列表对制定一个任意值
问题描述
我需要一些帮助来完成这个关于 f# 中的生成器的练习。
功能
List.zip : ('a list -> 'b list -> ('a * 'b) list)
和
List.unzip : (('a * 'b) list -> 'a list * 'b list)
在它们对相同长度的列表进行操作的条件下,它们是互逆的。为相同长度的整数列表对制定一个任意值
我试着写一些代码:
let length xs ys =
List.length xs = List.length ys
let samelength =
Arb.filter length Arb.from<int list>
它不起作用,我得到长度相同的类型不匹配:
错误:类型不匹配。期待 a
'a list -> bool
但给定 a'a list -> 'b list -> bool
。类型 bool 与类型不匹配'a list -> bool
。
编辑:
正如建议的那样,我尝试遵循步骤大纲,但我被卡住了。
let sizegen =
Arb.filter (fun x -> x > 0) Arb.from<int>
let listgen =
let size = sizegen
let xs = Gen.listOfLength size
let ys = Gen.listOfLength size
xs, ys
当然我有错误类型不匹配:
错误:类型不匹配。预计有类型
int
,但这里有类型Arbitrary<int>
编辑
我解决了这个练习,但是当我做测试时,我的生成器似乎没有工作,看起来另一个被调用了。
let samelength (xs, ys) =
List.length xs = List.length ys
let arbMyGen2 = Arb.filter samelength Arb.from<int list * int list>
type MyGeneratorZ =
static member arbMyGen2() =
{
new Arbitrary<int list * int list>() with
override x.Generator = arbMyGen2 |> Arb.toGen
override x.Shrinker t = Seq.empty
}
let _ = Arb.register<MyGeneratorZ>()
let pro_zip (xs: int list, ys: int list) =
(xs, ys) = List.unzip(List.zip xs ys)
do Check.Quick pro_zip
我得到错误:
错误:System.ArgumentException:list1 比 list2 短 1 个元素
但为什么?我的生成器应该只生成两个相同长度的列表。
解决方案
如果我们查看module的API 参考Arb
,并将鼠标悬停在 的定义上filter
,您会看到类型Arb.filter
为:
pred:('a -> bool) -> a:Arbitrary<'a> -> a:Arbitrary<'a>
这意味着谓词应该是一个返回 a 的参数的函数bool
。但是你length
的函数是两个参数的函数。你想把它变成一个只有一个参数的函数。
这样想吧。当你写的时候Arb.filter length Arb.from<int list>
,你说的是“我想生成一个任意的int list
(一次只生成一个),并根据length
规则对其进行过滤。” 但是length
您编写的规则需要两个列表并比较它们的长度。如果 FsCheck 只生成一个整数列表,它将与什么长度进行比较?没有第二个可以比较的列表,因此编译器实际上无法将您的代码变成有意义的东西。
您可能想要做的(尽管这有一个问题,我将在一分钟内解决)是生成一对列表,然后将其传递给您的length
谓词。即,您可能想要Arb.from<int list * int list>
. 这将生成一对整数列表,彼此完全独立。然后你仍然会在你的length
函数中得到一个类型不匹配,但你只需要将它的签名从let length xs ys =
to let length (xs,ys) =
,例如让它接收一个包含一对列表的单个参数,而不是让每个列表作为一个单独的参数。经过这些调整后,您的代码如下所示:
let length (xs,ys) =
List.length xs = List.length ys
let samelength =
Arb.filter length Arb.from<int list * int list>
但这仍然存在问题。具体来说,如果我们查看FsCheck 文档,我们会发现以下警告:
使用时
Gen.filter
,一定要提供一个返回概率高的谓词true
。如果谓词丢弃“太多”候选者,可能会导致测试运行速度变慢,或者根本不终止。
Arb.filter
顺便说一句,这同样适用于Gen.filter
。您的代码当前的方式是一个问题,因为您的过滤器将丢弃大多数列表对。由于列表是彼此独立生成的,因此它们通常具有不同的长度,因此您的过滤器false
大部分时间都会返回。我会建议一种不同的方法。既然你说这是一个练习,那我就不给你写代码了,你自己做会学到更多;我只是给你一个你想要采取的步骤的大纲。
- 生成一个非负 int
n
,它将是该对中两个列表的大小。(对于奖励积分,用于Gen.sized
获取您应该生成的数据的“当前大小”,并生n
成为 0 和 之间的值size
,以便您的列表对生成器,如 FsCheck 的默认列表生成器,将创建从小开始的列表慢慢变大)。 - 用于
Gen.listOfLength n
生成两个列表。(您甚至Gen.two (Gen.listOfLength n)
可以轻松生成一对相同大小的列表)。 - 不要忘记为一对列表编写一个适当的收缩器,因为练习希望您生成一个适当的
Arbitrary
,而Arbitrary
没有收缩器的一个在实践中并不是很有用。您可能可以Arb.mapFilter
在这里做一些事情,其中映射器是id
因为您已经生成了匹配长度的列表,但过滤器是您的length
谓词。然后使用Arb.fromGenShrink
将您的生成器和收缩器功能转换为适当的Arbitrary
实例。
如果该大纲不足以让您发挥作用,请询问另一个关于您遇到困难的问题,我很乐意尽我所能提供帮助。
编辑:
在您尝试使用 编写列表生成器的编辑中sizegen
,您有以下代码不起作用:
let listgen =
let size = sizegen
let xs = Gen.listOfLength size
let ys = Gen.listOfLength size
xs, ys
这sizegen
是一个Gen<int>
,您想从中提取int
参数。有几种方法可以做到这一点,但最简单的是gen { ... }
FsCheck 为我们提供的计算表达式。
顺便说一句,如果您不知道什么是计算表达式,它们是 F# 最强大的功能之一:它们在底层非常复杂,但它们允许您编写看起来非常简单的代码。您应该收藏https://fsharpforfunandprofit.com/series/computation-expressions.html和https://fsharpforfunandprofit.com/series/map-and-bind-and-apply-oh-my.html并计划稍后阅读. 如果您在第一次、第二次甚至第五次阅读时不理解它们,请不要担心:没关系。继续回到这两个系列的文章,并使用像gen
或这样的计算表达式seq
在实践中,最终概念会变得清晰。每次阅读这些系列时,您都会学到更多知识,并更接近那一刻,当一切都在您的大脑中“点击”时。
但回到你的代码。正如我所说,您想使用gen { ... }
计算表达式。在gen { ... }
表达式中,let!
赋值会将一个对象“解包”Gen<Foo>
到生成的Foo
中,然后您可以在进一步的代码中使用它。这就是你想用你的size
int 做什么。因此,我们将gen { ... }
在您的代码周围包装一个表达式,并获得以下内容:
let listgen =
gen {
let! size = sizegen
let xs = Gen.listOfLength size
let ys = Gen.listOfLength size
return (xs, ys)
}
请注意,我还在return
最后一行添加了一个关键字。在计算表达式中,return
具有相反的效果let!
。let!
关键字解包一个值(类型从Gen<Foo>
to Foo
),而return
关键字包装一个值(类型从Foo
to Gen<Foo>
)。所以那条return
线需要一个int list * int list
并将它变成一个Gen<int list * int list>
。幕后有一些非常复杂的代码,但在计算表达式的表面级别,您只需要考虑“展开”和“包装”类型来决定是否使用let!
or return
。
推荐阅读
- jquery - jQuery-单击图像上的坐标并在那里画一个圆圈
- python - 将新的日期时间添加到具有值的日期时间索引 Pandas 数据框
- php - 无法将自定义站点配置添加到 Silverstripe 4
- python - UUIDS 的 peewee ArrayField
- c# - 添加 CosmosDB 实体,(无法解析租户类型实体的 iD)
- c++ - 在编译时检查指针内存布局:联合方法在 constexpr 中失败
- r - 在 R 中自动运行文件
- angular6 - How to render the ngx-quill-editor content without html tags on JHipster project?
- java - Android Notification Listener Service 自行启动
- matrix - Prolog - 编写名为 Adugo 的棋盘游戏