Ocaml 流(又名“惰性列表”)和尾部递归
此问题使用以下“惰性列表”(也称为“流”)类型: 我的问题是:如何定义一个尾部递归函数Ocaml 流(又名“惰性列表”)和尾部递归,ocaml,lazy-evaluation,Ocaml,Lazy Evaluation,此问题使用以下“惰性列表”(也称为“流”)类型: 我的问题是:如何定义一个尾部递归函数lcycle,它以一个非空(非惰性)列表l作为参数,并返回对应于在元素l上重复循环的lazylist。例如: # ltake (lcycle [1; 2; 3]) 10;; - : int list = [1; 2; 3; 1; 2; 3; 1; 2; 3; 1] (ltake是List::take的一个惰性模拟;我在本文末尾给出了一个实现。) 我已经实现了几个非尾部递归版本的lcycles,例如: let
lcycle
,它以一个非空(非惰性)列表l
作为参数,并返回对应于在元素l
上重复循环的lazylist
。例如:
# ltake (lcycle [1; 2; 3]) 10;;
- : int list = [1; 2; 3; 1; 2; 3; 1; 2; 3; 1]
(ltake
是List::take
的一个惰性模拟;我在本文末尾给出了一个实现。)
我已经实现了几个非尾部递归版本的lcycles
,例如:
let lcycle l =
let rec inner l' =
match l' with
| [] -> raise (Invalid_argument "lcycle: empty list")
| [h] -> Cons (h, fun () -> inner l)
| h::t -> Cons (h, fun () -> inner t)
in inner l
…但我还没有写一个尾部递归
基本上,我遇到了这样一个问题:惰性评估是通过表单的构造实现的
Cons (a, fun () -> <lazylist>)
我认为这个定义没有什么问题。对
internal
的调用位于一个函数中,该函数在lcycle
返回之前不会被调用。因此,不存在烟囱安全问题
下面是将空列表测试移出惰性循环的替代方法:
let lcycle = function
| [] -> invalid_arg "lcycle: empty"
| x::xs ->
let rec first = Cons (x, fun () -> inner xs)
and inner = function
| [] -> first
| y::ys -> Cons (y, fun () -> inner ys) in
first
问题是你试图解决一个不存在的问题
of_list
函数不会占用任何堆栈空间,这就是懒惰列表如此强大的原因。让我试着解释一下这个过程。当您将of_list
函数应用于非空列表时,它将创建列表头部的Cons
和一个闭包,该闭包捕获对列表尾部的引用。之后,它会瞬间返回。没别的了。所以它只需要几个字的内存,并且没有一个使用堆栈。一个单词包含x
值,另一个包含闭包,它只捕获指向xs
的指针
然后,你解构这一对,得到你现在可以使用的值x
,然后函数next,这确实是一个闭包,当调用它时,它将应用于一个列表,如果它是非空的,将返回另一个Cons
。请注意,以前的cons将被销毁为垃圾,因此不会使用新内存
如果您不相信,您可以构建一个永远不会终止的of_list
函数(即,在列表上循环),并使用iter函数打印它。它将永远运行,不会占用任何内存
type 'a lazylist = Cons of 'a * (unit -> 'a lazylist)
let of_list lst =
let rec loop = function
| [] -> loop lst
| x :: xs -> Cons (x, fun () -> loop xs) in
loop lst
let rec iter (Cons (a, next)) f =
f a;
iter (next ()) f
懒散列表的创建是否会涉及尾部递归的概念?如果您坚持的话,基本上您的代码已经是“尾部递归的”。基本上,创建一个lazy_列表永远不会导致堆栈溢出,但是在lazy_列表上使用会导致堆栈溢出。您的
ltake
不是尾部递归的,您可以使其尾部递归。
let lcycle = function
| [] -> invalid_arg "lcycle: empty"
| x::xs ->
let rec first = Cons (x, fun () -> inner xs)
and inner = function
| [] -> first
| y::ys -> Cons (y, fun () -> inner ys) in
first
type 'a lazylist = Cons of 'a * (unit -> 'a lazylist)
let of_list lst =
let rec loop = function
| [] -> loop lst
| x :: xs -> Cons (x, fun () -> loop xs) in
loop lst
let rec iter (Cons (a, next)) f =
f a;
iter (next ()) f