haskell 这是递归模式中的某种态射吗?

bq3bfh9z  于 2023-05-29  发布在  其他
关注(0)|答案(3)|浏览(122)

我首先用下面的方法实现了欧几里得算法。

euclid x 0 = x
euclid x y = euclid y (x `mod` y)

该算法是尾递归的,我希望可以用recursion-schemes直观地编写它。然后,下面的 euc 是递归部分的摘录。这个 * 欧几里得 * 函数使用 euc,而 psi 用于一步处理。

euc :: (a -> Either t a) -> a -> t
euc psi = either id (euc psi) . psi

euclid :: Integral a => a -> a -> a
euclid x y = euc psi (x, y)
  where psi (x, y) | m == 0    = Left  y
                   | otherwise = Right (y, m)
          where m = x `mod` y
  • euc* 函数看起来像 apo 态射,但我不知道如何将 apo 专门化为 euc。在我看来,它们是完全不同的东西。是否可以将 euc 写为递归模式中的某种态射?
apo :: Functor f => (t -> f (Either (Fix f) t)) -> t -> Fix f
apo psi = In . fmap (uncurry either (id, apo psi)) . psi

问候。

koaltpgm

koaltpgm1#

我不知道你是否可以把它写成一个自同态,但我确实看到了一种你可以把它写成一个自同态的方法:

euc = hylo $ either id id

我还找到了一种将其写成Elgot代数的方法:

import Data.Functor.Identity
euc psi = elgot runIdentity (fmap Identity . psi)
pwuypxnk

pwuypxnk2#

Eithereucapo中扮演不同的角色。您使用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)

psiDelayed的余代数,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

假设我们正在执行一个ana,然后是一个cata,我们最好切换到hylo,它有效地组合了它们:

ghci> hylo (\case { DoneF n -> n; WaitingF n -> n }) psi (14,35)
7

在这一点上,我们可能会注意到DelayedF同构于Either。因为对于我们目前的目的,我们只需要hylo,而不是孤立的anacata,实际上可以用Either替换DelayedF,并完全跳过定义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's hylo solution,因为Either是一个数据结构的基本函子,它以某种方式实现了递归算法。

dzhpxtsq

dzhpxtsq3#

我使用apomorphism提出了这个解决方案:使用@duplode中的Delayed结构可能会使这段代码更好。

gcd' x y = cata alg $ apo coalg (x, y)
  where
    alg NilF = 0
    alg (ConsF a b) = a + b
    coalg (a, b) = case a `mod` b of
                     0 -> ConsF b (Left (Fix NilF))
                     m -> ConsF 0 $ Right (b, m)

相关问题