如何在 ReasonML 中的列表末尾附加一个元素(相当于Array.concat
JavaScript)?
2 回答
虽然 Neil 的回答在技术上是正确的,但它掩盖了一些您在接触之前可能需要考虑的细节append
;具体来说,虽然将元素添加到列表的开头非常便宜,但将元素添加到末尾却非常昂贵。
为了理解为什么,让我们看看列表是如何定义和构造的。列表的(概念)定义是:
// Reason
type list('a) = Cons('a, list('a)) | Nil;
(* OCaml *)
type 'a list = Cons of 'a* 'a list | Nil
其中Nil
表示列表的结尾(并且本身是一个空列表)并Cons
表示列表中的一个节点,包含一个类型的元素'a
和一个指向列表其余部分的指针(list('a)
, OCaml: 'a list
)。
如果我们去掉所有的语法糖和每个辅助函数,你将不得不构造一个像这样的列表:
// Reason
let myList = Cons(1, Cons(2, Cons(3, Nil)));
(* OCaml *)
let myList = Cons (1, Cons (2, Cons (3, Nil)))
然后,为了向这个列表的头部添加一个元素,我们构造一个包含我们的新元素和一个指向旧列表的指针的节点:
// Reason
let myBiggerList = Cons(0, myList);
(* OCaml *)
let myBiggerList = Cons (0, myList)
[0, ...myList]
这与做(OCaml: )完全相同0 :: myList
。如果myList
可以改变,我们当然不能这样做,但我们知道它不会,因为列表是不可变的。这使得它非常便宜,并且出于同样的原因,它也同样便宜,这就是为什么您通常会看到使用递归实现的列表处理函数,如下所示:
// Reason
let rec map = f =>
fun | [] => []
| [x, ...xs] => [f(x), ...map(f, xs)];
(* OCaml *)
let rec map f = function
| [] -> []
| x::xs -> (f x) :: (map f xs)
好的,那么为什么将元素添加到列表的尾部会如此昂贵?如果你回顾一下,myList
在末尾添加一个元素意味着将最后一个元素替换为. 但是我们需要替换,因为 that 指向 old ,并且因为它指向 old ,以此类推整个列表。每次添加元素时都必须这样做。这很快就加起来了。Nil
Cons(4, Nil)
Cons(3, ...)
Nil
Cons(2, ...)
Cons(3, ...)
那么你应该怎么做呢?
如果您要添加到末尾并且只是迭代它或总是从末尾删除元素,就像您在 JavaScript 中经常做的那样,您很可能只是颠倒了您的逻辑。与其添加和删除结尾,不如添加和删除开头。
如果您确实需要 FIFO 数据结构,其中元素在一端插入并在另一端取出,请考虑使用Queue代替。总的来说,看看这个标准容器的性能特征比较。
或者,如果这有点多,而您真的只是想像习惯 JavaScript 那样做,只需使用 anarray
而不是list
. 您将在模块中找到您熟悉的所有功能Js.Array
您可以使用List.append
或 的@
简写运算符List.append
。
let lstA = [ 1 ];
let lstB = lstA @ [ 2 ];
let lstC = List.append(lstB, [ 3 ]);
这是 List 方法的文档:https ://reasonml.github.io/api/List.html