Как написать читаемый код Clojure?

13

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

Какие общие соглашения следует соблюдать в отношении соглашений об именах и отступов в различных ситуациях?

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

(defn f [{x :x y :y z :z [a b c] :coll}] (print x " " y  " " z " " a " " b " " c)) 

В случае деструктуризации, лучше ли сделать это непосредственно на уровне параметров или запустить форму let и затем продолжить там?

Амог Талпалликар
источник
3
Хороший ответ о читабельности в переполнении стека. Вы можете проверить это. stackoverflow.com/a/1894891/1969106
yfklon
2
Написание читаемого кода на Лиспе вообще сложно. Они изобрели backronym "потерянный в лишних скобках" по причине.
Мейсон Уилер

Ответы:

23

Соглашения об именах

  • оставайтесь строчными для функций
  • использовать -для переноса слов (то, что будет подчеркивание или верблюд в других языках).

    (defn add-one [i] (inc i))

  • Предикаты (то есть функции, возвращающие true или false) заканчиваются ? примерами:odd? even? nil? empty?

  • Процедуры изменения состояния заканчиваются в !. Вы помните set!правильно? илиswap!

  • Выберите короткие имена переменных длины в зависимости от их охвата. Это означает, что если у вас действительно небольшая вспомогательная переменная, вы часто можете просто использовать однобуквенное имя. (map (fn [[k v]] (inc v)) {:test 4 :blub 5})при необходимости выбирайте более длинные имена переменных, особенно если они используются для большого количества строк кода, и вы не можете сразу угадать их назначение. (мое мнение).

    Я чувствую, что многие программисты на clojure предпочитают использовать общие и короткие имена. Но это, конечно, не совсем объективное наблюдение. Дело в том, что многие функции clojure на самом деле довольно общие.

Лямбда-функции

  • На самом деле вы можете назвать лямбда-функции. Это удобно для отладки и профилирования (мой опыт здесь с ClojureScript).

    (fn square-em [[k v]] {k (* v v)})

  • Используйте встроенные лямбда-функции #()как удобно

Пробелы

  • Не должно быть строк только для паренов. Т.е. сразу же закройте скобки. Помните, что для редактора и компилятора есть парены, отступы для вас.

  • Списки параметров функции переходят на новую строку

   (Определить минусы
     [AB]
     (список а))

Это имеет смысл, если вы думаете о строках документа. Они находятся между именем функции и параметрами. Следующая строка документа, вероятно, не самая мудрая;)

   (Определить минусы
     "Спаривание вещей"
     [AB]
     (список а))
  • Парные данные могут быть разделены новой строкой, пока вы сохраняете соединение
  (Def F 
    [{x: x 
      у: у 
      z: z  
      [abc]: coll}] 
    (выведите x "" y "" z "" a "" b "" c)) 

(Вы также можете войти, ,как вам нравится, но это не соответствует правилам).

  • Для отступов используйте достаточно хороший редактор. Несколько лет назад это был emacs для редактирования lisp, vim также хорош сегодня. Типичные clojure IDE также должны обеспечивать эту функциональность. Только не используйте случайный текстовый редактор.

    В vim в командном режиме вы можете использовать =команду для правильного отступа.

  • Если команда становится слишком длинной (вложенной и т. Д.), Вы можете вставить новую строку после первого аргумента. Теперь следующий код довольно бессмысленный, но он показывает, как можно группировать и делать отступы:

(+ (если-пусть [age (: личный возраст Coll)]
     (если (> 18 лет)
       возраст
       0))
   (количество (диапазон (- 3 б)
                 (уменьшить + 
                         (диапазон б 10)))))

Хороший отступ означает, что вам не нужно считать скобки. Скобки предназначены для компьютера (чтобы интерпретировать исходный код и сделать отступ). Отступы для вашего легкого понимания.

Функции высшего порядка forи doseqформы

Исходя из фона Схемы, я был довольно горд тем, что понял mapи лямбда-функции и т. Д. Так что довольно часто я писал бы что-то вроде этого

(map (fn [[k x]] (+ x (k data))) {:a 10 :b 20 :c 30})

Это довольно сложно читать. forФорма способ лучше:

(for [[k x] {:a 10 :b 20 :c30}]
  (+ x (k data)))

`map имеет много применений и действительно хорош, если вы используете именованные функции. Т.е.

(map inc [12 30 10]

(map count [[10 20 23] [1 2 3 4 5] (range 5)])

Использовать макросы Threading

Используйте пронизывающих макросы ->и ->>так же , как dotoесли это применимо.

Дело в том, что макросы потоков делают исходный код более линейным, чем состав функций. Следующий фрагмент кода довольно нечитаем без макроса потоков:

   (f (g (h 3) 10) [10 3 2 3])

сравнить с

   (-> 
     (h 3)
     (g 10)
     (f [10 3 2 3]))

С помощью макроса потоков можно избежать введения временных переменных, которые используются только один раз.

Другие вещи

  • Используйте строки документации
  • сохранить функции короткими
  • читать другой код clojure
wirrbel
источник
Эта функция с деструктуризацией выглядит прекрасно с отступом!
Амог Талпалликар
+1 для коротких функций. Множество маленьких функций намного более самодокументированы
Даниэль Гратцер
1
Я категорически не согласен с тем, что рекомендуется использовать короткие имена переменных, даже в «кратковременных» функциях. Хорошие имена переменных имеют решающее значение для удобства чтения и ничего не стоят, кроме нажатий клавиш. Это одна из вещей, которая беспокоит меня больше всего в сообществе Clojure. Есть много людей с почти враждебным сопротивлением описательным именам переменных. Ядро Clojure усеяно однобуквенными именами переменных для аргументов функций, и это значительно усложняет изучение языка (например, работает docили sourceв REPL). Конец разглагольствования, для иначе превосходного ответа
Натан Уоллес
@NathanWallace В некотором смысле, я согласен с вами, но в некоторых отношениях я не согласен. Длинные имена иногда имеют тенденцию делать функции сверхспецифичными. Таким образом, вы можете обнаружить, что какая-то общая операция фильтрации на самом деле является общей, в то время как applesвместо аргумента xsвы думали, что он специфичен для яблок. Кроме того, я бы также счел, что имена аргументов функции более значимы, чем, скажем, переменная цикла for. так что если нужно, вы можете иметь их дольше. В качестве последней мысли: я оставлю вас с «Код имени не значения» concatenative.org/wiki/view/Concatenative%20language/…
wirrbel
Я мог бы добавить абзац о чем-то вроде приватных и публичных интерфейсов. Особенно в отношении библиотек. Это аспект качества кода, о котором недостаточно говорится, и я многому научился, написав этот ответ.
wirrbel