我正在学习递归方案,事实证明对我来说,实现特定于列表类型的递归方案非常有用。但是,我被困在了阿朴性上。
这是我最近发现的载脂蛋白tails
的实现:
import Data.Functor.Foldable
tailsApo :: [a] -> [[a]]
tailsApo = apo coalgTails
where
coalgTails = \case
[] -> Cons [] (Left [])
li@(_:xs) -> Cons li (Right xs)
不幸的是,我无法使用GHCi导入Data.Functor.Foldable
,因为我收到未找到软件包的错误。另一个搜索显示了特定于列表的载脂蛋白的这种实现方式:
apoList :: ([b] -> Maybe (a, Either [b] [a])) -> [b] -> [a]
apoList f b = case f b of
Nothing -> []
Just (x, Left c) -> x : apoL f c
Just (x, Right e) -> x : e
很显然,apoList
的第一个参数与tailsApo
不匹配。我将类型扩展为类似apoList :: ([b] -> Either (a, b) [a])) -> [b] -> [a]
。
似乎没有关于此主题的新手入门信息。感谢您的帮助。
答案 0 :(得分:2)
apo :: (a -> Base t (Either t a )) -- g :: a -> Base t r
-> a -> t
apo g a = rec a where -- rec = apo g :: a -> t
rec = embed . fmap (either id rec) . g
{-
type family Base t :: * -> *
embed :: Base t t -> t
fmap (either id rec) :: Base t r -> Base t t
either id rec :: r -> t r ~ Either t a
g :: a -> Base t r r ~ Either t a
rec = apo g :: a -> t
-}
a
是种子。对于t ~ [b]
we'll have
type instance Base [b] = ListF b
data ListF b r = Nil | Cons b r
Base t (Either t a) ~ ListF b (Either [b] a)
~ Maybe (b, Either [b] a)
所以总的来说会
apoList :: (a -> Maybe (b, Either [b] a)) -> a -> [b]
apoList coalg a = case coalg a of
Nothing -> [] -- (embed Nil ) -- either
Just (b, Left bs) -> b : bs -- no more seed, no more steps to do! -- id $ bs
Just (b, Right a) -> b : apoList coalg a -- new seed, go on! -- apo g $ a
-- ^^^^^ (embed (Cons b bs))
如此
apoTails :: [a] -> [[a]] -- [[a]] ~ [b], b ~ [a]
apoTails = apoList tailsCoalg
where
-- tailsCoalg :: [a] -> Maybe ([a], Either [[a]] [a])
tailsCoalg [] = Just ([], Left [])
tailsCoalg s@(_:xs) = Just (s, Right xs)
答案 1 :(得分:2)
Data.Functor.Foldable
由 recursion-schemes 软件包提供。 apo
的类型为:
apo :: Corecursive t => (a -> Base t (Either t a)) -> a -> t
在这里,t
是展开生成的结构,而Base t
是其展开函数。广义上讲,基函子表示递归结构的一个层次,其思想是,如果我们将其无限地嵌套在其内部,则我们将得到与整个结构等效的类型-实际上,这正是Fix
来自Data.Functor.Foldable
的信息。 (在元注释上,似乎在递归方案中没有专门针对Base
的问答;拥有一个可能有用。)
Base
的列表是:
data ListF a b = Nil | Cons a b
所以apo
专用于:
apo @[_] :: (b -> ListF a (Either [a] b)) -> b -> [a]
如果我们想在不使用递归方案基础结构的情况下编写它,则可以使用ListF a b
与Maybe (a, b)
同构的事实:
Nil | Cons a b
Nothing | Just (a, b)
根据Maybe (a, b)
,签名将变为:
apoList :: (b -> Maybe (a, Either [a] b)) -> b -> [a]
在余数中(即apo
的函数参数),Nothing
(或<{>递归方案版本的Nil
)表示列表的生成应通过用空尾盖住列表来停止。这就是为什么即使您还使用Maybe
通过其他方式使展开短路的原因,仍然需要Either
的原因。
此apoList
的实现与您问题中的实现非常相似,不同之处在于此签名不将(b
类型的种子)限制为列表,并且翻转了Left
和Right
(这样Left
表示发生短路):
apoList :: (b -> Maybe (a, Either [a] b)) -> b -> [a]
apoList f b = case f b of
Nothing -> []
Just (x, Left e) -> x : e
Just (x, Right c) -> x : apoList f c