12

He estado aprendiendo el esquema y me acabo de dar cuenta de que realmente no sé cómo comentar correctamente mi código de esquema funcional. Sé cómo para agregar un comentario, por supuesto - agregas un ; y pones tu comentario después de él. Mi pregunta es ¿qué debo poner en mis comentarios, y donde debería comentar para la máxima legibilidad y comprensibilidad para otros programadores que leen mi código?Comentarios adecuados para la programación funcional

Aquí hay un fragmento de código que escribí. Es una función llamada display-n. Se puede invocar con cualquier cantidad de argumentos y genera cada argumento en la pantalla en el orden en que se proporcionan.

(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)))) 

Editar: Mejora de la tabulación y reemplazado '() con (quote()) para evitar SO echar a perder el formato.

No estoy seguro de cómo/dónde agregar comentarios para que sea más comprensible. Algunos códigos de esquema que he visto solo tienen comentarios en la parte superior, lo cual es genial si quieres usa el código, pero no es útil si quieres entenderlo/modificarlo.

Además, ¿cómo debo comentar las macros?

Respuesta

6

El estilo común para Lisp comenta es

  • cuatro puntos y comas para comentario de toda una subsección de un archivo.
  • Tres puntos y comas para introducir un solo procedimiento.
  • Dos puntos y comas para una descripción de la definición de expresión/procedimiento en la siguiente línea.
  • Un punto y coma para un comentario final.

Procedimiento comentarios Presentación probablemente deberían seguir el estilo de RNR documens, por lo que acaba de añadir comentarios a su procedimiento como está, sería algo como

 
;;; 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. No uso tres puntos y comas para la descripción del procedimiento completo, ya que daña el párrafo de relleno en Emacs.


Ahora, sobre el código, me desharía de todo el asunto definir-variable-como-un-lambda.Sí, entiendo que esta es la forma "más pura" de definir una función, y hace una buena consistencia con los procedimientos de definición son los resultados de LET y otros procedimientos, pero hay una razón para el azúcar sintáctico, y es para hacer las cosas más legible. Lo mismo para el LETREC: solo use un DEFINE interno, que es lo mismo pero más legible.

No es un gran problema que el parámetro DISPLAY-N-INNER se llame NUMS, ya que el procedimiento es muy corto y DISPLAY-N simplemente le entrega sus NUMS de todos modos. "DISPLAY-N-INNER" es una especie de nombre cojo, sin embargo. Le daría algo con más significado semántico, o le daría un nombre simple como "ITER" o "LOOP".

Ahora sobre el lógica del procedimiento. En primer lugar, (equal? (cdr nums) '()) es tonto, y es mejor que (null? (cdr nums)). En realidad, cuando opera en una lista completa, es mejor hacer que el caso base sea una prueba de si la lista en sí, y no su CDR, está vacía. De esta manera, el procedimiento no tendrá errores si no le pasa argumentos (a menos que desee que lo haga, pero creo que tiene más sentido para DISPLAY-N hacer nada si no obtiene nada). Por otra parte, se debe probar si a detener el procedimiento, no se si se debe continuar:

 
(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)) 

Pero con todo esto, yo diría que el procedimiento en sí mismo no es la mejor manera de cumplir con la tarea que hace, ya está demasiado preocupado con los detalles de atravesar una lista. En su lugar, usaría el método FOR-EACH más abstracto para hacer el trabajo.

 
(define (display-n . nums) 
    (for-each display nums)) 

De esta manera, en lugar de un lector del procedimiento de quedar empantanados en los detalles de las CAR y CDR, se puede entender que se acabo PARA CADA-mostrará cada elemento de NUMS.

+0

+1, todo eso es un buen consejo (especialmente la parte sobre el azúcar sintáctico). Además, tbh, de hecho, no sabía nada de 'null?', Así que eso es útil. Con respecto a cada uno, me doy cuenta de que esa es la mejor manera de abordar este problema en particular, pero entonces mi fragmento de código habría sido demasiado trivial :) – Cam

+0

