如何修复对函数的有效 属性 完成的 quickCheck 的匹配错误?

How to fix a matching error on a quickCheck done on a valid property of a function?

我想编写一个函数 search :: String -> Char -> [Int] 来 returns 第一个参数中所有出现的第二个参数的位置。例如:

search "Bookshop" 'o' == [1,2,6]
search "senselessness" 's' == [0,3,7,8,11,12]

根据@Will Ness,最终代码如下:

search :: (Num b, Eq a, Enum b) => [a] -> a -> [b]
search s char = [ i | (i,c) <- zip [0..] s, c==char]

我想用 属性 搜索做一个 QuickCheck 测试,即输出的元素不能多于第一个参数。如何做到这一点?我的骨架代码如下:

prop_search :: String -> Int -> Int -> Bool
prop_search x char = if length search x char <= length x then True else False

这会产生匹配错误,其中:

* Couldn't match expected type `Int -> Bool'
                  with actual type `Bool'
    * In the expression: False

为什么会这样?

prop_search 有几个问题。虽然 Haskell 中的编译器消息可能有点令人生畏,但第一个故障排除技巧是要意识到(除非您使用该语言的某些高级扩展)类型注释是多余的。编译器不需要类型注释,但最好包含它们,因为它有助于提高可读性。

因此,首先尝试查看表达式本身是否编译。在这里,我只使用 GHCi:

Prelude> prop_search x char = if length search x char <= length x then True else False

<interactive>:18:25: error:
    * Couldn't match expected type `t a -> t1 -> Int'
                  with actual type `Int'
    * The function `length' is applied to three arguments,
      but its type `([()] -> () -> [Integer]) -> Int' has only one
      In the first argument of `(<=)', namely `length search x char'
      In the expression: length search x char <= length x
    * Relevant bindings include
        char :: t1 (bound at <interactive>:18:15)
        x :: t a (bound at <interactive>:18:13)
        prop_search :: t a -> t1 -> Bool (bound at <interactive>:18:1)

所以那是行不通的,但这是与 OP 中发布的错误不同的另一个错误。它说什么?

The function `length' is applied to three arguments

length 的类型是什么?

Prelude> :t length
length :: Foldable t => t a -> Int

length 是一个接受单个 Foldable 参数和 returns 一个 Int 的函数。它需要一个而不是三个参数。

然而,在表达式 length search x char 中,Haskell 编译器看起来好像 length 是用三个参数调用的。

您想要测量 search x charlength,因此将其分组:

prop_search x char = if length (search x char) <= length x then True else False

现在可以编译了,这意味着你可以查询它的类型:

Prelude> :t prop_search
prop_search :: Eq a => [a] -> a -> Bool

请注意,这不是 OP 中给出的类型。该函数接受 两个 个参数 - 而不是三个。

如果需要,您可以复制该推断类型并将其作为类型注释添加到函数中。

最后,当你发现自己在写 if x then True else False 时,你可以只写 x:

prop_search x char = length (search x char) <= length x

这仍然有相同的类型:

Prelude> :t prop_search
prop_search :: Eq a => [a] -> a -> Bool

总之,您可能希望这样编写函数:

prop_search :: Eq a => [a] -> a -> Bool
prop_search x char = length (search x char) <= length x