Правильный комментарий для функционального программирования
Я изучаю схему, и я просто понял, что я действительно не знаю, как правильно прокомментировать код моей функциональной схемы. Я знаю, как добавить комментарий, конечно - добавьте ;
и поместите свой комментарий после него. Мой вопрос в том, что я должен поместить в свои комментарии, и где я должен комментировать максимальную удобочитаемость и понятность для других программистов, читающих мой код?
Вот фрагмент кода, который я написал. Это функция, называемая display-n
. Он может вызываться с любым количеством аргументов и выводит каждый аргумент на экран в том порядке, в котором они предоставляются.
(define display-n
(lambda nums
(letrec ((display-n-inner
(lambda (nums)
(display (car nums))
(if (not (equal? (cdr nums) (quote ()))
(display-n-inner (cdr nums))))))
(display-n-inner nums))))
Изменить: улучшена табуляция и заменена '()
на (quote ())
, чтобы избежать неправильного использования форматирования.
Я просто не знаю, как и где добавлять комментарии, чтобы сделать его более понятным. Некоторый код схемы, который я видел, просто имеет комментарии наверху, что отлично, если вы хотите использовать код, но не полезно, если хотите понять/изменить его.
Также - как мне прокомментировать макросы?
Ответы
Ответ 1
Общий стиль комментариев Lisp -
- Четыре точки с запятой для комментариев по целому подразделению файла.
- Три точки с запятой для введения одной процедуры.
- Две точки с запятой для описания определения выражения/процедуры в следующей строке.
- Одна точка с запятой для комментариев в конце строки.
Комментарии к обзору процедур, вероятно, должны соответствовать стилю RnRS documens, поэтому просто добавлять комментарии к вашей процедуре как есть, будет выглядеть примерно как
;;; Procedure: display-n NUM ...
;; Output each argument to the screen in the order they are provided.
(define
display-n (lambda nums
(letrec ((display-n-inner (lambda (nums)
(display (car nums))
(if (not (equal? (cdr nums) '()))
(display-n-inner (cdr nums))))))
(display-n-inner nums))))
N.B. Я не использую три точки с запятой для всего описания процедуры, так как он закручивает абзац заполнения в Emacs.
Теперь о коде, я бы выбрал всю команду define-variable-as-a-lambda. Да, я понимаю, что это "самый чистый" способ определить функцию, и это обеспечивает хорошую согласованность с определяющими процедурами - это результаты LET и других процедур, но есть причина синтаксического сахара, и это делает вещи более удобочитаемый. То же самое для LETREC - просто используйте внутреннюю DEFINE, что является одним и тем же, но более читаемым.
Не очень важно, что параметр DISPLAY-N-INNER называется NUMS, так как процедура настолько короткая, и DISPLAY-N просто передает свои NUMS прямо в нее. "DISPLAY-N-INNER" - это своего рода хромое имя. Вы бы дали ему что-то более смысловое значение или дали ему простое имя, например "ITER" или "LOOP".
Теперь о логике процедуры. Во-первых, (equal? (cdr nums) '())
глупо, и лучше, чем (null? (cdr nums))
. Фактически, когда вы работаете над целым списком, лучше всего сделать базовый пример проверкой того, пуст ли сам список, а не его CDR. Таким образом, процедура не будет ошибкой, если вы не передадите ей никаких аргументов (если вы этого не хотите), но я думаю, что для DISPLAY-N имеет смысл больше ничего не делать, если ничего не получает). Кроме того, вы должны проверить, следует ли останавливать процедуру, а не продолжать:
(define (display-n . nums)
(define (iter nums)
(if (null? nums)
#t ; It doesn't matter what it returns.
(begin (display (car nums))
(iter (cdr nums)))))
(iter nums))
Но при всем этом я бы сказал, что сама процедура не является лучшим способом выполнить задание, которое она делает, поскольку она слишком обеспокоена деталями прохождения списка. Вместо этого вы должны использовать более абстрактный метод FOR-EACH для выполнения работы.
(define (display-n . nums)
(for-each display nums))
Таким образом, вместо того, чтобы читатель процедуры погряз в деталях CAR и CDR, он может просто понять, что FOR-EACH будет отображать каждый элемент NUMS.
Ответ 2
Некоторые случайные примечания:
-
Традиционно код Scheme и Lisp использовал ;;;
для комментариев toplevel, ;;
для комментариев в коде и ;
для комментариев в той же строке, что и код, который они комментируют, Emacs поддерживает это, рассматривая каждую из них немного по-другому. Но особенно на стороне Scheme это уже не так популярно, но разница между ;;
и ;
по-прежнему распространена.
-
Большинство современных схем приняли новые виды комментариев: theres:
-
#|...|#
для комментария блока - полезно для длинных фрагментов текста, которые комментируют весь файл.
-
#;<expr>
- это комментарий, который заставляет реализацию игнорировать выражение, которое полезно для отладки.
-
Что касается фактического содержания того, что писать, что не отличается от любого другого языка, за исключением того, что с более функциональным подходом у вас обычно больше вариантов, как выложить свой код. Это также упрощает запись меньших функций, которые объединены в более крупные функциональные возможности, и это также меняет стиль документации, поскольку многие такие небольшие функции будут "самодокументироваться" (в том смысле, что их легко читать и очень очевидно, как они работают).
-
Мне не нравится звучать как сломанная запись, но я все же думаю, что вы должны провести некоторое время с HtDP. Одна вещь, которую он рекомендует в своем рецепте дизайна, - сначала написать примеры, затем документацию, а затем расширить ее до фактического кода. Кроме того, этот рецепт оставляет вам код с очень стандартным набором комментариев: типы ввода/вывода, назначение цели, документацию о том, как функция выполняется при необходимости, а примеры можно рассматривать как другую документацию ( который превратился бы в прокомментированный код в "реальном" коде). (Есть другие книги, которые занимают аналогичную позицию по документации.)
-
Наконец, документирование макросов не отличается от документирования любого другого кода. Единственное, что может отличаться от того, что написано в комментариях: вместо описания того, что делает какая-то функция, вы склонны описывать, какой код он также расширяет, поэтому комментарии также больше на мета уровне. Общий подход к макросам заключается в минимальной работе внутри макроса - только то, что необходимо на этом уровне (например, выражение переноса в (lambda () ...)
), и оставить фактическую реализацию функции. Это также помогает в документировании, поскольку две связанные части будут содержать комментарии о том, как макрос расширяется и как он выполняется независимо.
Ответ 3
Я следую подходу, подобному тому, что размещено здесь:
http://www.cc.gatech.edu/computing/classes/cs2360/ghall/style/commenting.html
Примечание: это для Common Lisp.
В частности:
" Four Semicolons(;;;;)
...denote a sub heading in the file...
Three Semicolons(;;;)
...denote a description of the succeeding function, macro, or
variable definition...
[I usually just most of the description into the "docstring"
of the function or variable.]
Two Semicolons(;;)
...denote a description of the succeeding expression...
One Semicolon(;)
...denotes an in-line comment that explains a particular element
of the expression on that line... Brevity is important for
inline comments"
Ответ 4
Я думаю, что отличным местом для начала было бы поставить ваше описание с одним предложением о том, что делает функция
Он может вызываться с любым количеством аргументов и выводит каждый аргумент на экран в том порядке, в котором они предоставляются.
в качестве комментария в начале.
Я не особенно разбираюсь в схеме, поэтому я не могу комментировать (:-) о том, ожидаются ли дополнительные пояснительные комментарии, объясняющие механику того, как функция достигает этого результата в соответствии с обычным стилем схемы ( но я не подозреваю).