该函数实际上有效,但是如果第一个元素等于x,它返回的列表将不是正确的顺序。
功能:
myelemIndices :: Eq a => a -> [a] -> [Int]
myelemIndices x [] = []
myelemIndices x l = if posic n l == x
then myReverse (n : myelemIndices x (init l))
else myelemIndices x (init l)
where n = length l - 1
会返回类似的内容:
myelemIndices 1 [1,2,1,2,1]
[2,0,4]
Posic功能等于:
posic :: Int -> [a] -> a
posic 0 (h:t) = h
posic x (a:b) = posic (x-1) b
myReverse与反向完全相同。我不是在寻找一种有效的功能,只是对我的修正。提前谢谢!
答案 0 :(得分:2)
每次找到元素时,您都会反转列表。这里的最小变化是使用myElemIndices x (init l) ++ [n]
而不是myReverse (n : myelemIndices x (init l))
答案 1 :(得分:2)
代码中的第一个反模式是您使用length
。 length
通常会在 O(n)( n 列表中的元素数量)中运行,但此外它很麻烦,因为列表可以有无限的长度。在这种情况下,length
永远不会终止。在函数式编程中,它有时被视为某些东西可能不正确的标志。
所以第一个问题是:我们需要length
吗?您的代码需要做的是返回索引。但是比如说你需要知道电话簿中的哪些页面上列出了“John”的名字,那么你不需要事先知道电话簿有多少页面:你可以简单地看看第一页。如果它有一个名字为John的人,那么你说这是第1页,无论如何,你都会进一步。
我们可以在这里使用相同的方法。我们唯一需要的是一个参数,用于跟踪我们当前正在查看的页面。我们可以通过定义一个将完成大部分工作的 new 函数来引入此参数。所以:
myElemIndices :: Eq a => a -> [a] -> [Int]
myElemIndices x l = go 0 l
where go = ...
所以我们定义了一个函数go
,第一个参数将跟踪页码。如果我们进行递归,我们将需要更新该数字。但是现在我们仍然需要定义go
函数。
基本案例很简单:如果我们到达列表末尾(电话簿),我们可以说我们不会再发现任何事件了。所以我们可以写:
go _ [] = []
这意味着,无论页码(_
)如何,如果没有页面([]
),我们会返回一个空列表作为匹配[]
。< / p>
如果我们没有到达电话簿的末尾,我们可以抓取头部h
和尾部t
。我们必须检查头h
是否与查询元素x
匹配。万一它,我们返回页码,否则我们不会。无论如何,我们一直在寻找更多页面。所以我们可以写:
go i (h:t) | x == h = i : <next-matches>
| otherwise = <next-matches>
<next-matches>
只是一个递归调用,我们更新页码(i+1
),我们继续列表的尾部,所以:
go i (h:t) | x == h = i : tl
| otherwise = tl
where tl = go (i+1) t
现在我们可以把它们放在一起:
myElemIndices :: Eq a => a -> [a] -> [Int]
myElemIndices x l = go 0 l
where go _ [] = []
go i (h:t) | x == h = i : tl
| otherwise = tl
where tl = go (i+1) t
我们仍然可以改进代码。首先,我们不必写:
myElemIndices x l = go 0 l
请注意,头部和身体都以l
结尾。我们可以省略它,并把它变成:
myElemIndices x = go 0
此外,我们也不需要将结果作为Int
。只要这些是Num
s,我们就可以了。所以我们可以将其概括为:
myElemIndices :: (Eq a, Num n) => a -> [a] -> [n]
myElemIndices x l = go 0 l
where go _ [] = []
go i (h:t) | x == h = i : tl
| otherwise = tl
where tl = go (i+1) t