Универсальный loop и нелокальные переходы
открытый урокЭтот раздел читается без входа. Войди, чтобы отмечать прогресс, вести заметки и решать задачи в редакторе. войти
Универсальный loop и нелокальные переходы
Сквозная мысль урока: убираем вложенность. Один
loop, у которого заголовок собирается из глаголов и модификаторов, схлопывает вложенные циклы в одну строку. Нелокальные переходы заменяют флаги и вложенныеifранним выходом. Конвейерные макросы разворачивают вложенные вызовы в цепочку шагов.
Один цикл, много глаголов
Заголовок это вектор из глаголов (что перебирать) и модификаторов (как фильтровать).
Глаголы:
:in работает и по файберам, то есть по генераторам из урока про файберы:
Модификаторы:
Разница между :when и :while важная: первый пропускает элемент и идёт дальше, второй заканчивает цикл целиком. Собери комбинацию в стенде и посмотри на список итераций:
Несколько глаголов подряд дают вложенные циклы:
Пять форм одного цикла
Заголовок везде одинаков, различается только результат:
| Форма | Что возвращает |
|---|---|
loop | nil, только ради побочных эффектов |
seq | массив значений |
tabseq | таблицу, тело даёт ключ и значение |
catseq | массив, склеенный из результатов |
generate | ленивый файбер вместо массива, см. 31-janet/12 · Файберы |
Один синтаксис заголовка, пять способов получить результат: это тот случай, когда экономия на синтаксисе окупается.
each это сокращение для частого случая. (each x коллекция тело) то же самое, что (loop [x :in коллекция] тело), только короче. В коде ты почти всегда увидишь each, а полный loop там, где нужны модификаторы.
Нелокальные переходы
break
break выходит из цикла немедленно:
label и return
break выходит только из цикла. Чтобы выйти из произвольного блока, в том числе из вложенных циклов сразу, есть пара label и return:
label задаёт метку и выполняет тело, return мгновенно завершает блок с этой меткой, отдав значение. Классическое применение это ранний выход из поиска.
Это не return из других языков. Он требует метку и выходит из блока с этой меткой, а не из функции: (return :found) без объявленной метки рядом упадёт с ошибкой. Думай так: label это дверь с табличкой, а return прыжок именно в эту дверь, минуя все промежуточные комнаты.
defer
defer гарантирует выполнение действия при выходе из блока, в том числе при ошибке:
Проверим на ошибке:
Это аналог finally из других языков. Для файлов и соединений есть готовая обёртка with, построенная поверх того же механизма. Дисциплина та же, что мы разбирали в уроке про владение ресурсами, только гарантируется рантаймом, а не типами.
Конвейерные макросы
Вложенность бывает не только в циклах и блоках. Когда данные проходят цепочку преобразований, вложенные вызовы читаются наизнанку:
Конвейерные макросы разворачивают такую цепочку в порядке выполнения.
-> подставляет результат первым аргументом:
->> подставляет последним:
Выбор диктует стандартная библиотека: map, filter, reduce и остальные функции работы с коллекциями принимают коллекцию последним аргументом, поэтому для них нужен ->>.
-?> и -?>> обрывают цепочку, как только получили nil:
Это избавляет от лестницы проверок на nil при навигации по вложенным данным. Тот же короткий путь, что даёт ?. в JavaScript.
as-> даёт имя промежуточному значению, когда позиция аргумента меняется:
Что запомнить
- Сквозная мысль: убираем вложенность. Циклы схлопывает заголовок
loop, флаги и вложенныеifубирает ранний выход, вложенные вызовы разворачивает конвейер. - Один
loopс глаголами:range,:down,:in,:keys,:pairs. :whenпропускает элемент,:whileобрывает цикл целиком,:letзаводит локальную переменную.- Один заголовок, пять форм:
loop,seq,tabseq,catseqи ленивыйgenerate. eachэто короткая запись частого случая с:in.breakвыходит из цикла,labelиreturnиз любого блока,deferгарантирует уборку даже при ошибке.->подставляет первым аргументом,->>последним, стандартная библиотека просит->>.-?>обрывает цепочку на первомnil.
Упражнения
Дальше
Фундамент языка пройден целиком. Дальше про организацию программ: как разложить код по файлам, оформить пакет и подключить чужой.
домашка