文件夹和zipWith(:)如何一起工作?

时间:2019-03-17 07:39:12

标签: haskell fold zipwith

我是Haskell的新手,并且遇到了以下困扰我的代码:

foldr (zipWith (:)) (repeat []) [[1,2,3],[4,5,6],[7,8,9,10]]

它会产生以下结果,在反复试验之后,我不完全确定为什么:

[[1,4,7],[2,5,8],[3,6,9]]

我的印象是(:)将项目添加到列表中,(repeat [])产生了无数的空列表[],而foldr则需要一个函数,项目和列表,并通过将函数与结果一起连续应用于列表中的每个项目来精简列表。

也就是说,我直观地理解以下代码如何产生10的结果:

foldr (+) 1 [2,3,4]

但是,我不确定foldr (zipWith (:)) (repeat [])为何要获取列表列表并生成另一个列表列表,这些列表中的项按其原始内部索引分组。

任何解释都是有启发性的。

2 个答案:

答案 0 :(得分:5)

这很简单。 foldr的定义是这样

foldr f z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

因此

foldr f z [a,b,c,...,n] = f a (f b (f c (...(f n z)...)))

或者在这里

foldr (zipWith (:)) (repeat []) [[1,2,3],[4,5,6],[7,8,9,10]]
=
zipWith (:) [1,2,3] 
  ( foldr (zipWith (:)) (repeat []) [[4,5,6],[7,8,9,10]] )
=
...
=
zipWith (:) [1,2,3] 
  ( zipWith (:) [4,5,6]
      ( zipWith (:) [7,8,9,10] 
          ( foldr (zipWith (:)) (repeat []) [] )))
=
zipWith (:) [1,2,3] 
  ( zipWith (:) [4,5,6]
      ( zipWith (:) [7,8,9,10] 
          ( repeat [] )))
=
zipWith (:) [1,2,3] 
  ( zipWith (:) [4,5,6]
      ( zipWith (:) [ 7, 8, 9,10] 
                    [[],[],[],[],[],[],....] ))
=
zipWith (:) [1,2,3] 
  ( zipWith (:) [ 4,  5,  6 ]
                [[7],[8],[9],[10]] )
=
zipWith (:) [ 1   , 2   , 3   ] 
            [[4,7],[5,8],[6,9]] 

就是这样。

(菜单上的下一个traverse ZipList [[1,2,3],[4,5,6],[7,8,9,10]] ... :)或以后。)


另一个例子是

foldr (+) 1 [2,3,4] 
= 2 + foldr (+) 1 [3,4] 
= 2 + (3 + foldr (+) 1 [4]) 
= 2 + (3 + (4 + foldr (+) 1 [])) 
= 2 + (3 + (4 + 1))
= 2 + (3 + 5)
= 2 + 8
= 10

因为+的两个参数都为 strict

zipWith在两个参数上都不严格,(:)也不严格,因此第一个序列应仅用作说明。实际强制将按照自上而下的顺序而不是自下而上的顺序进行。例如,

> map (take 1) . take 1 $ zipWith (:) (1 : undefined) (repeat undefined)
[[1]]

完全符合

map (take 1) . take 1 $ zipWith (:) (1 : undefined) (repeat undefined)
=
map (take 1) . take 1 $ zipWith (:) (1 : undefined) (undefined : repeat undefined)
=
map (take 1) . take 1 $ (1 : undefined) : zipWith (:) undefined (repeat undefined)
=
map (take 1) $ (1 : undefined) : take 0 (zipWith (:) undefined (repeat undefined))
=
map (take 1) $ (1 : undefined) : []
=
map (take 1) [(1 : undefined)]
=
[take 1 (1 : undefined)]
=
[1 : take 0 undefined]
=
[1 : []]
=
[[1]]

答案 1 :(得分:1)

因此,使用foldr从右边折叠列表的直觉(这不是递归定义),在简单的情况下您将获得以下信息:

foldr (+) 1 [2,3,4]
foldr (+) (4 + 1) [2,3]
foldr (+) (3 + 4 + 1) [2]
foldr (+) (2 + 3 + 4 + 1) []
(2 + 3 + 4 + 1)
10

让我们在您的示例中做同样的事情,并考虑初始元素repeat [] == [[],[],[],[], …]

foldr (zipWith (:)) ([[],[],[],[], ...]) [[1,2,3],[4,5,6],[7,8,9,10]] 
foldr (zipWith (:)) (zipWith (:) [7,8,9,10] [[],[],[],[], ...]) [[1,2,3],[4,5,6]] 

等一下。让我们开发zipWith (:) [7,8,9,10] [[],[],[],[], ...]

zipWith (:) [7,8,9,10] [[],[],[],[], ...] -- apply the funciton (:) pairwise
[7:[], 8:[], 9:[], 10:[]]                 -- we get rid of the infinite list at this point.
[[7],[8],[9],[10]]

从这里我们可以轻松地遵循其余代码

foldr (zipWith (:)) ([[],[],[],[], ...]) [[1,2,3],[4,5,6],[7,8,9,10]] 
foldr (zipWith (:)) (zipWith (:) [7,8,9,10] [[],[],[],[], ...]) [[1,2,3],[4,5,6]]
foldr (zipWith (:)) ([[7],[8],[9],[10]]) [[1,2,3],[4,5,6]]
foldr (zipWith (:)) (zipWith (:) [4,5,6] [[7],[8],[9],[10]]) [[1,2,3]]
foldr (zipWith (:)) (zipWith (:) [1,2,3] [[4:7],[5:8],[6:9]) []
zipWith (:) [1,2,3] [[4:7],[5:8],[6:9]
[[1,4,7],[2,5,8],[3,6,9]]

请注意,这不是foldr的正确定义,我们正在对结果进行中间评估,而不是像laskell那样懒惰地评估结果,但这只是出于理解的目的。