递归方案概括了`tails`

时间:2015-10-20 13:04:59

标签: haskell recursion functional-programming

我有一个递归方案样式结构,我想获得所有子结构的列表,包括完整结构 - 即等同于tails函数对{ {1}}。我认为可以通过调用List来实现这一点,在每个步骤映射回原始结构,然后将原始结构分别粘贴在前面,但这看起来非常麻烦:(未经测试,如果Haskell是不正确的;用para来编写,因为我还没有真正理解Mu结构。

Base

(即gtails :: Functor f => Mu f -> [Mu f] gtails = para (\a -> (fmap fst a) : (foldMap snd a)) 这是f=Prim,其他tails是概括)

有更好的方法吗?我意识到这不是很糟糕,但f可以恢复原来的#34;在那一步的结构感觉非常麻烦,fmap fst a是我在使用foldMap snd a时发现自己重复的事情(同样para使用fold a时再次感觉应该没必要)。

2 个答案:

答案 0 :(得分:10)

我没有看到para的任何问题。只需在每个Cons步骤中收回头部和尾部:

import Data.Functor.Foldable

tails :: [a] -> [[a]]
tails = para (\case Nil -> [[]]; Cons a (as, res) -> (a : as) : res)

为清楚起见,专门用于列表而没有recursion-schemes

para :: (a -> [a] -> b -> b) -> b -> [a] -> b
para f z []     = z
para f z (a:as) = f a as (para f z as)

tails :: [a] -> [[a]]
tails = para (\a as res -> (a : as) : res) [[]]

然而,如果你想更加一般,那么不太好的para表达方便了:

import qualified Data.Functor.Foldable as Rec (Foldable)

tails :: (Rec.Foldable t, Base t ~ Prim [a]) => t -> [t]
tails as = as : para (\case Nil -> []; Cons a (as, res) -> as : res) as

这适用于常规列表,但您也可以为其他type instance Base t = Prim [a] - s提供t以及Foldable实例,并使用它们。

或者,我们可以保留第一个tails定义,代价是引入Unfoldable约束:

tails' :: (Unfoldable t, Rec.Foldable t, Base t ~ Prim [a]) => t -> [t]
tails' = para (\case Nil -> [embed Nil]; Cons a (as, res) -> embed (Cons a as) : res)

这不是太糟糕,因为对于每个project,对于仿函数的修复点应该存在反embed,因此FoldableUnfoldable个实例自然成对出现。

答案 1 :(得分:1)

para确实是在这里使用的正确功能。如果您想要使用它,我已经将所有内容放在self-contained gist增加了示例中。

我们从定点Mu和通常的foldpara开始。

module Tails where

import Data.Function

newtype Mu f = In { out :: f (Mu f) }

fold :: Functor f => (f a -> a) -> Mu f -> a
fold alg = alg . fmap (fold alg) . out

para :: Functor f => (f (a, Mu f) -> a) -> Mu f -> a
para alg = alg . fmap (\m -> (para alg m, m)). out

然后,我们可以使用tails和其他para约束来实施Foldable,从而允许我们使用foldMap来收集列表monoid中的中间结果:

tails :: (Functor f, Foldable f) => Mu f -> [Mu f]
tails m = m : para (foldMap $ uncurry $ flip (:)) m