Все списки, с которыми вы имели дело до этого момента, либо создавались с помощью литералов и функции 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
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
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
Решение учителя откроется через: