Добавить элемент в конец списка

Как добавить элемент в конец списка в ReasonML (эквивалент Array.concat в JavaScript)?

2 ответа

Решение

Хотя ответ Нейла технически верен, в нем скрываются некоторые детали, которые вы, возможно, захотите рассмотреть, прежде чем добиваться append; в частности, что добавление элемента в начало списка очень дешево, добавление элемента в конец очень дорого.

Чтобы понять почему, давайте посмотрим, как список определяется и создается. (Концептуальное) определение списка:

type list('a) = Cons('a, list('a)) | Nil;

где Nil представляет конец списка (и сам по себе пустой список) и Cons представляет узел в списке, содержащий элемент типа 'a и указатель на остальную часть списка (list('a)).

Если бы мы убрали весь синтаксический сахар и каждую вспомогательную функцию, вам пришлось бы создать список следующим образом:

let myList = Cons(1, Cons(2, Cons(3, Nil)));

Чтобы добавить элемент в начало этого списка, мы создаем узел, содержащий наш новый элемент и указатель на старый список:

let myBiggerList = Cons(0, myList);

Это точно так же, как делать [0, ...myList], Если myList может измениться, мы не сможем сделать это, конечно, но мы знаем, что это не так, поскольку списки неизменны. Это делает это очень дешевым, и по той же причине так же дешево обходиться без головы, поэтому вы обычно будете видеть функции обработки списков, реализованные с использованием рекурсии, например:

let rec map = f =>
  fun | []         => []
      | [x, ...xs] => [f(x), ...map(f, xs)];

Хорошо, тогда почему так дорого добавить элемент в конец списка? Если вы посмотрите на myListдобавление элемента в конец означает замену финального Nil с, скажем, Cons(4, Nil), Но тогда нам нужно заменить Cons(3, ...) так как это указывает на старое Nil, а также Cons(2, ...) потому что это указывает на старое Cons(3, ...)и так далее по всему списку. И вы должны делать это каждый раз, когда добавляете элемент. Это быстро складывает.

Так что вы должны сделать вместо этого?

Если вы добавляете в конец и либо просто просматриваете его, либо всегда удаляете элементы с конца, как вы часто это делаете в JavaScript, вы, скорее всего, можете просто изменить свою логику. Вместо того, чтобы добавлять и снимать конец, добавляйте и снимайте начало.

Если вам действительно нужна структура данных FIFO, в которой элементы вставляются с одной стороны, а с другой - снимаются, рассмотрите возможность использования очереди. В общем, взгляните на это сравнение эксплуатационных характеристик стандартных контейнеров.

Или, если это все немного, и вы действительно хотите сделать это так, как вы привыкли к JavaScript, просто используйте array вместо 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

Смотрите ссылку на игровую площадку здесь: https://reasonml.github.io/en/try.html?reason=DYUwLgBMDOYIIQLwQNoQIwQLoG4BQokMYAQklLAgAKoQBM2+hFYAwuQDICWsAdAIYAHQSAB2AEwAUxEgBpaAZmwBKfEA

Другие вопросы по тегам