了解文件夹和展开器的工作

时间:2019-01-23 20:05:51

标签: haskell

我很难理解这些代码的工作方式。

“地图”功能必须将该功能应用于给定列表中的所有元素,并生成包含应用结果的列表。因此,我们给函数f和一些列表,然后在lambda表达式中,我们的列表转换为头“ x”和尾“ xs”,我们将函数“ f”应用于x并将其附加到“ xs”。但是接下来会发生什么呢? foldr的第二个参数(通常必须是某个起始值)如何以及如何使用。出于什么目的,空列表? 和“ rangeTo”函数:我们正在创建lambda表达式,在其中我们检查是否超出范围的末端,如果超过则结束,否则给出Nothing,或者如果我们不结束,则给出第一个数字对追加到结果列表中,第二个数字用作“ from”的下一个值。这是该功能中发生的所有事情,还是我缺少了什么?

--custom map function through foldr
map :: (a -> b) -> [a] -> [b]
map f = foldr (\x xs -> f x : xs) []
--function to create list with numbers from first argument till second and step "step"
rangeTo :: Integer -> Integer -> Integer -> [Integer]
rangeTo from to step =  unfoldr (\from -> if from >= to then Nothing else Just (from, from+step)) from

2 个答案:

答案 0 :(得分:1)

要了解 foldr如何在列表上运行。最好将foldr的{​​{3}}记为

foldr step z xs 
= x1 `step` foldr step z xs1                                -- where xs = x:xs1
= x1 `step` (x2 `step` foldr step z xs2)                    -- where xs = x1:x2:xs2
= x1 `step` (x2 `step` ... (xn `step` foldr step z [])...)  -- where xs = x1:x2...xn:[]

foldr step z [] = z

针对您的情况:

foldr (\x xs -> f x : xs) []

其中

step = (\x xs -> f x : xs)
z = []

根据foldr的定义,最里面的表达式

(xn `step` foldr step z []) 
首先评估

,即

xn `step` foldr step z []
= step xn (foldr step z [])
= step xn z
= step xn []  -- z = []
= f xn : []   -- step = (\x xs -> f x : xs)
= [f xn]

接下来会发生什么?评估进行为

x(n-1) `step` (xn `step` foldr step z [])
= step x(n-1) [f xn]
= f x(n-1) : [f xn]
= [f x(n-1), f xn]

直到:

x1 `step` (x2 ...
= step x1 [f x2, ..., f xn]
= [f x1, f x2, ... f xn]

答案 1 :(得分:0)

  

因此,我们给函数f和一些列表,然后在lambda表达式中,列表转换为头“ x”和尾“ xs”,将函数“ f”应用于x并将其附加到“ xs”。

情况并非如此。仔细看一下实现:

map :: (a -> b) -> [a] -> [b]
map f = foldr (\x xs -> f x : xs) []

此处有一个隐含变量,我们可以将其添加回:

map :: (a -> b) -> [a] -> [b]
map f ls = foldr (\x xs -> f x : xs) [] ls

map带有两个参数,一个函数f和一个列表ls。它将ls传递到foldr作为要折叠的列表,并将[]传递为起始累加器值。 lambda获取一个列表元素x和一个累加器xs(最初为[]),并返回一个新的累加器f x : xs。它不会在任何地方执行headtailxxs从来不在同一列表中。

让我们逐步评估一下该功能的工作原理:

map (1+) [2, 4, 8]

foldr (\x xs -> (1+) x : xs) [] [2, 4, 8] -- x = 8, xs = []
foldr (\x xs -> (1+) x : xs) [9] [2, 4]   -- x = 4, xs = [9]
foldr (\x xs -> (1+) x : xs) [5, 9] [2]   -- x = 2, xs = [5, 9]
foldr (\x xs -> (1+) x : xs) [3, 5, 9] [] --        xs = [3, 5, 9]

map (1+) [2, 4, 8] == [3, 5, 9]

空白列表从输入列表的右端开始累积通过f传递的值。


  

和函数“ rangeTo”:我们正在创建lambda表达式,在其中我们检查是否超出范围的末端,如果超出则结束,则给出Nothing,或者如果我们未结束,则给出对其中第一个数字附加到结果列表,第二个数字用作“ from”的下一个值。这是该功能中发生的全部事情,还是我缺少了什么?

是的,这就是事实。 lambda使用一个累加器,并返回下一个要放入列表中的值和一个新的累加器;如果列表应结束,则返回Nothing。在这种情况下,累加器是列表中的当前值。如果该值超出范围的末尾,则列表应结束。否则,它将通过添加步骤来计算下一个累加器。

同样,我们可以逐步进行评估:

rangeTo 3 11 2 -- from = 3, to = 11, step = 2

Just (3, 5)    -- from = 3
Just (5, 7)    -- from = 3 + step = 5
Just (7, 9)    -- from = 5 + step = 7
Just (9, 11)   -- from = 7 + step = 9
Nothing        -- from = 9 + step = 11, 11 >= to

rangeTo 3 11 2 == [3, 5, 7, 9]