Either
euc
在你和中扮演不同的角色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
它是数据结构的基本函子,它以某种方式实现了您的递归算法。