对于任意ADT,这是一个有意义的`scan`s'概括吗?

时间:2015-10-11 12:38:38

标签: haskell category-theory type-theory

我一直在想如何将scanl推广到任意ADT。 Prelude方法只是将所有内容视为列表(即Foldable),并将scanl应用于结构的flatened视图。相反,我倾向于认为scanl是一个操作,它将一个状态从树的每个节点传递给它的子节点,同时应用一个monoidal操作,因为它从根向下移动到叶子。因此,例如,在Data.Tree上,我们有:

scan :: (b -> a -> b) -> b -> Tree a -> Tree b
scan fn init (Node element children) 
    = Node (fn init element) 
        $ map (treeScan fn (fn init element)) children

所以,例如:

main = do
    prettyPrint $ scan (+) 0 $
        Node 1 [
            Node 1 [
                Node 1 [], 
                Node 1 []],
            Node 1 [
                Node 1 [], 
                Node 1 []]]

结果:

1
|
+- 2
|  |
|  +- 3
|  |
|  `- 3
|
`- 2
   |
   +- 3
   |
   `- 3

与将scanl独立应用于树的每个路径相同,保留原始结构。

问题很简单:这是一个有意义的概括吗?即,它是常用的,带有明确的解释,也许有不同的名称?

1 个答案:

答案 0 :(得分:1)

如果你转移到仿函数的定位点" generic"数据的表示,您可以将扫描(或者更确切地说,它的轻微概括,mapAccum)视为特殊类型的通用折叠。

这里有一些代码可以勾画出你应该能够继续的模式:

data Fix f a = Roll (f a (Fix f a))

cata :: Functor (f a) => (f a b -> b) -> Fix f a -> b
cata alg (Roll x) = alg $ fmap (cata alg) x

scan :: Functor (f a) => (f a (acc, Fix f b) -> (acc, f b (Fix f b))) -> Fix f a -> Fix f b
scan alg = snd . cata (fmap Roll . alg)

data ListF a b = NilF | ConsF a b deriving Functor

scanAlgList f z NilF = (z, NilF)
scanAlgList f z (ConsF a (acc,b)) =
            let val = f a acc
            in (val, ConsF val b)

data TreeF a b = LeafF a | BranchF a b b deriving Functor

scanAlgTree f (LeafF x) = (x, LeafF x)
scanAlgTree f (BranchF v (accL,l) (accR,r)) =
            let val = f v accL accR
            in (val, BranchF v l r)

吉本斯在他的article关于霍纳斯统治的过程中对此进行了讨论。他实际上首先描述了诸如“向下积累”这样的事情。在article from 1992 on"向上和向下累积树木"。