原始递归和同构关系之间有什么联系?

时间:2020-06-20 20:29:05

标签: haskell recursion functional-programming fold catamorphism

使用以下自然数的同构关系,我可以实现各种算术算法,而不必处理递归:

cataNat :: b -> (b -> b) -> Natural -> b
cataNat zero succ = go
  where
    go n = if (n <= 0) then zero else succ (go (n - 1))

fib :: Natural -> Natural
fib = fst . cataNat (0, 1) (\(a, b) -> (b, a + b))

cataNat在我看来类似于原始递归。不管提供了zerosucc的哪种组合,似乎至少应终止每个应用程序。每次迭代,总的问题将由最小/最简单的问题实例分解。因此,即使从技术上讲它不是原始递归,它似乎同样具有表现力。如果这是正确的话,则意味着同构不足以表示一般递归。为此,我们可能需要同质。我的推论是正确的吗,也就是说,等价性是否适用于任何类型的同形异能,而不仅仅是自然数?

1 个答案:

答案 0 :(得分:5)

原始递归直接对应于同态。

您是正确的,即同构同态具有与同构同等的理论力量,但是在操作上它们可以在重要方面有所不同。例如,让我们转到列表而不是Nats。

'fa'

看看cata :: b -> (a -> b -> b) -> [a] -> b cata = flip foldr -- I'm lazy, but this argument order makes a bit more sense for this example para :: b -> (a -> [a] -> b -> b) -> [a] -> b para z _ [] = z para z f (x:xs) = f x xs (para z f xs) -- Removes the first element from the list which is equal to the other argument delete1 :: Eq a => a -> [a] -> [a] delete1 x xs = cata (const []) (\el k found -> if not found && el == x then k True else el : k found) xs False -- Removes the first element from the list which is equal to the other argument delete2 :: Eq a => a -> [a] -> [a] delete2 x xs = para [] (\el raw processed -> if el == x then raw else el : processed) xs delete1相比是多么尴尬。您不仅必须通过使delete2的结果成为函数来扭曲逻辑,而且还存在非常实际的运营成本。找到匹配的元素后,您必须遍历列表中的所有内容,然后重新创建所有cata构造函数。这可能会在效率上产生明显的成本。相比之下,(:)在找到目标元素时,可以仅将列表的现有尾部用作其余部分,而无需查看它。当然,delete2的大多数用法(现实世界中不是此示例)不会产生函数,也不想访问列表的未处理尾部。对于他们来说,仅由于传递较少的数据,就可以使目录转换效率更高。

所以就理论力量而言,它们是等效的。从操作上讲,每种用法都有用,尽管变态现象更为普遍。

要以更笼统的方式扩展创意,请参见recursion-schemes库。它使用了一种看起来很不一样的表述,因此可以抽象出具有不同形状的数据类型,而不用为每种foldr / cata都可以使用的数据类型使用不同的类型。 。但这实际上只是包装相同构想的另一种方式,也涵盖了其他种类的词素,包括许多more niche(甚至possibly useless)。 >