Clojure: Внутреннее устройство списков, пары, cons, nil
Все списки, с которыми вы имели дело до этого момента, либо создавались с помощью литералов и функции list
либо являлись результатами вычисления каких-то выражений. Во всех этих случаях список выглядит, как нечто неделимое, цельное. Именно так работает абстракция: для работы со списками достаточно думать о списке, как о самостоятельной и самодостаточной сущности!
Тем из вас, кто сталкивался в других языках с массивами, могло показаться, что и в Clojure списки, это те же массивы — отдельные участки памяти компьютера, хранящие все значения рядом.
На самом же деле во всех языках семейства lisp, да и в большинстве других языков, располагающих к функциональному подходу, списки — это цепочки вложенных пар. А вот пара уже является неделимой структурой. И чтобы понять списки, нужно сначала освоить пары. Однако! В Clojure ситуация со списками немного иная, в списках используются обычный связный список с головой и хвостом. Если хочется лучше понять смысл пар, крайне рекомендуется прочесть Структуру и Интерпретацию Компьютерных Программ (SICP).
Для работы со списками существуют функции first
, rest
, last
:
(def my-list '(1 2 3))
(first my-list) ; => 1
(last my-list) ; => 3
(rest my-list) ; => '(2 3)
Помимо перечисленных функций выше, есть еще упрощающие обработку списков:
(count (list 1 2 3)) ; => 3
(empty? (list)) ; => true
(empty? (list 1 2 3)) ; => false
(list? (list 1 2 3)) ; => true
; нумерация для nth идет с нуля
(nth '(1 2 3) 1) ; => 2
Задание
Реализуйте функцию lookup
, которая бы должна принимать аргумент-ключ и список пар "ключ-значение" и возвращать либо пару "ключ-значение", где ключ равен первому аргументу, либо возвращать false
, если подходящих пар в списке не нашлось. Если подходящих пар найдётся несколько, вернуть нужно первую.
Примеры:
(def user-ages
(list (list "Tom" 31)
(list "Alice" 22)
(list "Bob" 42)))
(lookup "Bob" user-ages) ; '("Bob" . 42)
(lookup "Tom" user-ages) ; '("Tom" . 31)
(lookup "Moe" user-ages) ; false
Упражнение не проходит проверку — что делать? 😶
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
- Обязательно приложите вывод тестов, без него практически невозможно понять что не так, даже если вы покажете свой код. Программисты плохо исполняют код в голове, но по полученной ошибке почти всегда понятно, куда смотреть.
В моей среде код работает, а здесь нет 🤨
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Мой код отличается от решения учителя 🤔
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Прочитал урок — ничего не понятно 🙄
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.