Haskel' !!'在foldl中定义

时间:2014-12-08 17:45:20

标签: haskell recursion fold

我刚刚开始学习Haskell,我正在尝试在使用foldl的函数中定义“获取列表的第n个元素”(使用!!运算符)。我现在定义它没有foldl,只是使用递归。我想知道是否有人可以告诉我如何将我的代码更改为具有foldl的函数,并且可以描述正在发生的事情。提前谢谢!

get 1 (x:_) = x
get i (_:xs) = elementAt'' (i - 1) xs

2 个答案:

答案 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,只返回元组的元素部分。