1

如何在 ReasonML 中的列表末尾附加一个元素(相当于Array.concatJavaScript)?

4

2 回答 2

10

虽然 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 ,以此类推整个列表。每次添加元素时都必须这样做。这很快就加起来了。NilCons(4, Nil)Cons(3, ...)NilCons(2, ...)Cons(3, ...)

那么你应该怎么做呢?

如果您要添加到末尾并且只是迭代它或总是从末尾删除元素,就像您在 JavaScript 中经常做的那样,您很可能只是颠倒了您的逻辑。与其添加和删除结尾,不如添加和删除开头。

如果您确实需要 FIFO 数据结构,其中元素在一端插入并在另一端取出,请考虑使用Queue代替。总的来说,看看这个标准容器的性能特征比较

或者,如果这有点多,而您真的只是想像习惯 JavaScript 那样做,只需使用 anarray而不是list. 您将在模块中找到您熟悉的所有功能Js.Array

于 2018-03-01T16:07:48.490 回答
9

您可以使用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

在此处查看操场链接:https ://reasonml.github.io/en/try.html?reason=DYUwLgBMDOYIIQLwQNoQIwQLoG4BQokMYAQklLAgAKoQBM2+hFYAwuQDICWsAdAIYAHQSAB2AEwAUxEgBpaAZmwBKfEA

于 2018-03-01T14:05:21.200 回答