Haskell 自";“折叠”;isn';如果功能不足以写一个带有缩进的树型打印机,那么什么是高阶组合器呢?

Haskell 自";“折叠”;isn';如果功能不足以写一个带有缩进的树型打印机,那么什么是高阶组合器呢?,haskell,recursion,fold,combinators,Haskell,Recursion,Fold,Combinators,例如,给定以下树数据类型: data Tree a = Node [Tree a] | Leaf a deriving Show type Sexp = Tree String 如何使用高阶组合器表示一个“漂亮”的函数,它以适当的缩进打印树?例如: sexp = Node [ Leaf "aaa", Leaf "bbb", Node [ Leaf "ccc", Leaf "ddd",

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

data Tree a = Node [Tree a] | Leaf a deriving Show
type Sexp = Tree String
如何使用高阶组合器表示一个“漂亮”的函数,它以适当的缩进打印树?例如:

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
我希望该计划的结果是:

(aaa 
   bbb 
   (ccc 
       ddd 
       (eee 
           fff) 
       ggg 
       hhh) 
   jjj 
   kkk) 
下面是一个不完整的解决方案,使用“折叠”作为组合符,它不实现缩进:

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

显然不可能使用
fold
编写我想要的函数,因为它忘记了树结构。那么,什么是合适的高阶组合符,它足够通用,可以让我编写我想要的函数,但不如编写直接递归函数强大?

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
请注意,我向调用
fold
传递了一个额外的参数。这是缩进的初始状态,它之所以有效,是因为有了
r
的专门化,
fold
返回一个函数。

它很简单

onLast f xs = init xs ++ [f (last xs)]

pretty :: Sexp -> String
pretty = unlines . fold (node . concat) (:[]) where
    node  []    = [""]
    node (x:xs) = ('(' : x) : map ("  " ++) (onLast (++ ")") xs)

不知道,有单子吗?我想单子对我来说太强大了。显然,这对他们来说是可行的,但我想一些不太通用的东西仍然可以做到。关于折叠与上下文。。。我不确定。那听起来绝对像单子!我敢肯定,
fold
足够结实。但我现在正在打电话,所以我不能再检查一遍。试试中等强度的东西,也许是闭包?这很聪明。通过返回函数树,您可以通过将应用程序映射到子节点来向下传递该级别。愚蠢的是,我记得现在我曾经遇到过一个非常相似的情况,有人提出了类似的解决方案。哦。谢谢,没问题!这是一个相当不明显的把戏,直到你把它挤出足够多的时间。这是一个非常好的表达方式。