11

我“发明”了一种递归方案,它是变态的推广。当您折叠具有多态性的数据结构时,您无法访问子项,只能访问折叠的子结果:

{-# LANGUAGE DeriveFunctor #-}
import qualified Data.Map as M

newtype Fix f = Fix { unFix :: f (Fix f) }

cata :: Functor f => (f b -> b) -> Fix f -> b
cata phi = self where
    self = phi . fmap (\x -> self x) . unFix

折叠函数phi只能访问 的结果self x,而不能访问 original x。所以我添加了一个加入功能:

cataWithSubterm :: Functor f => (Fix f -> c -> b) -> (f b -> c) -> Fix f -> c
cataWithSubterm join phi = self
    where self = phi . fmap (\x -> join x (self x)) . unFix

现在可以以有意义的方式组合xself x,例如使用(,)

data ExampleFunctor a = Var String | Application a a deriving Functor

type Subterm = Fix ExampleFunctor

type Result = M.Map String [Subterm]

varArgs :: ExampleFunctor (Subterm, Result) -> Result
varArgs a = case a of
    Var _ -> M.empty
    Application ((Fix (Var var)), _) (arg, m) -> M.insertWith (++) var [arg] m

processTerm :: (ExampleFunctor (Subterm, Result) -> Result) -> Subterm -> Result
processTerm phi term = cataWithSubterm (,) phi term     

processTerm varArgs为每个标识符返回它在不同控制路径上接收到的实际参数列表。例如,bar (foo 2) (foo 5)它返回fromList [("foo", [2, 5])]

请注意,在此示例中,结果与其他结果统一组合,因此我希望存在使用Data.Foldable. 但总的来说,情况并非如此,因为phi可以应用其对内部结构的知识ExampleFunctor来以 Foldable 无法实现的方式组合“子项”和“子结果”。

我的问题是:我可以processTerm使用现代递归方案库中的库存函数进行构建recursion-schemes/Data.Functor.Foldable吗?

4

1 回答 1

12

折叠使得它“吃掉论点并保持它”被称为paramorphism。实际上,您的函数可以很容易地使用递归方案表示为

cataWithSubterm :: Functor f => (Fix f -> b -> a) -> (f a -> b) -> Fix f -> b
cataWithSubterm f g = para $ g . fmap (uncurry f)

此外,如果我们像您在 中那样提供(,),我们会得到cataWithSubtermprocessTerm

cataWithSubterm (,)  :: Functor f => (f (Fix f, b) -> b) -> Fix f -> b

para专门用于Fix

para                 :: Functor f => (f (Fix f, b) -> b) -> Fix f -> b
于 2013-08-02T11:33:00.313 回答