Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/cassandra/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/40.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ocaml 流(又名“惰性列表”)和尾部递归_Ocaml_Lazy Evaluation - Fatal编程技术网

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