Haskell 允许递归调用之间存在依赖关系的递归方案(有序亚同态?)
我对编写递归代码的高阶方法(递归方案)感兴趣,其中递归调用之间可能存在依赖关系Haskell 允许递归调用之间存在依赖关系的递归方案(有序亚同态?),haskell,functional-programming,fold,category-theory,recursion-schemes,Haskell,Functional Programming,Fold,Category Theory,Recursion Schemes,我对编写递归代码的高阶方法(递归方案)感兴趣,其中递归调用之间可能存在依赖关系 作为一个简化的例子,考虑遍历整数树的函数,检查总和是否小于某个值。我们可以对整棵树求和,并将其与最大值进行比较。或者,一旦超过最大值,我们可以保持运行和短路: data Tree = Leaf Nat | Node Tree Tree sumLT :: Nat -> Tree -> Bool sumLT max t = sumLT' max t > 0 sumLT' :: Nat -> T
作为一个简化的例子,考虑遍历整数树的函数,检查总和是否小于某个值。我们可以对整棵树求和,并将其与最大值进行比较。或者,一旦超过最大值,我们可以保持运行和短路:
data Tree = Leaf Nat | Node Tree Tree
sumLT :: Nat -> Tree -> Bool
sumLT max t = sumLT' max t > 0
sumLT' :: Nat -> Tree -> Int
sumLT' max (Leaf n) = max - n
sumLT' max (Node l r) =
let max' = sumLT' max l
in if max' > 0 then sumLT' max' r else 0
有没有一种方法可以用递归方案来表达这个想法——本质上是有序遍历?我感兴趣的是尽可能一般地组成这样的有序遍历
理想情况下,我希望使用某种方式来编写遍历,其中在数据结构上折叠(或展开)的函数决定遍历的顺序。无论我最终得到什么抽象,我都希望能够编写上面的sumLT'
遍历的逆序版本,我们从右向左:
sumLT'' :: Nat -> Tree -> Int
sumLT'' max (Leaf n) = max - n
sumLT'' max (Node l r) =
let max' = sumLT'' max r
in if max' > 0 then sumLT'' max' l else 0
与往常一样,折叠到endo函数中会给您一个处理顺序/状态传递的概念:
import Numeric.Natural
data Tree = Leaf Natural | Node Tree Tree
cata :: (Natural -> r) -> (r -> r -> r) -> Tree -> r
cata l n (Leaf a) = l a
cata l n (Node lt rt) = n (cata l n lt) (cata l n rt)
sumLT :: Natural -> Tree -> Bool
sumLT max t = cata (\ a max -> max - a) -- left-to-right
(\ l r max -> let max' = l max in
if max' > 0 then r max' else 0)
t max > 0
sumLT' :: Natural -> Tree -> Bool
sumLT' max t = cata (\ a max -> max - a) -- right-to-left
(\ l r max -> let max' = r max in
if max' > 0 then l max' else 0)
t max > 0
试一试:
> sumLT 11 (Node (Leaf 10) (Leaf 0))
True
> sumLT 11 (Node (Leaf 10) (Leaf 1))
False
> sumLT 11 (Node (Leaf 10) (Leaf undefined))
*** Exception: Prelude.undefined
> sumLT 11 (Node (Leaf 11) (Leaf undefined))
False
> sumLT 11 (Node (Leaf 10) (Node (Leaf 1) (Leaf undefined)))
False
> sumLT' 11 (Node (Leaf undefined) (Leaf 11))
False
与往常一样,Haskell的懒惰让他能够提前短路/退出。从示例中可以看出,如果cata的第二个参数(节点折叠函数)不要求其参数之一的值,则实际上根本不会访问相应的分支。我会利用Haskell的惰性来发挥我的优势。将树转换为列表(这是一个反同构),创建部分和,然后找到第一个大于限制的
{-# language DeriveFoldable #-}
module ShortSum where
import Data.Foldable
data Tree a = Leaf a | Node (Tree a) (Tree a)
deriving Foldable
type Nat = Int
type TreeN = Tree Nat
sumLt :: Nat -> TreeN -> Bool
sumLt mx = any (> mx) . scanl1 (+) . toList
你没有整数树,你有一个没有值的树。因此,
sumLT'
不会编译。请确保您的示例代码确实编译,除非您的问题是代码为什么不编译。好的捕获谢谢Int
s可以是否定的。你的意思是使用自然的?或者假设一个足够小的足够非负的Int
s,它们就不会被包围?这又是一个很好的发现。让我们让emNat
s,并假设我们有一个定理证明器或其他东西。这个例子(显然考虑不周)有点离题——我只是想举一个递归遍历的简单例子,其中递归调用之间存在依赖关系。我想你最好使用Foldable
或MFoldable
之类的东西来处理这类事情,因为它们是按顺序烘焙的foldr
当然可以胜任这项任务。酷!我想这可能正是我想要的——我的实际用例比我的示例要复杂一点(在获得有用答案方面可能是个错误),但我应该能够使用这个想法!