我有一个递归方案样式结构,我想获得所有子结构的列表,包括完整结构 - 即等同于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
时再次感觉应该没必要)。
答案 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
,因此Foldable
和Unfoldable
个实例自然成对出现。
答案 1 :(得分:1)
para
确实是在这里使用的正确功能。如果您想要使用它,我已经将所有内容放在self-contained gist增加了示例中。
我们从定点Mu
和通常的fold
和para
开始。
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