我刚刚开始学习Haskell,我正在尝试在使用foldl的函数中定义“获取列表的第n个元素”(使用!!运算符)。我现在定义它没有foldl,只是使用递归。我想知道是否有人可以告诉我如何将我的代码更改为具有foldl的函数,并且可以描述正在发生的事情。提前谢谢!
get 1 (x:_) = x
get i (_:xs) = elementAt'' (i - 1) xs
答案 0 :(得分:4)
几点说明:
首先请注意,您希望get 1
返回列表中的第一个元素,这不是许多语言中的常见选择,包括Haskell([2, 3, 5] !! 1 = 3
)。
其次,尽管elementAt
是列表上的递归函数,但它可以以旧式递归方式更有效地定义。 fold
函数在这里不是很好的选择,因为fold
遍历列表的每个元素。但我们希望elementAt
递归停止我们找到元素的那一刻。
鉴于此,以下是如何递归实现elementAt
:
elementAt :: Int -> [a] -> a
elementAt i (x:xs) = if i == 1 then x else elementAt (i-1) xs
这是使用foldl
的实现:
elementAt' :: Int -> [a] -> a
elementAt' i (x:xs) =
snd $
foldl
(\(j, a) b ->
if j < 1 then (j-1, a) else (j-1, b))
(i-1, x)
xs
foldl
的种子值是一个元组:(i-1, x)
其中x
是列表的头部。
请注意,fold
函数的返回结果必须与其种子的类型相同。因此,foldl
返回一个元组:(j-1, a)
,其中a
是最终结果,如果找到索引;否则(j-1, b)
其中b
是列表的当前元素。
你可以看到foldl
如何在列表的每个元素中找到我们正在寻找的索引处的元素(它继续返回前一个结果a
,这将是最终结果)。
PS。这些elementAt
函数不处理空列表的情况,或者当i大于列表的长度时;因此它们并非详尽无遗。
答案 1 :(得分:2)
我可以看到以下内容,有点神秘的方式使用foldl用于您的目的(它使用从零开始的索引,但可以轻松更改为1):
get i lst=
snd $
foldl (\p (j, y) -> if j == i then (j,y) else p ) (0, 0) (zip [0,1..] lst)
foldl部分正在使用元组(index, element)
,其列表是通过使用索引列表压缩给定列表生成的。作为第一个参数传递给foldl的函数是将所需元素的索引与当前传递的索引进行比较,如果索引匹配则返回当前元素,否则返回前一个元素。然后,最后通过使用snd
,只返回元组的元素部分。