代码之家  ›  专栏  ›  技术社区  ›  Shillington

Haskell:应用于太多参数的筛选器

  •  2
  • Shillington  · 技术社区  · 8 年前

    我试图获取列表中某个值的索引,方法如下:

    items = "Test"
    zipItems xs = zip xs [0..]
    thisItemNumber item = snd . head . filter (\(i, _) -> i == item) (zipItems items)
    

    运行此命令时,出现以下错误:

    * Couldn't match expected type `a -> [(a0, c)]'
                  with actual type `[(Char, Integer)]'
    * Possible cause: `filter' is applied to too many arguments
      In the second argument of `(.)', namely
        `filter (\ (i, _) -> i == item) (zipItems items)'
      In the second argument of `(.)', namely
        `head . filter (\ (i, _) -> i == item) (zipItems items)'
      In the expression:
        snd . head . filter (\ (i, _) -> i == item) (zipItems items)
    * Relevant bindings include
        thisItemNumber :: Char -> a -> c (bound at <interactive>:89:5)
    

    我不明白。在我看来:

    zipItems items
    

    [(Char, Int)]

    \(i, _) -> i == item
    

    有类型 (Char, a) -> Bool

    然后我将过滤器应用于一个类型 a -> Bool 和一个 a

    2 回复  |  直到 8 年前
        1
  •  7
  •   melpomene    8 年前

    你用的是 . 在那里。这个 . 运算符创建函数管道:

    (f . g) x = f (g x)
    

    在你的情况下,代码是

    snd . head . filter (\(i, _) -> i == item) (zipItems items)
    

    snd 是一个函数。

    head

    但是 filter (\(i, _) -> i == item) (zipItems items) 不是函数,而是列表。

    .

    * Couldn't match expected type `a -> [(a0, c)]'
                  with actual type `[(Char, Integer)]'
    

    说 . 需要某种函数(必须返回元组列表才能满足 snd . head )但实际上你给它的是一个列表。

    可能的解决方案:

    • 不要使用 . 完全:

      snd (head (filter (\(i, _) -> i == item) (zipItems items)))
      

      您的所有函数都已完全应用,因此不需要真正的函数管道。

    • 使用 $ 代替帕伦斯:

      snd $ head $ filter (\(i, _) -> i == item) $ zipItems items
      

      ))) 通过使用 $

    • 构建函数管道,但立即将其应用于参数:

      (snd . head . filter (\(i, _) -> i == item) . zipItems) items
      

      现在所有的操作数 都是函数,但我们把它应用到 items 最后。

    import Data.List
    
    thisItemNumber item = elemIndex item items
    

    这稍微改变了 thisItemNumber 到 Maybe Int 因为有可能 item 项目

    import Data.List
    import Data.Maybe
    
    thisItemNumber item = fromJust (elemIndex item items)
    
        2
  •  1
  •   willeM_ Van Onsem    8 年前

    不知何故,您编写了一个看起来像“函数管道”(与 (.)

    因为您定义了一系列函数,所以如果您使用应用程序执行函数,则需要将这些函数放在括号之间 zipItem items filter (\(i, _) -> i == item) . 如果你写:

    f . g x
    

    这就是:

    f . (g x)
    

    但是 (.) :: (b -> c) -> (a -> b) -> a -> c filter (\(i, _) -> i == item) (zipItems items) 是一个 [(Char, Int)] ,两人也是 匹配。

    因此,通过添加括号,我们得到:

    thisItemNumber :: (Enum c, Num c) => Char -> c
    thisItemNumber item = (snd . head . filter (\(i, _) -> i == item)) (zipItems items)

    listToMaybe :: [a] -> Maybe a 并返回一个 Maybe c ,因此很明显,此函数可能无法找到元素:

    import Data.Maybe(listToMaybe)
    
    thisItemNumber :: (Enum c, Num c) => Char -> Maybe c
    thisItemNumber item = (listToMaybe . map snd . filter ((item ==) . fst)) (zipItems items)

    例如:

    Prelude Data.Maybe> thisItemNumber 'L'
    Nothing
    Prelude Data.Maybe> thisItemNumber 'a'
    Nothing
    Prelude Data.Maybe> thisItemNumber 'T'
    Just 0
    Prelude Data.Maybe> thisItemNumber 'e'
    Just 1
    Prelude Data.Maybe> thisItemNumber 'X'
    Nothing
    

    也就是说,你在这里的目标,已经存在了(当然不是用这个特定的值),用 elemIndex :: Eq a => a -> [a] -> Maybe Int 功能。

    import Data.List(elemIndex)
    
    thisItemNumber :: (Enum c, Num c) => Char -> Maybe c
    thisItemNumber item = elemIndex item items

    或者我们可以从 Just fromJust :: Maybe a -> a :

    import Data.List(elemIndex)
    import Data.Maybe(fromJust)
    
    thisItemNumber :: (Enum c, Num c) => Char -> c
    thisItemNumber item = fromJust (elemIndex item items)

    尽管如前所述,这意味着您的函数可能会出错,这通常是 不

    推荐文章