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 -> Bool和一个a.这是怎么回事?

你在那里使用..运算符创建函数管道:

(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)

您以某种方式编写了一个函数,该函数看起来像"函数管道"(带有(.)运算符(和指定参数的方法之间的(错误(混合。

由于您定义了函数链,因此如果您在此处使用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

或者我们可以用fromJust :: Maybe a -> aJust数据构造函数中解包值:

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

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

最新更新