J может быть читаемым

в 20:37, , рубрики: apl, ненормальное программирование, функциональное программирование, метки:

Жесть! Это как программирование через регулярные выражения…
Нет, вы меня не заставите! Больше никогда!

Смотрю на код и чувствую себя идиотом. Это правда не эзотерический язык типа brainfuck? Им кто-то реально пользуется? И эти программы потом читают?

quicksort=: (($:@(<#[), (=#[), $:@(>#[)) ({~ ?@#)) ^: (1<#)

Perl нервно курит в сторонке.

Хабрахабр о J

imageJ — корейский среди языков программирования. Взрыв на фабрике спецсимволов. Куча скобочек, точек, слэшей, и всё это ещё и работает. Не иначе как по велению чёрной магии, а то и самого Сатаны.

Некоторые из тех, кто пишет на J, забывают простые правила написания любого кода в погоне за краткостью или просто увлекшись. Эти правила не новые, но они приобретают критическое значение применительно к APL-подобным языкам, потому как при чтении конструкций вроде ((]i.~[:{.[){([:{:[),]`([:<[$:[:>])@.([:32&=[:3!:0[:>]))"2 0 даже тренированный мозг сворачивается в трубочку.

Итак, простые правила написания читаемого кода на J под катом.

Без словарика под кат лучше не соваться. Вас предупреждали.
И напомню, список полезных ссылок здесь.

Пользуйтесь мнемониками

Если неудобно или просто не хочется часто лезть в словарь, чтобы подсмотреть, какими же всё-таки символами обозначается та или иная функция или что значит вон та конструкция — оберните её в глагол:

  head =: {.
  tail =: {:
  head 9 8 3 0 6 1 2 5 4 7
9
  tail 9 8 3 0 6 1 2 5 4 7
7

Разделяй и властвуй

Иногда лучше разбить сложную, запутанную конструкцию на несколько более простых.

  logistic =: dyad : '(^ - y % x) % x * *: >: ^ - y % x'

  exp =: dyad : '^ - y % x'
  logistic =: dyad : '(x exp y) % x * *: >: x exp y

Комментарии

Комментарии в J начинаются с NB., и это как раз тот случай, когда их не может быть много! Если залезть в стандартные библиотеки или промышленный код, можно наткнуться на тонны комментариев; зачастую их в несколько раз больше, чем самого кода: писать самодокументирующийся код на J сложно, и усилия обычно себя не оправдывают.

Больше пробелов, хороших и разных

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

  (]%[**:@>:@])[:^[:-%~
  (] % [ * *:@>:@]) [: ^ [: - %~

Явное лучше скрытого

Для сложных выражений часто лучше использовать эксплицитную запись вместо тацитной.

  (] % [ * *:@>:@]) [: ^ [: - %~
  dyad : '(] % (x * *:@>:@])) ^ -y%x'

Хуки и форки — ваши друзья

Для тех, кто не знал или забыл:

  (f g) y ⇔ y f (g y)
  x (f g) y ⇔ x f (g y)
  (f g h) y ⇔ (f y) g (h y)
  x (f g h) y ⇔ (x f y) g (x h y)

Эти простые конструкции часто помогают сократить количество кода вдвое-втрое, практически не ухудшая его читаемость.

Управляющие структуры

В J есть все привычные управляющие структуры, но использовать их можно только внутри эксплицитного глагола.

Ветвления работают так же, как в любом другом языке: если условие T вернуло 1, переходим к блоку B, иначе переходим к следующему блоку else./elseif., если он есть.

if. T do. B end.
if. T do. B else. B1 end.
if. T do. B elseif. T1 do. B1 elseif. T2 do. B2 end.

while. и whilst. исполняют блок B, пока T возвращает 1, с тем отличием, что whilst. пропускает проверку для первого прохода, так что B всегда выполняется как минимум один раз.

while. T do. B end.
whilst. T do. B end.

for. просто выполняет B столько раз, сколько элементов в T; for_i. создаёт переменные i и i_index — элемент и его индекс соответственно.

for. T do. B end.
for_i. T do. B end.

select. переходит к первому Ti, совпавшему с T, выполняя соответствующий блок. fcase.case. с «проваливанием».

select. T
 case. T0 do. B0
 case. T1 do. B1
 fcase.T2 do. B2
 case. T3 do. B3
end.

Если блок B выполнился с ошибкой — выполняем блок B1, иначе просто игнорируем его.

try. B catch. B1 end.


Попробуем применить эти правила к решателю судоку из этого поста.
Исходный исходный код:

  i =: ,((,|:)i.9 9),,./,./i.4$3
  c =: (#=[:#~.)@-.&0
  t =: [:(([:*/_9:c])"1#])i

Поделиться

* - обязательные к заполнению поля