Append an element at the end of a list [duplicate]

拥有回忆 提交于 2020-05-16 07:42:16

问题


How to append an element at the end of a list in ReasonML (the equivalent of Array.concat in JavaScript)?


回答1:


While Neil's answer is technically correct, it glosses over some details that you might want to consider before reaching for append; specifically that while adding an element to the beginning of a list is very cheap, adding an element to the end is very expensive.

To understand why, let's look at how a list is defined and constructed. The (conceptual) definition of a list is:

// Reason
type list('a) = Cons('a, list('a)) | Nil;
(* OCaml *)
type 'a list = Cons of 'a* 'a list | Nil

where Nil represents the end of a list (and by itself an empty list) and Cons represents a node in the list, containing an element of type 'a and a pointer to the rest of the list (list('a), OCaml: 'a list).

If we took away all the syntax sugar and every helper function, you would have to construct a list like this:

// Reason
let myList = Cons(1, Cons(2, Cons(3, Nil)));
(* OCaml *)
let myList = Cons (1, Cons (2, Cons (3, Nil)))

To add an element to the head of this list then, we construct a node containing our new element and a pointer to the old list:

// Reason
let myBiggerList = Cons(0, myList);
(* OCaml *)
let myBiggerList = Cons (0, myList)

This is exactly the same as doing [0, ...myList] (OCaml: 0 :: myList). If myList could change we wouldn't be able to do this, of course, but we know it doesn't since lists are immutable. That makes this very cheap, and for the same reason it's just as cheap to pop the head off, which is why you'll usually see list processing functions implemented using recursion, like this:

// 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)

Ok, so then why is it so expensive to add an element to the tail of the list? If you look back at myList, adding an element to the end means replacing the final Nil with, say, Cons(4, Nil). But then we need to replace Cons(3, ...) since that points to the old Nil, and Cons(2, ...) because it point to the old Cons(3, ...), and so on through the entire list. And you have to do that every time you add an element. That quickly adds up.

So what should you do instead?

If you're adding to the end and either just iterating through it or always taking elements off the end, like you often would in JavaScript, you cam most likely just reverse your logic. Instead of adding to and taking off the end, add to and take off the beginning.

If you actually need a FIFO data structure, where elements are inserted at one end and taken off at the other, consider using a Queue instead. In general, have a look at this comparison of the performance characteristics of the standard containers.

Or if this is all a bit much and you'd really just like to do it like you're used to from JavaScript, just use an array instead of a list. You'll find all the functions you're familiar with in the Js.Array module




回答2:


You can use List.append or the @ operator which is shorthand for List.append.

let lstA = [ 1 ];
let lstB = lstA @ [ 2 ];
let lstC = List.append(lstB, [ 3 ]);

Here is the documentation for List methods: https://reasonml.github.io/api/List.html

See a playground link here: https://reasonml.github.io/en/try.html?reason=DYUwLgBMDOYIIQLwQNoQIwQLoG4BQokMYAQklLAgAKoQBM2+hFYAwuQDICWsAdAIYAHQSAB2AEwAUxEgBpaAZmwBKfEA



来源:https://stackoverflow.com/questions/49051006/append-an-element-at-the-end-of-a-list

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!