Раздел 31 · Janet на практике

Универсальный loop и нелокальные переходы

middle~30 мин

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

Универсальный loop и нелокальные переходы

Сквозная мысль урока: убираем вложенность. Один loop, у которого заголовок собирается из глаголов и модификаторов, схлопывает вложенные циклы в одну строку. Нелокальные переходы заменяют флаги и вложенные if ранним выходом. Конвейерные макросы разворачивают вложенные вызовы в цепочку шагов.

Один цикл, много глаголов

Заголовок это вектор из глаголов (что перебирать) и модификаторов (как фильтровать).

Глаголы:

:in работает и по файберам, то есть по генераторам из урока про файберы:

Модификаторы:

Разница между :when и :while важная: первый пропускает элемент и идёт дальше, второй заканчивает цикл целиком. Собери комбинацию в стенде и посмотри на список итераций:

Несколько глаголов подряд дают вложенные циклы:

Пять форм одного цикла

Заголовок везде одинаков, различается только результат:

ФормаЧто возвращает
loopnil, только ради побочных эффектов
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.

Упражнения

Дальше

Фундамент языка пройден целиком. Дальше про организацию программ: как разложить код по файлам, оформить пакет и подключить чужой.

домашка

Домашка