最近,我尝试解决第66个Haskell 99问题(紧凑地布局树)。我成功了,但是对这里的解决方案感到困惑(http://www.haskell.org/haskellwiki/99_questions/Solutions/66)。

layout :: Tree a -> Tree (a, Pos)
layout t = t'
  where (l, t', r) = layoutAux x1 1 t
    x1 = maximum l + 1

    layoutAux :: Int -> Int -> Tree a -> ([Int], Tree (a, Pos), [Int])
    layoutAux x y Empty = ([], Empty, [])
    layoutAux x y (Branch a l r) = (ll', Branch (a, (x,y)) l' r', rr')
      where (ll, l', lr) = layoutAux (x-sep) (y+1) l
            (rl, r', rr) = layoutAux (x+sep) (y+1) r
            sep = maximum (0:zipWith (+) lr rl) `div` 2 + 1
            ll' = 0 : overlay (map (+sep) ll) (map (subtract sep) rl)
            rr' = 0 : overlay (map (+sep) rr) (map (subtract sep) lr)

-- overlay xs ys = xs padded out to at least the length of ys
-- using any extra elements of ys
overlay :: [a] -> [a] -> [a]
overlay [] ys = ys
overlay xs [] = xs
overlay (x:xs) (y:ys) = x : overlay xs ys


为什么计算“ x1”和“ sep”不会引起无限循环?
它们是如何计算的?

最佳答案

起作用的原因是Haskell的non-strict evaluation mode,而不是您在大多数语言中看到的严格评估。

在您给出的示例中,可以使用maximum l进行计算,因为从l函数返回的layoutAux不包含对x1的任何依赖关系。 x1用于返回值的t'部分。

下面的代码显示了类似的行为的另一个简单示例:

hello :: [Int] -> [Int]
hello x = x' where
  x' = hello' l x
  l = length x'
  hello' i lst = map (+i) lst


这不会永远循环,因为您不需要知道列表的长度就可以知道它的内容,这就是为什么l上的列表内容依赖性不会导致它永远循环的原因。而如果您使用类似maximum而不是长度的内容,则会导致它永远循环,因为maximum需要知道list的内容,而内容取决于maximum的结果。

10-08 08:53