Eithereuc在你和中扮演不同的角色apo。您Left用于表示递归基本情况,而apo用于表示核心递归Left的提前终止(即添加额外条件以中断展开)。但是,如果您想使用展开来表达您的算法,则无需提前终止,假设要展开足够的结构:
{-# LANGUAGE TemplateHaskell, TypeFamilies, KindSignatures #-}
{-# LANGUAGE DeriveFunctor, DeriveFoldable, DeriveTraversable #-}
{-# LANGUAGE LambdaCase #-}
import Data.Functor.Foldable
import Data.Functor.Foldable.TH
data Delayed a = Done a | Waiting (Delayed a)
deriving (Show)
makeBaseFunctor ''Delayed
ghci> :info DelayedF
data DelayedF a r = DoneF a | WaitingF r
psi :: Integral i => (i, i) -> DelayedF i (i, i)
psi (x, y) = case x `mod` y of
0 -> DoneF y
m -> WaitingF (y, m)
psi是 的一个余代数Delayed,并ana psi展开一个Delayed结构,其末端有 GCD:
ghci> delayedGCD = ana psi (14,35) :: Delayed Integer
ghci> delayedGCD
Waiting (Waiting (Done 7))
要获得最终结果,我们必须使用Delayed:
ghci> cata (\case { DoneF n -> n; WaitingF n -> n }) delayedGCD
7
鉴于我们正在执行 aana后跟 a cata,我们最好切换到hylo,它可以有效地组合它们:
ghci> hylo (\case { DoneF n -> n; WaitingF n -> n }) psi (14,35)
7
此时,我们可能会注意到 与DelayedF同构Either。因为对于我们当前的目的,我们只需要hylo,而不是单独地ana,cata实际上可以完全替换DelayedF并Either跳过定义Delayed(注意类型hylo没有提到隐含的递归数据结构,只提到它对应的基本函子):
euclid :: Integral a => a -> a -> a
euclid x y = hylo (either id id) psi (x, y)
where
psi :: Integral i => (i, i) -> Either i (i, i)
psi (x, y) = case x `mod` y of
0 -> Left y
m -> Right (y, m)
ghci> euclid 14 35
7
因此我们得到了Joseph Sible 的hylo解决方案,它之所以有效,是因为Either它是数据结构的基本函子,它以某种方式实现了您的递归算法。