如何在OCaml中获得指向列表末尾的指针?

时间:2019-03-27 11:34:56

标签: list ocaml cdr

使用lisps(例如Scheme),可以测试两个列表尾部是否相同:

(define ls '(1 2 3))

(define tail1 (cdr ls))  ; Get the tail of the list.
(define tail2 (cdr ls))

(eqv? tail1 tail2)  ; Check if identical. Returns true.

如何使用引用在OCaml中实现等效功能?

假设我有这个

let ls = ref [1; 2; 3]
let tail1 : int list ref = get_tail_ref ls
let tail2 : int list ref = get_tail_ref ls
assert (tail1 == tail2)  (* Ensure that they are identical. *)

这是正确的方法吗?如何定义get_tail_ref

4 个答案:

答案 0 :(得分:2)

OCaml标准库中的list类型是不可变的,您无法对其进行任何操作。将指向列表的指针放入可变单元格不会使列表本身可变。幸运的是,您可以实现可变列表,甚至可以遵循Lisp数据表示形式,例如

type ('a,'b) cell = {mutable car : 'a; mutable cdr : 'b}

类型系统会与您抗争:)

另外,看起来您在混淆指针和引用。在OCaml中,所有装箱的类型(如列表,字符串,浮点数等)都表示为指针。即时类型,例如intchar和数据构造函数,例如NoneMy_constructor用标记整数表示。 (顺便说一句,这是所有现代lisps使用的相同表示)。

引用只是标准库中定义为的类型

type 'a ref = {mutable contents : 'a}

因此它是一种盒装类型,其中包含指向任意值的可变指针。因此,例如,您可以在此处放置一个列表,{contents = [1;2]}, 它将包含指向不可变列表的指针。您可以将contents更改为指向其他列表,但不能更改列表本身。同样,这里有不可变的东西,您不能将不可变的东西变成可变的。

另外,请注意,例如OCaml将共享数据

let common_tail = [3;4]
let head_one = 1 :: common_tail
let head_two = 0 :: common_tail

它们将共享相同的尾巴,例如

List.tl head_one == List.tl head_two

通常来说,这是很无辜的,因为人们在OCaml中并没有太多使用可变数据类型,但是您实际上可以创建一个引用列表,它们也将被共享,例如,

let common_tail = [ref 3; ref 4]
let head_one = ref 1 :: common_tail
let head_two = ref 0 :: common_tail

如果您现在将cadr设置为33

List.hd (List.tl head_two) := 33;;

这将同时影响两个列表

# head_one;;
- : int ref list = [{contents = 1}; {contents = 33}; {contents = 4}]

# head_two;;
- : int ref list = [{contents = 0}; {contents = 33}; {contents = 4}]

答案 1 :(得分:0)

如果要获得列表的末尾,只需在其上调用List.tl。 或者,您可以使用模式匹配来提取尾巴。 例如,您可以按以下方式编写Lisp的nthcdr

let rec nthcdr n list =
  if (n <= 0) then
    list
  else match list with
       | [] -> []
       | _::list -> (nthcdr (n - 1) list)

您可以按以下方式使用它:

let x = [1; 2; 3; 4] in
  assert ((nthcdr 3 x) == (nthcdr 3 x))

实际上,以上函数将被包装在另一个函数中,该函数在递归之前检查N是否为负。

答案 2 :(得分:0)

首先,let ls = ref [1; 2; 3]在这种情况下没有太大意义-它使ls可变,但列表内容本身不可变。尝试像这样的smth:

let ls = [1; 2; 3]
let tail1 = List.tl ls (* Note the type is `int list option` here, not `int list` *)
let tail2 = List.tl ls
assert (tail1 = tail2)

请注意,在最后一行中使用=代替==很重要-您需要进行语义相等性检查,而不是物理上的相等性检查(有关差异的详细信息,请参见https://caml.inria.fr/pub/old_caml_site/FAQ/FAQ_EXPERT-eng.html#egalite) )。

答案 3 :(得分:0)

ocaml中的列表始终是指向列表或[]的指针。列表的尾部还是列表,因此您已经有了指针。另一方面,ref则增加了另一个间接含义。因此,您的ref [1; 2; 3]实际上是一个指向包含记录1和尾部地址的内存块的指针。

简而言之,检查2个列表的尾巴是否完全相同

List.tl list1 == List.tl list2

这检查物理相等性,检查它们是否都是相同的指针。请注意,您可以拥有内容相同但物理上并不相同的列表。只有从相同尾部构造的列表才会匹配。