对 Haskell 还是没有入门。在文章  https://www.allstoalls.com/blog/blog/29  中提到一句话

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

这真是一知半解,无知的话。

起因:那片文章最后的迭代 并不是像 《Combinatorial Generation》里面的迭代算法一样,只是 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 

先讲 foldr 可以用 foldl 实现

foldl 的实现

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

使用  foldl 实现  foldr 得到:

foldr f e l = foldl (\g b x -> g (f b x)) id l e

用这个来替换 cardProd9 中的 foldr 得到

cartProdN10 :: [[a]] -> [[a]]
cartProdN10 xss = 
 foldl (\g b x -> g (h1 b x)) id xss [[]] where 
  h1 xs yss = foldl (\g b x -> g (g1 b x)) id xs [] where
     g1 x zss = foldl (\g b x -> g (f b x)) id yss zss where 
         f xs wss = (x:xs):wss

这是在太丑陋了。

在 《Thinking Functionally with Haskell》 中有提到

只要满足 

f x g (y z) = g (f x y) z
f x e =  g e x

就有

foldr f e xs = foldl g e xs

我没有实际验证。但这么对称直接改动得到 (实际证明结果集满足算法,但返回顺序和 foldr 版本不同。所以其实我没有利用上面的公式,而是用对称性概念):

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

 

这样清爽多了。 以为要得到了验证,准备收手。不成想:

cartProdN11 [[1,2]| i <- [1 .. 1000]]

这个直接把 Haskell for Mac 跑挂了。

但是 

cartProdN9 [[1,2]| i <- [1 .. 1000]] 

居然秒出。这有点出乎我的意料。一番搜索找到 https://wiki.haskell.org/Foldr_Foldl_Foldl%27

换用文中的 foldl'

foldl' f z []     = z
foldl' f z (x:xs) = let z' = z `f` x 
                       in  z' `seq` foldl' f z' xs

但并没有解决问题。我依旧以为是 lazy 特性导致的问题。

继续搜索:https://www.fpcomplete.com/haskell/tutorial/all-about-strictness/

并采用其中的技术:

{-# LANGUAGE BangPatterns #-}

module D where
    
data StrictList a = Cons !a !(StrictList a) | Nil

strictMap :: (a -> b) -> StrictList a -> StrictList b
strictMap _ Nil = Nil
strictMap f (Cons a list) =
  let !b = f a
      !list' = strictMap f list
   in b `seq` list' `seq` Cons b list'

strictEnum :: Int -> Int -> StrictList Int
strictEnum low high =
  go low
  where
    go !x
      | x == high = Cons x Nil
      | otherwise = Cons x (go $! x + 1)

double :: Int -> Int
double !x = x * 2

list  :: Int -> StrictList Int
list !x = Cons x (Cons x Nil)

foldlS = \f z l ->
  case l of
    Nil -> z
    Cons !x !xs -> let !z' = z `f` x
                       in  z' `seq` foldlS f z' xs  

listlist :: StrictList (StrictList Int)
listlist = strictMap list $! strictEnum 1 100


myhead  :: StrictList a ->  a
myhead =  \l ->
  case l of
    Cons x xs -> x

t :: Int
t = myhead (myhead listlist)


cartProdN12 :: StrictList (StrictList a) -> StrictList (StrictList a)
cartProdN12 xss =
 foldlS h1 (Cons Nil Nil) xss where
  h1 !yss !xs = foldlS g Nil xs where
     g !zss !x = foldlS f zss yss where
       f !yss !xs = Cons (Cons x xs ) yss
         
r = cartProdN12 listlist
hr :: Int
hr =  myhead( myhead r)

依旧算不出来。

到此为止。 我已经没有思路了。由于是多层  foldl . 我的空间想象力无法得出结论。


我提了一个问题在  stackoverflow :

https://stackoverflow.com/questions/69863240/the-problem-of-foldl-with-multi-level-foldl-in-haskell#69863538

问题好像应该转换成: foldl  应该如何求出第一个结果 as fast as foldr.

再就是我的工具有问题: Haskell For Mac 算出第一个出来就不再算了 Lazy Lazy。给了我错误的诱导。

 

 

 

yxy post at 2021-11-05