【问题标题】:Recursive bottom-up traversal of algebraic data types代数数据类型的递归自下而上遍历
【发布时间】:2011-06-21 21:03:50
【问题描述】:

在 Haskell 中处理相当大的代数数据类型时,有一个特定的递归遍历不能通过折叠数据类型来捕获。例如,假设我有一个简单的数据类型来表示命题逻辑中的公式,并在它上面定义了一个折叠:

type FAlgebra α φ =
 (φ, φ,                -- False, True
  α -> φ,              -- Atom
  φ -> φ,              -- Negation
  φ -> φ -> φ,         -- Conjunction
  φ -> φ -> φ,         -- Disjunction
  φ -> φ -> φ,         -- Implication
  φ -> φ -> φ)         -- Bi-implication

fold :: FAlgebra α φ -> Form α -> φ
fold (f,t,lit,not,con,dis,imp,iff) = fold' where
 fold' (Fls)     = f
 fold' (Tru)     = t
 fold' (Lit α)   = lit α
 fold' (Not φ)   = not (fold' φ)
 fold' (Con φ ψ) = con (fold' φ) (fold' ψ)
 fold' (Dis φ ψ) = dis (fold' φ) (fold' ψ)
 fold' (Imp φ ψ) = imp (fold' φ) (fold' ψ)
 fold' (Iff φ ψ) = iff (fold' φ) (fold' ψ)

此递归方案为求值或查找文字等递归提供了简洁的答案:

eval :: (Ord α) => Map α Bool -> Form α -> Bool
eval v = fold (False, True, (fromJust . flip M.lookup v),
               not, (&&), (||), ((||) . not), (==))

literals :: (Ord α) => Form α -> Set α
literals = fold (S.empty, S.empty, S.singleton, id,
                 S.union, S.union, S.union, S.union)

但是,当我希望“扫描”数据类型时,它的表现并不好。在下文中,simp 是通过必要的模式匹配定义的辅助函数:

simplify :: Form α -> Form α
simplify (Not φ)   = simp (Not (simplify φ))
simplify (Con φ ψ) = simp (Con (simplify φ) (simplify ψ))
simplify (Dis φ ψ) = simp (Dis (simplify φ) (simplify ψ))
simplify (Imp φ ψ) = simp (Imp (simplify φ) (simplify ψ))
simplify (Iff φ ψ) = simp (Imp (simplify φ) (simplify ψ))
simplify φ         = φ

使用折叠定义简化当然会产生错误的结果。例如,以下是不等价的:

simplify = fold (Fls, Tru, Lit, (simp . Not), con Con, con Dis, con Imp, con Iff)
 where con f φ ψ = simp (f φ ψ)

简化等递归的最佳解决方案是什么?我应该定义一个类似于折叠数据类型的通用遍历,还是有一个标准的递归模式来定义这些函数?

【问题讨论】:

    标签: haskell recursion fold algebraic-data-types


    【解决方案1】:

    你试过Uniplate吗?对于仅适用于单一类型的操作,它可以执行自下而上的重写和重写直到一个固定点。

    例如:

    import Data.Generics.Uniplate.Direct
    import qualified Data.Map as M
    
    data Form a = Fls | Tru | Lit a
                | Not (Form a)
                | Con (Form a) (Form a)
                | Dis (Form a) (Form a)
                -- etc.
      deriving (Show, Eq)
    
    instance Uniplate (Form a) where
      uniplate (Not f) = plate Not |* f
      uniplate (Con f1 f2) = plate Con |* f1 |* f2
      uniplate (Dis f1 f2) = plate Dis |* f1 |* f2
      -- one case for each constructor that may contain nested (Form a)s
      uniplate x = plate x
    
    simplify :: Form a -> Form a 
    simplify = transform simp
     where
       simp (Not (Not f)) = f
       simp (Not Fls) = Tru
       simp (Not Tru) = Fls
       simp x = x
    
    test =
      simplify (Not (Not (Not (Not (Lit "a"))))) == Lit "a"
    

    适合你的相关函数是transformrewrite

    有关 Uniplate 的更深入的文档还有 a paper (PDF)

    【讨论】:

    • 以防万一,请将链接添加到论文中。
    • 您可以将链接添加为评论,以防您改变主意并在以后使用更有用的数据更新问题(可能添加链接)。这样你就不会得到你的答案得到 CWed。 :-)
    • Uniplate 库正是我想要的。感谢您突出显示transformrewrite。对于此类任务,它比其他 SYB 库更易于访问,因为该库专门处理重写。
    猜你喜欢
    • 1970-01-01
    • 2011-06-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-23
    • 1970-01-01
    • 1970-01-01
    • 2013-06-04
    相关资源
    最近更新 更多