我正在尝试获取列表中值的索引,这样做是这样的:
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 -> a
从Just
数据构造函数中解包值:
import Data.List(elemIndex)
import Data.Maybe(fromJust)
thisItemNumber :: (Enum c, Num c) =>Char ->c
thisItemNumber item = fromJust (elemIndex item items)
虽然,如前所述,这意味着您的函数可能会出错,这通常是不可取的。