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
Как с вами связаться? 🙃

Команда проекта находится в телеграм-сообществе по ссылке https://ttttt.me/HexletLearningBot. Там можно задать любой вопрос и повлиять на проект

Упражнение не проходит проверку — что делать? 😶

Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "Обсуждение". Как правильно задать вопрос:

В моей среде код работает, а здесь нет 🤨

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Мой код отличается от решения учителя 🤔

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Прочитал урок — ничего не понятно 🙄

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

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются

Привет! Я Тота и моя задача помочь в обучении. Чтобы активировать меня, нужно зарегистрироваться или залогиниться, если у вас уже есть аккаунт

Loading...

Ваше упражнение проверяется по этим тестам

1(ns list-internals-test
2  (:require [test-helper :refer [assert-solution]]
3            [index :refer [lookup]]))
4
5(assert-solution
6 [["foo" nil]
7  ["foo" (list (list "foo" "bar"))]
8  ["foo" (list (list "bar" "foo"))]
9  [42 (list (list 42 0)
10            (list 30 0)
11            (list 42 100500))]]
12 [false '("foo" "bar") false '(42 0)]
13 lookup)
14

Решение учителя откроется через:

20:00
waiting_clock