卫兵和哈斯克尔

时间:2017-03-09 17:51:38

标签: haskell

我有一个非常基本的Haskell功能。 它应该预先添加一个元组列表,如果不存在则返回它。 添加元组需要在前置之前进行编辑。

我期待一个类型如下的函数:

Num t => (a, t) -> [(a, t)] -> [(a, t)]

功能是这样的:

update x lst
| hasElement x lst == True = addElement x lst
| otherwise = lst
where hasElement element list = not (null (filter ((==element).fst) list))
      addElement a b = (fst a, (snd a) +1) : b

但是当我尝试加载模块时遇到错误:

• Occurs check: cannot construct the infinite type: a ~ (a, t)
  Expected type: [(a, t)]
    Actual type: [((a, t), t)]
• In the second argument of ‘addElement’, namely ‘lst’
  In the expression: addElement x lst
  In an equation for ‘update’:
      update x lst
        | hasElement x lst == True = addElement x lst
        | otherwise = lst
        where
            hasElement element list
              = not (null (filter ((== element) . fst) list))
            addElement a b = (fst a, (snd a) + 1) : b
• Relevant bindings include
    lst :: [((a, t), t)] (bound at pip.hs:40:10)
    x :: (a, t) (bound at pip.hs:40:8)
    update :: (a, t) -> [((a, t), t)] -> [(a, t)]
      (bound at pip.hs:40:1)

addElement 返回类型似乎全部崩溃,因为将其注释掉会使模块正常工作。

问题是:出了什么问题? 单独尝试功能似乎正如我所料。

谢谢, FB

1 个答案:

答案 0 :(得分:1)

该特定错误的原因是element包含密钥和值,但是(==element) . fst您尝试仅比较密钥。

实际获取密钥的最佳方法是在函数参数中对模式进行匹配。请注意,您根本不需要element变量,也不需要本地函数的其他参数:

update (key,y) lst
  | hasElement  = addElement   -- comparing `==True` is a no-op!
  | otherwise   = lst
 where hasElement = not . null $ filter ((==key).fst) lst
       addElement = (key, y+1) : b

我怀疑addElement的这种行为是否真的是你想要的:你不是用给定的密钥更新现有元素,而是添加一个具有相同密钥的新元素?

此外,notnullfilter的组合不必要地复杂化。你可以使用

       hasElement = any ((==key).fst) lst

最后,签名Num a => ...实际上还不够强大:您正在将密钥与==进行比较。仅当密钥具有Eq实例时才有效。所以,这是正确的签名:

(Eq a, Num t) => (a, t) -> [(a, t)] -> [(a, t)]