Common Lisp tiene un concepto, The Printer, que incluye todas las funciones para escribir representaciones textuales de los datos de Lisp. Una de esas funciones es format, que te permite dar formato a los datos como quieras.
Las capacidades de format y del Printer de Lisp en general son un tema amplio; aquí se describen los conceptos básicos del uso de format.
La función format recibe al menos dos argumentos: un stream al que imprimir y un string de control que define qué se imprime y cómo.
Los streams son un concepto aparte y se describen en otra parte. Por ahora, lo único que necesitas saber es que la impresión se hace en el stream y que hay dos valores especiales para este argumento: t y nil. t significa imprimir en la salida estándar (en realidad, el stream al que está ligada la variable *standard-output*), y nil significa crear un string y devolverlo.
Fíjate que format siempre se evaluará a nil, excepto en ese último caso, en el que se especifica nil como el stream. Esta es una forma común de hacer simplemente interpolación o construcción de strings.
(format nil "hello world") ; => "hello world"
(format t "hello world") ; => NIL (but "hello world" is printed to standard output)
El string de control puede contener «directivas de formato» que le dan instrucciones a format sobre qué imprimir y cómo interpretar cualquier otro parámetro que se le pase. En cierto sentido, el string de control es un programa que format interpreta y ejecuta. Todas las directivas empiezan con ~ y van seguidas de un carácter (por ejemplo, ~A, ~D, ~&). La mayúscula o minúscula del carácter no importa.
La mayoría de las directivas de formato simplemente interpolan un argumento de format en la salida. Se dice que «consumen» el argumento. Algunas directivas no consumen argumentos, o consumen más de uno. Una directiva incluso te permite saltar de un lado a otro en la lista de argumentos u omitir argumentos.
Una directiva de formato de uso muy general es ~A. Imprime una versión «legible para las personas» de los datos.
(format nil "Value = ~a" 10) ; => "10"
(format nil "hello ~a" "world") ; => "hello world"
(format nil "The list is: ~A" (list 1 2 3)) ; => "The list is: (1 2 3)"
Otra directiva de formato útil es ~S. Imprime una versión «legible para la máquina» de los datos. Puede ser muy útil si quieres poder interpretar la salida generada como datos de Lisp. En muchos casos se verá igual que ~A, pero fíjate que, por ejemplo, en el caso de los strings la salida incluirá las comillas (escapadas cuando se incluyan dentro de otro string), de modo que el valor se pueda volver a leer como el parámetro string.
(format nil "~s" 10) ; => "10"
(format nil "~s" "hello world") ; => "\"hello world\""
(format nil "~s" (list 1 2 3)) ; => "(1 2 3)"
Dos directivas más muy útiles son ~% y ~&. La primera genera un carácter de nueva línea, mientras que la segunda genera una «línea nueva». La diferencia es que ~% siempre genera una nueva línea, mientras que ~& la genera si en ese momento no estás al inicio de una línea. Ninguna de las dos consume un argumento.
(format nil "~%new~%lines") ; => "
new
lines"
(format nil "~&new~&lines" ; => "new
lines"
Por último, ~~ es una directiva sencilla que genera un ~ literal.
Como casi cualquier persona con un trabajo, Layla the Lisp Alien a veces necesita hacer reportes para dar a conocer información a los demás. Te pide que la ayudes a dar formato a los datos que tiene para estos reportes.
Lo primero que Layla necesita es una función: format-quarter-value, que recibe dos parámetros, el trimestre del calendario y el valor, y les da formato para un reporte. Debe evaluar a un string.
(format-quarter-value "last" 3.14) ; => "The value last quarter: 3.14"
(format-quarter-value "this" 0) ; => "The value this quarter: 0"
Layla te agradece por la función format-quarter-value y dice que será muy útil en la siguiente función que se necesita.
El siguiente reporte necesita mostrar el trimestre y el valor de 2 trimestres impresos en dos líneas.
Además, la salida debe ir al stream que se proporciona como primer argumento.
Por ejemplo: (format-two-quarters t "last" 3.14 "this" 0) producirá lo siguiente en la salida estándar:
The value last quarter: 3.14
The value this quarter: 0
Fíjate en las líneas en blanco que lo rodean.
Layla olvidó decirte que a los aliens de Lisp les gusta ver los valores de una forma que sea legible como datos de Lisp. Así que necesita una nueva función que pueda leerse como datos de Lisp (de nuevo, al stream especificado):
(format-two-quarters-for-reading nil "last" 3.14 "this" 0) ; =>
"(\"The value last quarter: 3.14\" \"The value this quarter: 0\")"
Regístrate en Exercism para aprender y dominar Common Lisp con 27 conceptos92 ejercicios y mentoría humana real, todo gratis.