使用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
?
答案 0 :(得分:2)
OCaml标准库中的list
类型是不可变的,您无法对其进行任何操作。将指向列表的指针放入可变单元格不会使列表本身可变。幸运的是,您可以实现可变列表,甚至可以遵循Lisp数据表示形式,例如
type ('a,'b) cell = {mutable car : 'a; mutable cdr : 'b}
类型系统会与您抗争:)
另外,看起来您在混淆指针和引用。在OCaml中,所有装箱的类型(如列表,字符串,浮点数等)都表示为指针。即时类型,例如int
,char
和数据构造函数,例如None
,My_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
这检查物理相等性,检查它们是否都是相同的指针。请注意,您可以拥有内容相同但物理上并不相同的列表。只有从相同尾部构造的列表才会匹配。