3

一个过去的论文问题问我;定义一个函数 p :: [a] -> [a] 交换列表中的每两个项目。您的函数应该将第一个与第二个项目交换,第三个与第四个交换,依此类推。通过列表理解定义一个,另一个通过递归定义。

好吧,这就是我想出的:

import Test.QuickCheck
p :: [a] -> [a]
p [] = []
p xs = concat [ [y,x] | ((x,y),i) <- zip (zip xs (tail xs)) [1..], odd i]
q :: [a] -> [a]
q [] = []
q (x:y:zs) | even (length zs) = [y,x] ++ q zs
           | otherwise = error "The list provided is not of even length"
prop_2 xs = even (length xs) ==> p xs == q xs
check2 = quickCheck prop_2

这些功能工作正常,但我想检查两者是否相同,所以我把 quickCheck 放在下面;但是由于某种原因,这给了我一个错误,说“使用prop_2引起的模糊类型变量[a0]”

我只是不明白这里出了什么问题,我看起来非常明智...... Haskell 到底在抱怨什么?

4

1 回答 1

9

让我们首先注释掉check2并询问 GHCi 的类型prop_2是什么:

*Main> :t prop_2
prop_2 :: Eq a => [a] -> Property

好的,按照您编写的方式prop_2,它适用于相等类中任何元素类型的列表。

现在,您要传递prop_2给该quickCheck函数。我们来看看quickChecknext的类型:

*Main> :t quickCheck
quickCheck :: Testable prop => prop -> IO ()

这个函数实际上有一个非常通用的类型。它适用于Testable课堂上的任何内容。那么这个Testable类是如何工作的呢?这里有几个基本实例,例如:

instance Testable Bool
instance Testable Property  -- this is a simplification, but it's more or less true

这些实例在 QuickCheck 库中定义,并告诉您可以快速检查常量布尔值以及属性类型的元素。

现在,测试不依赖于任何输入的属性并不是特别有趣。有趣的例子是这个:

instance (Arbitrary a, Show a, Testable prop) => Testable (a -> prop)

这就是说,如果您知道如何生成特定类型的随机值 ( Arbitrary a) 以及如何显示该类型的值 ( Show a),那么您还可以测试从该类型a到已经可测试的类型的函数prop

为什么?因为这就是 QuickCheck 的运作方式。在这种情况下,QuickCheck 将咨询Arbitrary实例以提出类型的随机测试用例a,将函数应用于每个测试用例,并检查结果是否为正。如果任何测试失败,它将打印一条消息,通知您测试失败,并打印测试用例(这也是有Show要求的原因)。

现在,在我们的情况下,这意味着我们应该能够快速检查prop_2:它是一个导致Property. 重要的是函数参数(类型[a]只要Eq a持有)是 classArbitrary的成员和 class 的成员Show

在这里,我们到达了错误的根源。现有信息不足以得出这一结论。正如我在一开始所说,prop_2适用于任何承认相等的元素类型的列表。没有内置规则说所有这些类型都在Arbitraryand中Show。但即使有,QuickCheck 应该生成什么样的列表?它应该生成布尔列表、单元类型列表、函数列表、字符列表、整数列表吗?这么多的选项,元素类型的选择很可能会影响到你是否发现了一个bug。(考虑到 GHC 会选择()只有一个元素的单元类型。那么您的属性将适用于任何两个函数pq保留输入列表的长度,无论它们是否具有您想要的交换属性。)

这就是为什么您需要向 GHC 提供额外的类型信息,以便它可以解析用于列表的元素类型。这样做很简单。您可以注释prop_2自己:

prop_2 :: [Integer] -> Property

或者,如果您不希望这样(因为您可能希望在不重新实现的情况下在不同类型的列表上运行测试prop_2),您可以在调用时添加类型注释quickCheck

check2 = quickCheck (prop_2 :: [Integer] -> Property)

现在代码编译了,我们可以运行check2

Main*> check2
+++ OK, passed 100 tests.
于 2012-12-15T01:46:49.810 回答