Скобки настолько пугают новичков, что весь интернет завален вопросами: "Почему в лиспе так много скобок?". Такие вопросы возникают не просто так: при обычном способе редактирования текста Lisp-программы с трудом поддаются модификации. Забытая скобка может стать причиной долгой отладки. Посмотрите на этот код:
(define (GET/hash #:rconn [rconn (current-redis-connection)] key
#:map-key [fkey identity]
#:map-val [fval identity])
(let loop ([lst (HGETALL #:rconn rconn key)] [h (hash)])
(if (null? lst) h
(loop (cddr lst) (hash-set h (fkey (car lst)) (fval (cadr lst)))))))
В самом конце очень много скобок. Представьте, что будет, если придётся обернуть какую-то часть кода в новый список или удалить ненужный список? Похожие проблемы возникают при редактировании HTML-файлов, когда нужно удалить как открывающий, так и закрывающий тег (либо, наоборот, добавить).
Очевидно, что обычный способ работы в редакторе не слишком подходит для модификации Lisp-программ. Поэтому в этом мире приняты другие подходы, о которых начинающие Lisp-программисты узнают случайно.
Секрет приятной работы с Lisp-кодом состоит в изменении точки зрения на этот код. В то время как в обычных языках мы модифицируем текст, в Lisp мы оперируем деревом. Для удобной работы с этим деревом нам понадобятся операции, которые помогают легко вставлять и удалять узлы, объединять их и разъединять. А еще неплохо было бы никогда не нарушать "баланс скобок".
Такой способ работы с кодом существует и называется "структурное редактирование". Исторически сложилось так, что расширения для редакторов принято называть "Paredit". Попробуйте загуглить: "<имя редактора> paredit lisp". Скажем, для продуктов компании JetBrains разработано специальное расширение Cursive. В документации этого расширения наглядно показаны возможности Paredit. Обязательно посмотрите эти гифки, они помогут понять принципы управления Lisp-кодом. У Paredit есть альтернатива Parinfer.
При правильном подходе в какой-то момент вы вдруг обнаружите, что структурное редактирование эффективнее обычного. Скобки перестанут быть проблемой, а при возврате в обычные языки вы начнёте испытывать неудобства.
Другой важный аспект — правильное форматирование. Длинные операции принято разбивать так, что операнды оказываются друг под другом:
(+ 234
88
123423)
Более сложный пример:
(- 100
(+ 4 100)
(- 1000
50))
Такая запись тоже требует привыкания, но взамен вы сможете с лёгкостью ориентироваться в аккуратно оформленном коде.
Выведите на экран значение выражения: 4 + 2 - 3 * 5 - 8 / 7
. Выполните форматирование кода так, чтобы он легче воспринимался (код можно разбить по-разному).
Если вы зашли в тупик, то самое время задать вопрос в «Обсуждениях». Как правильно задать вопрос:
Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.
Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи.
В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.
Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в «Обсуждениях». Идеально, если вы сформулируете непонятные моменты в виде вопросов. Обычно нам нужно несколько дней для внесения правок.
Кстати, вы тоже можете участвовать в улучшении курсов: внизу есть ссылка на исходный код уроков, который можно править прямо из браузера.
Ваше упражнение проверяется по этим тестам
1#lang racket
2
3(require "../../../src/tests.rkt")
4
5(assert-output "-71/7")
6
Решение учителя откроется через: