我是Haskell的初学者,我的练习程序一直遇到麻烦。对于这个特定的对象,我想在列表中找到元素的索引(第一个元素为0)。 如果给定的元素未出现在列表中,则程序返回-1。
这是我的代码:
indexOf :: (Eq a) => a -> [a] -> Int
indexOf n [] = (-1)
indexOf n (x:xs)
| n == x = length(xs)
| otherwise = n `indexOf` xs
我有C和Java的经验,所以我的直觉是每次我浏览列表时都会增加一个计数器,但是,我一直提醒自己那不是Haskell的工作原理。 我知道我的代码每次都要遍历列表的开头,当我执行“ length(xs)”时,它只是在查找列表其余部分的长度。 显然,我在这里很困惑。谁能提供有关如何使这段代码起作用的任何指针或建议?
答案 0 :(得分:7)
我要解决的方法是创建另一个递归函数,其具有相同的特征以及额外的Int
参数以用作累加器:
indexOf :: (Eq a) => a -> [a] -> Int
indexOf n xs = go 0 n xs
where
go i n [] = (-1)
go i n (x:xs)
| n == x = i
| otherwise = go (i+1) n xs
答案 1 :(得分:5)
除了计数器(在列表中向下增加)之外,您还可以在再次返回时修改返回值:
indexOf :: (Eq a) => a -> [a] -> Int
indexOf n [] = -1
indexOf n (x:xs)
| n == x = 0
| otherwise = case n `indexOf` xs of
-1 -> -1
i -> i + 1
答案 2 :(得分:5)
该函数已经存在于库(elemIndex
)中,但是无论如何我们还是要实现它。
鉴于xs = [x0,x1,...]
,我们有zip xs [0..] = [(x0,0),(x1,1),...]
。然后,我们可以在后一个列表中搜索满足谓词\(x,_) -> x==n
的一对。
import Data.List
indexOf :: (Eq a) => a -> [a] -> Maybe Int
indexOf n xs = fmap snd . find (\(x,_) -> x==n) $ zip xs [0..]
上面,zip
添加了索引,find
成功返回Just (n,index)
,fmap snd
将其转换为Just index
。
请注意,我们希望返回Nothing
而不是返回-1
,这在Haskell中并不是惯用的,在Haskell中,我们更喜欢使用Maybe
。
最后,请注意,上面的代码并不是效率低下:由于懒惰,zip
只会将索引添加到find
所需要的那些元素上,因此除非进行扫描,否则它不会扫描整个列表找不到所需的元素。
作为练习,您可能希望使用显式递归来编码fmap snd . find (\(x,_) -> x==n)
。
答案 3 :(得分:4)
在Haskell中,通常使用Maybe a
返回值a
,但是当并非所有输入本身都有效时。对于无效输入,我们返回Nothing
,对于有效输入,我们返回Just x
,结果为x
。
因此,我们可以实现indexOf
,在其中将空白列表映射到Nothing
上。如果列表的第一项等于我们要寻找的项,则返回Just 0
。如果第一项与我们要寻找的项不相等,则递归到列表的末尾,并增加包装在Just
中的值,只要存在以下值:
indexOf :: Eq a => a -> [a] -> Maybe Int
indexOf y = go
where go [] = Nothing
go (x:xs) | x == y = Just 0
| otherwise = (1+) <$> go xs