错误 - 无法推断实例

Phi*_*lip 1 haskell types type-inference instance

我是Haskell的初学者.我有这个功能

test f xs 
  | length xs == 0  = []
  | f (head xs)     = head xs : test f (tail xs)
  | otherwise       = test f (tail xs)
Run Code Online (Sandbox Code Playgroud)

此函数应该从列表xs的每个元素中创建一个列表,该列表与f值匹配,但它会返回错误:

test 1 [1,2]
ERROR - Cannot infer instance
*** Instance   : Num (a -> Bool)
*** Expression : test 1 [1,2]
Run Code Online (Sandbox Code Playgroud)

这部分:

| f (head xs)
Run Code Online (Sandbox Code Playgroud)

应该是Bool但是呢?它是否作为过滤器工作?如何使此功能起作用?提前致谢.

lef*_*out 5

始终使用类型签名

显然,f它是一种谓词:您将它应用于列表中的值,并产生一个布尔值.所以类型test

test :: (a -> Bool) -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

BTW的标准名称是filter.

如果你自己不确定签名,你可以问GHCi

> :t test
test :: (a -> Bool) -> [a] -> [a]
Run Code Online (Sandbox Code Playgroud)

...但我强烈建议从签名开始:先考虑一下你希望你的功能完成什么,然后担心实现.

无论如何,要测试test函数,你需要给它一个(a -> Bool)函数和一个列表.是1功能吗?我不敢因此test 1 [1,2]无法理解.你可能意味着什么test (\x -> x==1) [1,2],也可以简单地写test (==1) [1,2].

关于您的代码的几点评论:

  • 您正在使用警卫来检查列表是否为空.首先,要检查列表是否为空,从不评估它length- 这需要遍历整个列表.你可以使用这个null功能 - 检查容器是否为空......
  • ......但是仍然存在实际从列表中取出值的问题.您确实可以使用非空列表的第一个值head,但这只在您首次检查它是非空的分支中是安全的.这很容易出错.一个更好的解决方案是在列表头上进行模式匹配:而不是

    test f xs 
      | length xs == 0  = []
      | f (head xs)     = ...
    
    Run Code Online (Sandbox Code Playgroud)

    test f [] = []
    test f (x:xs)
      | f x   = ...
    
    Run Code Online (Sandbox Code Playgroud)

    这样,您就不会错误地评估空列表的头部,因为编译器确保x仅在已检查为非空的子句的范围内.

查看标准函数的源代码filter供最终参考.


实际上,1 可以是一个函数...数字文字在Haskell中被重载; 你理论上可以写

instance Num (Integer -> Bool) where
  fromInteger n x = x == n
Run Code Online (Sandbox Code Playgroud)

然后你可以定义f = 1 :: Integer -> Bool,也可以使用它test.但是......这将是一个非常糟糕的主意; 这样的实例会造成非常混乱的代码.