Подробней о last
Поскольку в заметке про last отметились только профильные специалисты (в Телеграме), считаю, нужна его подробная версия. Я и сам понимаю, что для таких огрызков служит Твиттер, но чего нет — того нет.
Итак, дело вот в чем. Коллега спрашивает у Chat GPT, насколько дорого получить последний элемент вектора функцией last в Кложе. На это чат говорит — все тип-топ, у вектора доступ к последнему элементу работает за O(1).
Все бы ничего, но в ответе напутаны правда и ложь, и в целом он не верный. Чтобы в этом убедиться, откроем исходный код функции last:
(dеf
^{:doc "Return the last item in coll, in linear time"}
last (fn ^:static last [s]
(if (next s)
(recur (next s))
(first s))))
Полагаю, даже человек, едва знакомый с Лиспом, увидит паттерн. Функция принимает коллекцию, и если в ней больше одного элемента (есть хвост), то она вызывает себя с хвостом. Когда длина хвоста равна одному, возвращается этот элемент.
В коде нет проверок на тип коллекции, например, отдельно для вектора или списка. Все очень просто и линейно.
Наконец, докстринг функции как бы говорит нам: вернуть последний элемент коллекции за линейное время. Линейное, Карл, то есть такое, что растет с числом элементов, то есть O(N).
Поэтому буквальный ответ на вопрос таков: нет, функция last вернет последний элемент вектора за O(N) шагов. Что не подходит для больших векторов.
Спрашивается, откуда у чата уверенность в правоте? Дело в том, что вектор действительно хранит ссылку на последний элемент. Для доступа к нему служит функция с забавным названием peek (заглянуть). Она работает только для вектора и для всего остального кинет ошибку.
Можно предъявить Ричу Хикки: надо было сделать интерфейс Lastable
с методом
getLast
. Обычные коллекции бегут с головы, а вектор берет с конца. Все это
упаковано в last
, который переключает на нужную логику. Все довольны и
смеются. Но имеем то, что имеем, тем более что я согласен с текущим положением
дел. Из всех коллекций только вектор может вернуть последний элемент. Остальным
коллекциям это не положено по идеологическим причинам.
Скорее всего, чат прочитал вопросы на StackOverflow и слепил некую компиляцию. Вроде бы да, но вроде бы нет. В этом и состоит вред: чат выдает что-то, очень похожее на истину. Выглядит настолько правдоподобно, что принимаешь за чистую монету. Но если проверить, то окажется, что не в карты, а в нарды, и не корову, а машину, и не выиграл, и проиграл.
Поэтому я не пользуюсь чатом и аналогами, я в них даже не зарегистрирован. И вам советую прибегать к ним реже.
Нашли ошибку? Выделите мышкой и нажмите Ctrl/⌘+Enter
vlad, 24th Oct 2024, link
Эмм, я прям взял специально поленился что-либо менять и скопировал с текста в промпт одно предложение “насколько дорого получить последний элемент вектора функцией last в Кложе” - ответ на англ.
In Clojure, the
last
function retrieves the last element of a collection, but it operates in linear time (O(n)) for most collection types, including vectors. This is becauselast
traverses the entire collection to find the final element, even for data structures like vectors where faster access is possible due to indexing. This inefficiency arises becauselast
internally creates a sequence from the collection, which leads to unnecessary overhead, particularly for vectors【5†source】【6†source】.If you’re working with vectors and need efficient access to the last element, it is recommended to use the
peek
function instead. Unlikelast
,peek
operates in constant time (O(1)) for vectors because it directly accesses the last element stored in the tail of the data structure without traversing the entire collection【7†source】【8†source】.So, in summary, while
last
is convenient, it can be costly for large vectors due to its linear time complexity. For better performance, especially with vectors, you should usepeek
.и ссылки на источники CLOJUREDOCS FRESHCODEIT CODESIGNAL LEARN D. MILLER
а если не задавать мое дефолтное ограничение писать всегда на англ, то да - на русском в ответе бред как у вас в примере с O(1).