我有一个通常是循环的有向图,并且我想找到节点和接收器之间的关系。有向图的遍历似乎非常适合memoization with recursion,因为该图可以映射到每个节点的遍历结果并进行查询,类似于the canonical Fibonacci memoization。但是,我发现检测周期阻碍了记忆化工作,因为周期检测需要了解路径,并且可能有许多路径通向同一节点,因此映射到图形的结果似乎取决于path参数。但是,当忽略循环时,结果实际上是明确的,但是我看不出有什么方法可以将其传达给编译器。
作为一个简单但说明性的示例,假设我想找到图中某个节点可到达的所有接收器。 DFS的幼稚算法看起来像这样:
import qualified Data.Set as Set
type AdjList = [[Int]]
adj :: AdjList
adj = ...
sinks :: Int -> [Int]
sinks = sinks' Set.empty where
sinks' :: Set.Set Int -> Int -> [Int]
sinks' path node | node `Set.member` path = [] -- this forms a cycle
| null (adj !! node) = [node] -- this is a sink
| otherwise = concatMap (sinks' (Set.insert node path)) (adj !! node)
试图将其写成备忘录显然会导致尝试填充path参数时遇到问题:
sinks = sinks' Set.empty where
sinks' path = (map (sinks'' {- what goes here? -}) [0..(length adj - 1)] !!) where
sinks'' path' node | node `Set.member` path' = [] -- this forms a cycle
| null (adj !! node) = [node] -- this is a sink
| otherwise = concatMap (sinks' (Set.insert node path')) (adj !! node)
例如,在该图上的遍历中,我们可以看到从A到循环的路径如何不同,但是如果记住了节点D的结果,则仅需执行一次遍历D的遍历:
我被迫用一个显式表手动编写此备忘录吗?