我正在从命令式(主要是 C)编程角度学习 Haskell,并对 Prolog 有一些基本的了解。在尝试解决一些练习时,我遇到了一个困扰我一段时间的练习。找到答案后,更多的疑问出现了。我必须生成一个无限的列表列表,从作为输入给出的字母表开始(也称为 kleene 闭包)。该函数定义如下:
kleene :: [a] -> [[a]]
kleene xs = []:[kle++[x] | kle <- kleene xs, x <- xs]
Run Code Online (Sandbox Code Playgroud)
它有效,但我似乎不明白它是如何工作的(我认为这是练习的要点)。根据我对列表理解的理解,我获取第一个列表的第一个元素(此处kleene xs),然后遍历第二个列表的元素(xs)。此示例显示了 kleene 闭包的前 20 个元素。
> take 20 $ kleene [1,2,3]
> [[],[1],[2],[3],[1,1],[1,2],[1,3],[2,1],[2,2],[2,3],[3,1],[3,2],[3,3],[1,1,1],[1,1,2],[1,1,3],[1,2,1],[1,2,2],[1,2,3],[1,3,1]]
Run Code Online (Sandbox Code Playgroud)
现在,这是怎么回事?为什么对此的评价没有失控?是否是因为惰性评估,如果不需要的话,可以继续进行而无需真正计算?如果我尝试替换并计算表达式,我就会陷入循环:
[[],[kleene [1,2,3],1],[kleene [1,2,3],2],[kleene [1,2,3],3],[kleene [1,2,3],...]
Run Code Online (Sandbox Code Playgroud)
为了计算 kleene,我必须无限地计算它。在第一行,一旦我解决了 kleene 我得到
[[],[[],kleene [1,2,3],1,1],[[], kleene [1,2,3],1,2],[[], kleene [1,2,3],1,3],...]
Run Code Online (Sandbox Code Playgroud)
但这样它就应该永远持续下去。为什么我得到的输出逐渐变大,而不是无限大的元素?是不是因为惰性求值,每次递归调用都算作链表的一个元素,导致下一个元素 of只是后续的递归调用,而每次都kle携带 的元素?xs有人可以帮助我理解这一点吗?