Formato es una función en Common Lisp que puede producir texto formateado usando una cadena de formato similar a la cadena de formato printf . Proporciona más funcionalidad que printf
, permitiendo al usuario generar números en varios formatos (incluidos, por ejemplo: hexadecimal, binario, octal, números romanos e inglés), aplicar ciertos especificadores de formato solo bajo ciertas condiciones, iterar sobre estructuras de datos, generar datos de forma tabular, e incluso recursiva, llamando format
internamente para manejar estructuras de datos que incluyen sus propias cadenas de formato preferidas. Esto se origina funcionalmente en Lisp Machine Lisp del MIT , [1] donde se basó en Multics ioa_
[ cita requerida ] .
La format
función está especificada por la sintaxis [2]
formato destino controlCadena y resto formatoArgumentos
Las directivas en la cadena de control se interpolan utilizando los argumentos de formato y la secuencia de caracteres así construida se escribe en el destino.
El destino puede ser una secuencia, una cadena dinámica T
o la NIL
constante; el último de los cuales presenta un caso especial en el sentido de que crea, formatea y devuelve un nuevo objeto de cadena, mientras que T
se refiere a la salida estándar, que suele ser equivalente a la consola. Los flujos en Common Lisp comprenden, entre otros, salidas de cadenas y flujos de archivos; por lo tanto, al ser capaz de escribir en tal variedad de destinos, esta función unifica capacidades distribuidas entre distintos comandos en algunos otros lenguajes de programación, como C printf
para salida de consola, sprintf
formato de cadenas y fprintf
escritura de archivos.
La multitud de tipos de destino se ejemplifica a continuación:
;; Imprime "1 + 2 = 3" en la salida estándar y devuelve ``NIL''. ( formato T "1 + 2 = ~d" 3 ) ;; Crea y devuelve una nueva cadena que contiene "1 + 2 = 3". ( formato NIL "1 + 2 = ~d" 3 ) ;; Crea y devuelve una nueva cadena que contiene "1 + 2 = 3". ( con-salida-a-cadena ( salida ) ( formato de salida "1 + 2 = ~d" 3 )) ;; Escribe en el archivo "outputFile.txt" la cadena "1 + 2 = 3". ( con-abrir-archivo ( salida "outputFile.txt" :dirección :salida :si-no-existe :crear :si-existe :append ) ( formatear salida "1 + 2 = ~d" 3 )) ;; Agrega a la cadena dinámica la cadena "1 + 2 = 3". ( let (( cadena de salida ( make-array 0 : tipo de elemento 'carácter : ajustable T : puntero de relleno 0 ))) ( declarar ( tipo cadena cadena de salida )) ( formato cadena de salida "1 + 2 = ~ d" 3 ) ( la cadena de salida-cadena ))
La cadena de control puede contener caracteres literales, así como el carácter meta ~
(tilde), que delimita las directivas de formato . Mientras que los literales en la entrada se repiten palabra por palabra, las directivas producen una salida especial, que a menudo consume uno o más argumentos de formato.
Una directiva de formato, introducida por a ~
, va seguida de cero o más parámetros de prefijo, cero o más modificadores y el tipo de directiva. Por lo tanto, una definición directiva debe ajustarse al patrón
~[ parámetros de prefijo ][ modificadores ] tipo de directiva
El tipo de directiva siempre se especifica mediante un solo carácter, sin distinguir entre mayúsculas y minúsculas en el caso de letras. Los datos que una directiva de formato debe procesar, si son necesarios, se denominan argumento de formato y pueden ser cero o más objetos de cualquier tipo compatibles. Si se aceptan dichos datos y en qué cantidad depende de la directiva y de los posibles modificadores que se le apliquen. La directiva tipo ~%
, por ejemplo, se abstiene del consumo de cualquier argumento de formato, mientras que ~d
espera que se imprima exactamente un número entero, y ~@{
, una directiva influenciada por el modificador de signo de arroba, procesa todos los argumentos restantes.
La siguiente directiva, ~b
espera un objeto numérico de los argumentos de formato y escribe su equivalente binario (base 2) en la salida estándar.
( formato T "~b" 5 )
Cuando las configuraciones son permisivas, se pueden especificar parámetros de prefijo.
Los parámetros de prefijo permiten una inyección de información adicional en una directiva para operar, similar a la operación de parámetros cuando se proporcionan a una función. Los parámetros de prefijo son siempre opcionales y, si se proporcionan, deben ubicarse entre el tipo de introducción ~
y los modificadores o, si no hay ninguno, el tipo de directiva. Los valores están separados por comas, pero no toleran espacios en blanco a ambos lados. El número y tipo de estos parámetros depende de la directiva y de la influencia de posibles modificadores.
Se pueden utilizar dos caracteres particulares como valores de parámetros de prefijo con interpretación distintiva: v
o V
actúa como marcador de posición para un número entero o carácter de los argumentos de formato que se consume y se coloca en su lugar. El segundo carácter especial, #
, se sustituye por el recuento de argumentos de formato respetando su consumo. Ambos v
y #
habilitan el comportamiento definido por contenido dinámico inyectado en la lista de parámetros de prefijo.
El v
valor del parámetro introduce una funcionalidad equivalente a una variable en el contexto de la programación general. Dado este escenario simple, para rellenar a la izquierda una representación binaria del número entero 5
con al menos ocho dígitos con ceros, la solución literal es la siguiente:
( formato T "~8,'0b" 5 )
Sin embargo, el primer parámetro de prefijo que controla el ancho de salida puede definirse en términos del v
carácter, delegando la especificación del valor del parámetro al siguiente argumento de formato, en nuestro caso 8
.
( formato T "~v,'0b" 8 5 )
Las soluciones de este tipo son particularmente beneficiosas si partes de la lista de parámetros de prefijo se describen mediante variables o argumentos de función en lugar de literales, como es el caso en el siguiente código:
( let (( número de dígitos 8 )) ( declarar ( tipo ( entero 0 * ) número de dígitos )) ( formato T "~v,'0b" número de dígitos 5 ))
Aún más apropiado en aquellas situaciones que involucran entrada externa, se puede pasar un argumento de función a la directiva de formato:
( defun print-as-hexadecimal ( número a formato número de dígitos ) "Imprime el NÚMERO A FORMATO en el sistema hexadecimal (base 16), relleno a la izquierda con ceros hasta al menos el NÚMERO DE DÍGITOS. " ( declarar ( escribir número número a formatear )) ( declarar ( escribir ( entero 0 * ) número de dígitos )) ( formato T "~v,'0x" número de dígitos número a formatear ) ) ( imprimir como hexadecimal 12 2 )
#
como parámetro de prefijo cuenta los argumentos de formato que aún no han sido procesados por las directivas anteriores, y lo hace sin consumir nada de esta lista. La utilidad de un valor insertado dinámicamente está restringida predominantemente a casos de uso relacionados con el procesamiento condicional. Como el número de argumento sólo puede ser un número entero mayor o igual a cero, su significado coincide con el de un índice en las cláusulas de una ~[
directiva condicional.
La interacción del #
valor del parámetro de prefijo especial con la directiva de selección condicional ~[
se ilustra en el siguiente ejemplo. La condición establece cuatro cláusulas, accesibles a través de los índices 0, 1, 2 y 3 respectivamente. El número de argumentos de formato se emplea como medio para la recuperación del índice de cláusulas; para hacerlo, insertamos #
en la directiva condicional que permite que el índice sea un parámetro de prefijo. #
calcula el recuento de argumentos de formato y sugiere este número como índice de selección. Los argumentos, no consumidos por este acto, están disponibles y procesados por las directivas de la cláusula seleccionada.
;; Imprime "ninguno seleccionado". ( formato T "~#[ninguno seleccionado~;uno seleccionado: ~a~;dos seleccionados: ~a y ~a~:;más seleccionados: ~@{~a~^, ~}~]" ) ;; Imprime "uno seleccionado: BUNNY". ( formato T "~#[ninguno seleccionado~;uno seleccionado: ~a~;dos seleccionados: ~a y ~a~:;más seleccionados: ~@{~a~^, ~}~]" 'conejito ) ;; Imprime "dos seleccionados: BUNNY y PIGEON". ( formato T "~#[ninguno seleccionado~;uno seleccionado: ~a~;dos seleccionados: ~a y ~a~:;más seleccionados: ~@{~a~^, ~}~]" 'conejito 'paloma ) ;; Imprime "más seleccionados: CONEJITO, PALOMA, RATÓN". ( formato T "~#[ninguno seleccionado~;uno seleccionado: ~a~;dos seleccionados: ~a y ~a~:;más seleccionados: ~@{~a~^, ~}~]" 'conejito 'paloma ' ratón )
Los modificadores actúan en calidad de banderas que pretenden influir en el comportamiento de una directiva. La admisión, la magnitud de la modificación del comportamiento y el efecto, al igual que con los parámetros de prefijo, dependen de la directiva. En algunos casos graves, la sintaxis de una directiva puede variar hasta el punto de invalidar ciertos parámetros de prefijo; este poder distingue especialmente a los modificadores de la mayoría de los parámetros. Los dos caracteres modificadores válidos son @
(arroba) y :
(dos puntos), posiblemente en combinación como :@
o @:
.
El siguiente ejemplo ilustra un caso bastante leve de influencia ejercida sobre una directiva por el @
modificador: simplemente garantiza que la representación binaria de un número formateado siempre esté precedida por el signo del número:
( formato T "~@b" 5 )
A continuación se incluye una enumeración de las directivas de formato, incluida su sintaxis completa y sus efectos modificadores. [3]
Un ejemplo de una printf
llamada C es el siguiente:
printf ( "Color %s, número1 %d, número2 %05d, hexadecimal %x, flotante %5.2f, valor sin signo %u. \n " , "rojo" , 123456 , 89 , 255 , 3,14 , 250 );
Usando Common Lisp, esto es equivalente a:
( formato t "Color ~A, número1 ~D, número2 ~5,'0D, hexadecimal ~X, flotante ~5,2F, valor sin signo ~D.~%" "rojo" 123456 89 255 3,14 250 ) ;; imprime: Color rojo, número1 123456, número2 00089, hexadecimal FF, flotante 3,14, valor sin signo 250.
Otro ejemplo sería imprimir cada elemento de la lista delimitado por comas, lo que se puede hacer usando las directivas ~{ , ~^ y ~ }: [4]
( let (( comestibles ' ( huevos, pan , mantequilla, zanahorias ))) ( formato t "~{~A~^, ~}.~%" comestibles ) ; Imprime en mayúsculas ( formato t "~:(~{~A~^ , ~}~).~%" comestibles )) ; Capitaliza la producción ;; estampados: HUEVOS, PAN, MANTEQUILLA, ZANAHORIAS. ;; estampados: Huevos, Pan, Mantequilla, Zanahorias.
Tenga en cuenta que no solo se itera directamente la lista de valores format
, sino que las comas se imprimen correctamente entre los elementos, no después de ellos. Un ejemplo aún más complejo sería imprimir una lista utilizando frases habituales en inglés:
( let (( plantilla "Los afortunados ganadores fueron:~#[ none~; ~S~; ~S y ~S~ ~:;~@{~#[~; and~] ~S~^,~}~] ." )) ( formato nil plantilla ) ;; ⇒ "Los afortunados ganadores fueron: ninguno." ( formato nil plantilla 'foo ) ;; ⇒ "Los afortunados ganadores fueron: FOO." ( formato nil plantilla 'foo ' bar ) ; ; ⇒ "Los afortunados ganadores fueron: FOO y BAR." ( formato nil plantilla 'foo 'bar 'baz ) ;; ⇒ "Los afortunados ganadores fueron: FOO, BAR y BAZ ( formato nil plantilla 'foo 'bar ' ". baz 'quux ) ;; ⇒ "Los afortunados ganadores fueron: FOO, BAR, BAZ y QUUX" .
La capacidad de definir una nueva directiva proporciona los medios para la personalización. El siguiente ejemplo implementa una función que imprime una cadena de entrada en minúsculas, mayúsculas o estilo inverso, permitiendo también una configuración del número de repeticiones.~/functionName/
( defun mydirective ( destino formato-argumento dos puntos-modificador-supplied-p at-sign-modifier-supplied-p &opcional ( repeticiones 1 )) "Esta función representa una devolución de llamada adecuada como directiva en una invocación de ``formato'', esperando una cadena como su FORMATO-ARGUMENTO para imprimir REPETICIONES el número de veces al DESTINO --- Los indicadores COLON-MODIFIER-SUPPLIED-P y AT-SIGN-MODIFIER-SUPPLIED-P esperan un booleano generalizado cada uno, siendo los representantes del Modificadores ``:'' y ``@'' respectivamente. Su influencia se define de la siguiente manera: - Si no se establece ningún modificador, el FORMATO-ARGUMENTO se imprime sin más modificaciones. - Si se establece el modificador de dos puntos, pero no el arroba. -modificador de signo, el ARGUMENTO DE FORMATO se convierte a minúsculas antes de imprimir - Si se establece el modificador en, pero no el modificador de dos puntos, el ARGUMENTO DE FORMATO se convierte a mayúsculas antes de imprimir - Si se configuran ambos modificadores, el. FORMAT-ARGUMENT se invierte antes de imprimir --- El número de veces que se imprimirá la cadena FORMAT-ARGUMENT está determinado por el parámetro de prefijo REPETITIONS, que debe ser un número entero no negativo y su valor predeterminado es uno." ( declarar ( tipo ( o nulo ( eql T ) cadena de flujo ) destino )) ( declarar ( tipo T formato-argumento )) ( declarar ( tipo T modificador de dos puntos-suministrado-p )) ( declarar ( tipo T at-sign-modificador-suministrado ) -p )) ( declarar ( escribir ( entero 0 * ) repeticiones )) ( let (( argumento de formato de cadena a imprimir )) ( declarar ( escribir cadena cadena a imprimir )) ;; Ajuste STRING-TO-PRINT según los modificadores. ( cond (( y modificador-de-dos puntos-suministrado-p en-signo-modificador-suministrado-p ) ( setf string-to-print ( cadena inversa para imprimir ))) ( modificador de dos puntos suministrado-p ( setf cadena para imprimir ( cadena en minúscula cadena para imprimir ))) ( modificador de signo de arroba suministrado-p ( setf cadena- to-print ( string-upcase string-to-print ))) ( T NIL )) ( repeticiones de repetición de bucle do ( formato de destino "~a" string-to-print )))) ;; Imprime "Hola" una sola vez. ( formato T "~/midirectiva/" "Hola" ) ;; Imprime "Hola" tres veces. ( formato T "~3/midirectiva/" "Hola" ) ;; Imprime un "Hola" en minúscula (= "hola") tres veces. ( formato T "~3:/midirectiva/" "Hola" ) ;; Imprime un "Hola" en mayúscula (= "HOLA") tres veces. ( formato T "~3@/midirectiva/" "Hola" ) ;; Imprime un "Hola" invertido (= "olleH") tres veces. ( formato T "~3:@/midirectiva/" "Hola" )
Si bien format
es algo infame por su tendencia a volverse opaco y difícil de leer, proporciona una sintaxis notablemente concisa pero poderosa para una necesidad común y especializada. [4]
Está disponible una tabla de resumen FORMAT de Common Lisp. [5]