由于"折叠"不足以写一个带缩进的树漂亮的打印机,那么高阶组合器是什么?

Mai*_*tor 15 recursion haskell combinators fold

例如,给出以下树数据类型:

data Tree a = Node [Tree a] | Leaf a deriving Show
type Sexp = Tree String
Run Code Online (Sandbox Code Playgroud)

如何使用高阶组合器表达"漂亮"功能,该组合打印出适当缩进的树?例如:

sexp = 
    Node [
        Leaf "aaa", 
        Leaf "bbb",
        Node [
            Leaf "ccc",
            Leaf "ddd",
            Node [
                Leaf "eee",
                Leaf "fff"],
            Leaf "ggg",
            Leaf "hhh"],
        Leaf "jjj",
        Leaf "kkk"]
pretty = ????
main = print $ pretty sexp
Run Code Online (Sandbox Code Playgroud)

我希望该程序的结果是:

(aaa 
   bbb 
   (ccc 
       ddd 
       (eee 
           fff) 
       ggg 
       hhh) 
   jjj 
   kkk) 
Run Code Online (Sandbox Code Playgroud)

这是一个不完整的解决方案,使用"fold"作为组合器,不实现缩进:

fold f g (Node children) = f (map (fold f g) children)
fold f g (Leaf terminal) = g terminal
pretty = fold (\ x -> "(" ++ (foldr1 ((++) . (++ " ")) x) ++ ")") show
main = putStrLn $ pretty sexp
Run Code Online (Sandbox Code Playgroud)

显然不可能编写我想要使用的函数fold,因为它忘记了树结构.那么,什么是一个适当的高阶组合器,它足够通用,允许我编写我想要的函数,但是比编写直接递归函数更不强大?

J. *_*son 15

fold足够强大; 诀窍是我们需要实例r化为当前缩进级别的读者monad.

fold :: ([r] -> r) -> (a -> r) -> (Tree a -> r)
fold node leaf (Node children) = node (map (fold node leaf) children)
fold node leaf (Leaf terminal) = leaf terminal

pretty :: forall a . Show a => Tree a -> String
pretty tree = fold node leaf tree 0 where

  node :: [Int -> String] -> Int -> String
  node children level = 
    let childLines = map ($ level + 1) children
    in unlines ([indent level "Node ["] ++ childLines ++ [indent level "]"])

  leaf :: a -> Int -> String
  leaf a level = indent level (show a)

  indent :: Int -> String -> String -- two space indentation
  indent n s = replicate (2 * n) ' ' ++ s
Run Code Online (Sandbox Code Playgroud)

请注意,我将一个额外的参数传递给调用fold.这是缩进的初始状态,它起作用,因为有了这个特化r,fold返回一个函数.

  • 没问题!这是一个相当不明显的技巧,直到你把它挤出足够多次. (2认同)