camlspotter的答案已经足够好了。我只想在这里添加更多点。
首先,对于write a function that receives a finite list and returns an infinite, circular version of it
的问题,它可以在代码/实现级别完成,只要你真的使用该函数,它将有stackoverflow的问题,并永远不会返回。
的是你所试图做一个简单的版本是这样的:
let rec circle1 xs = List.rev_append (List.rev xs) (circle1 xs)
val circle: 'a list -> 'a list = <fun>
可以编译和理论是正确的。在[1;2;3]
上,它应该生成[1;2;3;1;2;3;1;2;3;1;2;3;...]
。
但是,当然,它会失败,因为它的运行将是无止境的,并最终stackoverflow。
那么为什么let rec circle2 = 1::2::3::circle2
将工作?
让我们看看如果你这样做会发生什么。
首先,circle2
是一个值,它是一个列表。在OCaml获得此信息后,它可以为列表2的存储器表示形式创建一个静态地址。
存储器的实际值为1::2::3::circle2
,实际值为Node (1, Node (2, Node (3, circle2)))
,即A节点为int 1,节点地址为int 2,节点地址为int 3,地址为circle2。但我们已经知道circle2的地址了,对吧?所以OCaml只是把circle2的地址放在那里。
一切都会奏效。
另外,通过这个例子,我们也可以知道这样一个事实,即对于这样定义的无限循环列表实际上并不会限制内存的成本。它不会生成一个真正的无限列表来消耗所有的内存,相反,当一个圆圈结束时,它会跳回到列表头部。
让我们回到circle1
的例子。 Circle1是一个函数,是的,它有一个地址,但我们不需要或不需要它。我们想要的是功能应用circle1 xs
的地址。它不像circle2,它是一个函数应用程序,这意味着我们需要计算一些东西来获取地址。所以,
OCaml将做List.rev xs
,然后尝试获得地址circle1 xs
,然后重复,重复。
好的,那么为什么我们有时会得到Error: This kind of expression is not allowed as right-hand side of 'let rec'
?
从http://caml.inria.fr/pub/docs/manual-ocaml/extn.html#s%3aletrecvalues
the let rec binding construct, in addition to the definition of recursive functions, also supports a certain class of recursive definitions of non-functional values, such as
let rec name1 = 1 :: name2 and name2 = 2 :: name1 in expr which binds name1 to the cyclic list 1::2::1::2::…, and name2 to the cyclic list 2::1::2::1::…Informally, the class of accepted definitions consists of those definitions where the defined names occur only inside function bodies or as argument to a data constructor.
如果使用let rec
定义绑定,说let rec name
。此name
只能在函数体或数据构造函数中使用。
在前面的两个示例中,circle1
位于函数体(let rec circle1 = fun xs -> ...
)中,而circle2
位于数据构造函数中。
如果你做let rec circle = circle
,它会给出错误,因为圆不在两个允许的情况下。 let rec x = let y = x in y
也不会这样做,因为再次,x不在构造函数或函数中。
这里也是一个明确的交代:
https://realworldocaml.org/v1/en/html/imperative-programming-1.html
科Limitations of let rec
这不是一个圆形问题。你不能编写一个表达式,它可能会导致在rec的rhs中进行递归计算:请参阅http://stackoverflow.com/questions/19859953/limitations-of-let-rec-in-ocaml – camlspotter 2014-10-21 01:47:48
您发布该链接的所有建议涉及使用可变引用(直接或通过“懒惰”来打结)。重写'cycle'是否真的不可能,所以它只使用letrec? – hugomg 2014-10-21 04:20:50
你的代码会产生'无界值ys'的错误,而不是'let rec error'。我想最后'ys'应该是'result',以便产生你说的错误 – 2014-10-21 09:49:01