启发自: Mary Sheeran, Chalmer  的演讲  In praise of Higher Order Functions and of some friends and heroes  。我觉得推导的不直观。而且跳跃性太大。自己重新推导了一遍。

递归版

cartProdN1 :: [[a]] -> [[a]]
cartProdN1 [] = [[]]
cartProdN1 (xs:yss) = [ x:ys | x <- xs, ys <- cartProdN1 yss ]

 

迭代版:

cartProdN9 :: [[a]] -> [[a]]
cartProdN9 xss = 
 foldr h1 [[]] xss where 
  h1 xs yss = foldr g [] xs where
     g x zss = foldr f zss yss where 
         f xs yss = (x:xs):yss 
 

其中 h1 一个一个处理  xs 和 聚合结果。

然后 xs 又是一个 list 。对 xs 而言也需要 遍历处理。然后处理 xs 的每一个元素的时候。

有人可能会说 foldr 怎么就是迭代解法。我们认为 foldr 可以用 foldl 实现。 然后 foldl 是尾递归。可以等价为迭代处理。不是本文的主题,暂且不说。

 


 

推导如下:

cartProdN1' :: [[a]] -> [[a]]
cartProdN1' xss = 
   h xss where 
      h [] = [[]]
      h (xs:yss) = [ x:ys | x <- xs, ys <- h yss ]  

化 haskell 的模式匹配到一个函数。起名 cartPordN1', 因为比较直观没有用到什么技巧。

提取 (h  yss) 为 yss 也比较直观

cartProdN1'' :: [[a]] -> [[a]]
cartProdN1'' xss = 
   h xss where 
      h [] = [[]]
      h (xs:yss) =  g xs (h yss) where  
          g xs yss = [x:ys | x <- xs, ys <- yss]

看一下 foldr 的结构

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

h 遍历 xss  和 foldr f 结构一样。

cartProdN2 :: [[a]] -> [[a]]
cartProdN2 xss = 
  foldr h [[]] xss where 
    h xs yss = [x:ys | x <- xs, ys <- yss]

列表展开的定义 。 第一个元素和 后面所有的匹配。加上后面整体的结果。

cartProdN3 :: [[a]] -> [[a]]
cartProdN3 xss = 
  foldr h [[]] xss where 
    h (x:xs) yss = 
      map (x:) yss ++ [x:ys| x <- xs, ys <- yss]

然后可以识别出列表推导就是 h 自己的一个调用  [x:ys| x <- xs, ys <- yss] = h xs yss 。而且替换成 h ,h 变成了递归函数,必须要有 base case。

cartProdN4 :: [[a]] -> [[a]]
cartProdN4 xss = 
 foldr h [[]] xss where 
  h [] yss = [] 
  h (x:xs) yss = (map (x:) yss) ++ (h xs yss)

使用演讲中的 mapa

mapa :: (b -> a) -> [a] -> [b] -> [a]
mapa g z l = (map g l) ++ z

替换得到

cartProdN5 :: [[a]] -> [[a]]
cartProdN5 xss = 
 foldr h [[]] xss where 
  h [] yss = []
  h (x:xs) yss = mapa (x:) (h xs yss) yss

观察 mapa 的结构 。 z 当作累积参数可以提前被追加。

mapa :: (b -> a) -> [a] -> [b] -> [a]
mapa g z l = foldr f z l where
    f x y = (g x):y 

替换 mapa 为 foldr 得到。

cartProdN6 :: [[a]] -> [[a]]
cartProdN6 xss = 
 foldr h [[]] xss where 
  h [] yss = [] 
  h (x:xs) yss = foldr f (h xs yss) yss where
    f xs yss = (x:xs):yss  

其中 mapa 中的 g  = (x:) ;  x = xs ;  z = yss; z =  (h xs yss); y  = yss (这是 f 的参数 yss ,注意和 foldr 的参数 yss 区别)

这里并没有结束。 h 这里递归函数。并不是迭代。我们的目的是消除递归调用。

消除递归调用 h 的方法。就是 h 提取出来。引入辅助函数。

cartProdN7 :: [[a]] -> [[a]]
cartProdN7 xss = 
 foldr h [[]] xss where 
   h xs yss = h' xs
    where
      h' []     = []
      h' (x:xs) = foldr f (h' xs) yss where 
        f xs yss = (x:xs):yss 

cardPord6 和 cardProd7 为什么等价。  这一块没有想好怎么描述。 看起来也不直观。 等待后续继续观察。。。

继续引入辅助函数 g . 这一步相对直观, 就是把 foldr f 抽离出来。

cartProdN8 :: [[a]] -> [[a]]
cartProdN8 xss = 
 foldr h1 [[]] xss where 
  h1 xs yss = h' xs
   where
    h' []     = []
    h' (x:xs) = g x (h' xs)
    g x zss = foldr f zss yss
      where 
        f xs yss = (x:xs):yss     

可以发现。 h' 本身就符合 foldr 的结构

cartProdN9 :: [[a]] -> [[a]]
cartProdN9 xss = 
 foldr h1 [[]] xss where 
  h1 xs yss = foldr g [] xs where
     g x zss = foldr f zss yss where 
         f xs yss = (x:xs):yss 

g 就是 h' foldr 化的 参数 f 。


cardProdN6 -> cardProdN7 还是想解释一下。但发现自己只能用 python 描述了。

def f(xs, yss):
    if xs == []:
        return []
    x xs <- xs
    return foldr g (f xs) yss

def f(xs, yss):
    def f'(xs):
        if xs == []:
            return []
        x xs <- xs
        return foldr g (f' xs) yss
    return f' xs

 

jiamo post at 2021-05-25