bob*_*bob 13 recursion haskell functional-programming fold catamorphism
对自然数使用以下 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))
Run Code Online (Sandbox Code Playgroud)
cataNat对我来说看起来类似于原始递归。无论提供zero和 的哪种组合,至少它的每个应用程序似乎都可以终止succ。在每次迭代中,整个问题都被最小/最简单的问题实例分解。因此,即使它在技术上不是原始递归,它似乎也具有同样的表现力。如果这是真的,则意味着 catamorphism 不足以表达一般递归。为此,我们可能需要一个hylomorphism。我的推理是否正确,也就是说,等价性是否适用于任何类型的 catamorphism,而不仅仅是自然数?
原始递归直接对应于一个paramorphism。
您是正确的,即 catamorphism 具有与 paramorphism 等效的理论力量,但它们在操作方面可能在重要方面有所不同。例如,让我们使用列表而不是 Nats。
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
Run Code Online (Sandbox Code Playgroud)
看怎么别扭delete1,相比于delete2。您不仅必须通过cata生成函数的结果来扭曲逻辑,而且还有非常实际的运营成本。找到匹配元素后必须遍历列表中的所有内容,并重新创建所有(:)构造函数。这可能会显着降低效率。相比之下,delete2当它找到目标元素时,可以只使用列表的现有尾部作为余数,甚至不用看它。当然,foldr(现实世界,不是这个例子)的大多数用途都不会产生函数,也不想访问列表的未处理尾部。对他们来说,仅仅因为传递较少的数据,catamorphism 的效率会稍微提高一些。
因此,就理论功率而言,它们是等效的。在操作方面,每个都有用,尽管 catamorphisms 更常见。
对于这个想法的一些更一般的扩展,请参阅递归方案库。它使用一个相当不同的外观的想法的制剂,使得它可以通过数据类型具有不同形状的抽象的,而不是需要一个不同类型cata/para每个数据类型也可以应用到。但它确实只是包装相同想法的另一种方式,并且还涵盖了其他类型的态射,包括更多的 利基(甚至可能无用)的态射。