【发布时间】:2021-05-24 01:02:59
【问题描述】:
以下程序在 GHC 中生成 <<loop>>。
...显然。事后看来。
这是因为walk 正在计算一个固定点,但有多个可能的固定点。当列表理解到达图形遍历的末尾时,它“询问”answer 的下一个元素;但这正是它已经在尝试计算的。我想我认为程序会到达,呃,列表的末尾,然后停止。
我不得不承认,我对这段漂亮的代码有点感伤,希望我能让它工作。
-
我应该怎么做?
-
我如何预测“打结”(指的是表达式中的值,说明如何计算值)是一个坏主意?
import Data.Set(Set)
import qualified Data.Set
-- Like `Data.List.nub`, remove duplicate elements from a list,
-- but treat some values as already having been seen.
nub :: Set Integer -> [Integer] -> [Integer]
nub _ [] = []
nub seen (x:xs) =
if Data.Set.member x seen
then nub seen xs
else x : nub (Data.Set.insert x seen) xs
-- A directed graph where the vertices are integers.
successors :: Integer -> [Integer]
successors x = [(x + 2) `mod` 7, (x + 3) `mod` 7]
-- Breadth first search of a directed graph. Returns a list of every integer
-- reachable from a root set in the `successors` graph.
walk :: [Integer] -> [Integer]
walk roots =
let rootSet = Data.Set.fromList roots
answer = roots ++ nub rootSet [y | x <- answer, y <- successors x]
in answer
main = putStrLn $ show $ walk [0]
【问题讨论】:
-
可能不如你要找的那么令人满意,但不要将最终列表定义为一个固定点,而是尝试每个级别的访问节点列表列表。
-
我现在不能写出完整的答案,但简而言之:你的问题不是打结本身,而是
nub的使用。 -
如果您已经为
Data.Set加入containers,您可能会喜欢nubOrd。 -
写递归数据结构就像写递归函数;没有允许所有有效的惰性循环定义但避免所有循环的通用方法,并且无论如何并非所有 unproductive 定义都包含抛出
NonTermination的循环thunk。但是可以通过根据“只回顾”之类的原则组织代码来解决大类问题——使用单调“索引”来避免逻辑循环,就像递归基本案例和结构递归一样——以及“ 请勿触摸”——避免在计算“结束”之前检查圆形结构以避免意外循环。 -
一些可能相关的例子here。
标签: haskell fixed-point-iteration tying-the-knot