Como otra forma intermedia, ¿por qué usar el procedimiento interno? Puede hacer el recursivo externo sin pérdida de encapsulación. – Svante

+0

@Svante: Es porque el 'nums' externo es una lista formada por todos los argumentos proporcionados a la función. Llamar recursivamente al procedimiento externo solo proporcionaría un parámetro: los argumentos en forma de lista. El problema es que 'display-n' en realidad espera múltiples argumentos, no un argumento que sea una lista, por lo que no funcionaría. Una vez dicho esto, su sugerencia sería posible usando 'apply', excepto que no estoy seguro de cuán eficiente' apply' es, por lo que aún podría no ser una buena solución. – Cam

0

creo que un gran lugar para comenzar sería poner su descripción de una sola frase de lo que hace la función de

Se le puede llamar con cualquier número de argumentos y da salida a cada argumento a la pantalla en el orden que son provistos

como un comentario al principio.

No soy particularmente versado en el esquema, así que no puedo comentar (:-) sobre si se esperarán comentarios adicionales línea por línea explicando la mecánica de cómo la función logra ese resultado de acuerdo con el estilo de esquema normal (pero sospecho que no).

2

que siguen un enfoque similar a lo que está publicado aquí:

http://www.cc.gatech.edu/computing/classes/cs2360/ghall/style/commenting.html

Nota: esto es para Common Lisp.

Específicamente:

" 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

Algunas notas al azar:

  • Tradicionalmente, Esquema y código Lisp ha utilizado ;;; para comentarios de nivel superior, ;; para comentarios en el código, y ; para comentarios sobre el misma línea que el código que están comentando. Emacs tiene soporte para esto, tratando cada uno de estos un poco diferente. Pero especialmente en el lado de Scheme esto ya no es tan popular como lo fue, pero la diferencia entre ;; y ; es aún común.

  • mayoría de los sistemas modernos han adoptado nuevos tipos de comentarios: theres:

    • #|...|# para un comentario de bloque - útil para piezas largas de texto que comentar todo el archivo.
    • #;<expr> es un comentario que hace que la implementación ignore la expresión, que es útil para la depuración.
  • En cuanto al contenido real de lo que escribir, que no es diferente a cualquier otro idioma, excepto que con un enfoque más funcional que por lo general tiene más opciones sobre cómo diseñar su código. También hace que sea más conveniente escribir funciones más pequeñas que se combinan en grandes piezas de funcionalidad, y esto también cambia el estilo de la documentación, ya que muchas de esas funciones pequeñas serán "autodocumentadas" (en el sentido de que son fáciles de leer y muy obvio en cómo están trabajando).

  • Odio sonar como un disco rayado, pero sigo pensando que debería pasar algo de tiempo con HtDP. Una cosa que fomenta en su receta de diseño es escribir ejemplos primero, luego la documentación, y luego expandir eso al código real. Además, esta receta le deja un código que tiene un conjunto de comentarios muy estándar: los tipos de entrada/salida, una declaración de propósito, alguna documentación sobre cómo se implementa la función cuando sea necesario y los ejemplos pueden considerarse como otro tipo de documentación (que se convertiría en código comentado en código "real"). (Hay otros libros que toman una posición similar con la documentación.)

  • Finalmente, la documentación de macros no es diferente de la documentación de cualquier otro código. Lo único que puede ser muy diferente es lo que está escrito en los comentarios: en vez de describir lo que está haciendo alguna función, tiendes a describir qué código es y también expande, por lo que los comentarios son más sobre el nivel meta. Un enfoque común para las macros es hacer un trabajo mínimo dentro de la macro: solo lo que se necesita en ese nivel (por ejemplo, expresiones wrap en (lambda() ...)), y dejar la implementación real a una función. Esto ayuda a documentar también, ya que las dos piezas relacionadas tendrán comentarios sobre cómo se expande la macro y cómo se ejecuta, de forma independiente.

+0

+1, Gracias - útil como de costumbre. En particular, aprecio el segundo y último punto. – Cam

Cuestiones relacionadas