Haskell:过滤器应用于过多的参数

时间:2018-09-22 08:42:07

标签: haskell

我正在尝试获取列表中某个值的索引,如下所示:

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 -> Boola。这怎么了?

2 个答案:

答案 0 :(得分:7)

您在这里使用..运算符创建函数管道:

(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可能不会出现在items中。如果要忽略此错误:

import Data.List
import Data.Maybe

thisItemNumber item = fromJust (elemIndex item items)

答案 1 :(得分:1)

您以某种方式编写了一个函数,看起来像是“函数管道”(使用(.)运算符)与指定参数的方法之间的(错误)混合。

由于您定义了功能链,因此如果在此处使用zipItem items执行功能应用程序,则需要将这些功能放在括号之间,因为否则功能应用程序仅与链条的最后一项绑定:{{1 }}。如果您写:

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

然后将其插入为:

f . g x

但是f . (g x) 运算符希望第二个操作数是一个函数,其中(.) :: (b -> c) -> (a -> b) -> a -> cfilter (\(i, _) -> i == item) (zipItems items),因此两者 not 不匹配。

因此,通过添加方括号,我们可以获得:

[(Char, Int)]

由于可能找不到该元素,因此使用listToMaybe :: [a] -> Maybe a并返回一个thisItemNumber :: (Enum c, Num c) => Char -> c thisItemNumber item = (snd . head . filter (\(i, _) -> i == item)) (zipItems items)可能会很有用,这样很明显该函数可能无法找到该元素:

Maybe c

例如:

import Data.Maybe(listToMaybe)

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

话虽这么说,elemIndex :: Eq a => a -> [a] -> Maybe Int函数已经存在(您当然打算在这里做这些事情)(当然不包含此特定值)。

所以最后一个功能等效于:

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

或者我们可以用fromJust :: Maybe a -> aimport Data.List(elemIndex) thisItemNumber :: (Enum c, Num c) => Char -> Maybe c thisItemNumber item = elemIndex item items数据构造函数中解包该值:

Just

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