过去的纸质问题问我;定义函数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抱怨?
答案 0 :(得分:7)
让我们首先评论check2
并向GHCi询问prop_2
的类型:
*Main> :t prop_2
prop_2 :: Eq a => [a] -> Property
好的,所以你编写prop_2
的方式,它适用于相等类中任何元素类型的列表。
现在,您希望将prop_2
传递给quickCheck
函数。我们来看看quickCheck
下一个的类型:
*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
成立)就是类Arbitrary
的成员和类Show
的成员。
这里我们得出错误的来源。现有信息不足以得出这一结论。正如我在开头所说,prop_2
适用于允许相等的任何元素类型的列表。没有内置规则表明所有这些类型都在Arbitrary
和Show
中。但即使有,QuickCheck应该生成什么样的列表?它应该生成布尔列表,单位类型列表,函数列表,字符列表,整数列表吗?如此多的选项以及元素类型的选择可能会影响您是否发现了错误。 (考虑到GHC会仅使用一个元素选择单位类型()
。然后,您的属性将保留任何两个函数p
和q
,保留输入列表的长度,无论是否他们有你想要的交换财产。)
这就是为什么你需要向GHC提供额外的类型信息,以便它可以解析用于列表的元素类型。这样做很简单。您可以注释prop_2
本身:
prop_2 :: [Integer] -> Property
或者,如果您不希望这样(因为您可能希望在不重新实现prop_2
的情况下在不同类型的列表上运行测试),您可以在调用quickCheck
时添加类型注释:< / p>
check2 = quickCheck (prop_2 :: [Integer] -> Property)
现在代码编译,我们可以运行check2
:
Main*> check2
+++ OK, passed 100 tests.