Está en la página 1de 597

Common Lisp Prctico Peter Seibel

ese libro es mortalmente sexy - Xach en #Lisp

Esta pginas, contienen el texto del libro Common Lisp Prctico publicado por Apress. Estas pginas contienen ahora el texto final tal y como aparece en el libro. Si encuentra algn error en estas pginas, por favor enve un correo a book@gigamonkeys.com. Estas pginas se mantendrn en lnea indefinidamente. Espero que servir como una til introduccin a Common Lisp para la gente que tienen curiosidad acerca de Lisp, pero tal vez an no lo suficientemente curioso para desembolsar mucho dinero para un libro de rboles muertos y un buen tutorial de Common Lisp para gente que quiere bajar a la codificacin real de inmediato. Sin embargo, no deje que eso le impida comprar la versin impresa a disposicin de Apress en su librera favorita local o en lnea. Por la experiencia de navegacin completa, se puede leer la carta a los lectores que aparece en la contraportada de la edicin treeware del libro.

1. Introduccin: Por qu Lisp? 2. Enjabonar, enjuagar y repetir: Una gira por REPL 3. Prctico: Una base de datos simple 4. Sintaxis y semntica 5. Funciones 6. Variables 7. Macros: Constructos Estndar de Control 8. Macros: definiendo las nuestras 9. Prctico: Creacin de una unidad de marco de pruebas 10. Nmeros, caracteres y cadenas 11. Colecciones 12. Lo llamaron LISP por una razn: el procesamiento de listas 13. Ms all de las Listas: Otros usos de las clulas Cons 14. Archivos y archivos de E / S 15. Prctico: Una biblioteca portable de nombre de ruta 16. Reorientacin a objetos: Funciones Genricas 17. Reorientacin a objetos: Clases 18. Una pequea Receta de FORMAT 19. Ms all del manejo de excepciones: Condiciones y Reinicios 20. Operadores Especiales 21. La programacin en Grande: Paquetes y smbolos 22. LOOP para Cinturones Negros 23. Prctico: un filtro de spam 24. Prctico: los archivos binarios de anlisis sintctico 25. Prctico: Un analizador de ID3 26. Prctico: Programacin Web con AllegroServe 27. Prctico: Una base de datos MP3 28. Prctico: un servidor Shoutcast 29. Prctico: Un Navegador de MP3 30. Prctico: una biblioteca de la generacin de HTML, el intrprete 31. Prctico: una biblioteca de la generacin de HTML, el compilador 32. Conclusin: Qu sigue?

1. Introduccin: Por qu Lisp?


Si usted piensa que el mayor placer en la programacin viene de obtener una gran cantidad de cdigo que con sencillez y claridad exprese su intencin, entonces es probable que la programacin Common Lisp sea lo ms divertido que puedes hacer con una computadora. Usted conseguir ms cosas, ms rpido, con lo que lo hara con casi cualquier otro idioma. Eso es una afirmacin audaz. Puedo justificarlo? No en slo algunas pginas de este captulo usted va a tener que aprender algo de Lisp y verlo por si mismo - por lo tanto el resto de este libro. Por ahora, vamos a empezar con algunos datos anecdticos, la historia de mi propio camino hacia Lisp. Luego, en la siguiente seccin, voy a explicar la ganancia que creo que obtendr aprendiendo Common Lisp. Yo soy uno de lo que debe ser un nmero bastante pequeo de la segunda generacin de hackers de Lisp. Mi padre comenz su carrera en computadoras escribiendo un sistema operativo en assembler que utiliz para recopilar datos para su tesis de doctorado en fsica. Despus de ejecutar los sistemas informticos en varios laboratorios de fsica, por la dcada de 1980 haba dejado por completo la fsica y trabajaba en una gran compaa farmacutica. Esta empresa tena un proyecto en curso para desarrollar software para modelar los procesos de produccin en sus plantas de productos qumicos - si se aumenta el tamao de este recipiente, cmo afecta a la produccin anual? El equipo original, que escriba en FORTRAN, haba quemado la mitad del dinero y casi todo el tiempo asignado al proyecto, sin nada que mostrar por sus esfuerzos. Esta es la dcada de 1980 y la mitad de la inteligencia artificial (IA) de auge, Lisp estaba en el aire. As que mi padre - en ese momento no era un Lisper - fue a la Universidad Carnegie Mellon (UCM) para hablar con algunas de las personas que trabajan en lo que se convertira en Common Lisp sobre si Lisp podra ser un buen lenguaje para este proyecto. La gente de UCM le mostr algunos demos de cosas que estaban trabajando, y qued convencido. A su vez, convenci a sus jefes para que su equipo se haga cargo del proyecto y hacerlo en Lisp. Un ao ms tarde, y slo con lo que quedaba del presupuesto original, su equipo hizo una aplicacin que trabaja con las caractersticas con las que el equipo original haba perdido toda esperanza de entregar. Mi padre atribuye el xito de su equipo a su decisin de usar Lisp. Ahora, eso es slo una ancdota. Y tal vez mi pap se equivoca acerca de por qu sucedi. O tal vez era mejor Lisp slo en comparacin con otras lenguas de la poca. En estos das tenemos un montn nuevos lenguajes de lujo , muchos de los cuales han incorporado caractersticas de Lisp. De verdad estoy diciendo que Lisp puede ofrecer los mismos beneficios hoy en da como los que ofreci a mi padre en la dcada de 1980? Sigue leyendo. A pesar de los esfuerzos de mi padre, yo no aprend todo Lisp en la escuela secundaria. Despus de una carrera universitaria que no implicaba mucha programacin en cualquier

lenguaje, fui seducido por la Web y de nuevo en los ordenadores. Yo trabaj por primera vez en Perl, aprender lo suficiente para ser peligroso, mientras que construa un foro de discusin en lnea para Mother Jones sitio Web de la revista y luego pasar a una tienda web, Organic Online, donde trabaj en grande - por el momento en sitios Web como Nike durante los Juegos Olmpicos de 1996. Ms tarde me mud a Java como un desarrollador principiante en WebLogic, que ahora forma parte de BEA. Despus de WebLogic, me un a otro, donde yo era el jefe de programacin de un equipo en la construccin de un sistema de mensajera transaccional en Java. En el camino, mi inters general en lenguajes de programacin me llev a explorar lenguajes principales como C, C + + y Python, as como menos conocidos, como los Smalltalk, Eiffel, y Beta. As que yo saba dos idiomas por dentro y por fuera y estaba familiarizado con otra media docena. Eventualmente, sin embargo, me di cuenta de mi inters por los lenguajes de programacin se basaba en realidad en la idea sembrada por los cuentos de mi padre de Lisp que las lenguas diferentes son realmente diferentes, y que, a pesar de la equivalencia formal de Turing de todos los lenguajes de programacin, que realmente se puede ms cosas con mayor rapidez en algunos idiomas que otros y tienen ms divertido que hacer. Sin embargo, irnicamente, nunca me haba pasado mucho tiempo con el Lisp mismo. Por lo tanto, empec a hacer algo de hacking Lisp en mi tiempo libre. Y cada vez que lo hice, fue emocionante ver lo rpido que fue capaz de ir desde la idea hasta el cdigo de trabajo. Por ejemplo, en unas vacaciones, con una semana de hackear Lisp, decid intentar escribir una versin de un programa - un sistema para la cra de algoritmos genticos para jugar el juego de Go - que yo haba escrito al principio de mi carrera como un programador de Java. Incluso en desventaja por mis conocimientos rudimentarios de entonces Common Lisp y tener que buscar las funciones ms bsicas, que todava se siente ms productivo de lo que hubiera sido volver a escribir el mismo programa en Java, incluso con varios aos adicionales de experiencia en Java adquirida desde la escritura de la primera versin. Un experimento similar llevado a la biblioteca voy a discutir en el Captulo 24. Al principio de mi tiempo en WebLogic yo haba escrito una biblioteca, en Java, para desmontar archivos de clase Java. Funcion, pero el cdigo era un poco un lo y difcil de modificar o ampliar. Haba intentado varias veces, a lo largo de los aos, para volver a escribir la biblioteca, pensando que con mi cada vez mejores chuletas de Java que encontrara alguna manera de hacerlo que no empantanar en pilas de cdigo duplicado. Nunca he encontrado una manera. Pero cuando trat de hacerlo en Common Lisp, me tom slo dos das, y terminamos, no slo con un analizador de archivos de clases de Java, sino con una biblioteca de uso general para desarmar cualquier tipo de archivo binario. Vers cmo la biblioteca funciona en el captulo 24 y se utiliza en el captulo 25 para escribir un analizador para las etiquetas ID3 incrustada en los archivos MP3.

Por qu Lisp?
Es difcil, en tan slo unas pocas pginas de un captulo introductorio, explicar por qu los usuarios de un lenguaje como ste, y es an ms difcil explicar por qu usted debe invertir su

tiempo en el aprendizaje de un idioma determinado. La historia personal slo nos lleva hasta el momento. Tal vez me gusta Lisp, porque de alguna forma peculiar est conectada a mi cerebro. Incluso podra ser de origen gentico, ya que mi padre lo tiene tambin. As que antes de sumergirse en el aprendizaje de Lisp, es razonable querer saber cuales van a ser sus ganancias. Para algunos idiomas, la ganancia es relativamente obvia. Por ejemplo, si desea escribir cdigo de bajo nivel en Unix, se debe aprender C. O si desea escribir ciertos tipos de aplicaciones multi-plataforma, usted debe aprender Java. Y an las empresas utilizan una gran cantidad de C++, as que si usted desea conseguir un trabajo en uno de ellas, usted debe aprender C++. Para la mayora de idiomas, sin embargo, la recompensa no es tan fcil de clasificar, sino que tiene que ver con criterios subjetivos, tales como qu se siente al utilizar el lenguaje. A los defensores de Perl les gusta decir que Perl "hace fcil las cosas fciles y posibles las cosas difciles" y se deleitan con el hecho de que, como reza el lema de Perl lo ha hecho, "No hay ms que una forma de hacerlo."1 Los fans de Python, por otro lado, dicen que Python es limpia y sencilla y que el cdigo Python es fcil de entender porque, como su lema dice: "Slo hay una manera de hacerlo." As que, por qu Common Lisp? No hay ningn beneficio obvio para la adopcin de Common Lisp es el camino de C, Java y C++ (a menos que, por supuesto, usted posea una mquina Lisp). Los beneficios de usar Lisp tienen mucho ms que ver con la experiencia de su uso. Voy a pasar el resto de este libro mostrando las caractersticas especficas de Common Lisp y cmo usarlos para que puedas verlo por ti mismo. Por ahora voy a tratar de dar un sentido de la filosofa de Lisp. Lo ms cercano a un lema de Common Lisp es una frase como, "el lenguaje de programacin programable". Aunque crptico, esa descripcin es la raz de la enorme ventaja de Common Lisp sobre los otros idiomas. Ms que cualquier otro idioma, Common Lisp sigue la filosofa de que lo que es bueno para el diseador del lenguaje es bueno para los usuarios de la lengua. Por lo tanto, cuando usted est en la programacin Common Lisp, casi nunca se encuentra deseando que el idioma admita alguna caracterstica que hara que su programa sea ms fcil de escribir, porque, como se ver a lo largo de este libro, usted puede agregar el mismo rasgo. En consecuencia, un programa comn de Lisp tiende a proporcionar una asignacin ms clara entre sus ideas acerca de cmo funciona el programa y el cdigo que realmente escribe. Sus ideas no quedan ocultas por cdigo repetitivo y modismos que se repiten sin cesar. Esto hace que su cdigo sea ms fcil de mantener porque usted no tiene que vadear a travs de pginas y pginas de cdigo cada vez que necesite hacer un cambio. Incluso los cambios sistmicos para el comportamiento de un programa a menudo se puede lograr con cambios relativamente pequeos en el cdigo actual. Esto tambin significa que va a desarrollar un cdigo ms rpido, hay menos cdigo para escribir, y que no pierda el tiempo por ah tratando de encontrar una manera limpia de expresarse dentro de los lmites de la lengua. 2 1 Perl es tambin digno de aprender como "la cinta de la Internet." 2 Por desgracia, hay poca investigacin real en la productividad de los diferentes idiomas. Un informe que

Common Lisp es un lenguaje excelente para la programacin de exploracin - si usted no sabe exactamente cmo el programa va a funcionar cuando usted se sienta primero a escribir, Common Lisp ofrece varias funciones para ayudarle a desarrollar su cdigo de forma incremental y de forma interactiva. Para empezar, el bucle interactivo leer-evaluar-mostrar, que voy a presentar en el prximo captulo, le permite interactuar continuamente con el programa que se desarrolla. Escribir una nueva funcin. Testearla. Cambiarla. Intentar un enfoque diferente. Usted nunca tiene que parar por un largo ciclo de compilacin.3 Otras caractersticas de los sistemas Lisp y Common Lisp son un estilo de programacin interactiva y escritura dinmica. Debido a lo primero, se tarda menos tiempo en convencer al compilador para que permita
4

ejecutar el cdigo y ms tiempo en realidad funcionando y

trabajando en ello , y lo segundo le permite desarrollar hasta el cdigo de manejo de errores en forma interactiva. Otra consecuencia de ser "un lenguaje de programacin programable" es que Common Lisp, adems de incorporar pequeos cambios que hacen que determinados programas sean ms fciles de escribir, puedan adoptar fcilmente grandes ideas nuevas sobre cmo los lenguajes de programacin deben trabajar. Por ejemplo, la implementacin original del sistema de objetos Common Lisp (CLOS), sistema de objetos de gran mbito Common Lisp, fue como una biblioteca escrita en porttiles de Common Lisp. Esto permiti a los programadores de Lisp ganar experiencia real con las facilidades antes de ser incorporado oficialmente a la lengua. Cualquiera sea el nuevo paradigma que viene, es muy probable que Common Lisp ser capaz de absorberlo sin necesidad de cambios en el ncleo del lenguaje. Por ejemplo, un Lisper ha escrito recientemente una biblioteca, AspectL, que aade soporte para programacin orientada a aspectos (AOP) de Common Lisp 5. Si AOP resulta ser la prxima novedad, Common Lisp ser
muestra Lisp saliendo bien en comparacin con C++ y Java en la combinacin de programador y la eficiencia del programa se discute en http://www.norvig.com/java-lisp.html . 3 Los psiclogos han identificado un estado de nimo llamado flujo en el que somos capaces de una increble concentracin y productividad. La importancia del flujo en la programacin ha sido reconocida por casi dos dcadas desde que se analizan en el libro clsico sobre los factores humanos en la programacin Peopleware: Proyectos Productivos y de los equipos de Tom DeMarco y Lister Timoteo (Dorset House, 1987). Los dos hechos clave sobre flujo es que se tarda unos 15 minutos para llegar a un estado de flujo y que incluso breves interrupciones pueden romperlo, que requiere otra inmersin de 15 minutos para volver a entrar. DeMarco y Lister, al igual que la mayora de los autores posteriores, se referan en su mayora con el flujo de la destruccin de interrupciones, tales como timbres de telfonos y las visitas inoportunas del jefe. Con menos frecuencia considerado, pero probablemente igual de importante para los programadores son las interrupciones causadas por nuestras herramientas. Idiomas que requieren, por ejemplo, una recopilacin extensa antes de que pueda probar su ltima versin del cdigo puede ser tan perjudicial para el flujo como un telfono de ruido o un jefe entrometido. Por lo tanto, una manera de ver Lisp es que es un lenguaje diseado para mantenerse en un estado de flujo.

4 Este punto est destinado a ser un poco controvertido, al menos con algunas personas. Tipos estticos y
dinmicos es una de las guerras clsicas en la programacin religiosa. Si vienes de C++ y Java (o de tipos estticos de lenguajes funcionales como Haskel y ML) y se niega a considerar la vida sin controles de tipo esttico, y que tambin podra dejar este libro ahora. Sin embargo, antes de hacerlo, primero deberas ver a alguien que se describe como "fantico de tipos estticos" Robert Martin (autor del diseo orientado a objetos C+ + utilizando el mtodo de Booch [Prentice Hall, 1995]) y C++ y Java autor Bruce Eckel (autor de Thinking in C++ [Prentice Hall, 1995] y Pensando en Java [Prentice Hall, 1998]) han tenido que decir acerca de la escritura dinmico en sus weblogs (http://www.artima.com/weblogs/viewpost.jsp?thread=4639 y http://www.mindview.net/WebLog/log-0025 ). Por otro lado, la gente viene de Smalltalk, Python, Perl o Ruby debe sentirse como en casa con este aspecto de Common Lisp. 5 AspectL es un proyecto interesante en la medida en AspectJ, su predecesor, basado en Java, fue escrito por

capaz de apoyarlo sin ningn tipo de cambios en el lenguaje de base y sin pre-procesadores y compiladores adicionales extra.6

Donde empez
Common Lisp es el descendiente moderno del lenguaje Lisp concebido por primera vez por John McCarthy en 1956. Lisp fue diseado alrededor del ao 1956 para "procesamiento de datos simblicos"7 y deriva su nombre de una de las cosas para la que era bastante bueno: procesamiento de listas. Hemos recorrido un largo camino desde entonces: Common Lisp contiene una gran variedad de tipos de datos modernos, donde se pueden encontrar un sistema de condiciones que, como se ver en el captulo 19, ofrece todo un nivel de flexibilidad que les falta a los sistemas de lenguajes como Java, Python y C++, servicios de gran mbito para hacer programacin orientada a objetos, y varios servicios de lenguaje que simplemente no existen en otros lenguajes de programacin. Cmo es esto posible? Que cosa en la Tierra provocara la evolucin de un lenguaje tan bien equipado? Bueno, McCarthy fue (y sigue siendo) un investigador de inteligencia artificial (IA), y muchas de las caractersticas que construy en su versin inicial de la lengua hizo un excelente lenguaje de programacin de IA. Durante el auge de la IA de la dcada de 1980, Lisp permaneci como la herramienta favorita de los programadores que escriben software para resolver problemas difciles, tales como el teorema automatizado de pruebas, la planificacin y programacin, y la visin por ordenador. Estos eran los problemas que requieren una gran cantidad de software difcil de escribir, para hacer mella en ellos, los programadores de IA fue necesario un lenguaje poderoso, y que creci en el lenguaje Lisp que necesitaban. Y la Guerra Fra ayud - como el Pentgono invirti dinero en la Defense Advanced Research Projects Agency (DARPA), una gran parte de ella fue a la gente que trabaja en problemas tales como simulaciones de campo de batalla a gran escala, planificacin automtica, y las interfaces en lenguaje natural. Estas personas tambin usaron Lisp y continuaron empujando para hacer lo que fuera necesario. Las mismas fuerzas que impulsaron las caractersticas de la evolucin de Lisp tambin empujaron sobre otras dimensiones - grandes problemas de la IA consumen una gran cantidad de recursos informticos sin embargo sus cdigos, si se ejecuta la ley de Moore a la inversa durante 20 aos, usted puede imaginarse cmo los escasos recursos informticos de hardware estaban alrededor de la dcada de los '80. Los chicos de Lisp encontraron todo tipo de maneras de exprimir el rendimiento de sus implementaciones. Las implementaciones Common Lisp modernas son las herederas de aquellos primeros esfuerzos y, a menudo incluyen formas bastante sofisticadas, nativas de la mquina de generacin de cdigo compiladores. Mientras
Gregor Kiczales, uno de los diseadores de objetos de Common Lisp y los sistemas de metaobject. Para muchos Lispers, AspectJ parece Kiczales intentando acondicionar sus ideas de Common Lisp en Java. Sin embargo, Pascal Costanza, el autor de AspectL, cree que hay ideas interesantes en la AOP que podran ser tiles en Common Lisp. Por supuesto, la razn de poner en prctica AspectL como una biblioteca se debe a la increble flexibilidad de diseo del Protocolo de objetos de Common Lisp Meta Kiczales. Para llevar a cabo AspectJ, Kiczales tena que escribir lo que era esencialmente un compilador independiente que recopila un nuevo lenguaje en el cdigo fuente de Java. La pgina del proyecto se encuentra en AspectLhttp://common-lisp.net/ proyecto / aspectl / . 6 O a verlo de otra, ms tcnicamente precisos, as, Common Lisp viene con una instalacin integrada para la integracin de compiladores para lenguajes integrados.

7 Lisp 1.5 Manual del Programador (MIT Press, 1962)

que hoy, gracias a la ley de Moore, es posible obtener un rendimiento til de un lenguaje puramente interpretado, que ya no es un problema para Common Lisp. Como voy a mostrar en el captulo 32, con adecuadas declaraciones, un buen compilador de Lisp puede generar cdigo de mquina muy similar a lo que podra ser generado por un compilador de C. La dcada de 1980 fue tambin la era de las mquinas Lisp, con varias empresas, lo ms famoso Symbolics, la produccin de computadoras que corran Lisp nativo desde los chips hacia arriba. Por lo tanto, Lisp se convirti en un lenguaje de programacin de sistemas, que se utiliza para escribir sistemas operativos, editores, compiladores, y casi todo lo dems que se ejecutaba en las mquinas Lisp. De hecho, en la dcada de 1980, con varios laboratorios de IA y los vendedores de mquinas Lisp, que proporcionaron sus propias implementaciones de Lisp, se produjo tal proliferacin de sistemas de Lisp y dialectos que la gente de DARPA comenz a expresar su preocupacin por la fragmentacin de la comunidad Lisp. Para solucionar este problema, un grupo de base de los hackers de Lisp se reunieron en 1981 y comenz el proceso de estandarizacin de un nuevo lenguaje llamado Common Lisp que combina las mejores caractersticas de los actuales dialectos de Lisp. Su trabajo fue documentado en el libro La Lengua de Common Lisp por Guy Steele (Digital Press, 1984) - CLtL para los entendidos de Lisp. En 1986 las primeras implementaciones de Common Lisp estaban disponibles, y la escritura estaba en el muro de los dialectos que se pretenda sustituir. En 1996, el American National Standards Institute (ANSI) public una norma para Common Lisp que construyeron y ampliaron el idioma especificado en CLtL, aadiendo algunas nuevas caractersticas importantes tales como la CLOS y el sistema de condiciones. E incluso que no era la ltima palabra: como CLtL antes, el estndar ANSI intencionalmente deja espacio para que los ejecutores experimenten con la mejor manera de hacer las cosas: una implementacin completa de Lisp proporciona un rico entorno de ejecucin con el acceso a los artefactos de interfaz grfica de usuario, mltiples hilos de control, sockets TCP / IP, y mucho ms. En estos das Common Lisp est evolucionando al igual que otros lenguajes de cdigo abierto - la gente que lo utilizan escribe las bibliotecas que necesitan y, a menudo las ponen a disposicin de los dems. En los ltimos aos, en particular, ha habido una aceleracin de la actividad en bibliotecas de cdigo abierto Lisp. As, por un lado, Lisp es uno de los idiomas "clsicos" de las ciencias de la computacin, sobre la base de las ideas que han resistido la prueba del tiempo. 8 Por otro lado, se trata de un fondo moderno de uso general del lenguaje, cuyo diseo refleja un enfoque muy pragmtico de posible resolucin de problemas reales eficiente y slida. El nico inconveniente del patrimonio "clsico" de Lisp es que mucha gente todava andan por ah con ideas acerca de Lisp basado en un cierto sabor especial de Lisp que fueron expuestos en algn momento particular en el casi medio siglo desde McCarthy invent Lisp. Si alguien te dice que Lisp no es ms que un intrprete, que es lento, o que tiene que utilizar la recursividad para todo, les

8 Ideas vistas por primera vez en Lisp son las construcciones if / then / else, llamadas a funciones recursivas,

asignacin dinmica de memoria, recoleccin de basura, funciones de primera clase, el ligadura de lxico, programacin interactiva, compilacin incremental, y escritura dinmica.

preguntamos de qu dialecto de Lisp estn hablando y si la gente llevaba pantalones de campana cuando se enter de ello.9

Pero me enter de Lisp una vez, y no era como lo que ests describiendo Si usted ha utilizado Lisp en el pasado, usted puede tener ideas sobre "Lisp" que tienen poco que ver con Common Lisp. Mientras que Common Lisp ha suplantado a la mayora de los dialectos descendientes, no es el nico dialecto de Lisp que queda, y dependiendo de dnde y cundo fueron expuestos a Lisp, puede muy bien haber aprendido uno de estos dialectos. Con excepcin de Common Lisp, el propsito general del dialecto de Lisp que todava tiene una activa comunidad de usuarios es el Scheme. Common Lisp ha adoptado algunas importantes caractersticas sustanciales de Scheme, pero nunca tuvo la intencin de reemplazarlo. Originalmente diseado en el MIT, donde fue rpidamente objeto de un uso como lengua de enseanza para estudiantes de pregrado de cursos de informtica, Scheme ha sido siempre dirigido a un nicho de idioma diferente al de Common Lisp. En particular, los diseadores de Scheme se han centrado en mantener el ncleo del lenguaje tan pequeo y tan simple como sea posible. Esto tiene ventajas obvias para una enseanza de idiomas y tambin para la programacin de los investigadores del lenguaje a los que les gusta ser capaz de probar formalmente cosas acerca de los idiomas. Tambin tiene la ventaja de que el idioma es relativamente fcil de entender como se especifica en la norma. Sin embargo, lo hace a costa de omitir muchas caractersticas tiles que se han estandarizado en Common Lisp. Las implementaciones individuales de Scheme pueden proporcionar estas caractersticas en la aplicacin de formas especficas, pero la omisin de la norma hace que sea ms difcil de escribir cdigo de Scheme porttil que escribir cdigo portable de Common Lisp. Scheme tambin hace hincapi en un estilo de programacin funcional y el uso de la recursividad mucho ms de lo que hace Common Lisp. Si usted ha estudiado Lisp en la universidad y se qued con la impresin de que slo era un lenguaje acadmico sin aplicacin real, es probable que haya aprendido Scheme. Esto no quiere decir que sea una caracterizacin justa de todo Scheme, pero es an menos aplicable a Common Lisp, que fue diseado expresamente para ser un lenguaje de ingeniera del mundo real en lugar de una teora del lenguaje "puro". Si has aprendido Scheme, usted debe tambin ser consciente de que hay una serie de sutiles diferencias entre Scheme y Common Lisp que puede hacerle tropezar. Estas

9 Uno de los mitos ms repetidos acerca de Lisp es que est "muerto". Si bien es cierto que Common Lisp no es

tan ampliamente utilizado como, por ejemplo, Visual Basic o Java, parece extrao describir como "muerto" a un lenguaje que se sigue utilizando para el desarrollo de nuevas aplicaciones y que sigue atrayendo a nuevos usuarios. Algunas recientes historias Lisp exitosas incluyen Viaweb de Paul Graham, que se convirti en Yahoo Store cuando Yahoo compr su compaa, ITA Software de precios de pasajes areos y sistema de compras, QPX, utilizado por el mercado de venta de bonos en lnea Orbitz y otros; juego de Naughty Dog's juego para PlayStation 2, Jak y Daxter, que es en gran parte escrito en un dialecto del Naughty Dog de dominio especfico de Lisp llamado GOAL, cuyo compilador es en s mismo escrito en Common Lisp, y Roomba, la aspiradora robtica autnoma, cuyo software est escrito en L, un subconjunto compatible de Common Lisp. Tal vez an ms reveladora es el crecimiento del sitio web Common-Lisp.net, que recibe de cdigo abierto de proyectos comunes de Lisp, y el nmero de grupos locales de usuarios de Lisp que han surgido en los ltimos dos aos.

diferencias son tambin la base para varias guerras religiosas perennes entre los exaltados en el Common Lisp y las comunidades Scheme. Voy a tratar de sealar algunas de las diferencias ms importantes a medida que avanzamos. Otros dos dialectos de Lisp todava en uso generalizado son Elisp, el lenguaje de extensin para el editor Emacs, y Autolisp, el lenguaje de extensin para la herramienta de diseo asistido por ordenador AutoCAD. Aunque es posible que ms lneas de Elisp y Autolisp se han escrito que de cualquier otro dialecto de Lisp, no se puede utilizar fuera de su programa de control, y ambos se muestran bastante anticuados en comparacin con alguno de los sistemas Common Lisp. Si usted ha utilizado alguno de estos dialectos, preprese para saltar en la mquina del tiempo Lisp y saltar hacia adelante varias dcadas.

Que este libro es para


Este libro es para usted si usted es curioso acerca de Common Lisp, independientemente de si usted ya est convencido de que quiere usarlo o si simplemente quieres saber de que se trata todo este alboroto. Si has aprendido algo de Lisp, pero ya han tenido problemas para hacer el salto de los ejercicios acadmicos a los programas reales, este libro va hacia su camino. Por otro lado, usted no tiene que estar ya convencido de que desea usar Lisp para sacar algo de este libro. Si usted es un hombre pragmtico puro y duro que quiere saber cules son las ventajas que Common Lisp tiene sobre lenguajes como Perl, Python, Java, C o C#, este libro le dar algunas ideas. O tal vez ni siquiera se preocupe sobre el uso de Lisp - tal vez usted ya est seguro de Lisp no es realmente mejor que otros idiomas que usted conoce pero est molesto por que algunos Lisper dicen que es porque simplemente no "lo obtuvo". Si es as, este libro le dar una introduccin directa a los puntos de Common Lisp. Si despus de leer este libro, usted todava piensa que Common Lisp no es mejor que su idioma favorito, usted estar en una excelente posicin para explicar exactamente por qu. Yo cubrir no slo la sintaxis y la semntica de la lengua, sino tambin cmo se puede utilizar el software para escribir cosas tiles. En la primera parte del libro, me ocupar de la propia lengua, con cierta mezcla de "prctica", captulos, donde le mostrar cmo escribir cdigo real. Entonces, despus de haber cubierto la mayor parte de la lengua, incluyendo varias partes de otros libros que dejar para que lo averige por su cuenta, el resto del libro consta de nueve captulos ms prctica donde voy a ayudarle a escribir varios programas de tamao medio que realmente hacen las cosas que te pueden interesar: un filtro de spam, archivos binarios de anlisis sintctico, un catlogo MP3, flujo MP3 en una red, y una interfaz web para el catlogo de MP3 y el servidor. Despus de terminar este libro, estar familiarizado con todas las caractersticas ms importantes de la lengua y cmo encajan entre s, tendrs que utilizar Common Lisp para escribir varios programas no triviales, y usted estar bien preparado para continuar explorando el idioma por su cuenta. Mientras que para todos el camino hacia Lisp es diferente, espero que este libro le ayude a allanar el camino para ti. Por lo tanto, vamos a empezar.

2. Enjabonar, enjuagar, repetir: Una gira por el REPL


En este captulo se establece el entorno de programacin y escribir sus primeros programas de Common Lisp. Vamos a utilizar el fcil de instalar en una caja de Lisp desarrollado por Matthew dans y Evins Mikel, que los paquetes de un Common Lisp en Emacs, un poderoso Lisp-aware editor de texto, y el asfalto, un un entorno comn de desarrollo basado en Lisp parte superior de Emacs. Esta combinacin proporciona un estado de la tcnica Common Lisp que entorno de desarrollo compatible con el estilo incremental de desarrollo interactivo que caracteriza la programacin Lisp. El medio ambiente SLIME tiene la ventaja adicional de proporcionar una interfaz de usuario bastante uniforme, independientemente del sistema operativo y Common Lisp que usted elija. Voy a utilizar el Lisp en un entorno de caja a fin de tener un entorno de desarrollo especfico de qu hablar, la gente que quiere explorar otros entornos de desarrollo tales como los entornos de desarrollo grfico integrado (IDE) proporcionada por algunos de los proveedores comerciales de Lisp o entornos sobre la base de otros editores no deberan tener demasiados problemas la traduccin de los conceptos bsicos. 2 La eleccin de una implementacin de Lisp Lo primero que tienes que hacer es elegir una implementacin de Lisp. Esto puede parecer una cosa extraa que tiene que hacer para que la gente utiliza para lenguajes como Perl, Python, Visual Basic (VB), C # y Java. La diferencia entre el Common Lisp, y es que estos idiomas Common Lisp es definido por su nivel - no hay ni una sola implementacin controlada por un dictador benevolente, como Perl y Python, ni una implementacin cannica controlado por una sola empresa, como sucede con VB , C # y Java. Cualquier persona que quiera leer la norma y aplicar el lenguaje es libre de hacerlo. Adems, los cambios a la norma tiene que ser hecho de acuerdo con un proceso controlado por el cuerpo de estndares American National Standards Institute (ANSI). Ese proceso est diseado para mantener una sola entidad, como un solo proveedor, de ser capaz de cambiar arbitrariamente la norma. 3Por lo tanto, el Common Lisp estndar es un contrato entre un proveedor

comn Lisp y Common Lisp programadores. El contrato que dice que si usted escribe un programa que utiliza las caractersticas de la lengua de la forma en que se describe en la norma, puede contar con su programa de comportarse de la misma en cualquier implementacin conforme. Por otro lado, la norma no puede cubrir todo lo que es posible que desee hacer en sus programas - algunas cosas que se dejaron deliberadamente no especificados con el fin de permitir la experimentacin continua de los ejecutores en las zonas donde no haba consenso sobre la mejor manera para el idioma para apoyar ciertas caractersticas. As que cada aplicacin ofrece algunas caractersticas ms all de lo que est especificado en la norma. Dependiendo de qu tipo de programacin que va a hacer, puede tener sentido que elegir una sola aplicacin que tiene las caractersticas adicionales que usted necesita y usarla. Por otro lado, si estamos entregando fuente de Lisp para ser utilizado por otros, tales como bibliotecas, tendr que - en lo posible - a escribir Common Lisp portable. Para escribir el cdigo que debe ser sobre todo porttiles, pero que las necesidades de las instalaciones no se define por la norma, Common Lisp proporciona una manera flexible de escribir cdigo "conditionalized" sobre las funciones disponibles en una aplicacin particular. Usted ver un ejemplo de este tipo de cdigo en el captulo 15 cuando desarrollamos una simple biblioteca que suaviza las diferencias entre las diferentes implementaciones de Lisp como tratar con nombres de archivo. Por el momento, sin embargo, la caracterstica ms importante de una aplicacin es si funciona en nuestro sistema operativo favorito. La gente de Franz, fabricantes de Allegro Common Lisp, estn poniendo a disposicin una versin de prueba de su producto para su uso con este libro que se ejecuta en Linux, Windows y OS X. La gente en busca de una implementacin de cdigo abierto tiene varias opciones. SBCL 4 es una alta calidad de implementacin en cdigo abierto que compila a cdigo nativo y se ejecuta en una amplia variedad de sistemas Unix, incluyendo Linux y OS X. SBCL se deriva de CMUCL, 5 que es un Common Lisp desarrollado en Carnegie Mellon University, y , como CMUCL, es en gran medida en el dominio pblico, excepto algunas secciones bajo licencia Berkeley Software Distribution (BSD) licencias de estilo. . CMUCL s es otra buena opcin, aunque SBCL tiende a ser ms fcil de instalar y ahora es compatible con 21-bit Unicode 6 Para los usuarios de OS X, OpenMCL es una excelente eleccin - que compila a cdigo mquina, compatible con las discusiones, y tiene una integracin bastante buena con

herramientas OS X Carbon y Cocoa. Otras implementaciones de cdigo abierto y comerciales estn disponibles. Vase el Captulo 32 para los recursos de los que usted puede obtener ms informacin. Todo el cdigo Lisp en este libro debera funcionar en cualquier implementacin de Common Lisp conforme a menos que se indique lo contrario, y el asfalto ser suavizar algunas de las diferencias entre las implementaciones de que nos proporciona una interfaz comn para interactuar con Lisp. El resultado que se muestra en este libro es de Allegro corriendo en GNU / Linux, en algunos casos, otros Lisp puede generar mensajes de error ligeramente diferente o salida de un depurador. Conectarse y empezar con Lisp en una caja Desde el Lisp en una caja de embalaje ha sido diseado para obtener Lispers nuevo en marcha y funcionando en un entorno de desarrollo de primer nivel Lisp con mnimo esfuerzo, todo lo que necesita hacer para ponerlo en marcha es para agarrar el paquete adecuado para su sistema operativo preferido y el Lisp del Lisp en un sitio web de cuadro de lista en el captulo 32 y luego siga las instrucciones de instalacin. Desde Lisp en una caja utiliza Emacs como editor, usted necesitar por lo menos saber un poco acerca de cmo usarlo. Tal vez la mejor manera de empezar a trabajar con Emacs es trabajar a travs de su base de tutorial. Para iniciar el tutorial, seleccione el primer elemento del men Ayuda, Emacs tutorial. O pulse la tecla Ctrl, tipo h , suelte la tecla Ctrl y pulse t .La mayora de los comandos de Emacs son accesibles a travs de tales combinaciones de teclas, porque las combinaciones de teclas son tan comunes, los usuarios de Emacs tiene una notacin para describir las combinaciones de teclas que evita tener que escribir constantemente con combinaciones como "Pulse la tecla Ctrl, tipo h , suelte la tecla Ctrl, y luego presione t ". Teclas a pulsar conjuntamente - un acorde clave llamada - se escriben juntas y separadas por un guin. Teclas, o los acordes fundamentales, que se presione en la secuencia estn separados por espacios. En un acorde clave, C representa la tecla Ctrl y M representa la tecla Meta (tambin conocido como Alt). Por lo tanto, podemos escribir la combinacin de teclas que acabamos de describir que se inicia el tutorial de este modo: Ch t .

El tutorial se describen los otros comandos tiles y las combinaciones de teclas que le invocan. Emacs tambin viene con una extensa documentacin en lnea usando su propio integrado en el navegador de documentacin hipertexto Informacin. Para leer el manual, el tipo de Ch i . El sistema de informacin viene con su propio tutorial, accesible con slo pulsar h , mientras que la lectura del manual. Por ltimo, Emacs proporciona bastantes maneras de conseguir ayuda, todos los obenlazados a combinaciones de teclas a partir de Ch .Escribiendo Ch? ser una lista completa. Dos de las ms tiles, adems de la tutora, son Ch k , lo que nos permite escribir cualquier combinacin de teclas y nos dice qu comando se invoca, y w Ch , que nos permite introducir el nombre de un comando y nos dice qu combinacin de teclas se invoca . El bit de otras cruciales de la terminologa de Emacs, para las personas que se niegan a trabajar con el tutorial, es la nocin de un buffer . Mientras se trabaja en Emacs, cada archivo se edita estar representada por un tampn diferente, slo una de ellas es "actual" en un momento dado. El buffer actual recibe todas las entradas lo que se ingrese y los comandos que invocan. Tampones tambin se utilizan para representar las interacciones con programas tales como Common Lisp. Por lo tanto, una accin comn que usted tome es "cambiar amortiguadores", que significa hacer un tampn diferente el buffer actual para que pueda editar un archivo en particular o interactuar con un programa en particular. El comandoswitch-to-buffer , unidos a la combinacin de teclas Cx b , pide el nombre de un bfer en el rea en la parte inferior del marco de Emacs. Al ingresar un nombre de buffer, Tab golpear completar el nombre basado en los caracteres escritos hasta el momento o le mostrar una lista de terminaciones posibles. El indicador sugiere tambin un buffer por defecto, que puede aceptar slo pulsar Intro. Tambin puede cambiar de bfer seleccionando un buffer en el men Buffers. En ciertos contextos, otras combinaciones de teclas pueden estar disponibles para cambiar a ciertos topes. Por ejemplo, al editar los archivos de Lisp fuente, la combinacin de teclasCc Cz pasa a la memoria intermedia en la que interactan con Lisp. Free Your Mind: Programacin Interactiva Al iniciar Lisp en una caja, debe consultar a un tampn que contiene un mensaje que se parece a esto:

CL-USUARIO>

Este es el indicador de Lisp. Al igual que un Unix o intrprete de comandos DOS, el indicador de Lisp es un lugar donde se puede escribir expresiones que har que las cosas sucedan. Sin embargo, en lugar de leer e interpretar una lnea de comandos de la shell, Lisp lee las expresiones Lisp, las evala de acuerdo a las reglas de Lisp, e imprime el resultado. Luego lo hace de nuevo con la siguiente expresin que se escribe. Ese ciclo interminable de la lectura, la evaluacin, y la impresin es por eso que es llamado el bucle leer-evaluar-mostrar , o REPL, para abreviar. Tambin es conocido como el nivel superior , el oyente de alto nivel , o el oyente Lisp . Desde el entorno proporcionado por el REPL, puede definir y redefinir los elementos del programa, tales como variables, funciones, clases y mtodos, evaluar cualquier expresin Lisp, cargar archivos que contienen cdigo Lisp cdigo fuente o compilado, compilar los archivos de todo o funciones individuales, entrar en el depurador; paso a travs de cdigo y inspeccionar el estado de cada uno de los objetos de Lisp. Todas las instalaciones estn integradas en el lenguaje, accesible a travs de funciones definidas en el estndar del lenguaje. Si tuviera que hacerlo, se podra construir un entorno de programacin bastante razonable de slo el REPL y cualquier editor de texto que no conoce la manera correcta de aplicar sangra al cdigo Lisp. Sino por la experiencia de la verdadera programacin Lisp, se necesita un medio ambiente, tales como limo, que le permite interactuar con Lisp, tanto a travs de la REPL y durante la edicin de archivos de cdigo fuente.Por ejemplo, usted no quiere tener que cortar y pegar una definicin de la funcin de un archivo de origen en el REPL o tener que cargar un archivo de todo slo porque ha cambiado una de las funciones, su entorno Lisp que vamos a evaluar o elaborar tanto las expresiones individuales y archivos de todo directamente desde tu editor. La experimentacin en el REPL Para probar el REPL, es necesario una expresin Lisp que se puede leer, evaluar y mostrar. Una de las ms simples formas de expresin Lisp es un nmero. En el indicador de Lisp, puede escribir 10 seguido de retorno y debera ver algo como esto:
CL-USUARIO> 10 10

El primer 10 es el que ha escrito. El lector de Lisp, la R en REPL, lee el texto "10" y crea un objeto Lisp que representa el nmero 10. Este objeto es una auto-evaluacin del objeto, lo que significa que cuando se le da al evaluador, el E en REPL, se evala a s mismo. Este valor se le da a la impresora, que muestra el 10 en la lnea por s mismo. Aunque esto puede parecer mucho trabajo slo para volver al punto de partida, las cosas se ponen un poco ms interesante cuando se le da a Lisp algo ms sustancioso Lisp para masticar. Por ejemplo, puede escribir (+ 2 3) en el indicador de Lisp.
CL-USUARIO> (+ 2 3)
5

Cualquier cosa que est entre parntesis es una lista, en este caso una lista de tres elementos, el smbolo + , y los nmeros 2 y 3. Lisp, en general, evala mediante el tratamiento de las listas el primer elemento como el nombre de una funcin y el resto de los elementos que las expresiones sean evaluadas para producir los argumentos de la funcin. En este caso, el smbolo + es el nombre de una funcin que realiza la suma. 2 y 3 se evalan y luego pasan a la funcin de adicin, que devuelve 5. El valor 5 se pasa a la impresora, que se imprime. Lisp puede evaluar una expresin de la lista de otras maneras, pero no es necesario entrar en ellos de inmediato. En primer lugar tenemos que escribir. . . "Hola, mundo", estilo Lisp Ningn libro de programacin est completo sin un "hola, mundo" imprimir "hola, mundo".
CL-USUARIO> "hola, mundo" "Hola, mundo"
7

del

programa. Pues resulta, que es trivialmente fcil conseguir en el REPL para

Esto funciona porque las cadenas, como nmeros, tienen una sintaxis literal que es entendido por el lector Lisp y se auto-evaluan de los objetos: Lisp lee la cadena entre comillas dobles y una instancia de un objeto de cadena en la memoria cuando evala, se evala a s mismo y luego se imprime en la sintaxis literal misma. Las comillas no son parte del objeto de cadena en la memoria -; solamente es la sintaxis que le dice al lector que debe leer una

cadena. Enva a la impresora y se imprime la cadena, ya que intenta imprimir objetos en la misma sintaxis que el lector entienda. Sin embargo, esto realmente no puede calificar como un programa "hola, mundo" . Es ms como un valor "hola, mundo". Puede dar el paso hacia un verdadero programa escrito en cdigo que, por efecto secundario imprime la cadena "Hola mundo" a la salida estndar. Common Lisp proporciona un par de maneras de emitir la salida, pero ms flexible es la funcin FORMAT. FORMAT toma un nmero variable de argumentos, pero solo dos argumentos son requeridos la salida y una cadena. Usted ver en el prximo captulo cmo la cadena puede contener directivas incorporadas que le permitirn interpolar a los argumentos de la cadena, al estilo de printf o de cadenas de Python % . Siempre y cuando la cadena no contiene un ~ , que se emitir tal y como est. Se pasa t como primer argumento, que enva su salida a la salida estndar. Por lo que la expresin FORMAT que imprimir "hola, mundo" se ve as:
CL-USUARIO> (format t "hola, mundo") hola, mundo NIL
8

Algo a notar sobre el resultado de la expresin FORMAT es NIL en la lnea despus del "hola, mundo" de salida. NIL es el resultado de la evaluacin de la expresin FORMAT, impresa por el REPL. ( NIL es la versin en Lisp de falso y / o null. Ms sobre esto en el captulo 4.) A diferencia de las otras expresiones que hemos visto hasta el momento, la expresin FORMAT es ms interesante por sus efectos secundarios - a la salida de impresin estndar en este caso que por su valor de retorno. Sin embargo, todas las expresiones Lisp evalan algn resultado.
9

Sin embargo, sigue siendo discutible si usted ha escrito todava un verdadero "programa". Pero usted lo est consiguiendo. Y que se est viendo el estilo bottom-up de la programacin con el apoyo de REPL: se puede experimentar con diferentes enfoques y construir una solucin de partes que ya hemos probado. Ahora que tiene una expresin simple que hace lo que quiere, slo hay que empaquetarlo en una funcin. Las funciones son uno de los pilares

bsicos de programa en Lisp y se pueden definir con una expresin DEFUN como esta:
CL-USUARIO> (defun hola-mundo () (format t "hola, mundo")) Hola-mundo

El hola mundo- despus de la DEFUN es el nombre de la funcin. En el captulo 4 veremos exactamente que caracteres se pueden utilizar en un nombre, pero por ahora basta decir que muchos de los caracteres, como por ejemplo - , que son ilegales en los nombres en otros idiomas son legales en Common Lisp. Es norma de estilo Lisp - en lnea para no mencionar ms con la tipografa Ingls normal - para formar nombres compuestos con guiones, como hola-mundo, en lugar del guion bajo, como en hola_mundo, o con caracteres en maysculas dentro como en holaMundo. El () despus del nombre delimita la lista de parmetros, que est vaco en este caso porque la funcin no tiene argumentos. El resto es el cuerpo de la funcin. Por un lado, esta expresin, como todas las otras que he visto, es slo otra expresin que lee, evala, e imprime el REPL. El valor de retorno, en este caso es el nombre de la funcin que se acaba de definir.
10

Pero al igual que la

expresin FORMAT, esta expresin es ms interesante por los efectos secundarios que tiene que por su valor de retorno. A diferencia de la expresin FORMAT, sin embargo, los efectos secundarios son invisibles: cuando esta expresin se evala, una nueva funcin que no toma ningn argumento y con el cuerpo (format t "hola, mundo") se crea y se le da el nombre hola-mundo . Una vez que haya definido la funcin, se le puede llamar as:
CL-USUARIO> (hola-mundo) hola, mundo NIL

Se puede ver que la salida es la misma que cuando se evalu la expresin FORMAT directamente, incluyendo el valor impreso NIL en el REPL. Las funciones de Common Lisp automticamente devolvern el valor de la ltima expresin evaluada.

Guardando su trabajo Se podra argumentar que esto es un completo programa de clases "hola, mundo". Sin embargo, todava tiene un problema. Si sale y reinicia Lisp, la definicin de la funcin se ha ido. Despus de haber escrito muy bien una funcin, usted querr guardar su trabajo. Es muy fcil. Slo tiene que crear un archivo en el que guardar la definicin. En Emacs se puede crear un nuevo archivo tecleando Cx Cf y luego, cuando Emacs se lo solicite, introducir el nombre del archivo que desea crear. Y no importa donde se guarde particularmente el archivo. Es habitual que el nombre de los archivos de cdigo fuente en Lisp tengan la extensin .lisp, a pesar de que algunas personas utilicen .cl en su lugar. Una vez creado el archivo, puede escribir la definicin que ya ha entrado en el REPL. Algunas cosas a destacar son que despus de escribir el parntesis de apertura y la palabra DEFUN, en la parte inferior de la ventana de Emacs, SLIME le dir los argumentos esperados. La forma exacta depender de la ejecucin de Common Lisp que est usando, pero es probable que sea algo como esto:
(Defun nombre varlist &rest cuerpo)

El mensaje desaparecer a medida que comience a introducir cada nuevo elemento, pero volver a aparecer cada vez que entras en un espacio. Cuando usted est entrando en la definicin en el archivo, puede elegir a romper la definicin a travs de dos lneas despus de la lista de parmetros. Si pulsa Intro y luego Tab, SLIME automticamente aplicar el dentado adecuado en la segunda lnea, de esta manera:
(Defun hola-mundo () (Formato t "hola, mundo"))
11

SLIME tambin ayudar a que coincida con el parntesis - a medida que escribe un parntesis de clausura, parpadear el parntesis de apertura correspondiente. O puede simplemente escribir Cc Cq para invocar el comando de SLIME, que inserta el parntesis de clausura que sea necesario para que coincida con todos los parntesis abiertos.

Ahora usted puede conseguir esta definicin en su entorno Lisp en varios aspectos. La forma ms fcil es escribir Cc Cc con el cursor en cualquier lugar o inmediatamente despus de DEFUN, que ejecuta el comando de slimecompilar-defun, que a su vez enva la definicin de Lisp para ser evaluado y compilado. Para asegurarse de que esto est funcionando, usted puede hacer algunos cambios a hola-mundo , recompilarlo, y luego volver a la REPL, usando Cc Cz o Cx b , y lo llama de nuevo. Por ejemplo, usted podra hacerlo un poco ms gramatical.
(Defun hola-mundo () (Format t "Hola, mundo!"))

A continuacin, vuelva a compilar con Cc Cc y escriba Cc Cz para cambiar a la REPL para probar la nueva versin.
CL-USUARIO> (hola-mundo) Hola, mundo! NIL

Probablemente usted tambin desea guardar el archivo que hemos estado trabajando, en el hello.lisp buffer, tipo Cx Cs para invocar el comando Emacs guarda-buffer . Ahora, para tratar de volver a cargar esta funcin desde el archivo de origen, tendr que dejar de Lisp y reiniciar. Para dejar de fumar puede utilizar un acceso directo SLIME: en el REPL, escriba una coma. En la parte inferior de la ventana de Emacs, se le pedir que para un comando. Tipo de dejar de fumar (o sayoonara ), y luego pulsa Enter. Este saldr de Lisp y cerrar todos los buffers creado por BABA como el buffer REPL. 12 Ahora reinicia SLIME tecleando Mx limo . Apenas para las muecas, se puede tratar de invocar hola-mundo .
CL-USUARIO> (hola-mundo)

En ese punto SLIME se abrir un nuevo buffer que se inicia con algo que se parece a esto:
intento de llamar `hola-mundo ', que es una funcin no definida. [Estado de tipo indefinido-FUNCTION] Se reinicia: 0: [TRY AGAIN-] Trate de llamar hola-mundo nuevo. 1: [RETURN VALUE] Devolver un valor en lugar de llamar hola-mundo. 2: [Valor de uso] Trate de llamar a una funcin que no sea hola-mundo. 3: [STORE-VALUE] Setf el smbolo de funcin de la hola-mundo y llamar de nuevo. 4: [ANULAR] Abortar el manejo solicitud de baba.

5: [ANULAR] Abortar por completo de este proceso. Backtrace: 0: (Swank:: DEBUG EN EMACS # <UNDEFINED-FUNCTION @ #x716b082a>) 1: ((FLET Swank: Swank-debugger-hook Swank:: DEBUG-IT)) 2: (Swank: Swank-debugger-hook # <UNDEFINED-FUNCTION @ # #x716b082a> funcin> SWANK-DEBUGGER-HOOK>) 3: (ERROR # <UNDEFINED-FUNCTION @ #x716b082a>) 4: (EVAL (hola-mundo)) 5: (Swank:: EVAL-regin "(hola-mundo) "T)

Rayos! Qu pas? Bueno, es que trat de invocar una funcin que no existe. Pero a pesar de la explosin de la produccin, Lisp realmente manejar esta situacin con gracia. A diferencia de Java o Python, Common Lisp no se derrumbar con una falta excepcin ni desbordar la pila. Y definitivamente no har un volcado de memoria slo porque trat de invocar una funcin que falta. En lugar de eso Lisp te deja en el depurador. Mientras usted est en el depurador todava tiene acceso completo a Lisp, para que pueda evaluar expresiones para examinar el estado de nuestro programa e incluso arreglar las cosas. Por ahora, no te preocupes por eso, simplemente escriba q para salir de la depuracin y volver a la REPL. El bfer de depuracin va a desaparecer, y el REPL mostrar lo siguiente:
CL-USUARIO> (hola-mundo) ; Evaluacin abortado CL-USUARIO>

Obviamente hay ms que hacer desde dentro del depurador que slo abortar vase, por ejemplo, en el captulo 19 como el depurador se integra con el sistema de tratamiento de errores. Por ahora, sin embargo, lo importante es saber que siempre se puede salir de ella, y de nuevo a la REPL, escribiendo q. De vuelta en el REPL puede intentarlo de nuevo. Lisp no saba la definicin de hola-mundo . As que hay que dejar que Lisp sepa acerca de esta definicin guardada en el archivo hola.lisp . Hay varias maneras de que usted podra hacer esto. Usted podra cambiar de nuevo a la memoria intermedia que contiene el archivo (tipo Cx by luego ingrese hola.lisp cuando se le solicite) y volver a compilar la definicin como lo hizo antes con Cc Cc . O puede cargar el archivo completo, lo que sera un enfoque ms conveniente si el archivo contiene un montn de definiciones, con la funcin de CARGA en el REPL como esta:

CL-USUARIO> (load "hello.lisp") ; Loading / home / peter / mi-lisp-programas / hello.lisp T

La T significa que ha sido cargado correctamente.

13

La carga de un archivo

con LOAD es esencialmente equivalente a escribir cada una de las expresiones en el archivo en el REPL en el orden en que aparecen en el archivo, as que despus de la llamada a LOAD, hola-mundo se define:
CL-USUARIO> (hola-mundo) Hola, mundo! NIL

Otra forma de cargar un valor de un archivo de definiciones es compilar el primer archivo con compile-file y luego cargar con LOAD el archivo compilado resultante, llamado archivo FASL, que es la abreviatura de carga rpida de archivos .compile-file devuelve el nombre del archivo FASL, por lo que se puede compilar y cargar desde el REPL de esta manera:
CL-USUARIO> (load (compile-file "hola.lisp")) ;;; Compilacin de archivo hola.lisp ;;, Escrito msnm archivo hola.fasl ;;; Fasl escribir completa , Carga rpida / home / peter / mi-lisp-programas / hello.fasl
T

SLIME tambin proporciona soporte para la carga y la compilacin de los archivos sin utilizar el REPL. Cuando ests en un buffer de cdigo fuente, puede usar Cc Cl para cargar el archivo con -slime-cargar el archivo . Emacs le pedir el nombre de un archivo para cargar con el nombre del archivo actual ya rellenado, puede simplemente presionar Enter. O puede escribir Cc Ck para compilar y cargar el archivo representado por el buffer actual. En algunas implementaciones de Lisp Comn, la compilacin de cdigo de esta manera har que sea un poco ms rpido, en otros, no, por lo general, ya que siempre se compila todo. Esto debera ser suficiente para dar una idea de cmo funciona la

programacin Lisp. Por supuesto que no han cubierto todos los trucos y tcnicas, pero ya no he visto los elementos esenciales - la interaccin con el REPL probar cosas, la carga y la prueba de cdigo nuevo ajuste y depuracin. Los Hackers serios de Lisp a menudo mantienen una imagen Lisp correr

durante das y das, y agregan, redefinen, y prueban su programa de forma gradual. Adems, incluso cuando la aplicacin Lisp se ha implementado, a menudo hay todava una forma de llegar a un REPL. Usted ver en el captulo 26 cmo se puede utilizar el REPL y Slime para interactuar con el Lisp que est ejecutando un servidor web al mismo tiempo, ya que es servir las pginas Web. Es incluso posible utilizar SLIME para conectarse a un Lisp que se ejecuta en una mquina diferente, lo que le permite - por ejemplo - para depurar un servidor remoto como si fuera local. Un ejemplo an ms impresionante de depuracin remota se produjo en 1998 en la misin Deep Space 1 de la NASA. Un ao y medio despus de que la nave espacial fue lanzada, un poco de cdigo Lisp iba a controlar la nave durante dos das mientras se realizaban una serie de experimentos. Por desgracia, una sutil falla en el cdigo haba escapado a la deteccin durante las pruebas en tierra y ya estaba en el espacio. Cuando el error se manifiest en el espacio a 100 millones de kilmetros de distancia de la Tierra - el equipo fue capaz de diagnosticar y corregir el cdigo que se ejecutaba, lo que permiti completar los experimentos. siguiente manera:
La depuracin de un programa que se ejecuta en un trozo de hardware de 100 millones de dlares que est a 100 millones de kilmetros es una experiencia interesante. Tener un bucle leer-evaluar-imprimir ejecutandose en la nave result muy valioso para encontrar y solucionar el problema.
14

Uno de los programadores lo describe de la

Usted no est listo para enviar cualquier cdigo Lisp en el espacio profundo, pero en el prximo captulo usted obtendr un salto en la escritura de un programa un poco ms interesante que "hola, mundo".

Un modo de interaccin Superior Lisp para Emacs 2 Si ha tenido una mala experiencia con Emacs anteriormente, usted debe tratar de Lisp en una caja

como un IDE que le pasa a utilizar un editor Emacs, como como su editor de texto, no habr necesidad de convertirse en un gur de Emacs Lisp con el programa . Es, sin embargo, rdenes de magnitud ms agradable para el programa Lisp con un editor que tiene la conciencia bsica Lisp. Como mnimo, usted querr un editor que automticamente puede igualar () s para ti y sabe

automticamente sangra al cdigo Lisp. Debido a que Emacs es en s mismo en gran parte por escrito en un dialecto de Lisp, Elisp, tiene un poco de apoyo para la edicin de cdigo Lisp. Emacs es tambin profundamente arraigado en la historia de Lisp y la cultura de los hackers de Lisp: el Emacs original y sus predecesores inmediatos, TECMACS y TMACS, fueron escritos por Lispers en el Instituto de Tecnologa de Massachusetts (MIT). Los editores de las mquinas Lisp eran versiones de Emacs escrito enteramente en Lisp. Los dos primeros Emacs Lisp de la mquina, siguiendo la tradicin hacker de acrnimos recursivos, se EINE y Zwei, que se mantuvo para EINE no es Emacs y ZWEI fue EINE principio. Ms tarde, los utilizados descendiente de ZWEI, llamado, ms prosaicamente, ZMACS.
3 En la prctica, hay muy pocas posibilidades de la lengua estndar se est revisando - mientras que

hay un pequeo puado de las verrugas que la gente les gustara la limpieza, el proceso de ANSI no se presta a la apertura de una norma existente para pequeos retoques, y ninguna de las verrugas que pueden ser limpiados en realidad causan a nadie ninguna dificultad seria. El futuro de Common Lisp de estandarizacin es probable que contine a travs de estndares de facto, al igual que la "normalizacin" de Perl y Python - como el experimento de distintos actores con interfaces de programacin de aplicaciones (API) y bibliotecas para hacer las cosas no se especifica en el estndar del lenguaje, otros ejecutores podr adoptar o personas desarrollarn bibliotecas de portabilidad de suavizar las diferencias entre las implementaciones de funciones no especificadas en el estndar del lenguaje.
4 Acero Banco Common Lisp 5 CMU Common Lisp 6 SBCL horquilla de CMUCL con el fin de centrarse en la limpieza de los componentes internos y por

lo que es ms fcil de mantener. Sin embargo, el tenedor ha sido amable, correcciones de errores tienden a propagarse entre los dos proyectos, y se habla de que algn da se unirn de nuevo juntos.
7 El venerable "hola, mundo" es anterior incluso a la clsica Kernighan y Ritchie libro C que jug un

papel muy importante en su popularizacin. El original "hola, mundo" parece haber venido de Brian Kernighan "un tutorial introductorio a la Lengua B", que fue parte del informe de los Laboratorios Bell Ciencias de la Computacin Tcnica N 8: El lenguaje de programacin B publicado en enero de 1973. (Se encuentra disponible en http://cm.bell-labs.com/cm/cs/who/dmr/bintro.html .)
8 Estas son algunas otras expresiones que tambin se puede imprimir la cadena "Hola Mundo": 9 Pues bien, como se ver cuando hable de la devolucin de valores mltiples, es tcnicamente

posible escribir expresiones que se evalan no tienen valor, pero incluso estas expresiones son tratados como devolverNIL cuando se evalan en un contexto en el que espera un valor.
10 Voy a discutir en el Captulo 4 Por qu el nombre se ha convertido en maysculas. 11 Tambin podra haber entrado en la definicin de dos lneas en el REPL, como el REPL Lee

expresiones de todo, no lneas.


12 atajos SLIME no forman parte de Common Lisp - they're comandos con limo.

13 Si por alguna razn la CARGA no va limpio, obtendr otro error y caer de nuevo en el depurador.

Si esto sucede, la razn ms probable es que Lisp no puede encontrar el archivo, probablemente porque su idea del directorio de trabajo actual no es la misma donde se encuentra el archivo. En ese caso, puede salir del depurador, escriba q y luego usar el acceso directo SLIME cd para cambiar la idea de Lisp del directorio actual - escriba una coma y luego cd cuando se le solicite un comando y luego el nombre del directorio en el hola. lisp se salv.
14 http://www.flownet.com/gat/jpl-lisp.html

3. Prctico: Una base de datos simple


Obviamente, antes de poder iniciar la construccin de software real en Lisp, tendr que aprender el idioma. Pero seamos sinceros - usted puede estar pensando "Common Lisp prctico, no es un oxmoron? Por qu molestarse en aprender todos los detalles de un idioma a menos que sea realmente bueno para hacer algo que te importe?" As que voy a empezar por dar un pequeo ejemplo de lo que puedes hacer con Common Lisp. En este captulo voy a escribir una simple base de datos para hacer el seguimiento de los CDs. Vamos a usar tcnicas similares en el captulo 27 cuando se crea una base de datos de archivos MP3 de nuestro servidor de streaming de MP3. De hecho, se podra pensar en esto como parte del proyecto de software MP3 - despus de todo, con el fin de tener un montn de archivos MP3 para escuchar, puede ser til para poder hacer un seguimiento de los CDs que tiene y cules necesita copiar. En este captulo, voy a cubrir lo suficiente sobre Lisp a medida que avanzamos para que usted entienda cmo funciona el cdigo. Pero voy a pasar por alto algunos detalles. Por ahora no es necesario que se preocupe por las cosas pequeas - los siguientes captulos cubren todas las construcciones de Common Lisp que se usan aqu, y ms, de una manera mucho ms sistemtica. Una nota de terminologa: Voy a hablar de un puado de operadores de Lisp en este captulo. En el captulo 4, usted aprender que Common Lisp ofrece tres tipos diferentes de operadores: las funciones , macros , y operadores especiales. A los efectos de este captulo, usted realmente no necesita saber la diferencia. Yo, sin embargo, me refiero a los diferentes operadores, funciones o macros u operadores especiales que se precisan, en vez de tratar de oculta rlos detalles detrs de la palabra operador. Por ahora se puede tratar las funciones, macros y operadores especiales ya que todos ms o menos equivalentes.1 Adems, tenga en mente que no voy a sacar hacia fuera todas las tcnicas comunes de la mayora de los programas sofisticados Lisp para su primer post "hola, mundo". El punto de este captulo no es que as es como se escribira una base de datos en Lisp, sino que el punto es para que te hagas una idea de lo que es la programacin en Lisp y para ver cmo incluso un programa Lisp puede ser relativamente bastante sencillo y con muchas caractersticas. CD y discos Para hacer un seguimiento de los CDs que deben ser arrancados de MP3 y CDs que en primer lugar, cada registro de la base de datos contendr el ttulo y el artista del CD, una calificacin de hasta qu punto el usuario lo desee, y una bandera diciendo si ha sido arrancado. Por lo
1Antes de continuar, sin embargo, es de vital importancia que se le olvide todo lo que puede saber acerca de como C# define al estilo de "macros" tal como se aplica en la C pre-procesador. La Macro Lisp es una bestia totalmente diferente.

tanto, para empezar, usted necesita una manera de representar a un registro de base de datos nica (en otras palabras, un CD). Common Lisp te da muchas opciones de estructuras de datos desde un simple elemento de una lista de cuatro a uno de clase definido por el usuario, utilizando el Sistema de Objetos de Common Lisp (CLOS). Por ahora usted puede permanecer en el extremo sencillo del espectro y el uso de una lista. Usted puede hacer una lista con la funcin LIST, que, muy apropiadamente, devuelve una lista de sus argumentos.
CLUSUARIO>(list123) (123)

Usted podra utilizar una lista de cuatro elementos, la asignacin de un puesto determinado en la lista a un campo determinado en el registro. Sin embargo, otro sabor de la lista denominada lista de propiedades , o plist para abreviar - es an ms conveniente. Una plist es una lista donde todos los dems elementos, comenzando con el primero, es un smbolo que describe al siguiente elemento de la lista. No voy a entrar ahora en todos los detalles de lo que es exactamente un smbolo, bsicamente se trata de un nombre. Para los smbolos del nombre de los campos de la base de datos de CD, puede utilizar un tipo especial de smbolo, llamado clave de smbolos. Una palabra clave es el nombre que comienza con dos puntos (:), por ejemplo, :foo. He aqu un ejemplo de un plist usando los smbolos de palabras clave :a, :b y :c como los nombres de propiedad:
CLUSUARIO>(list:a1:b2:c3) (:A1:B2:C3)

Tenga en cuenta que puede crear una lista de propiedades con la misma funcin LIST tal cual como se utiliza para crear otras listas, es el contenido lo que lo hace un plist. Lo que hace conveniente a la forma de plists para representar a los registros en una base de datos es la funcin GETF, que contiene un plist y un smbolo y devuelve el valor en el plist siguiente al smbolo, lo que hace que plist sea una pobre especie de tabla hash. Lisp tiene una verdadera tabla hash tambin, pero plists es suficiente para sus necesidades por el momento y es ms fcil de guardar en un archivo, que ser muy til ms adelante.
CLUSUARIO>(getf(list:a1:b2:c3):a) 1 CLUSUARIO>(getf(list:a1:b2:c3):c) 3

Teniendo en cuenta todo eso, usted puede escribir fcilmente una funcin que llevar cuatro campos como argumentos y devolver un plist que representa ese CD.
(Defunhacercd(tituloartistavaloracionarranco) (list:titulotitulo:artistaartista:valoracionvaloracion:arrancoarranco))

La palabra DEFUN nos dice que se define una nueva funcin. El nombre de la funcin es hacercd . Despus de el nombre viene de la lista de parmetros. Esta funcin tiene cuatro parmetros: titulo, artista, calificacion y arranco. Todo lo que sigue despus de la lista de parmetros es el cuerpo de la funcin. En este caso el cuerpo es slo una llamada a LIST.

Cuando se llama a hacer-cd, los argumentos pasados a la llamada tendrn la obligacin de ser las variables en la lista de parmetros. Por ejemplo, para agregar un disco para el CD de Roses de Kathy Mattea, se podra llamar a hacer-cd de esta manera:
CLUSUARIO>(hacercd"Roses""KathyMattea"7t) (:TITULO"Roses":ARTISTA"KathyMattea":VALORACION7:ARRANCOT)

Llenando el CD
Un solo registro, sin embargo, no hace una base de datos. Es necesario construir algunas ms grandes para mantener los registros. Una vez ms, por razones de simplicidad, una lista parece una buena opcin. Adems de la simplicidad se puede utilizar una variable global, *db*, que se puede definir con la macro defvar. Los asteriscos (*) en el nombre son una convencin de nomenclatura de Lisp de las variables globales.2
(Defvar*db*nil)

Usted puede utilizar la macro PUSH para agregar elementos a *db*. Pero es probablemente una buena idea abstraer un poco las cosas, por lo que se debe definir una funcin de complemento que agrega un registro a la base de datos.
(Defunagregarregistro(cd)(pushcd*db*))

Ahora usted puede utilizar agregar-registro y hacer-cd en conjunto para aadir registros a la base de datos de CDs.
CLUSUARIO>(agregarregistro(hacercd"Rosas","KathyMattea"7t)) ((:TITULO"Roses":ARTISTA"KathyMattea":VALORACION7:ARRANCOT)) CLUSUARIO>(agregarregistro(hacercd"Fly","DixieChicks"8t)) ((:TITULO"Fly":ARTISTA"DixieChicks":VALORACION8:ARRANCOT) (:TITULO"Roses":ARTISTA"KathyMattea":VALORACION7:ARRANCOT)) CLUSUARIO>(agregarregistro(hacercd"Inicio","DixieChicks"9t)) ((:TITULO"Home":ARTISTA"DixieChicks":VALORACION9:ARRANCOT) (:TITULO"Fly":ARTISTA"DixieChicks":VALORACION8:ARRANCOT) (:TITULO"Roses":ARTISTA"KathyMattea":VALORACION7:ARRANCOT))

El material impreso por el REPL despus de cada llamada a la extensin registro es el valor de retorno, que es el valor devuelto por la ltima expresin en el cuerpo de la funcin, el PUSH. Y PUSH devuelve el nuevo valor de la variable que se trata de modificar. As que lo que ests viendo en realidad es el valor de la base de datos despus de que el registro se ha agregado.

En cuanto a los contenidos de bases de datos


Tambin puede ver el valor actual de *db* cada vez que desee escribiendo *db* en el REPL.
CLUSUARIO>*db* ((:TITULO"Home":ARTISTA"DixieChicks":VALORACION9:ARRANCOT) (:TITULO"Fly":ARTISTA"DixieChicks":VALORACION8:ARRANCOT) (:TITULO"Roses":ARTISTA"KathyMattea":VALORACION7:ARRANCOT))

2 Usar una variable global tambin tiene algunas desventajas - por ejemplo, usted puede tener slo una base de

datos a la vez. En el captulo 27, con ms de la lengua debajo de su correa, usted estar listo para construir una base de datos ms flexible. Tambin se ver en el captulo 6, que incluso usar una variable global es ms flexible en Common Lisp que en otros idiomas.

Sin embargo, eso no es una forma muy satisfactoria de ver la salida. Puede escribir usuario, de esta manera:
TITULO:Inicio ARTISTA:DixieChicks VALORACION:9 ARRANCO:T TITULO:Fly ARTISTA:DixieChicks VALORACION:8 ARRANCO:T TITULO:Roses ARTISTA:KathyMattea VALORACION:7 ARRANCO:T

una

funcin que arroja el contenido de la base de datos *db* en un formato ms legible para el

La funcin se parece a esto:


(defunmostrardb() (dolist(cd*db*) (formatt"~{~a:~10t~a~%~}~%"cd)))

Esta funcin trabaja recorriendo todos los elementos de *db* con la macro dolist, asignando cada elemento a la variable cd. Para cada valor de cd, se utiliza la funcin FORMAT para imprimir. Es cierto que la llamada a FORMAT es un poco crptica. Sin embargo, FORMAT no es particularmente ms complicado que el printf de C o una funcin de Perl o que el operador de cadenas % de Python. En el captulo 18 voy a discutir FORMAT con mayor detalle. Por ahora podemos tomar esta llamada poco a poco. Como se vio en el captulo 2, FORMAT toma al menos dos argumentos, el primero es el medio hacia donde se enva la salida, t es la abreviatura del medio *standar-output* (salida-estndar). El segundo argumento de FORMAT es una cadena de formato que puede contener tanto texto literal y directivas que le dicen a FORMAT aspectos como la forma para interpolar el resto de sus argumentos. Las Directivas de format comienzan con ~ (de manera parecida a las directivas de printf que comienzan con %). FORMAT comprende decenas de directivas, cada una con su propio conjunto de opciones 3. Sin embargo, por ahora slo nos centraremos en los que necesita para escribir mostrar-db.

3 Una de las mejores directivas de FORMAT es la directiva ~R. Alguna vez quiso saber cmo decir un nmero
muy grande en palabras del ingls? Lisp sabe. Evale lo siguiente: (format t "~r" 1606938044258990275541962092) y usted debe ver (ajustado para mejorar la legibilidad): "Un octillones seiscientos seis septillones novecientos treinta y ocho trillones cuarenta y cuatro trillones doscientos cincuenta y ocho mil billones novecientos noventa billn doscientos setenta y cinco mil millones quinientos cuarenta y una millones novecientos sesenta y dos mil noventa y dos"

El signo ~ es una directiva de esttica, que significa que se toma un argumento y la muestra en un forma legible. Esto mostrar las palabras clave sin (:), y cadenas sin comillas. Por ejemplo:
USUARIOCL>(formatt"~a","DixieChicks") DixieChicks NIL

o:
CLUSUARIO>(formatt"~a":titulo) TITULO NIL

La directiva t~ es de tabulacin. 10t~ le dice a FORMAT que emita los espacios suficientes para pasar a la dcima columna antes de procesar el siguiente ~a . t ~ no consume ningn argumento.
CLUSUARIO>(formatt"~a:~10t~a":artista"DixieChicks") ARTISTA:DixieChicks NIL

Ahora las cosas se ponen un poco ms complicado. Cuando FORMAT ve ~ { el siguiente argumento que se toma debe ser una lista. FORMATO recorre esa lista, el procesamiento de las directivas entre las ~{ y ~}, el consumo de tantos elementos de la lista como sean necesarios para cada elemento de la lista. En mostrar-db, FORMAT consume una palabra clave y un valor de la lista cada vez que pasa a travs del bucle. La directiva ~% no consume ningn argumento, pero le dice a FORMAT que emita una nueva lnea. Luego, despus de ~} finaliza el bucle, el ltimo ~% le dice a FORMAT que emita una nueva lnea ms para poner una lnea en blanco entre cada CD. Tcnicamente, se podra tambin utilizar FORMAT para recorrer sobre la base de datos misma, transformando tu funcin mostrar-db en una sola lnea.
(Defunmostrardb() (formatt"~{~{~a:~10t~a~%~}~%~}"*db*))

Eso es muy cool o muy tenebroso dependiendo de su punto de vista.

La mejora de la interaccin con el usuario


Mientras que nuestra funcin agregar-registro funciona bien para agregar registros, es un poco lispeada para cualquier usuario ocasional. Y si desea aadir un montn de discos, no es muy conveniente. Por lo que se puede escribir una funcin para indicar al usuario que se desea obtener informacin sobre un conjunto de CDs. De inmediato usted sabe que necesita alguna manera de pedir al usuario una informacin y que l la lea. As que vamos a escribir eso.
(defunleermensaje(mensaje) (format*queryio*"~a:"mensaje) (forceoutput*queryio*) (readline*queryio*))

Puede utilizar su viejo amigo FORMAT para emitir un aviso. Tenga en cuenta que no hay ~% en la cadena de formato, por lo que el cursor se mantendr en la misma lnea. La llamada a la FORCE-OUTPUT es necesaria en algunas implementaciones de Lisp para asegurarse de que no espera un salto de lnea antes de imprimir el mensaje. A continuacin puede leer una sola lnea de texto con la funcin bien llamada READ-LINE. La variable *query-io * es una variable global (lo que se puede decir debido a la convencin * de nomenclatura para variables globales) que contiene el flujo de entrada conectado a la terminal. El valor de retorno del sistema de lectura ser el ltimo valor, la llamada a READ-LINE, devuelve la cadena que se lee (sin el salto de lnea al final). Usted puede combinar su funcin existente hacer-cd con la funcin leer-mensaje para construir una funcin que crea un nuevo registro CD de datos desde los datos que se obtienen para cada valor ledo.
(Defunagregaruncd() (hacercd (leermensaje"Ttulo") (leermensaje"Artista") (leermensaje"Valoracin") (leermensaje"Arranco[y/n]")))

Eso es casi correcto. Excepcin que leer-mensaje devuelve una cadena, que, aunque es conveniente para los campos Ttulo y artista, no lo es tanto para los campos Valoracin y Arranc, que deben ser un nmero y una variable booleana. Dependiendo de lo sofisticado de la interfaz de usuario que desea, usted puede establecer arbitrariamente las longitudes para validar los datos que el usuario introduce. Por ahora vamos a inclinarnos hacia la forma rpida y sucia: se puede cubrir la funcin leer-mensaje para el campo Valoracin con una llamada a la funcin de Lisp PARSE-INTEGER, de esta manera:
(parseinteger(leermensajeValoracin))

Por desgracia, el comportamiento predeterminado de PARSE-INTEGER es para sealar un error si no se puede analizar un entero fuera de la cadena o si hay alguna basura no numrica en la cadena. Sin embargo, puede tomar un argumento permitida), que le dice que se relaje un poco.
(parseinteger(leermensaje"Valoracin"):junkallowedt)

opcional : junk-allowed (basura-

Pero todava hay un problema: si no puede encontrar un nmero entero en medio de toda la basura, PARSE-INTEGER devolver NIL en lugar de un nmero. Para continuar con el mtodo rpido y sucio, puede que slo quieras colocar el valor 0 (cero) y continuar. La macro OR de Lisp es justo lo que necesita. Es similar a los "cortocircuitos" | | en Perl, Python, Java, y C; este toma una serie de expresiones, las evala una a la vez, y devuelve el primer valor no nil (o NIL si todos son todos los NIL). As que usted puede utilizar lo siguiente:
(OR(parseinteger(leermensaje"Valoracin"):junkallowedt)0)

para obtener un valor por defecto de 0.

Arreglar los cdigos para obtener el camplo Arranc es bastante ms simple. Usted slo puede usar la funcin Y-OR-N-P de Common Lisp.
(yornp"Arranc[y/n]:")

En efecto, esta ser la parte ms slida de cargar-un-cd, como Y-OR-N-P se verificar si el usuario escribe algo que no comience con y , Y , n , o N . Colocando estas partes juntas se obtiene una funcin cargar-un-cd razonablemente robusta.
(Defunagregaruncd() (hacercd (leermensaje"Ttulo") (leermensaje"Artista") (or(parseinteger(leermensaje"Valoracin"):junkallowedt)0) (yornp"Arranc[y/n]:")))

Finalmente, puede terminar en una interfaz para "aadir un montn de CDs" envolviendo agregar-un-cd en una funcin que se repite hasta que el usuario termine. Puede utilizar la forma simple de la macro LOOP, que ejecuta repetidamente un conjunto de expresiones hasta que salga con una llamada a RETURN. Por ejemplo:
(Defunagregarcds() (Loop(agregarregistro(agregaruncd)) (If(not(yornp"Cargarcdnuevo?"))(return))))

Ahora usted puede utilizar agregar-cds para aadir mas CDs a la base de datos.
USUARIOCL>(agregarcds) Ttulo:Rockin'thesuburbs Artista:BenFolds Valoracin:6 Arranc(si(y)/no(n))y Cargarcdnuevo?(si(y)/no(n))y Ttulo:GiveUsaBreak/ Artista:Limpopo Valoracin:10 Arranc(si(y)/no(n))y Cargarcdnuevo?(si(y)/no(n))y Ttulo:LyleLovett Artista:LyleLovett Valoracin:9 Arranc(si(y)/no(n))y Cargarcdnuevo?(si(y)/no(n))n NIL

Guardar y cargar la base de datos


Tener una forma conveniente de aadir registros a la base de datos est muy bien. Pero el usuario no estar muy feliz si tiene que volver a introducir todos los registros cada vez que salga y reinicie Lisp. Afortunadamente, con las estructuras de datos que est utilizando para representar los datos, es trivialmente fcil guardarlos en un archivo y volver a cargarlo ms tarde. Aqu hay una funcin guardar-db que toma un nombre de archivo como argumento y guarda el estado actual de la base de datos:
(Defunguardardb(archivo) (withopenfile(salidaarchivo :direction:output :ifexists:supersede)

(withstandardiosintax (print*db*salida))))

La macro WITH-OPEN-FILE abre un archivo, une el medio a una variable, ejecuta un conjunto de expresiones, a continuacin, cierra el archivo. Tambin se asegura que el archivo est cerrado, incluso si algo sale mal, mientras evala el cuerpo. La lista que sigue inmediatamente despus de WITH-OPEN-FILE no es una llamada a funcin, sino ms bien es parte de la sintaxis definida por WITH-OPEN-FILE. Contiene el nombre de la variable que contendr el flujo de archivos a los que voy a escribir en el cuerpo de WITH-OPEN-FILE, un valor que debe ser un nombre de archivo, a continuacin, algunas opciones para controlar cmo se abre el archivo. Aqu se especifica que se va a abrir el archivo para escritura con :direction :output y que desea sobrescribir un archivo existente del mismo nombre, si es que ya existe uno con :ifexists :supersede. Una vez que se haya abierto el archivo, todo lo que tienes que hacer es imprimir el contenido de la base de datos con (print *db* out). A diferencia de FORMAT, PRINT imprime objetos de Lisp en una forma que pueda ser leda por el lector de Lisp (el intrprete). La macro WITHSTANDARD-IO-SINTAX se asegura de que ciertas variables que afectan el comportamiento de PRINT se establezcan en sus valores estndar. Vamos a usar la misma macro al leer los datos de nuevo para asegurarse de que el lector Lisp y la impresora estn operando de manera compatible. El argumento de guardar-db debe ser una cadena que contenga el nombre del archivo adonde el usuario desea guardar la base de datos. La forma exacta de la cadena depender de qu sistema operativo est utilizando. Por ejemplo, en una mquina Unix que deberas poder llamar a guardar-db de esta manera:
CLUSUARIO>(guadardb"~/miscds.db") ((:TITULO"LyleLovett":ARTISTA"LyleLovett":VALORACION9:ARRANCOT) (:TITULO"GiveUsaBreak":ARTISTA"Limpopo":VALORACION10:ARRANCOT) (:TITULO"Rockin'theSuburbs":ARTISTA"BenFolds":VALORACION6:ARRANCOT) (:TITULO"Home":ARTISTA"DixieChicks":VALORACION9:ARRANCOT) (:TITULO"Fly":ARTISTA"DixieChicks":VALORACION8:ARRANCOT) (:TITULO"Roses":ARTISTA"KathyMattea":VALORACION9:ARRANCOT))

En Windows, el nombre podra ser algo como "c: /mis-cds.db" o " c: \ \ mis-cds.db."4 Usted puede abrir este archivo en cualquier editor de texto para ver lo que parece. Usted debe ver algo muy parecido a las impresiones del REPL cuando escribe *db*. La funcin de cargar la base de datos es similar a:
(Defuncargardb(archivo) (withopenfile(entradaarchivo) (withstandardiosintax (Setf*db*(readentrada)))))

Esta vez no es necesario especificar la opcin :direction para WITH-OPEN-FILE, ya que se desea el valor por defecto :input. Y en lugar de print, se utiliza la funcin READ para leer el 4 Windows realmente entiende las barras diagonales en nombres de archivo a pesar de que normalmente se utiliza una barra
invertida como el separador de directorio. Esto es conveniente ya que de lo contrario tienes que escribir las barras invertidas doble, porque la barra invertida es el carcter de escape en las cadenas de Lisp.

medio entrada. Este es el mismo lector utilizado por el REPL y puede leer cualquier expresin Lisp que podras escribir en el intrprete REPL. Sin embargo, en este caso, usted est leyendo y guardando la expresin, no evalundola. Una vez ms, la macro WITH-STANDARD-IOSINTAX se asegura de que READ est utilizando la misma sintaxis bsica que guardar-db uso cuando PRINT (IMPRIMIO) los datos. La macro SETF es el principal operador de asignacin de Common Lisp. Establece su primer argumento al resultado de la evaluacin de su segundo argumento. As que la variable *db* contendr el objeto que resulta de leer el archivo, es decir, la lista de listas escritas por guardar-db. Usted tiene que tener cuidado con una cosa cargar-db destruye todo lo que contena la variable *db* antes de la llamada. As que si usted ha agregado registros con agregar-registro o agregar-cds que no se hayan guardado con guardar-db, los vas a perder.

Consulta de la Base de Datos


Ahora que tiene una manera de guardar y volver a cargar la base de datos junto con una interfaz de usuario conveniente para agregar nuevos registros, que pronto podra tener los registros suficientes de tal modo que no se requerir cargar la base de datos completa slo para ver algunos datos. Lo que se necesita es una forma de consulta de la base de datos. Deberas poder ser capaz de escribir algo como esto:
(Seleccionar:artista"DixieChicks")

y obtener una lista de todos los registros donde el artista es Dixie Chicks. Una vez ms, resulta que la opcin de guardar los registros en una lista dar sus frutos. La funcin REMOVE-IF-NOT tiene un predicado y una lista y devuelve una lista que slo contiene los elementos de la lista original que coinciden con el predicado. En otras palabras, se ha eliminado todos los elementos que no coinciden con el predicado. Sin embargo, REMOVEIF-NOT realmente no quita nada - se crea una nueva lista, dejando a la lista original intacta. Es como ejecutar el control en un archivo. El argumento del predicado puede ser cualquier funcin que acepta un nico argumento y devuelve un valor booleano NIL para falso y cualquier otro valor para verdadero. Por ejemplo, si usted quiere extraer todos los elementos, incluso a partir de una lista de nmeros, se puede utilizar REMOVE-IF-NOT de la siguiente manera:
CLUSUARIO>(removeifnot#'evenp'(12345678910)) (246810)

En este caso, el predicado es la funcin EVENP , que devuelve true si su argumento es un nmero par. La notacin divertida #' es la abreviatura de "Dame la funcin con el siguiente nombre". Sin el #', Lisp tratara a evenp como el nombre de una variable y buscara el valor de la variable, no a la funcin. Tambin puede utilizar REMOVE-IF-NOT con una funcin annima. Por ejemplo, si EVENP no existiera, se podra escribir la expresin anterior de la siguiente manera:
CLUSUARIO>(removeifnot#'(lambda(x)(=0(modx2)))'(12345678910))

(246810)

En este caso, el predicado es la funcin annima:


(Lambda(x)(=0(modx2)))

que comprueba que su argumento es igual a 0 mdulo 2 (en otras palabras, es par). Si usted quisiera extraer slo los nmeros impares con una funcin annima, debera escribir esto:
CLUSUARIO>(removeifnot#'(lambda(x)(=1(modx2)))'(12345678910)) (13579)

Tenga en cuenta que lambda no es el nombre de la funcin - es el indicador de que est definiendo una funcin annima.5 Aparte de la falta de un nombre, una expresin Lambda se parece mucho a un DEFUN: la palabra lambda es seguida por una lista de parmetros, que es seguido por el cuerpo de la funcin. Para seleccionar todos los discos de las Dixie Chicks en la base de datos utilizando REMOVEIF-NOT, usted necesita una funcin que devuelve verdadero cuando el campo artista de un disco es "Dixie Chicks". Recuerde que se opt por la representacin plist para los registros de la base de datos ya que la funcin GETF puede extraer campos de nombre de un plist. As que asumiendo que cd es el nombre de una variable que contiene un registro de base de datos nica, puede utilizar la expresin (getf cd :artista) para extraer el nombre del artista. La funcin EQUAL , cuando se les da argumentos de cadena, los compara carcter por carcter. Por lo tanto (equal (cd getf: artista) "Dixie Chicks") pondr a prueba si el campo de artista de un CD dado es igual a "Dixie Chicks" . Todo lo que necesitas hacer es envolver esa expresin en una funcin Lambda para generar una funcin annima y pasarla a REMOVE-IF-NOT.
CLUSUARIO>(removeifnot #'(Lambda(cd)(equal(getfcd:artista)"DixieChicks"))*db*) ((:TITULO"Home":ARTISTA"DixieChicks":VALORACION9:ARRANCOT) (:TITULO"Fly":ARTISTA"DixieChicks":VALORACION8:ARRANCOT))

Ahora supongamos que usted quiere envolver toda esa expresin en una funcin que toma el nombre del artista como un argumento. Se puede escribir de esta manera:
(Defunseleccionarporartista(artista) (removeifnot #'(Lambda(cd)(equal(getfcd:artista)artista)) *db*))

Observe cmo la funcin annima, contiene un cdigo que no se ejecutar hasta que se invoque REMOVE-IF-NOT, sin embargo, puede hacer referencia a la variable artista. En este caso la funcin annima no slo le ahorrar el tener que escribir una funcin regular - le permite escribir una funcin que deriva parte de su significado - el valor de artista - desde el contexto en el que se incrusta. As que eso es seleccionar por artista. Sin embargo, la seleccin por artista es slo uno de los tipos de consultas que le gustara efectuar. Usted podra escribir muchas ms funciones, como por ejemplo seleccionar-por-titulo, seleccionar-por-valoracion, seleccionar- por-titulo-y-artista, y 5
La palabra lambda es usada en Lisp a causa de una conexin a principios del clculo lambda, un formalismo matemtico inventado para el estudio de las funciones matemticas.

as sucesivamente. Pero todos estaran ms o menos igual, excepto por el contenido de la funcin annima. En su lugar, puede hacer una ms general, seleccione la funcin que toma una funcin como argumento.
(Defunseleccionar(selectorfn) (removeifnotselectorfn*db*))

Entonces, qu pas con la #'? Bueno, en este caso usted no utiliza REMOVE-IF-NOT para la funcin denominada selector-fn. Usted desea que el uso de la funcin annima que se pasa como un argumento para seleccionar en la variable seleccin-fn. Aunque '# regresa en el llamado a seleccionar la funcin.
CLUSUARIO>(seleccionar#'(lambda(cd)(equal(getfcd:artista)"DixieChicks"))) ((:TITULO"Home":ARTISTA"DixieChicks":VALORACION9:ARRANCOT) (:TITULO"Fly":ARTISTA"DixieChicks":VALORACION8:ARRANCOT))

Pero esto es realmente muy grave a futuro. Afortunadamente, usted puede terminar la creacin de la funcin annima.
(Defunartistaselector(artista) #'(Lambda(cd)(equal(getfcd:artista)artista)))

Esta es una funcin que devuelve una funcin que hace referencia a una variable que - parece - no volver a existir despus de la llamada a artista-selector6. Puede parecer raro ahora, pero en realidad funciona de la manera que mas quisieras -si usted llama a artista-selector con el argumento "Dixie Chicks", se obtiene una funcin annima que coincide con los CDs cuyos campos :artista son "Dixie Chicks", y si se le llama con "Lyle Lovett", se obtiene una funcin diferente que coincidir con los campos :artista iguales a "Lyle Lovett". As que ahora puede volver a escribir la llamada a seleccionar de esta manera:
CL-USUARIO> (seleccionar (artista-selector "Dixie Chicks")) ((: TTULO "Home": ARTISTA "Dixie Chicks": Categora 9: Ripped T) (: TTULO "Fly": ARTISTA "Dixie Chicks": 8 Nota: Ripped T))

Ahora slo te falta algunas funciones ms para generar selectores. Pero as como no quiero tener que escribir seleccionar-por-titulo, seleccionar-por-valoracion , y as sucesivamente, ya que todos ellos seran muy similares, no vas a querer escribir un montn de funciones casi idnticas de selectores de funciones, generadores, uno para cada campo. Por qu no escribir una funcin de propsito general de seleccin de funcines, una funcin que, en funcin de qu argumentos se le pasa, va a generar una funcin de seleccin para los diferentes campos o incluso una combinacin de campos? Usted puede escribirlo como una funcin, pero primero necesita un curso intensivo en un tema conocido como parmetros de palabras clave. En las funciones que he escrito hasta ahora, que ha especificado una simple lista de parmetros, que estn destinados a los alegatos correspondientes en la llamada a la funcin. Por ejemplo, la siguiente funcin:
(Defunfoo(abc)(listabc))

6 El trmino tcnico para una funcin que hace referencia a una variable en su mbito que lo contiene es un clausura debido
a que la funcin "se cierra sobre" la variable. Voy a hablar de clausuras con ms detalle en el captulo 6.

tiene tres parmetros a, b y c y debe ser llamada con tres argumentos. Pero a veces es posible que desee crear una funcin que pueda ser llamada con un nmero variable de argumentos. Los parmetros de palabras clave son una forma de lograr este objetivo. Una versin de foo que utiliza parmetros de palabra clave podra tener este aspecto:
(Defunfoo(&keyabc)(listabc))

La nica diferencia es &key al principio de la lista de argumentos. Sin embargo, las llamadas a este nuevo foo se vern muy diferentes. Estas llamadas son todas legales, con el resultado a la derecha de :
(foo:a1:b2:c3)(123) (foo:c3:b2:a1)(123) (foo:a1:c3)(1NIL3) (foo)(NILNILNIL)

Como muestran estos ejemplos, el valor de las variables de a, b y c estn enlazados a los valores que siguen a la palabra clave correspondiente. Y si una palabra clave no est presente en la convocatoria, la variable correspondiente se establece a NIL . Estoy pasando por alto un montn de detalles de cmo se especifican los parmetros de palabra clave y cmo se relacionan con otros tipos de parmetros, pero lo que necesita saber es un detalle ms. Normalmente, si una funcin se llama sin argumentos para un parmetro de palabra clave en particular, el parmetro tendr el valor NIL. Sin embargo, a veces se quiere ser capaz de distinguir entre un NIL que fue aprobado explcitamente como argumento a un parmetro de palabra clave y el valor por defecto de NIL. Para permitir esto, cuando se especifica un parmetro de palabra clave que usted puede reemplazar el nombre simple, con una lista compuesta por el nombre del parmetro, un valor por defecto, y otro nombre de parmetro, llamado parmetro suministrado-p. El parmetro suministrado-p se establecer en funcin de verdadero o falso sobre si un argumento se pasa en realidad para ese parmetro la palabra clave en una llamada en particular a la funcin. Aqu hay una versin de foo que utiliza esta funcin:
(defunfoo(&keya(b20)(c30cp))(listabccp))

Ahora las mismas llamadas de antes muestran estos resultados:


(foo:a1:b2:c3)(123T) (foo:c3:b2:a1)(123T) (foo:a1:c3)(1203T) (foo)(NIL2030NIL)

El selector de funcin general, que puedes llamar where (donde) por razones que pronto se harn evidentes si ests familiarizado con bases de datos SQL, es una funcin que toma cuatro parmetros clave que corresponden a los campos en los registros de nuestro CD y genera un selector de funciones que selecciona los CD que contienen todas los valores dados a where. Por ejemplo, se le permitir decir cosas como esta:
(seleccionar(where:artista"DixieChicks"))

o esta:

(seleccionar(where:valoracion10:arranconil))

La funcin se parece a esto:


(defunwhere(&keytituloartistavaloracion(arranconilarrancop)) #'(lambda(cd) (and (iftitulo(equal(getfcd:titulo)titulo)t) (ifartista(equal(getfcd:artista)artista)t) (ifvaloracion(equal(getfcd:valoracion)valoracion)t) (ifarrancop(equal(getfcd:arranco)arranco)t))))

Esta funcin devuelve una funcin annima que devuelve la lgica AND de una clusula por campo en los registros de nuestro CD. Cada clusula comprueba si el argumento apropiado fue aprobada y luego o bien se compara con el valor en el campo correspondiente en el registro de CD o devuelve t, la versin de Lisp de verdadero, si el parmetro no fue aprobado. Por lo tanto, la funcin de seleccin devolver t slo para CDs que contienen todos los argumentos pasados a where.7 Tenga en cuenta que es necesario utilizar una lista de tres elementos para especificar el parmetro de palabra clave arranco porque lo que se necesita saber es si la persona que llama en realidad pas :arranco nil, es decir, "Seleccionar CDs cuyo campo arranco es nulo", o si :arranco se queda fuera por completo, es decir, "No me importa el valor del campo arranco".

Actualizar los registros existentes - Otro uso para WHERE


Ahora que tienes bien generalizadas las funciones seleccionar y where, usted se encuentra en buenas condiciones para escribir la siguiente funcin que toda base de datos necesita - una forma de actualizar determinados registros en particular. En SQL el comando update se utiliza para actualizar un conjunto de registros que cumplan una determinada clusula where. Eso me parece un buen modelo, sobre todo porque ya tienes un generador de clusulas where. De hecho, la funcin update es ms que nada la aplicacin de algunas ideas que ya hemos visto: usar un selector de funcin para seleccionar los registros a actualizar y utilizar argumentos de palabra clave para especificar los valores de cambio. La nueva pieza principal es el uso de la funcin MAPCAR que recorre una lista, *db* en este caso, y devuelve una nueva lista con los resultados de aplicar una funcin a cada elemento de la lista original.
(defunupdate(selectorfn&keytituloartistavaloracion(arranconilarrancop)) (setf*db* (mapcar #'(lambda(registro) (when(funcallselectorfnregistro) (Sititulo (setf(getfregistro:titulo)titulo)) (Siartista (setf(getfregistro:artista)artista)) (Sivaloracion (setf(getfregistro:valoracion)valoracion)) (Siarrancop (setf(getfregistro:arranco)arranco))) registro)*db*)))

7 Tenga en cuenta que en Lisp, una forma SI, como todo lo dems, es una expresin que devuelve un valor. De hecho, es ms
como el operador ternario ( ? ) en Perl, Java y C en que esta es legal en los idiomas siguientes: some_var = some_boolean? valor1: valor2; mientras que esto no es: some_var = if (some_boolean) valor1, valor2 dems; porque en esos idiomas, si es una declaracin, no una expresin.

Otra cosa nueva cosa que encontramos aqu es el uso de SETF en una forma compleja, tal como (getf registro :titulo). Voy a discutir SETF con mayor detalle en el captulo 6, pero por ahora slo tiene que saber que es un operador de asignacin general que se puede utilizar para asignar un montn de "lugares" que no son slo variables. (Es una coincidencia que SETF y GETF tienen nombres tan similares - no tienen ninguna relacin especial.) Por ahora es suficiente saber que despus de (setf (getf registro :titulo) titulo), el plist referenciado por registro tendr el valor de la variable de ttulo despus del nombre de la propiedad :titulo. Con esta funcin update, si usted decide que realmente aprecia a Dixie Chicks y que la valoracin de todos sus discos deben ir a 11, se puede evaluar la forma siguiente:
CLUSUARIO>(update(where:artista"DixieChicks"):valoracion11) NIL

Y esto queda as.


CLUSUARIO>(seleccionar(where:artista"DixieChicks")) ((:TITULO"Home":ARTISTA"DixieChicks":VALORACION11:ARRANCOT) (:TITULO"Fly":ARTISTA"DixieChicks":VALORACION11:ARRANCOT))

Puede incluso agregar una funcin para borrar registros de la base de datos.
(Defuneliminarregistros(selectorfn) (Setf*db*(removeifselectorfn*db*)))

La funcin de remove-if es el complemento de remove-if-not; esta devuelve una lista con todos los elementos que coinciden con el predicado eliminado. Como remove-if-not, no afecta realmente a la lista que es pasada, pero a travs de la asignacin del resultado en *db*, eliminar-registros8 cambia el contenido de la base de datos.
9

Eliminar la duplicacin y ganar en grande


Hasta ahora todos los cdigos de la base de datos soportan insertar, seleccionar, actualizar y eliminar, para no hacer mencin a la interfaz de usuario de lnea de comandos para agregar nuevos registros y volcar el contenido, es un poco ms de 50 lneas. Total. 10 Sin embargo, todava hay cierta duplicacin de cdigo molesto. Y resulta que puede eliminar la duplicacin y hacer que el cdigo sea ms flexible, al mismo tiempo. La duplicacin en la que estoy pensando es en la funcin where. El cuerpo de la funcin es un conjunto de clusulas de este tipo, uno por cada campo:
(iftitulo(equal(getfcdtitulo)titulo)t)

8 Es necesario utilizar el nombre , eliminar-registro en lugar de los ms obvios eliminar porque ya hay una funcin en Lisp
llamado DELETE. El sistema de paquetes Lisp te da una manera de lidiar con estos conflictos de nombres, lo que podra tener una funcin llamada borrar si quieres. Pero no estoy dispuesto a explicar los paquetes por el momento.

Si usted est preocupado de que este cdigo crea una prdida de memoria, puede estar seguro: Lisp es el lenguaje que invent la recoleccin de basura (y la asignacin del montn para el caso). La memoria utilizada por el antiguo valor de *db* se recupera automticamente, asumiendo que nadie ms se aferra a una referencia a l, que nada de esto es el cdigo.

10 Un amigo mo que una vez fue entrevistar a un ingeniero para un trabajo de programacin y le hizo una pregunta de la
entrevista tpica: cmo saber cundo una funcin o mtodo es demasiado grande? Bueno, dijo el candidato, no me gusta cualquier mtodo que sea ms grande que mi cabeza. Quiere decir que no puede mantener todos los detalles en la cabeza? No, quiero decir que pongo la cabeza contra el monitor, y el cdigo no debe ser ms grande que mi cabeza.

En este momento no es tan malo, pero como todos la duplicacin de cdigo que tiene el mismo costo: si desea cambiar la forma en que funciona, usted tiene que cambiar varias copias. Y si cambia los campos de un CD, usted tendr que agregar o quitar clusulas de where . Y actualizar sufre de la misma clase de la duplicacin. Es doblemente molesto ya que el objetivo de la funcin es generar de forma dinmica un poco de cdigo que comprueba los valores que te importan, por qu habra que hacer un trabajo en la comprobacin en tiempo de ejecucin si el ttulo incluso se paso hacia dentro? Imagnese que usted estaba tratando de optimizar este cdigo y descubri que se trataba de pasar demasiado tiempo comprobando incluso el ttulo y el resto de los parmetros de palabra clave que se crearon?11 Si realmente quera eliminar todos los controles de tiempo de ejecucin, que podra ir a travs de un programa y encontrar todos los lugares a los que llamar , donde y ver exactamente cules son los argumentos que est pasando. A continuacin, puede reemplazar cada llamada a where con una funcin annima que slo hace el clculo necesario. Por ejemplo, si usted encuentra este fragmento de cdigo:
(seleccionar(where:titulo"GiveUsaBreak":arrancot))

que podra cambiar a lo siguiente:


(seleccionar #'(Lambda(cd) (and(equal(getfcd:titulo)"GiveUsaBreak") (equal(getfcd:arranco)t))))

Tenga en cuenta que la funcin annima es diferente de la que where podra haber devuelto, usted no est tratando de salvar a la llamada a where, pero en lugar de ello, proporciona una funcin de seleccin ms eficiente. Esta funcin annima tiene clusulas slo para los campos que realmente le preocupan en este sitio de la llamada, por lo que no hace ningn trabajo extra en la forma en que lo hara una funcin devuelta where. Usted probablemente podra imaginarse pasar por el todo el cdigo fuente y arreglar todas las llamadas a where de esta manera. Pero es probable que tambin pueda imaginarse que sera un enorme trabajo. Si hubiera un nmero suficiente de ellas, y esto fuera lo suficientemente importante, incluso podra ser til escribir algn tipo de preprocesador que convierta las llamadas a where al cdigo que tendras que escribir a mano. La funcin de Lisp que hace a esto trivialmente fcil es su sistema de macro. No puedo enfatizar lo suficiente que las macros de Common Lisp comparten nada mas que el nombre con las macros de C y C++. Cuando el pre-procesador C funciona por la sustitucin de texto y entiende casi nada de la estructura de C y C++, una macro Lisp es esencialmente un generador de cdigo que se ejecuta automticamente por el compilador. 12 Cuando una expresin Lisp contiene una llamada a una macro, en lugar de evaluar los argumentos y pasar

11Es

improbable que el costo de comprobar si los parmetros de palabra clave haba sido aprobada sera una carga

detectable en el rendimiento ya comprobar si una variable es NIL va a ser bastante barato. Por otro lado, estas funciones devuelto por donde van a estar en el centro del bucle interno de cualquier seleccionar , actualizar o eliminar filas- llamada, ya que tienen que ser llamado una vez a la entrada en la base de datos. De todos modos, a ttulo ilustrativo, esto tendr que hacer.

a la funcin, el compilador de Lisp pasa los argumentos, sin evaluar, con el cdigo de macro, que devuelve una nueva expresin Lisp que luego es evaluado en el lugar de la llamada a la macro original. Voy a empezar con un ejemplo simple y tonto, y mostrar como se puede reemplazar la funcin where con una macro. Antes de que pueda escribir este ejemplo, necesito introducir rpidamente una nueva funcin: REVERSE que toma una lista como argumento y devuelve una nueva lista que es su reverso. Por lo tanto (reverse '(1 2 3)) se evala como (3 2 1). Ahora vamos a crear una macro.
(Defmacrorevertir(expr)(reverseexpr))

La principal diferencia sintctica entre una funcin y una macro es que se define una macro con defmacro lugar de DEFUN. La definicin de una macro se compone de un nombre, al igual que una funcin, una lista de parmetros, y un conjunto de expresiones, asi como una funcin. Sin embargo, una macro tiene un efecto totalmente diferente. Puede utilizar esta macro de la siguiente manera:
CLUSUARIO>(revertir("hola,mundo"tformat)) hola,mundo NIL

Cmo funciona? Cuando el REPL comienza a evaluar la expresin revertir, reconoce que revertir es el nombre de una macro. Por lo que sale de la expresin ("hola, mundo" t format) sin evaluar, lo cual es bueno porque no es una forma jurdica Lisp. Entonces pasa esa lista al cdigo de revertir. El cdigo en revertir pasa la lista a REVERSE, que regresa la lista (formatt"hola,mundo"). revertir pasa ese valor de regreso al REPL, que luego se evala en el lugar de la expresin original. La macro revertir define as un nuevo lenguaje que se parece mucho a Lisp - slo que hacia atrs - que se puede usar en cualquier momento simplemente envolviendo una expresin Lisp al revs en una llamada a la macro revertir. Y, en un programa Lisp compilado, ese nuevo lenguaje es tan eficiente como Lisp normal porque todo el cdigo de macro - el cdigo que genera la nueva expresin - se ejecuta en tiempo de compilacin. En otras palabras, el compilador genera exactamente el mismo cdigo si usted escribe (revertir("holamundo" tformat)) o (formatt"hola,mundo"). Entonces, cmo ayuda esto con la duplicacin de cdigo en where? Bueno, usted puede escribir una macro que genere exactamente el cdigo que necesita para cada llamada en particular a where. Una vez ms, el mejor enfoque es la construccin de nuestro cdigo de abajo hacia arriba. En el selector de funciones optimizado, que tena una expresin de la siguiente forma para cada campo real contemplado en la llamada original a where:
(equal(getfcdcampo)valor) 12 Las macros son tambin dirigidos por el intrprete - sin embargo, es ms fcil de entender el punto de macros cuando se
piensa acerca de cdigo compilado. Al igual que con todo lo dems en este captulo, me ocupar de esto en mayor detalle en captulos posteriores.

As que vamos a escribir una funcin que, dado el nombre de un campo y un valor, devuelve una expresin. Dado que una expresin es slo una lista, se podra pensar que podra escribir algo como esto:
(defunhacercomparacionexpr(campovalor);incorrecto (listequal(listgetfcdcampo)valor))

Sin embargo, hay un truco: como usted sabe, cuando Lisp ve un nombre simple, como el campo o valor que no sea como el primer elemento de una lista, se asume que es el nombre de una variable y busca su valor. Eso est bien para el campo y el valor, que es exactamente lo que se quiere. Pero trataremos igual, getf, y cd de la misma manera, que no es lo que queremos. Sin embargo, tambin sabemos como dejar la evaluacin desactivada en Lisp: con una comilla simple delante ('). As que si usted escribe la funcin hacercomparacionexpr como esta, har lo que desea:
(defunhacercomparacionexpr(campovalor) (list'equal(list'getf'cdcampo)valor))

Se puede probar en el REPL.


CLUSUARIO>(hacercomparacionexpr:Nota10) (EQUAL(GETFCD:CALIFICACIN)10) CLUSUARIO>(makecomparacinexpr:ttulode"GiveUsaBreak") (EQUAL(GETFCD:TTULO)"GiveUsaBreak")

Resulta que hay una forma mejor de hacerlo. Lo que realmente me gustara es una manera de escribir una expresin que no es evaluada en su mayora y tener alguna forma de seleccionar algunas expresiones que se desea evaluar. Y, por supuesto, no es slo un mecanismo. Una comilla inclinada hacia adelante (`) antes de una expresin detiene la evaluacin tal como lo hace una comilla simple.
CLUSUARIO>`(123) (123) CLUSUARIO>'(123) (123)

Sin embargo, en una expresin con comilla hacia adelante, cualquier subexpresin que sea precedida por una coma se evala. Observar el efecto de la coma en la segunda expresin:
`(12(+12))==>(12(+12)) `(12,(+12))==>(123)

Utilizando comilla hacia adelante, usted puede escribir hacercomparacionexpr de esta manera:
(defunhacercomparacionexpr(campovalor) `(equal(getfcd,campo),valor))

Ahora bien, si uno mira hacia atrs a la funcin de seleccin optimizada a mano, se puede ver que el cuerpo de la funcin consisti en una expresin de comparacin por pares campo / valor, todo ello envuelto en una expresin and. Por el momento, supongamos que usted va a organizar los argumentos de la macro where para ser pasados como una lista simple. Usted necesitar una funcin que pueda tomar los elementos de una lista de pares y recoger los

resultados de la llamada a hacercomparacionexpr para cada par. Para llevar a cabo esa funcin, usted puede sumergirse en la bolsa de trucos avanzados de Lisp y sacar a los fuerte y poderosa macro LOOP.
(defunhacercomparacionlista(campos) (loopwhilecampos collecting(hacercomparacionexpr(popcampos)(popcampos))))

Una discusin completa de LOOP tendr que esperar hasta el captulo 22, por ahora slo tenga en cuenta que esta expresin LOOP es exactamente lo que necesita: se realiza un bucle mientras haya elementos que quedan en el campo de lista, apareciendo de dos en dos, ejecutando hacer-comparacin-expr, y recogiendo los resultados que se devuelven al final del bucle. La macro POP realiza la operacin inversa de macro PUSH que se utiliza para meter registros a *db*. Ahora slo necesitas envolver la lista devuelta por hacer-comparacion-lista en una funcin AND y en una funcin annima, lo cual puedes hacerlo en la macro where misma. Utilizando una comilla hacia adelante de nuevo para hacer una plantilla que tu puedes rellenar interpolando el valor de hacercomparacionlista, esto es trivial.
(defmacrowhere(&restclauses) `#'(lambda(cd)(and,@(hacercomparacionlistaclauses))))

Esta macro utiliza una variante de , (nombrada como ,@) antes de la llamada a hacer comparacionlista. La ,@ "empalma" el valor de la siguiente expresin - que debe evaluar como una lista - en la lista encerrada. Usted puede ver la diferencia entre , y ,@ en las dos expresiones siguientes:
`(and ,(list 1 2 3)) (AND (1 2 3)) `(and ,@(list 1 2 3)) (AND 1 2 3)

Tambin se puede utilizar ,@ para empalmar en el medio de una lista.


`(and ,@(list 1 2 3) 4) (AND 1 2 3 4)

La otra caracterstica importante de la macro where es el uso de &rest en la lista de argumentos. Como &key, &rest modifica la forma de los argumentos que se analizan. Con un &rest en su lista de parmetros, una funcin o macro puede tomar un nmero arbitrario de argumentos, que se recogen en una lista nica que se convierte en el valor de la variable cuyo nombre sigue a &rest . De esta manera usted llama a where:
(where:titulo"GiveUsaBreak":arrancot)

la variable clauses contendr la lista.


(:titulo"GiveUsaBreak":arrancot)

Esta lista se pasada a hacercomparacionlista, que devuelve una lista de expresiones de comparacin. Usted puede ver exactamente lo que el cdigo de una llamada a where va a generar con la funcin MACROEXPAND1. Si pasa MACROEXPAND1, una forma que representa una

llamada a la macro, se llamar al cdigo de macro con los argumentos adecuados y devolver la expansin. As que usted puede verificar las llamadas a where anteriores, como esta:
CLUSER>(macroexpand1'(where:titulo"GiveUsaBreak":arrancot)) #'(LAMBDA(CD) (AND(EQUAL(GETFCD:TITULO)"GiveUsaBreak") (EQUAL(GETFCD:ARRANCO)T))) T

Se ve bien. Vamos a intentarlo de verdad.


CLUSER>(seleccionar(where:titulo"GiveUsaBreak":arrancot)) ((:TITULO"GiveUsaBreak":ARTISTA"Limpopo":VALORACION10:ARRANCOT))

Funciona. Y la macro where con sus dos funciones de ayuda es en realidad una lnea ms corta que la funcin where anterior. Y es ms general en que ya no est vinculada a los campos especficos en nuestros registros de CD.

Conclusin
Ahora, una cosa interesante ha sucedido. Ha eliminado la duplicacin e hizo que el cdigo sea ms eficiente y ms general, al mismo tiempo. Eso es a menudo la manera de hacerlo con una macro bien elegida. Esto tiene sentido porque una macro es solo otro mecanismo para la creacin de abstracciones - la abstraccin en el nivel sintctico, y las abstracciones son formas de definicin ms concisa de expresar generalidades subyacentes. Ahora, el cdigo en la minibase de datos que es especfico para los CD y los campos en ellos est en las funciones hacer cd, agregaruncd , y agregarcds. De hecho, nuestro nueva macro trabajara con cualquier base de datos basada en plist. Sin embargo, esto dista mucho de ser una base de datos completa. Usted probablemente puede pensar en un montn de caractersticas para agregar, como soportar mltiples tablas o consultas ms elaboradas. En el captulo 27 vamos a construir una base de datos MP3 que incorpora algunas de esas caractersticas. El punto de este captulo fue para darle una breve introduccin a un puado de caractersticas de Lisp y mostrar cmo se utilizan para escribir cdigo que sea un poco ms interesante que "hola, mundo". En el prximo captulo vamos a empezar una visin ms sistemtica de Lisp.

4. Sintaxis y semntica
Despus de que la gira relmpago, vamos a detenernos por un par de captulos a examinar de forma ms sistemtica las caractersticas que usted ha utilizado hasta ahora. Voy a empezar con un resumen de los elementos bsicos de la sintaxis de Lisp y la semntica, lo que significa, por supuesto, que primero debe abordar esa cuestin candente. . .

Qu pasa con todos los parntesis?


La sintaxis de Lisp es un poco diferente de la sintaxis de las lenguas descendientes de Algol. Las dos caractersticas ms obvias son el amplio uso de los parntesis y la notacin de prefijo. Por alguna razn, muchas personas se desaniman por la sintaxis. Los detractores de Lisp tienden a describir la sintaxis como "rara" y "molesta". Lisp, dicen, se llama asi por Lotes de Irritantes y Superfluos Parntesis. La gente de Lisp, por otro lado, tiende a considerar un Lisp sintaxis de sus grandes virtudes. Cmo es que lo que es tan desagradable para un grupo es una fuente de placer a otro? No puedo tratar realmente en forma completa la sintaxis de Lisp antes de explicar las macros un poco ms a fondo, pero puedo comenzar con un dato histrico que sugiere que puede vale la pena mantener la mente abierta: cuando John McCarthy invent Lisp, tena la intencin de implementar una sintaxis parecida al Algol, a la que l llam M-expresiones. Sin embargo, nunca lleg a hacerlo. Explic por qu no en su artculo "Historia de Lisp." 1

El proyecto de la definicin de M-expresiones y su compilacin o al menos su traduccin en S-expresiones no se finaliz ni se abandon explcitamente. Slo retrocedi en un futuro indefinido, y aparecin una nueva generacin de programadores que prefiri las [S-expresiones] a cualquier notacin parecida a FORTRAN o ALGOL que podra ser concebida.
En otras palabras, las personas que han utilizado Lisp en los ltimos 45 aos han gustado de la sintaxis y han descubierto que esta hace que el lenguaje sea ms poderoso. En los prximos captulos, usted comenzar a entender por qu.

Romper la Caja Negra


Antes de examinar los detalles de la sintaxis de Lisp y la semntica, vale la pena tomar un momento para mirar cmo se define y cmo se diferencia de otros lenguajes. En la mayora de los lenguajes de programacin, el procesador de lenguaje - si hay un intrprete o un compilador - funciona como una caja negra: tu colocas una secuencia de

1http://www-formal.stanford.edu/jmc/history/lisp/node3.html

caracteres que representa el texto de un programa en la caja negra, y - dependiendo de si se trata de una intrprete o un compilador - o bien ejecuta los comportamientos indicados o produce una versin compilada del programa que ejecutar los comportamientos cuando se ejecuta. Dentro de la caja negra, por supuesto, los procesadores de lenguaje se dividen en subsistemas en los cuales cada uno responsable de una parte de la tarea de traducir un texto del programa en el comportamiento o el cdigo objeto. Una divisin tpica es la de dividir el procesador en tres fases, cada una de las cuales se ocupan de lo siguiente: un analizador lxico rompe las cadenas de caracteres en smbolos y alimenta a un analizador que genera un rbol que representa las expresiones en el programa, de acuerdo con el lenguaje de la gramtica. Este rbol - llamado rbol sintctico abstracto - alimenta luego a un evaluador que, interpreta directamente o compila en algn otro idioma como el cdigo de la mquina. Debido a que el procesador de lenguaje es una caja negra, las estructuras de datos utilizadas por el procesador, como los smbolos y los rboles sintcticos abstractos, son importantes slo para el implementador del lenguaje. En Common Lisp las cosas se comportan un poco diferente, con consecuencias tanto para el implementador y por cmo el lenguaje se define. En lugar de una simple caja negra que va desde el texto al comportamiento del programa en un solo paso, Common Lisp define dos cajas negras, una que traduce el texto en objetos de Lisp, y otra que implementa la semntica del lenguaje en los trminos de esos objetos. La primera caja es llamada el lector, y la segunda es llamada el evaluador.2 Cada caja negra define un nivel de sintaxis. El lector define como las cadenas de caracteres se pueden traducir en objetos de Lisp llamados s-expresiones.3 Debido a que la sintaxis de la sexpresin comprende la sintaxis de las listas de objetos arbitrarios, incluyendo otras listas, las s-expresiones pueden representar expresiones de rboles arbitrarias, asi como el rbol sintctico abstracto generado por los programas de anlisis para otros lenguajes no Lisp. El evaluador define una sintaxis de formas Lisp que puede construirse a partir de las sexpresiones. No todas las s-expresiones son formas legales Lisp pero todas las secuencias de caracteres cualesquiera son s-expresiones legales. Por ejemplo, (foo 1 2) y ("foo" 1 2) ambas son s-expresiones, pero slo la primera puede ser una forma Lisp debido a que una lista que comienza con una cadena no tiene sentido como una forma de Lisp.

Los implementadores de Lisp, como los implementadores de cualquier lenguaje, tienen muchas maneras de poner en

prctica un evaluador, que van desde un "puro" intrprete que interpreta los objetos dados al evaluador directamente a un compilador que traduce los objetos en cdigo mquina que posteriormente ejecuta. En el medio estn las implementaciones que compilan la entrada en una forma intermedia, como cdigos para una mquina virtual y luego interpretan los cdigos. La mayora de las implementaciones de Common Lisp en estos das usan algn tipo de compilacin, incluso cuando la evalan el cdigo en tiempo de ejecucin.

3 Algunas veces la frase s-expresin se refiere a la representacin textual y otras veces a los objetos que resultan de la lectura
de la representacin textual. Por lo general, est claro por el contexto en que es entendida o bien la distincin no es tan importante.

Esta divisin de la caja negra tiene un par de consecuencias. Una de ellas es que se puede utilizar s-expresiones, como se vio en el captulo 3, como un formato de datos externalizables para datos que no son cdigo fuente, utilizando READ para leer y PRINT para imprimir.4 La otra consecuencia es que, dado que la semntica del lenguaje se define en trminos de rboles de objetos en lugar de cadenas de caracteres, es ms fcil generar el cdigo en el lenguaje de lo que sera si tuviera que generar el cdigo como texto. La generacin de cdigo desde cero es slo marginalmente ms fcil - la construccin de listas frente a la construccin de cadenas es aproximadamente la misma cantidad de trabajo. La ganancia real, sin embargo, esta en que se puede generar cdigo mediante la manipulacin de los datos existentes. Esta es la base para las macros de Lisp, que voy a discutir con ms detalle en captulos posteriores. Por ahora me centrar en los dos niveles de sintaxis definido por Common Lisp: la sintaxis de las sexpresiones entendido por el lector y la sintaxis de las formas Lisp entendido por el evaluador.

S-expresiones
Los elementos bsicos de s-expresiones son listas y tomos. Las listas estn delimitadas por parntesis y pueden contener cualquier nmero de elementos separados por espacios. Los tomos son todo lo dems.5 Los elementos de las listas son ellos mismos s-expresiones (en otras palabras, tomos o listas anidadas). Los comentarios - que no son, tcnicamente hablando, s-expresiones - comienzan con punto y coma, se extienden hasta el final de una lnea, y son tratados esencialmente como espacios en blanco. Y eso es ms o menos lo mismo. Debido a que las listas son sintcticamente tan triviales, las nicas reglas sintcticas que necesitas conocer son las que rigen la forma de diferentes tipos de tomos. En esta seccin voy a describir las reglas para los tipos ms utilizados de tomos: nmeros, cadenas, y nombres. Despus de eso, voy a cubrir como las s-expresiones compuestas de estos elementos pueden ser evaluados como formas Lisp. Los nmeros son bastante sencillos: cualquier secuencia de dgitos - posiblemente precedido de un signo (+ o -), conteniendo un punto decimal (.) o una barra invertida (/), o terminar con un marcador de exponente - se lee como un nmero. Por ejemplo:
123;elnmeroenterocientoveintitrs 7.3;laproporcindetressptimos 1.0;elnmerodepuntoflotantedeprecisinenundefecto 1.0e0;otraformadeescribirelmismonmerodepuntoflotante 1.0d0;elnmerodepuntoflotanteenel"doble"deprecisin 1.0e4;elequivalentedepuntoflotanteaundiezmilsima 42;elnmeroenterocuarentaydos 42;Elenteronegativocuarentaydos 1/4;larelacinnegativadeunacuartaparte 2/8;otraformadeescribirnegativosdeunacuartaparte 246/2;otraformadeescribirelenterocientoveintitrs

4No todos los objetos de Lisp se pueden escribir de una manera que puede ser leda de nuevo. Pero cualquier cosa que usted
puede LEER READ se puede imprimir de vuelta "de manera legible" con PRINT.

5 La lista vaca, (), que tambin se puede escribir NIL, es a la vez un tomo y una lista.

Estas diferentes formas representan diferentes tipos de nmeros: enteros, proporciones y de punto flotante. Lisp tambin es compatible con los nmeros complejos, que tienen su propia notacin y que voy a discutir en el captulo 10. Como algunos de estos ejemplos, se puede anotar el mismo nmero de muchas maneras. Pero independientemente de cmo se escriben, todos los nmeros racionales - enteros y fracciones - se representan internamente en forma "simplificada". En otras palabras, los objetos que representan -2/8 o 246 /2 no son distintos de los objetos que representan -1/4 y 123. De manera similar, 1,0 y 1.0e0 son slo diferentes formas de escribir el mismo nmero. Por otro lado, 1.0, 1.0d0, y 1 pueden todos denotar distintos objetos porque las diferentes representaciones de punto flotante y enteros son de diferentes tipos. Vamos a guardar los detalles sobre las caractersticas de los diferentes tipos de nmeros para el captulo 10. Los literales de cadenas, como se vio en el captulo anterior, se encierran entre comillas dobles. Dentro de una cadena una barra invertida ( \ ) se traslada al siguiente carcter, haciendo que se incluya lo que sigue en la cadena, independientemente de lo que sea. Los dos nicos caracteres que deben estar fuera de una cadena son las comillas dobles y la barra invertida misma. Todos los dems caracteres pueden ser incluidos en una cadena literal, sin estar independientemente de su significado fuera de la cadena. Algunas ejemplos de cadenas literales son los siguientes:
"foo";lacadenaquecontieneloscaracteresf,o,yo. "fo\o";lamismacadena "fo\\o";lacadenaquecontieneloscaracteresf,o,\,yo. "fo\"o";lacadenaquecontieneloscaracteresf,o,"yo.

Los nombres usados en los programas de Lisp, como FORMAT y hola-mundo, y *db* son representados por objetos llamados smbolos. El lector no sabe nada acerca de cmo un nombre que se da va a ser utilizado - ya sea el nombre de una variable, una funcin, o algo ms. Slo lee una secuencia de caracteres y construye un objeto para representar el nombre. 6 Casi cualquier caracter puede aparecer en un nombre. Sin embargo, los espacios en blanco no pueden, porque los elementos de las listas estn separadas por espacios en blanco. Los dgitos pueden aparecer en los nombres, siempre y cuando el nombre en su conjunto no pueda ser interpretado como un nmero. Del mismo modo, los nombres pueden contener puntos, pero el lector no puede leer un nombre que conste slo de puntos. Diez caracteres que sirven a otros fines sintcticos no pueden aparecer en los nombres: abrir y cerrar parntesis, las comillas dobles y simples, de acento grave, coma, dos puntos, punto y coma, la barra invertida, y la barra vertical. Incluso estos caracteres pueden usarse, si usted est dispuesto a usarlos precediendo el caracter con una barra invertida o colocando barras verticales alrededor del nombre. Dos caractersticas importantes de la forma en que el lector traduce los nombres a los objetos smbolos tienen que ver con la forma en que trata el caso de las letras en los nombres y cmo se asegura de que el mismo nombre se lea siempre como el mismo smbolo. Durante la lectura
6 De hecho, como veremos ms adelante, los nombres no estn intrnsecamente enlazadas a cualquier cosa. Usted puede utilizar el mismo nombre, dependiendo del contexto, para referirse tanto a una variable y una funcin, para no hablar de otras posibilidades.

de los nombres, el lector convierte todos los caracteres en un nombre con sus equivalentes en maysculas. As, el lector leer foo , FOO y Foo como el mismo smbolo: FOO. Sin embargo, \f \o\o y |foo| sern ledos como foo, que es un objeto diferente al smbolo FOO. Por eso, cuando se define una funcin en el REPL e imprime el nombre de la funcin, se convierte en maysculas. El estilo estndar, en estos das, es escribir el cdigo en minsculas y dejar que el lector cambie los nombres a maysculas.7 Para asegurarse de que el mismo nombre textual es ledo siempre como el mismo smbolo, el lector guarda internamente los smbolos - despus de que ha ledo el nombre y lo ha convertido todo en maysculas, el lector buscar en una tabla denominada paquete a un smbolo existente con el mismo nombre. Si no puede encontrar uno, crea un nuevo smbolo y lo aade a la tabla. De lo contrario, devuelve el smbolo que ya est en la tabla. Por lo tanto, en cualquier momento el mismo nombre aparece en cualquier s-expresin, el mismo objeto ser utilizado para representarlo.8 Dado que los nombres pueden contener muchos ms caracteres en Lisp de lo que pueden en lenguajes derivados del Algol, ciertas convenciones de nombres son distintos a Lisp, como el uso de los nombres con guin como hola-mundo. Otra convencin importante es que en las variables globales los nombres que comienzan y terminan con *. Del mismo modo, los nombres de las constantes comienzan y terminan en +. Y algunos de los programadores nombrarn particularmente a las funciones de bajo nivel con nombres que comienzan con % o %%. Los nombres definidos en el lenguaje estndar utilizan slo los caracteres alfabticos (AZ), adems de * , + , - , / , 1 , 2 , < , = , > y &. La sintaxis de listas, nmeros, cadenas y smbolos pueden describir un buen porcentaje de los programas de Lisp. Otras reglas describen las notaciones para vectores literales, caracteres individuales, y matrices, de los que me ocupar cuando hable de los tipos de datos asociados en los captulos 10 y 11. Por ahora, lo fundamental es entender cmo se puede combinar nmeros, cadenas y smbolos con listas delimitadas por parntesis para construir las sexpresiones que representan los rboles de objetos arbitrarios. Algunos ejemplos sencillos:
x () (123) ("Foo""bar") (Xyz) (X1"foo") (+(*23)4) ;elsmboloX ;Lalistavaca ;unalistadetresnmeros ;unalistadedoscadenas ;unalistadetressmbolos ;unalistadeunsmbolo,unnmero,yunacadena ;unalistadeunsmbolo,unalista,yunnmero.

Un ejemplo un poco ms complejo es el siguiente una lista de cuatro elementos que contiene dos smbolos, la lista vaca, y otra lista, que contiene en s misma dos smbolos y una cadena:
(defunholamundo() (formatt"hola,mundo"))

El comportamiento de la conversin automatizada del lector puede, de hecho, cambiarse a medida, pero la comprensin sobre cundo y cmo cambiar requiere una discusin mucho ms profunda de la relacin entre los nombres, smbolos y otros elementos del programa la que estoy listo para entrar en un momento. 8 Voy a discutir la relacin entre los smbolos y los paquetes con ms detalle en el captulo 21.

S-expresiones como formas Lisp


Despus de que el lector ha traducido un montn de texto en s-expresiones, las s-expresiones pueden ser evaluadas como cdigo de Lisp. O algunos de ellos pueden - no todas las sexpresiones que el lector pueda leer necesariamente pueden ser evaluados como cdigo Lisp. Las reglas de evaluacin de Common Lisp definen un segundo nivel de sintaxis que determinan que las s-expresiones pueden ser tratados como formas Lisp. 9 Las reglas sintcticas en este nivel son muy simples. Cualquier tomo - cualquier cosa que no sea lista o la lista vaca - es una forma legal Lisp como cualquier lista que tiene un smbolo como primer elemento. 10 Por supuesto, lo interesante de las formas Lisp no es su sintaxis, sino la forma en que es evaluada. Para propsitos de discusin, usted puede pensar en el evaluador como una funcin que toma como argumento una forma Lisp sintcticamente correcta y devuelve un valor, que podemos llamar el valor de la forma. Por supuesto, cuando el evaluador es un compilador, esto es un poco una simplificacin - en ese caso, al evaluador se le da una expresin y genera el cdigo que calcular el valor apropiado cuando se ejecute. Sin embargo, esta simplificacin me permite describir la semntica de Common Lisp en trminos de cmo los diferentes tipos de formas Lisp son evaluados por esta funcin terica. Las formas ms simples Lisp, los tomos, pueden dividirse en dos categoras: los smbolos y todo lo dems. Un smbolo, evaluado como una forma, es considerado el nombre de una variable y es evaluado como el valor actual de la variable. 11 Voy a discutir en el captulo 6 cmo las variables reciben sus valores en el primer lugar. Tambin debe tener en cuenta que ciertas "variables" son de esa vieja incongruencia de la programacin: "variables constantes". Por ejemplo, el smbolo de PI nombra una variable constante cuyo valor es el mejor punto flotante de aproximacin a la constante matemtica pi . Todos los dems tomos - nmeros y cadenas son tipos que hemos visto hasta ahora son objetos auto-evaluables. Esto significa que cuando tal expresin es pasada a a la funcin de evaluacin terica, es simplemente devuelta. Ya has visto ejemplos de auto-evaluacin de los objetos en el captulo 2 al escribir 10 y "Hola Mundo" en el REPL. Tambin es posible que los smbolos se auto-evalen en el sentido de que en las variables su nombre ser asignado como el valor del smbolo mismo. Dos importantes constantes que se definen de esta manera T y NIL, los valores cannicos de verdadero y falso. Voy a hablar de su papel como booleanos en la seccin "Verdad, falsedad e igualdad." Otra clase de auto-evaluacin de los smbolos son los smbolos de palabras clave - cuyos nombres empiezan por : .Cuando el lector guarda internamente un nombre, automticamente se define una variable constante con el nombre y con el smbolo como valor.

9 Por supuesto, otros niveles de correccin existen en Lisp, como en otros idiomas. Por ejemplo, el s-expresin que resulta de
la lectura (foo 1 2) es sintcticamente bien formada, pero se puede evaluar slo si foo es el nombre de una funcin o macro. 10 Una de otro tipo que rara vez es usada como forma Lisp es una lista cuyo primer elemento es una forma lambda. Voy a discutir este tipo de forma en el captulo 5. 11 Otra posibilidad existe - es posible definir macros smbolo que se evalan de forma ligeramente diferente. No se preocupe por ellos.

Las cosas se ponen ms interesantes cuando se considera cmo se evalan las listas. Todas las formas legales de lista comienzan con un smbolo, pero hay tres tipos de formas de lista que son evaluados en tres formas muy diferentes. Para determinar qu tipo de forma toma una lista, el evaluador debe determinar si el smbolo que inicia la lista es el nombre de una funcin, una macro o un operador especial. Si el smbolo no se ha definido an - como puede ser el caso si usted est compilando el cdigo que contiene referencias a las funciones que sern definidas ms adelante - es asumido como un nombre de funcin 12. Me referir a los tres tipos de formas como formas de llamadas a funcin , formas macro y formas especiales.

Las llamadas a funciones


La regla de clculo para las formas de llamada a funcin es muy sencilla: evaluar los elementos restantes de la lista como formas Lisp y pasar los valores resultantes a la funcin llamada. Esta regla tiene, evidentemente, algunas restricciones sintcticas adicionales en una forma de llamada a funcin: todos los elementos de la lista despus del primero deben estar bien construidos como formas Lisp. En otras palabras, la sintaxis bsica de una forma de llamada a funcin es la siguiente, donde cada uno de los argumentos en s es una forma Lisp:
(nombredefuncinargumento*)

Por lo tanto, la siguiente expresin es evaluada en primer lugar evaluando 1, luego evaluando 2, y luego pasa los valores resultantes a la funcin +, que devuelve 3:
(+12)

Una expresin ms complejas, como las siguientes se evala de forma similar, excepto que la evaluacin de los argumentos (+ 1 2) y (- 3 4) implica primero la evaluacin de sus argumentos y la aplicacin de las funciones propias de ellos:
(*(+12)(34))

Eventualmente, los valores 3 y -1 se pasan a la funcin *, que devuelve -3. Como muestran estos ejemplos, las funciones son usadas para muchas de las cosas que requieren una sintaxis especial en otros idiomas. Esto ayuda a mantener regular la sintaxis de Lisp.

Operadores Especiales
Dicho esto, no todas las operaciones se pueden definir como funciones. Debido a que todos los argumentos a una funcin se evalan antes de que la funcin sea llamada, no hay manera de escribir una funcin que se comporte como el operador IF que usted utiliza en el captulo 3. Para ver por qu, consideremos la siguiente forma:
(ifx(tformat"si")(formatt"no"))

12

En Common Lisp un smbolo puede nombrar tanto a un operador - funcin, macro, u operador especial como a una variable. Esta es una de las principales diferencias entre el Common Lisp y Scheme. La diferencia es a veces descrito como Common Lisp que es un Lisp-2 contra el Scheme que es un Lisp-1 - un Lisp-2 tiene dos espacios de nombre, uno para los operadores y otro para las variables, pero un Lisp-1 utiliza un nico espacio de nombre. Ambas opciones tienen sus ventajas, y los partidarios de uno y otro pueden debatir interminablemente cual es mejor.

Si IF fuera una funcin, el evaluador evaluara las expresiones argumento de izquierda a derecha. El smbolo x sera evaluado como una variable produciendo algn valor, luego (format t "si") se evala como una llamada a funcin, dando NIL despus de la impresin de "si" a la salida estndar. Entonces (en formato t "no") se evaluara, imprimiendo "no" y tambin devolviendo NIL. Slo despus de las tres expresiones que se evaluaron los valores resultantes se pasarn al IF, demasiado tarde para que se controle cual de los dos expresiones FORMAT se evala. Para resolver este problema, Common Lisp define una docena de los llamados operadores especiales, IF es uno de ellos, que hacen cosas que las funciones no pueden hacer. Hay 25 en total, pero slo un puado se utilizan directamente en la programacin del da a da. 13 Cuando el primer elemento de una lista es un smbolo que designa a un operador especial, el resto de las expresiones se evalan de acuerdo a la regla para ese operador.

La norma correspondiente a IF es bastante fcil: evaluar la primera expresin. Si se evala como no- NIL, evaluar la siguiente expresin y devolver su valor. En caso contrario, devolver el valor de la evaluacin de la tercera expresin o NIL si la tercera expresin se omite. En otras palabras, la forma bsica de un expresin IF es la siguiente:
(ifformacondicinformaentonces[formasino])

La forma-condicin siempre ser evaluada y luego uno u otro de la forma-entonces o de la forma forma-sino. Un operador especial an ms simple es QUOTE, que tiene una sola expresin como "argumento" y simplemente devuelve, sin evaluar. Por ejemplo, lo siguiente evala la lista (+ 1 2), no el valor 3:
(quote(+12))

No hay nada especial acerca de esta lista, se puede manipular como cualquier lista que se pueda crear con la funcin LIST.14 QUOTE es usada comunmente con una sintaxis especial para que se construye en el lector. En lugar de escribir lo siguiente:
(quote(+12))

usted puede escribir lo siguiente:


'(+12)

13 Los otros proporcionan caractersticas tiles, pero un tanto esotricas. Voy a hablar de las caractersticas que soportan mas adelante. 14 Pues bien, existe una diferencia los objetos literales como las listas con comillas, incluyendo las cadenas con comillas dobles, las matrices literales, y los vectores (cuya sintaxis se ver ms adelante), no deben ser modificados. En consecuencia, ninguna lista que desees manipular debe crearse con LIST.

Esta sintaxis es una pequea extensin de la sintaxis de s-expresin entendida por el lector. Desde el punto de vista del evaluador, ambas expresiones tienen el mismo aspecto: una lista cuyo primer elemento es el smbolo QUOTE y cuyo segundo elemento es la lista (+ 1 2).15 En general, los operadores especiales implementan caractersticas del lenguaje que requieren algn tratamiento especial por parte del evaluador. Por ejemplo, varios operadores especiales manipulan el entorno en el que otras formas sern evaluadas. Una de ellas, que voy a discutir en detalle en el captulo 6, es LET, que se utiliza para crear nuevas asignaciones de variables. La forma siguiente se evala a 10 porque el segundo x se evala en un entorno donde es el nombre de una variable establecida por LET con el valor 10:
(Let((x10))x)

Macros
Mientras que los operadores especiales extienden la sintaxis de Common Lisp ms all de lo que solo puede expresarse con las llamadas a funciones, el conjunto de operadores especiales es fijado por el lenguaje estndar. Las Macros, por otro lado, dan a los usuarios de la lengua una forma de extender su sintaxis. Como se vio en el captulo 3, una macro es una funcin que toma s-expresiones como argumentos y devuelve una expresin Lisp, que luego es evaluada en el lugar de la forma macro. La evaluacin de una forma macro procede en dos fases: En primer lugar, los elementos de la forma macro se pasan, sin evaluar, a la funcin macro. En segundo lugar, la forma devuelta por la funcin macro - llamada su expansin - se evala de acuerdo a las reglas de evaluacin normal. Es importante mantener las dos fases de la evaluacin de una forma macro claro en su mente. Es fcil perder la cuenta cuando ests escribiendo expresiones en el REPL porque las dos fases suceden una tras otra y el valor de la segunda fase se devuelve inmediatamente. Sin embargo, cuando se compila el cdigo Lisp, las dos fases ocurren en momentos completamente diferentes, por lo que es importante tener claro cuando est pasando esto. Por ejemplo, cuando se compila un archivo completo de cdigo fuente con la funcin COMPILE-FILE, todas las formas macro en el archivo se expanden de forma recursiva hasta que el cdigo se compone de nada ms que formas de llamada de funcin y formas especiales. Este cdigo sin macros se compila luego en un archivo FASL que la funcin LOAD sabe cmo cargar. El cdigo compilado, sin embargo, no se ejecuta hasta que se carga el archivo. Debido a que las macros generan su expansin en tiempo de compilacin, que pueden hacer cantidades relativamente grandes de trabajo de su expansin sin tener que pagar por ello, cuando se carga el archivo o las funciones definidas en el archivo son llamadas. Dado que el evaluador no evala los elementos de la forma de macro antes de pasarlos a la funcin de macro, no es necesario que sean formas Lisp correctas. Cada macro asigna un significado a la s-expresiones en forma macro, en virtud de cmo se los utiliza para generar su 15 Esta sintaxis es un ejemplo de una macro lector . La Macro lector modifica la sintaxis del lector que se utiliza para traducir
el texto en objetos de Lisp. Es, de hecho, posible definir tu propia macro lector, pero eso es una facilidad que rara vez se utiliza en la lengua. Cuando la mayora de Lispers hablan de "ampliacin de la sintaxis" de la lengua, estn hablando de macros regulares, como lo veremos en un momento.

expansin. En otras palabras, cada una macro define su propia sintaxis local. Por ejemplo, la macro REVERSE del captulo 3 define una sintaxis en la que una expresin es una forma legal hacia atrs si se trata de una lista que es el reverso de una forma jurdica Lisp. Voy a hablar un poco ms acerca de las macros en el libro. Por ahora, lo importante es darse cuenta de que las macros - mientras que sintcticamente son similares a las llamadas a funciones - sirven a una finalidad muy distinta, proporcionando un gancho dentro del compilador.16

Verdad, falsedad e Igualdad


Dos ltimas cosas de conocimientos bsicos usted necesita para ponerse el cinturn son la nocin de Common Lisp de verdad y falsedad y lo que significa que dos objetos de Lisp sean "iguales". La verdad y la falsedad son - en este campo - claro: el smbolo NIL es el nico valor falso, y todo lo dems es cierto. El smbolo T es el valor de verdad cannico y se puede utilizar cuando se necesita devolver un valor distinto de NIL y no se tiene otra cosa a mano. Lo nico complicado de NIL es que es el nico objeto que es tanto un tomo y una lista: adems de valor falso, tambin se usa para representar la lista vaca. 17 Esta equivalencia entre NIL y la lista vaca se construye en el lector: si el lector ve (), lee como el smbolo NIL. Son completamente intercambiables. Y debido a que NIL, como he mencionado anteriormente, es el nombre de una variable constante con el smbolo NIL como su valor, las expresiones nil, (), 'nil , y '() todas evalan la misma cosa las formas sin comillas son evaluadas como una referencia a la variable constante cuyo valor es el smbolo NIL, pero en las formas con el operador especial QUOTE se evala el smbolo directamente. Por la misma razn, tanto t como 't van a evaluarse a la misma cosa: el smbolo T.

16 Personas sin experiencia en el uso de macros Lisp o, peor an, teniendo las cicatrices del preprocesador de C que le han infligido heridas, tienden a ponerse nerviosos cuando se dan cuenta que las llamadas a macro parecen llamadas a funciones regulares. Esto resulta no ser un problema en la prctica por varias razones. Una de ellas es que las formas macro suelen ser un formato diferente de las llamadas a funciones. Por ejemplo, puede escribir lo siguiente: (dolist (x foo) (print x)) ms que esto: (dolist (x foo) (print x)) o (dolist (x foo) (print x)) la forma en que lo hara si DOLIST fuera una funcin. Un buen entorno Lisp dar formato automticamente a las llamadas macro correctamente, incluso para las macros definidas por el usuario. E incluso si una forma DOLIST que estaba escrita en una sola lnea, hay varias pistas de que se trata de una macro: Por un lado, la expresin (x foo) es significativa por s misma slo si x es el nombre de una funcin o macro. Combine eso con la aparicin posterior de x como una variable, y es bastante sugerente que DOLIST es una macro que est creando un enlace para una variable llamada x . Convenciones de nombres tambin ayudan en construcciones de bucles, que son siempre las macros - se dan con frecuencia los nombres que comienzan con do.

17

El uso de la lista vaca como falso es un reflejo de la herencia de Lisp como un lenguaje de procesamiento de listas as como el uso de el nmero entero 0 como falso en C es un reflejo de su herencia como una lengua poco personalizable. No todos los valores booleanos Lisp se manejan de la misma manera. Otra de las muchas diferencias sutiles entre Common Lisp y Scheme cuyo fuego de guerra puede arder durante das es el uso en Scheme de un valor falso distintivo #f, que no es el mismo valor que el smbolo nil o la lista vaca, lo que a su vez, es distinto uno de otro.

Utilizando frases como "lo misma cosa", por supuesto, plantea la cuestin de lo que significa para los dos valores ser "lo mismo". Como se ver en los prximos captulos, Common Lisp proporciona una serie de predicados la igualdad de tipo especfico: = se utiliza para comparar los nmeros, CHAR= para comparar caracteres, y as sucesivamente. En esta seccin hablaremos de los cuatro predicados "genricos" de igualdad funciones a las que se pueden pasar dos objetos Lisp y devolver verdadero si son equivalentes, y falso en caso contrario. Ellos son, por orden de discriminacin, EQ , EQL , EQUAL , y EQUALP. EQ pruebas la "identidad de objeto" - dos objetos son EQ si son idnticos. Por desgracia, la identidad del objeto de los nmeros y los caracteres depende de cmo los tipos de datos son implementados en un Lisp particular. Por lo tanto, EQ pueden considerar dos nmeros o caracteres de dos con el mismo valor que es equivalente, o puede que no. Las Implementaciones tienen libertad de accin suficiente como para que la expresin (eq 3 3) legalmente puede evaluar a verdadero o falso. Ms al punto, (eq x x) se puede evaluar como verdadero o falso si el valor de x pasa a ser un nmero o un carcter. Por lo tanto, nunca debe usar EQ para comparar los valores que pueden ser nmeros o caracteres. Puede parecer que trabajar de una manera predecible para ciertos valores en una aplicacin particular, pero que no tienen ninguna garanta de que funcionar de la misma manera, si cambia las implementaciones. Y las implementaciones de cambio puede significar simplemente actualizar su puesta en prctica de una nueva versin - si cambia su ejecutor Lisp cmo se representan los nmeros o caracteres, el comportamiento de EQ podra muy bien cambiar tambin. Por lo tanto, Common Lisp define EQL para comportarse como EQ, excepto que tambin se garantiza que considerar dos objetos de la misma clase que representa el mismo valor numrico o un carcter equivalente. Por lo tanto, (EQL 1 1) se garantiza que sea verdad. Y (EQL 1 1.0) se garantiza que sea falso, ya que el valor entero 1 y el valor de punto flotante son instancias de clases diferentes. Hay dos escuelas de pensamiento acerca de cundo utilizar EQ y cundo usar EQL: El "uso EQ cuando sea posible " se sostiene que puedes usar EQ cuando usted sabe que no van a estar comparando nmeros o caracteres, porque (a) es una manera de indicar que no se van a comparar los nmeros o caracteres, y (b) ser marginalmente ms eficiente desde que EQ no tiene que comprobar si sus argumentos son nmeros o caracteres. "Utilizar siempre EQL " se dice que no se debe usar EQ, porque (a) la ganancia potencial en claridad se pierde porque cada vez que alguien lee su cdigo - incluido usted - ve un EQ, que tienen que parar y comprobar si se est se usa correctamente (en otras palabras, que nunca va a ser llamado a comparar los nmeros o caracteres) y (b) que la diferencia de rendimiento entre EQ y EQL es despreciable en comparacin con los cuellos de botella de rendimiento real. El cdigo de este libro est escrito en el estilo "usar siempre EQL ".18

18 Incluso el estndar del lenguaje es un poco ambivalente acerca de cul de EQ o EQL se debe preferir. identidad de objeto se
define por EQ, pero la norma define la frase de la misma cuando se habla de los objetos en el sentido de EQL a menos que otro predicado se menciona explcitamente. Por lo tanto, si quieres estar al 100 por ciento tcnicamente correcto, se puede decir que (- 3 2) y (- 4 3) evaluar "al mismo" objeto, pero no que evalan a "idnticos" objetos. Esta es, sin duda, un tipo de problema

Los otros dos predicados la igualdad, la EQUAL y EQUALP, son de carcter general en el sentido de que pueden operar en todo tipo de objetos, pero son mucho menos fundamentales que EQ o EQL. Cada uno de ellos define un concepto un poco menos exigente que la equivalencia de EQL, permitiendo diferentes objetos que se consideran equivalentes. No hay nada especial acerca de las concepciones particulares de la equivalencia de estas funciones, excepto la aplicacin que han demostrado ser muy tiles por los programadores de Lisp en el pasado. Si estos predicados no se ajustan a sus necesidades, siempre se puede definir su propia funcin de predicado que compara los diferentes tipos de objetos en la forma en que usted necesite. EQUAL afloja la discriminacin de EQL al considerar las listas equivalentes si tienen la misma estructura y contenido, de forma recursiva, de acuerdo con EQUAL. EQUAL tambin considera las cadenas equivalentes si contienen los mismos caracteres. Tambin define una definicin ms laxa de la equivalencia de EQL para los vectores de nombres de ruta, dos tipos de datos que voy a discutir en los prximos captulos. Para todos los otros tipos, que vuelve a caer en EQL . EQUALP es similar a la EQUAL, excepto que es mucho menos exigente. Se considera que dos cadenas equivalentes si contienen los mismos caracteres, ignorando las diferencias en el caso. Tambin se consideran dos caracteres equivalentes si slo difieren en maysculas. Los nmeros son equivalentes en virtud de EQUALP si representan el mismo valor matemtico. Por lo tanto, (equalp 1 1.0) es verdadero. Las listas son EQUALP si sus elementos son EQUALP, del mismo modo, las matrices son EQUALP si sus elementos son EQUALP. Al igual que con EQUAL, hay algunos tipos de datos que no he cubierto an de los cuales EQUALP puede considerar equivalente a dos objetos que ni EQL ni EQUAL lo hacen. Para todos los tipos de datos, EQUALP vuelve a caer en EQL.

El formato de cdigo Lisp


Mientras que el formato de cdigo es, estrictamente hablando, ni una cuestin sintctica ni semntica, el formato correcto es importante para la lectura y la escritura de cdigo con fluidez e idiomticamente. La clave para el formato de cdigo Lisp es que se haga la sangra correctamente. La sangra debe reflejar la estructura del cdigo de modo que no sea necesario contar con parntesis para ver qu va con qu. En general, cada nuevo nivel de anidacin se sangra un poco ms, y, si los saltos de lnea son necesarios, los elementos en el mismo nivel de anidamiento se alinean. Por lo tanto, una llamada a la funcin que debe ser dividido en varias lneas podra escribirse as:
(algunafuncinargconunlargonombre otroargconnombreaunmaslargo)

Las formas macro y especiales que implementan las construcciones de control suelen usar un sangrado un poco diferente: en el "cuerpo" los elementos son dos espacios de sangra en relacin con el parntesis de apertura de la forma. Por lo tanto:
como ngeles-sobre-la-cabeza-de-un-alfiler.

(defun imprimir-lista (lista) (Dolist (Lista I) (Formato t "elemento: ~ a ~%" i)))

Sin embargo, usted no necesita preocuparse demasiado acerca de estas reglas, porque un buen ambiente de Lisp como SLIME se har cargo de ellas por usted. De hecho, una de las ventajas de una sintaxis regular de Lisp es que es bastante fcil para software como editores para saber como hacer los guiones. Debido a que la sangra que se supone que refleja la estructura del cdigo y la estructura est marcada por parntesis, es fcil dejar que el editor haga los guiones de cdigo para usted. En SLIME, tecleando Tab al principio de cada lnea har la sangra de manera apropiada, o puede volver a sangrar una expresin completa, situando el cursor en el parntesis de apertura y escribiendo C-M-q. O puede volver a sangrar la totalidad del cuerpo de la funcin desde cualquier lugar dentro de l tecleando C-c M-q. De hecho, los programadores experimentados Lisp tienden a confiar en el manejo de la sangra de su editor de forma automtica, no slo por que su cdigo se ve bien, sino para detectar errores: una vez que te acostumbras a cmo el cdigo se supone que es una sangra, un parntesis fuera de lugar ser fcilmente reconocible por la muesca extraa que su editor le ofrece. Por ejemplo, suponga que escribe una funcin que se supona iba a tener el siguiente aspecto:
(Defunfoo() (If(test) (Hacerunacosa) (Hacerotracosa)))

Ahora bien, supongamos que accidentalmente dej el clausura de parntesis despus del test. Debido a que no debe preocuparse en contar parntesis, probablemente habra aadido un parntesis de ms al final de la forma DEFUN, obtenindose este cdigo:
(Defunfoo() (If(prueba (Hacerunacosa) (Hacerotracosa))))

Sin embargo, si ha sido sangrado tecleando Tab al comienzo de cada lnea, usted no tiene cdigo de esa manera. En su lugar, tendra la siguiente:
(Defunfoo() (If(prueba (Hacerunacosa) (Hacerotracosa))))

Al ver las clusulas then y else con la sangra salida fuera de lugar de slo un sangrado en relacin con IF, se nota inmediatamente que algo anda mal. Otra regla importante es que el formato de parntesis de clausura siempre se ponen en la misma lnea que el ltimo elemento de la lista que se est cerrando. Es decir, no escribo esto:
(defunfoo() (dotimes(i10)

(formatt"~d.hola~%i) ) )

en su lugar escribo lo siguiente:


(defunfoo() (dotimes(i10) (formatt"~d.hola~%"i)))

La cadena de )))s al final puede parecer imponente, pero siempre que el cdigo est bien indentado los parntesis deben desaparecer - no hay necesidad de dar una prominencia indebida mediante la difusin de ellos en varias lneas. Por ltimo, los comentarios deben ir precedidos de uno a cuatro puntos y comas en funcin del mbito de la observacin de la siguiente manera:
;;;;Cuatropuntosycomasseutilizanparauncomentarioencabezadodelarchivo. ;;;Uncomentariocontrespuntosycomasporlogeneralserunprrafo ;;;Loscomentariosseaplicanaunagranpartedecdigoquesigue, (defunfoo(x) (dotimes(ix) ;;Dospuntoycomaindicaestecomentarioseaplicaalcdigo ;;Quelesigue.Tengaencuentaqueestecomentarioeslamismasangra ;;Comoelcdigoquesigue. (algunallamadaafuncion) (otroi) ;estecomentarioseaplicasoloaestalnea (yotro) ;yestoesparaestalnea (baz)))

Ahora ya ests listo para empezar a mirar con mayor detalle los bloques de construccin principales de los programas de Lisp, las funciones, variables y macros. A continuacin: funciones.

5. Funciones
Despus de las reglas de la sintaxis y la semntica, los tres componentes ms bsicos de todos los programas Lisp son funciones, variables y macros. Usted utiliz los tres, mientras trabajaba en la construccin de la base de datos en el captulo 3, pero pas por alto muchos de los detalles de cmo funcionan y cmo usarlos mejor. Voy a dedicar los prximos captulos a estos tres temas, a partir de funciones, que - al igual que sus homlogos en otros idiomas proporcionan el mecanismo bsico para la abstraccin, as, la funcionalidad. La mayor parte de Lisp en s consta de funciones. Ms de tres cuartas partes de los nombres definidos en las funciones de nombre de la lengua estndar. Todos los tipos de datos predefinidos se definen exclusivamente en trminos de que funciones operan en ellos. Incluso el poderoso sistema de objetos de Lisp se basa en una extensin conceptual de las funciones, las funciones genricas, de lo que me ocupar en el captulo 16. Y, a pesar de la importancia de las macros a la Manera de Lisp, al final toda la funcionalidad real es proporcionada por las funciones. Las Macros se ejecutan en tiempo de compilacin, por lo que el cdigo que generan - el cdigo que realmente har funcionar el programa despus de que todas las macros sean expandidas - consistir en su totalidad de llamadas a funciones y operadores especiales. Por no mencionar que, las macros en s son tambin funciones, aunque son las funciones que se utilizan para generar cdigo en lugar de llevar a cabo las acciones del programa.1

La definicin de nuevas funciones


Normalmente, las funciones se definen mediante la macro DEFUN. El esqueleto bsico de un DEFUN se parece a esto:
(defunnombre(parmetro*) "Cadenadedocumentacinopcional." cuerpodeforma*)

Cualquier smbolo puede ser utilizado como un nombre de funcin. 2 Por lo general, los nombres de funcin contiene slo letras y guiones, pero los otros caracteres estn permitidos y son utilizados en ciertas convenciones de nombres. Por ejemplo, las funciones que convierten un
1 A pesar de la importancia de las funciones de Common Lisp, que no es realmente exacto describirlo como un lenguaje funcional. Es cierto que algunas caractersticas de Common Lisp, como su lista de funciones de manipulacin, se han diseado para ser utilizadas en un estilo forma-cuerpo* y que Lisp tiene un lugar prominente en la historia de la programacin funcional - McCarthy introdujo muchas ideas que ahora se consideran importantes en la programacin funcional -, pero Common Lisp fue diseado expresamente para apoyar a muchos estilos diferentes de programacin. En la familia Lisp, Scheme es lo ms parecido a un lenguaje funcional "puro", e incluso tiene varias caractersticas que lo descalifican como una pureza absoluta en comparacin con idiomas como el Haskell y ML. 2 Bueno, casi cualquier smbolo. Es indefinido lo que ocurre si se utiliza cualquiera de los nombres definidos en el estndar del lenguaje como un nombre para una de sus propias funciones. Sin embargo, como veremos en el captulo 21, el sistema de paquetes Lisp te permite crear nombres en espacios de nombres distintos, as que esto no es realmente un problema.

tipo de valor a otro usan a veces -> en el nombre. Por ejemplo, una funcin para convertir cadenas a artefactos se podra llamar cadena->flash. La convencin de nombres ms importantes es el que se menciona en el captulo 2, que es la construccin de nombres compuestos con guiones en vez de guin bajo o letras en maysculas. Por lo tanto, frob-flash es mejor estilo Lisp que cualquiera frob_artefacto o frobartefacto. Una lista de parmetros de funcin define las variables que se utilizan para mantener los argumentos pasados a la funcin cuando se llama.3 Si la funcin no tiene argumentos, la lista est vaca, escrito como (). Diferentes sabores de los parmetros se manejan si es necesario, los argumentos opcionales, mltiples, y los de palabra clave. Voy a discutir esos detalles en la siguiente seccin. Si una cadena literal sigue a la lista de parmetros, es una cadena de documentacin que debe describir el propsito de la funcin. Cuando la funcin est definida, la cadena de documentacin se asocia con el nombre de la funcin y luego se pueden obtener con la funcin DOCUMENTATION.4 Finalmente, el cuerpo de un DEFUN consiste en cualquier nmero de expresiones Lisp. Ellas sern evaluadas con el fin de que la funcin sea llamada y el valor de la ltima expresin se devolver como el valor de la funcin. O el operador especial return-from se puede utilizar para volver de inmediato desde cualquier lugar de una funcin, como lo veremos en un momento. En el captulo 2, escribi una funcin hola-mundo, que se pareca a esto:
(Defunholamundo()(formatt"hola,mundo"))

Ahora puede analizar las partes de esta funcin. Su nombre es hola-mundo, su lista de parmetros est vaca por lo que no tiene argumentos, no tiene cadena de documentacin, y su cuerpo se compone de una expresin.
(formatt"hola,mundo")

La siguiente es una funcin un poco ms compleja:


(defun suma-detallada (x y) "Suma dos nmeros cualesquiera, despus imprime un mensaje." (format t "Sumando ~d y ~d.~%" x y) (+ x y))

Esta funcin se llama suma-detallada, tiene dos argumentos que sern asignados a los parmetros x e y, tiene una cadena de documentacin, y tiene un cuerpo que consta de dos
3 Las listas de parmetros a veces tambin se denominan listas lambda debido a la relacin histrica entre la nocin de Lisp de las funciones y el clculo lambda. 4 Por ejemplo, lo siguiente: (documentation 'foo 'function) devuelve la cadena de documentacin de la funcin foo. Ntese, sin embargo, que las cadenas de documentacin son destinadas al consumo humano, no el acceso mediante programacin. Una implementacin de Lisp no es necesario que las guarde y se le permite deshacerse de ellas en cualquier momento, por lo que los programas portables no debe depender de su presencia. En algunas implementaciones de una implementacin de una variable definida se debe establecer antes que se van a almacenar cadenas de documentacin.

expresiones. El valor devuelto por la llamada a + se convierte en el valor de retorno de sumadetallada.

Listas de parmetros de funcin


No hay mucho ms que decir acerca de los nombres de funcin o cadenas de documentacin, y tendr una buena parte del resto de este libro para describir todas las cosas que puede hacer en el cuerpo de una funcin, lo cual nos deja terminar ya con la lista de parmetros. El propsito bsico de una lista de parmetros es, por supuesto, declarar las variables que van a recibir los argumentos pasados a la funcin. Cuando una lista de parmetros es una simple lista de nombres de variables - como en para todos los parmetros requeridos. de suma-detallada - los parmetros se llaman Cada parmetro es asignado al argumento parmetros requeridos. Cuando una funcin se llama, deben ser abastecida con un argumento correspondiente. Si una funcin es llamada con pocos o demasiados argumentos, Lisp dar una seal de error. Sin embargo, las listas de parmetros de Common Lisp tambin le dan formas ms flexibles de asignacin de argumentos a los parmetros de una llamada a funcin. Adems de los parmetros requeridos, una funcin puede tener parmetros opcionales. O una funcin puede tener un solo parmetro que est destinado a una lista que contiene los parmetros extra. Y, por ltimo, los argumentos se pueden asignar a los parmetros de uso de palabras clave en lugar de la posicin. Por lo tanto, las listas de Common Lisp de parmetros proporcionan una solucin conveniente a varios problemas comunes de codificacin.

Parmetros opcionales
Mientras que muchas funciones, como suma-detallada, slo tienen los parmetros requeridos, no todas las funciones son tan simples. A veces una funcin tiene un parmetro que slo a ciertas llamadas importa, tal vez porque hay un valor predeterminado razonable. Un ejemplo es una funcin que crea una estructura de datos que puede crecer segn sea necesario. Dado que la estructura de datos puede crecer, no importa - desde un punto de vista correcto - el tamao inicial. Sin embargo, personas que llaman que tienen una buena idea del nmero de elementos que van a poner en la estructura de datos puede mejorar el rendimiento mediante la especificacin de un tamao inicial especfico. La mayora de las personas que llaman, sin embargo, probablemente no dejarn que el cdigo de la estructura de datos implemente un buen valor de propsito general. En Common Lisp puedes acomodar a ambos tipos de llamadas mediante el uso de un parmetro opcional, que para los que llaman y no les importa, puede obtenerse un valor predeterminado razonable, y otros usuarios pueden proporcionar un valor especfico.5

5 En las lenguas que no son compatibles con los parmetros opcionales directamente, los programadores suelen encontrar la
manera de simular. Una tcnica consiste en utilizar un sin valor distinguido, los valores que la persona que llama puede pasar para indicar que desea el valor predeterminado de un parmetro determinado. En C, por ejemplo, es comn el uso de NULL como un valor distinguido. Sin embargo, un protocolo entre la funcin y de sus llamadas es ad hoc - en algunas de las funciones o para algunos de los argumentos NULL puede ser el valor distinguido, mientras que en otras funciones o por otros argumentos el valor mgico puede ser -1 o alguna constante #definida.

Para definir una funcin con parmetros opcionales, despus de los nombres de los parmetros necesarios, coloque el smbolo &optional seguidos por los nombres de los parmetros opcionales. Un ejemplo simple es el siguiente:
(defunfoo(ab&optionalcd)(listabcd))

Cuando se invoca la funcin, los primeros argumentos son asignados a los parmetros requeridos. Despus de todos los parmetros necesarios se han dado los valores, si no hay ningn argumento a la izquierda, sus valores se asignan a los parmetros opcionales. Si los argumentos se agotan antes los parmetros opcionales lo hacen, el resto de parmetros opcionales estn asignados a NIL. Por lo tanto, la funcin definida anteriormente da el siguiente resultado:
(Foo12)==>(12NILNIL) (Foo123)==>(123NIL) (Foo1234)==>(1234)

Lisp todava comprueba que un nmero apropiado de argumentos son pasados a la funcin en este caso entre dos y cuatro, ambos inclusive - y ser una seal de error si la funcin se llama con muy pocos o demasiados. Por supuesto, a menudo se desea un valor predeterminado distinto de NIL. Usted puede especificar el valor predeterminado mediante la sustitucin del nombre del parmetro con una lista que contiene un nombre y una expresin. La expresin se evala solamente si la persona que llama no pasa argumentos suficientes para proporcionar un valor para el parmetro opcional. El caso comn es simplemente para proporcionar un valor en la expresin.
(defunfoo(a&optional(b10))(listab))

Esta funcin requiere un argumento que ser asignado al parmetro a. El segundo parmetro b, tomar el valor del segundo argumento, si es que existe, o 10.
(Foo12)==>(12) (Foo1)==>(110)

A veces, sin embargo, es posible que tenga ms flexibilidad para elegir el valor por defecto. Es posible que desee calcular un valor predeterminado basado en otros parmetros. Y se puede el valor predeterminado puede referirse a parmetros que se producen antes en la lista de parmetros. Si estuviera escribiendo una funcin que devuelve una especie de representacin de un rectngulo y que ha querido dejar muy conveniente para hacer plazas, se puede utilizar una lista de argumentos como este:
(defun hacer-rectngulo (ancho &optional (alto ancho)) ...)

lo que causara la altura de los parmetros tengan el mismo valor que el ancho de parmetro a menos que estn explcitamente especificados. De vez en cuando, es til saber si el valor de un argumento opcional fue suministrado por la persona que llama o es el valor predeterminado. En lugar de escribir cdigo para comprobar si el valor del parmetro es el valor predeterminado (que no funciona de todos modos, si la

persona que llama le pasa de forma explcita el valor predeterminado), puede agregar otro nombre de variable para especificar la falta de valor del parmetro. A esta variable se le asigna true si la persona que llama en realidad suministra un argumento para este parmetro y NIL lo contrario. Por convencin, estas variables tienen usualmente el mismo nombre que el parmetro actual con una "-p" agregada al final. Por ejemplo:
(defunfoo(ab&optional(c3cp)) (listabccp))

Esto da resultados como este:


(Foo12)==>(123NIL) (Foo123)==>(123T) (Foo124)==>(124T)

Parmetros rest
Los parmetros opcionales sirven cuando se tienen parmetros discretos para los cuales la persona que llama puede o no desea proporcionar los valores. Sin embargo, algunas funciones toman un nmero variable de argumentos. Varias de las funciones incorporadas que has visto ya trabajan de esta manera. FORMAT tiene dos argumentos requeridos, el medio y la cadena de control. Pero despus de eso se necesita un nmero variable de argumentos en funcin de valores que deben ser interpolados en la cadena de control. La funcin + tambin tiene un nmero variable de argumentos - no hay ninguna razn particular para limitar la suma a slo dos nmeros, sino que se suma un nmero de valores. (Tambin funciona con cero argumentos, devolviendo un 0, la identidad con la adicin.) Las siguientes son todas las llamadas legales de esas dos funciones:
(formatt"hola,mundo") (formatt"hola,~a"nombre) (formatt"x:~dy:~d"xy) (+) (+1) (+12) (+123)

Obviamente, usted puede escribir funciones que toman un nmero variable de argumentos, simplemente les da un montn de parmetros opcionales. Pero eso sera increblemente costoso - slo escribir la lista de parmetros sera bastante malo, y que no entra en tratar con todos los parmetros en el cuerpo de la funcin. Para hacerlo correctamente, usted tendra que tener parmetros opcionales tantos como el nmero de argumentos que legalmente se puede pasar de una llamada a la funcin. Este nmero depende de la implementacin, pero se garantiza que al menos 50. Y en las implementaciones actuales que va desde 4096 a 536.870.911.6 Blech. Ese tipo de dolor de cabeza tedioso, sin duda no es el camino Lisp. En cambio, Lisp permite incluir un parmetro comodn despus del smbolo &rest. Si una funcin incluye un parmetro &rest, cualquier argumento restante despus de que los valores han sido distribuidos a todos los parmetros requeridos y opcionales es recogido en una lista

6 La constante CALL-ARGUMENTS-LIMIT le dice los valores especficos de la aplicacin.

que se convierte en el valor del parmetro &rest. Por lo tanto, las listas de parmetros de FORMAT y + probablemente lucen como esto:
(defunformat(mediocadena&restvalores)...) (defun+(&restnmeros)...)

Parmetros de palabras clave


Los parmetros opcionales y restantes te dan un poco de flexibilidad, pero tampoco te van a ayudar mucho en la siguiente situacin: Supongamos que tenemos una funcin que toma cuatro parmetros opcionales. Ahora bien, supongamos que la mayora de los lugares en que se llama a la funcin, la persona que llama desea proporcionar un valor para slo uno de los cuatro parmetros y, adems, que las personas que llaman estn divididos en cuanto a qu parmetros van a utilizar. Las personas que llaman que quieren proporcionar un valor para el primer parmetro estn bien ellos solo pasan el nico argumento opcional y deja en blanco el resto. Pero todos los otros usuarios que tienen que pasar un valor de entre uno y tres argumentos cualesquiera de ellos. No es exactamente el problema que los parmetros opcionales fueron diseados para resolver? Por supuesto que s. El problema es que los parmetros opcionales son todava de posicin - si la persona que llama quiere pasar un valor explcito para el cuarto parmetro opcional, resulta que los tres primeros parmetros opcionales son necesarios para la persona que llama. Por suerte, existe otro sabor de parmetros, los parmetros de palabra clave, que permiten al llamador especificar que valores van con qu parmetros. Para dar a una funcin parmetros de palabras clave, despus de cualquier parmetro requerido, &optional, y &rest tu incluyes el smbolo &key y cualquier nmero de especificadores de parmetro de palabra clave, que funcionan como especificadores de parmetros opcionales. He aqu una funcin que tiene slo parmetros de palabra clave:
(defunfoo(&keyabc)(listabc))

Cuando esta funcin se llama, cada uno de los parmetros clave est enlazado al valor inmediatamente despus de una palabra clave del mismo nombre. Recuerde que en el captulo 4 que las palabras clave son nombres que empiezan con dos puntos y que estn definidos de forma automtica como constantes auto evaluables. Si una palabra clave determinada no aparece en la lista de parmetros, al parmetro correspondiente se le asigna su valor por defecto, al igual que un parmetro opcional. Debido a que los argumentos de palabras clave estn etiquetados, que se pueden pasar en cualquier orden, siempre y cuando sigan los argumentos necesarios. Por ejemplo, foo se puede invocar como sigue:
(foo) (foo:a1) (foo:b1) (foo:c1) (foo:a1:c3) ==>(NILNILNIL) ==>(1NILNIL) ==>(NIL1NIL) ==>(NILNIL1) ==>(1NIL3)

(foo:a1:b2:c3) (foo:a1:c3:b2)

==>(123) ==>(123)

Al igual que con los parmetros opcionales, los parmetros de palabra clave puede proporcionar una forma de valor por defecto y el nombre de una variable con -p agregada. En tanto los parmetros de palabra clave y opcionales, la forma de valor por defecto puede hacer referencia a los parmetros que aparecen anteriormente en la lista de parmetros.
(defunfoo(&key(a0)(b0bp)(c(+ab))) (listabcbp)) (foo:a1) (foo:b1) (foo:b1:c4) (foo:a2:b1:c4) ==>(101NIL) ==>(011T) ==>(014T) ==>(214T)

Adems, si por alguna razn, desea que la palabra clave que la persona que llama use un parmetro diferente del nombre del parmetro real, puede reemplazar el nombre del parmetro con otra lista que contiene la palabra clave que se utiliza al llamar a la funcin y el nombre para ser utilizado por el parmetro. La siguiente definicin de foo:
(defunfoo(&key((:arbola))((:bombonb)0)((:cerezac)0cp)) (listabccp))

permite que la persona lo llame as:


(foo:arbol10:bombon20:cereza30)==>(102030T)

Este estilo es til sobre todo si se quiere desvincular por completo a la API pblica de la funcin de los detalles internos, por lo general debido a que desea utilizar los nombres de variable cortas, en lugar de palabras descriptivas en el API. Esto no es, sin embargo, utilizado con frecuencia.

Mezcla de diferentes tipos de parmetros


Es posible, pero poco frecuente, el uso de los cuatro sabores de los parmetros en una sola funcin. Cuando ms de un sabor de parmetro se utiliza, debe ser declarado en la orden que he discutido: en primer lugar los nombres de los parmetros requeridos, a continuacin, los parmetros opcionales, los parmetros resto, y finalmente los parmetros de palabra clave. Por lo general, sin embargo, en las funciones que utilizan mltiples sabores de los parmetros, que se combinan varios parmetros requeridos con un sabor o, posiblemente, otros se combinen con parmetros &optional y &rest. Las otras dos combinaciones, ya sean los parmetros &optional y &rest combinados con parmetros &key, pueden llevar a un comportamiento un tanto sorprendente. Combinando los parmetros &optional y &key obtenemos resultados inesperados que usted probablemente debera evitar por completo. El problema es que si una persona no proporciona valores para todos los parmetros opcionales, a continuacin, estos parmetros se comern las palabras clave y los valores destinados a los parmetros de palabra clave. Por ejemplo, esta funcin mezcla imprudentemente los parmetros &optional y &key:

(defunfoo(x&optionaly&keyz)(listxyz))

Si se llama as, funciona muy bien:


(foo12:z3) ==>(123)

Y esto tambin est bien:


(foo1) ==>(1nilnil)

Pero esto ser una seal de error:


(foo1:z3) ==>ERROR

Esto se debe a la palabra clave :z se toma como un valor para llenar el parmetro opcional, dejando slo el argumento 3 para ser procesados. En ese punto, Lisp espera sea un par palabras clave/valor o nada y se quejar. Quizs an peor, si la funcin hubiera tenido dos parmetros &optional, esta ltima convocatoria se producira en los valores : z y 3 que estarn asignados a los dos parmetro &optional y el parmetro &key z obteniendo el valor predeterminado NIL con ninguna indicacin de que algo anduvo mal. En general, si usted se encuentra escribiendo una funcin que utiliza tanto los parmetros &optional como &key, es probable que slo deba cambiarlo para que use todos los parmetros &key que son ms flexibles, y siempre puedes agregar nuevos parmetros de palabras clave sin molestar a personas que llamaban anteriormente a dicha funcin. Tambin puedes quitar los parmetros de palabra clave, siempre y cuando nadie los est utilizando. 7 En general, el uso de parmetros de palabras clave ayuda a hacer el cdigo mucho ms fcil de mantener y evolucionar - si es necesario aadir un poco de nuevo comportamiento para una funcin que requiere de nuevos parmetros, puede agregar parmetros de palabra clave sin tener que tocar, o incluso volver a compilar, el cdigo existente que llama a la funcin. Usted puede combinar de forma segura los parmetros &rest y &key, pero el comportamiento puede ser un poco sorprendente al principio. Normalmente, la presencia de cualquier &rest o &key en una lista de parmetros hace que todos los valores restantes despus de los parmetros requeridos y los &optional se han llenado para ser procesados de una manera particular - ya sea reunida en una lista para un parmetro &rest o asignado al parmetro correspondiente &key basados en las palabras clave. Si ambos &rest, y &key aparecen en una lista de parmetros, entonces ambas cosas suceden - todos los valores restantes, que incluyen a las palabras claves en si, se recogen en una lista que est asignada a los parmetros &rest, y los valores apropiados son tambin asignados a los parmetros &key. Por lo tanto, dada esta funcin:
(defunfoo(&restresto&keyabc)(listrestoabc))

se obtiene este resultado:

Cuatro funciones estndar toman ambos argumentos tanto &optional como &key - READ-FROM-STRING, PARSENAMESTRING, WRITE-LINE, y WRITE-STRING. Ellos se quedaron as durante la estandarizacin para la compatibilidad con dialectos anteriores de Lisp. READ-FROM-STRING tiende a ser el que captura a nuevos programadores de Lisp con ms frecuencia - una llamada como (read-from-string s :start 10) parece ignorar el argumento de palabra clave :start, lee desde el ndice 0 en lugar de 10. Eso es porque READ-FROM-STRING tambin cuenta con dos parmetros &optional que se tragan a los argumentos :start y 10.

(foo :a 1 :b 2 :c 3) ==> ((:A 1 :B 2 :C 3) 1 2 3)

Valores devueltos por las funciones


Todas las funciones que he escrito hasta ahora han utilizado el comportamiento predeterminado de devolver el valor de la ltima expresin evaluada como su propio valor de retorno. Esta es la forma ms comn de devolver un valor desde una funcin. Sin embargo, a veces es conveniente ser capaz de volver desde el centro de una funcin, como cuando se quiere salir de las construcciones de control anidados. En estos casos se puede utilizar el operador especial RETURN-FROM para regresar de inmediato cualquier valor de la funcin. Usted ver en el captulo 20, que RETURN-FROM actualmente no est vinculada a las funciones en todo, sino que es usado para regresar de un bloque de cdigo definido con el operador especial BLOCK. Sin embargo, DEFUN ajusta automticamente el cuerpo de la funcin entera en un bloque con el mismo nombre que la funcin. Por lo tanto, la evaluacin de un RETURN-FROM con el nombre de la funcin y el valor que desea regresar a causar que la funcin salga de inmediato con ese valor. RETURN-FROM es un operador especial, cuyo primer "argumento" es el nombre del bloque desde el cual se retorna. Este nombre no es evaluado y por lo tanto no es citado. La siguiente funcin usa bucles anidados para encontrar el primer par de nmeros, cada uno a menor que 10, cuyo producto es mayor que el argumento, y usa RETURN-FROM para devolver el par en el momento en que es encontrado:
(defunfoo(n) (dotimes(i10) (dotimes(j10) (when(>(*ij)n) (returnfromfoo(listij))))))

Admitmoslo, tener que especificar el nombre de la funcin de la que se vuelve es algo doloroso - por un lado, si cambia el nombre de la funcin, tendr que cambiar el nombre utilizado en RETURN-FROM, as.8 Pero tambin es el caso de que explcitamente los RETURNFROM se utilizan con mucha menos frecuencia en Lisp que las declaraciones return en lenguas derivadas de C, porque todas las expresiones Lisp, incluyendo las construcciones de control tales como los bucles y condicionales, se evalan como un valor. As que no es un gran problema en la prctica.

Las funciones como datos, tambin conocidas como funciones de orden superior

8 Otra macro, RETURN, no requiere un nombre. Sin embargo, no se puede usar en lugar de return-from para evitar tener que especificar el nombre de la funcin, es azcar sintctico para el retorno de un bloque llamado NIL. Voy a cubrir, junto con los detalles de BLOCK y RETURN-FROM, en el captulo 20.

Mientras que la principal forma de utilizar las funciones es llamarlas por su nombre, hay una serie de situaciones en las cuales es til ser capaz de tratar las funciones como datos. Por ejemplo, si usted puede pasar una funcin como un argumento a otro, puede escribir una funcin de ordenamiento de propsito general mientras provee a la persona que llama de una funcin que sirve para comparar dos elementos. Entonces el mismo algoritmo subyacente se puede utilizar con muchas funciones de comparacin diferentes. Del mismo modo, las devoluciones de llamada y los ganchos dependen de su capacidad de almacenar referencias a cdigo con el fin de ejecutarlos ms tarde. Dado que las funciones ya estn en la forma estndar para abstraer bits de cdigo, tiene sentido permitir que las funciones sean tratadas como datos.9 En Lisp, las funciones son ms que otro tipo de objeto. Cuando se define una funcin con DEFUN, en realidad ests haciendo dos cosas: creacin de una nueva funcin y darle un nombre. Tambin es posible, como se vio en el captulo 3, usar expresiones Lambda para crear una funcin sin nombre. La representacin real de un objeto de funcin, ya sea con nombre o annima, es opaco - en un Lisp nativo-compilado, es probable que consista sobre todo de cdigo mquina. Las nicas cosas que necesitas saber es cmo obtenerlas y la forma de invocarlas una vez que la tienes. El operador especial FUNCIN proporciona el mecanismo para obtener un objeto funcin. Se toma un solo argumento y devuelve la funcin con ese nombre. El nombre no contiene comillas. Por lo tanto, si usted ha definido una funcin foo, as:
CLUSUARIO>(defundoble(x)(*2x)) DOBLE

usted puede obtener el objeto funcin as:10


CLUSUARIO>(functiondoble) #<InterpretedFunctionDOBLE>

De hecho, ya has utilizado FUNCTION, pero esto fue encubierto. La sintaxis #', que se utiliza en el captulo 3, es el azcar sintctico para FUNCTION, as como ' es el azcar sintctico para QUOTE11. Por lo tanto, tambin se puede obtener el objeto de la funcin de DOBLE as:
CLUSUARIO>#'doble #<InterpretedFunctionDOBLE>

Una vez que tenga el objeto funcin, no hay realmente una sola cosa que puedes hacer con l invocarlo. Common Lisp proporciona dos funciones para invocar una funcin a travs de un
9 Lisp, por supuesto, no es la nica lengua que trata a las funciones como datos. C utiliza punteros a funciones, Perl utiliza referencias a subrutinas, Python utiliza un esquema similar al Lisp y C# introduce delegados, que son esencialmente punteros a funciones tipificados, como una mejora sobre la reflexin y los mecanismos annimos de clase de Java. 10 La representacin exacta de un objeto impreso funcin ser diferente de una implementacin a otra.

11La mejor manera de pensar A FUNCTION es como un tipo especial de expresin con comilla. Poner comillas a un smbolo
impide que se evale todo, lo que resulta en el smbolo en s mismo ms que el valor de la variable llamada por ese smbolo. FUNCTION tambin evita la normal regla de clculo, pero, en lugar de prevenir que el smbolo sea evaluado en absoluto, hace que sea evaluado como el nombre de una funcin, tal y como lo hara si se utilizase como el nombre de la funcin en una expresin de llamada a la funcin.

objeto de la funcin: FUNCALL y APPLY12. Slo se diferencian en la forma de obtener los argumentos que se pasan a la funcin. Funcall debe utilizarse cuando se conoce el nmero de argumentos que se van a pasar a la funcin en el momento de escribir el cdigo. El primer argumento de funcall es el objeto funcin que se invoca, y el resto de argumentos son pasados a la funcin. Por lo tanto, las dos expresiones siguientes son equivalentes:
(foo 1 2 3) (funcall #'foo 1 2 3)

Sin embargo, no tiene mucho sentido en el uso de FUNCALL llamar a una funcin cuyo nombre se sabe cuando se escribe el cdigo. De hecho, las dos anteriores expresiones, es muy probable que compile exactamente las mismas instrucciones de mquina. La siguiente funcin muestra un uso ms adecuado de FUNCALL. Se acepta un objeto de funcin como un argumento y grafica un simple histograma en ASCII de los valores devueltos por la funcin cuando se invoca en los valores de min a max, dando un cierto incremento a la vez .
(defunplot(fnminmaxincremento) (loopforifrommintomaxbyincrementodo (looprepeat(funcallfni)do(formatt"*")) (formatt"~%")))

La expresin FUNCALL calcula el valor de la funcin para cada valor de i. El LOOP interior utiliza ese valor calculado para determinar cuntas veces debe imprimir un asterisco a la salida estndar. Tenga en cuenta que usted no utiliza FUNCTION o # ' para obtener el valor de la funcin de fn, que desea que sea interpretada como una variable, ya que el valor de la variable ser el objeto funcin. Usted puede llamar a plot con cualquier funcin que toma un argumento numrico nico, como la funcin integrada EXP que devuelve el valor de e elevado a la potencia de su argumento.
CL-USUARIO> (plot #'exp 0 4 1/2) * * ** **** ******* ************ ******************** ********************************* ****************************************************** NIL

FUNCALL, sin embargo, no le har ningn bien, cuando la lista de argumentos es conocida slo en tiempo de ejecucin. Por ejemplo, para seguir con la funcin plot por otro momento, supongamos que usted haya obtenido una lista que contiene un objeto funcin, un valor mnimo y mximo, y el valor del incremento. En otras palabras, la lista contiene los valores que

12 De hecho, hay un tercero, el operador especial MULTIPLE-VALUE-CALL, pero me lo reservo para cuando hable de las
expresiones que devuelven valores mltiples en el captulo 20.

desea pasar como argumentos a plot. Supongamos que esta lista se encuentra en la variable plot-data. Se podra invocar plot sobre los valores de esa lista como sigue:
(plot(firstplotdata)(secondplotdata)(thirdplotdata)(fourthplotdata))

Esto funciona bien, pero es bastante molesto tener que desempacar explcitamente los argumentos de manera que puedas pasrselas a plot. Ah es donde APPLY entra en juego Al igual que FUNCALL, el primer argumento de APPLY es un objeto funcin. Sin embargo, despus de que el objeto funcin, en lugar de argumentos individuales, se espera una lista. A continuacin, aplica la funcin a los valores de la lista. Esto le permite escribir lo siguiente en su lugar:
(apply#'plotplotdata)

Para mayor comodidad, APPLY tambin puede aceptar "perder" los argumentos, siempre y cuando el ltimo argumento sea una lista. Por lo tanto, si la trama de datos contena slo el mnimo, el mximo, y los valores de paso, an podra utilizar APPLY como sigue para graficar la funcin EXP en ese rango:
(apply#'plot#'expplotdata)

A APPLY no le importa si la funcin que se aplica toma argumentos &optional, &rest, o &key - la lista de argumentos producidos por la combinacin de argumentos perdidos con la lista final debe ser una lista de argumentos legales para la funcin con argumentos suficientes para todos los parmetros necesarios y los parmetros clave slo son apropiados.

Funciones annimas
Una vez que usted comience a escribir, o incluso simplemente usando, las funciones que aceptan otras funciones como argumentos, se ve obenlazado a descubrir que a veces es molesto tener que definir y nombrar una funcin por separado que se utiliza en un solo lugar, sobre todo cuando nunca se llama a por su nombre. En donde parece ser un exceso definir una nueva funcin con DEFUN, puede crear una funcin "annima" usando una expresin Lambda. Como se discuti en el captulo 3, una expresin Lambda luce como esto:
(Lambda(parmetros)cuerpo)

Una manera de pensar a las expresiones Lambda es como un tipo especial de nombre de funcin donde el nombre mismo describe directamente lo que hace la funcin. Esto explica por qu puede utilizar una expresin Lambda en lugar de un nombre de funcin con # '.
(funcall#'(lambda(xy)(+xy))23)==>5

Puede incluso utilizar una expresin Lambda como el "nombre" de una funcin en una llamada a funcin. Si lo desea, puede escribir la expresin anterior funcall ms concisa.
((Lambda(xy)(+xy))23)==>5

Pero esto casi nunca se hace, solamente vale la pena sealar que es legal con el fin de enfatizar que las expresiones Lambda pueden ser utilizadas donde sea que pueda estar el nombre de una funcin normal.13 Las funciones annimas pueden ser tiles cuando necesita pasar una funcin como argumento a otra funcin y la funcin que tiene que pasar es bastante simple de expresar en una lnea. Por ejemplo, suponga que desea representar la funcin 2x . Usted puede definir la siguiente funcin:
(defun doble (x) (* 2 x))

que luego se podra pasar a plot.


CL-USUARIO> (plot #' doble 0 10 1) ** **** ****** ******** ********** ************ ************** **************** ****************** ******************** NIL

Pero es ms fcil, y posiblemente ms clara, al escribir lo siguiente:


CL-USUARIO> (diagrama # '(lambda (x) (* 2 x)) 0 10 1) ** **** ****** ******** ********** ************ ************** **************** ******************

13 En Common Lisp tambin es posible utilizar una expresin Lambda como argumento para funcall (o alguna otra funcin
que tome un argumento de funcin, como SORT o MAPCAR ) sin '# antes, de esta manera: (funcall (lambda (x y) (+ x y)) 2 3)

Esto es legal y es equivalente a la versin con el "# , pero por una difcil razn. Histricamente las expresiones Lambda no eran expresiones que se pudieran evaluar por si mismas. Lambda no era el nombre de una funcin, macro, u operador especial. Por el contrario, una lista que comienza con el smbolo de Lambda era una construccin sintctica especial que Lisp reconoca como una especie de nombre de funcin. Pero si eso an fuera cierto, entonces (funcall (lambda ...)) (...) sera ilegal porque funcall es una funcin y la regla de clculo normal de una llamada de funcin requiere que la expresin Lambda sea evaluada. Sin embargo, posteriormente en el proceso de normalizacin ANSI, con el fin de hacer posible la implementacin de ISLISP, otro dialecto de Lisp que se estaba estandarizando al mismo tiempo, en sentido estricto como una capa de compatibilidad a nivel de usuario de Common Lisp, se defini que una macro Lambda se expande dentro de una llamada a FUNCTION envuelta alrededor de la expresin LAMBDA. En otras palabras, las siguiente expresin LAMBDA: (Lambda () 42) Se expande dentro de lo siguiente cuando se produce en un contexto en el que evala: (function (lambda () 42)) ; o # '(lambda () 42) Esto hace que su uso sea legal en una posicin de valor, como un argumento para funcall. En otras palabras, es puroazcar sintctico. La mayora de la gente o bien utiliza siempre # ' antes de las expresiones Lambda en posiciones de valor o nunca. En este libro, yo siempre uso # '.

******************** NIL

Otro uso importante de las expresiones Lambda es en la fabricacin de clausuras , funciones que capturan parte del entorno en el que fueron creadas. Usted utiliz algo de clausuras en el captulo 3, pero los detalles de cmo trabajan las clausuras y para lo que son utilizadas, realmente esta mas relacionado sobre cmo trabajan las variables y no tanto sobre funciones, as que voy a salvar a esa discusin para el prximo captulo.

6. Variables
El siguiente bloque de construccin bsico que tenemos que mirar son las variables. Common Lisp soporta dos tipos de variables: Lxica y dinmica. 1 Estos dos tipos corresponden aproximadamente a variables "locales" y "globales" en otros lenguajes. Sin embargo, la correspondencia es slo aproximada. Por un lado, en algunos lenguajes "locales", sus variables son, de hecho, iguales a las variables dinmicas de Common Lisp. 2 Y por otro lado, en algunos lenguajes "las variables locales tienen mbito lxico sin ofrecer todas las capacidades de las variables lxicas de Common Lisp. En particular, no todos los idiomas que ofrecen variables con mbito lxico soportan clausuras. Para hacer las cosas un poco ms confusas, muchas de las formas que tratan con variables utilizan tanto variables lxicas como dinmicas. As que voy a empezar por discutir algunos aspectos de las variables de Lisp que se aplican a los dos tipos y luego cubrir las caractersticas especficas de las variables lxicas y dinmicas. Entonces voy a hablar del operador de asignacin de propsito general de Common Lisp, SETF, que se utiliza para asignar nuevos valores a las variables y sobre cada lugar que pueda contener un valor.

Fundamentos de las variables


Al igual que en otros idiomas, en Common Lisp las variables Lisp son lugares con nombre que pueden contener un valor. Sin embargo, en Common Lisp, las variables no se escriben de la forma en que se encuentran en lenguajes como Java o C++. Es decir, que no es necesario declarar el tipo de objeto que cada variable puede contener. En cambio, una variable puede contener valores de cualquier tipo y los valores llevar informacin del tipo que puede ser usada para comprobar los tipos en tiempo de ejecucin. Por lo tanto, Common Lisp es de tipos dinmicos los errores de tipo son detectados de forma dinmica. Por ejemplo, si pasa algo que no sea un nmero a la funcin +, Common Lisp sealar un error de tipo. Por otro lado, Common Lisp es un lenguaje de tipos fuertes en el sentido de que todos los errores de tipo sern detectados - no hay manera de tratar un objeto como una instancia de una clase que no lo es.3

1Las variables dinmicas son tambin a veces llamadas

variables especiales por razones que veremos ms adelante en este captulo. Es importante ser conscientes de este sinnimo, como algunas personas (y las implementaciones de Lisp) utilizan una de ellas mientras que otros utilizan la otra.

2 Los primeros Lisps tendan a usar las variables dinmicas para variables locales, por lo menos cuando se interpretaban.
Elisp, el dialecto de Lisp que utiliza Emacs, es un poco atrasado en este sentido, sigue apoyando slo las variables dinmicas. Otros lenguajes han recapitulado esta transicin de las variables dinmicas a las variables lxicas - Perl usa variables locales, por ejemplo, son dinmicos, mientras eran mis variables, en Perl 5, son lxicas. Python nunca haba tenido en verdad variables dinmicas sino nicamente cierto mbito lxico en la versin 2.2. (Las Variables lxicas de Python estn todava un poco limitadas en comparacin con el Lisp, debido a la fusin de asignacin y enlace en la sintaxis del lenguaje.) 3 En realidad, no es del todo cierto decir que todos los errores de tipo siempre sern detectadas - es posible el uso de declaraciones opcionales para indicar al compilador que ciertas variables siempre contendrn objetos de un tipo en particular y desactivar la comprobacin de tipos en tiempo de ejecucin en determinadas regiones del cdigo. Sin embargo, las declaraciones de este tipo se utilizan para optimizar el cdigo despus de que ha sido desarrollado y depurado, y no durante el desarrollo normal.

Todos los valores de Lisp son, conceptualmente al menos, referencias a objetos. 4 En consecuencia, asignar a una variable un nuevo valor cambia el objeto al que se refiere dicha variable, pero no tiene ningn efecto sobre el objeto referenciado anteriormente. Sin embargo, si una variable contiene una referencia a un objeto mutable, puedes usar esa referencia para modificar ese objeto, y la modificacin ser visible para cualquier cdigo que tenga una referencia al mismo objeto. Una forma de introducir nuevas variables que ya se utilizaron para definir parmetros de funcin. Como se vio en el captulo anterior, cuando se define una funcin con DEFUN, la lista de parmetros definen las variables que contendr los argumentos de la funcin cuando se llama. Por ejemplo, esta funcin define tres variables - x , y e z - para contener sus argumentos.
(defun foo (x y z) (+ x y z))

Cada vez que se llama una funcin, Lisp crea nuevos enlaces para contener los argumentos pasados por el que llama a la funcin. Un enlace es la manifestacin en tiempo de ejecucin de una variable. Una variable - la que puede apuntar en el cdigo fuente del programa - puede tener muchos enlaces diferentes durante una ejecucin del programa. Una variable, incluso puede tener varios enlaces al mismo tiempo, los parmetros de una funcin recursiva, por ejemplo, son usados para cada llamada a la funcin. Al igual que con todas las variables de Common Lisp, los parmetros de funcin contienen referencias a objetos.5 De este modo, puede asignar un nuevo valor a un parmetro de funcin en el cuerpo de la funcin, que no afectarn a los enlaces creados para otra llamada a la misma funcin. Pero si el objeto pasado a una funcin es mutable y lo cambias en la funcin, los cambios sern visibles para la persona que llama ya que tanto la persona que llama y el destinatario hacen referencia al mismo objeto. Otra forma que introduce nuevas variables es el operador especial LET . El esqueleto de una forma LET se parece a esto:
(let (variable*) cuerpo-de-la-forma*)

donde cada variable es una variable de forma de inicializacin. Cada forma de inicializacin es una lista que contiene un nombre de variable y una forma de valor inicial o bien - como una abreviatura para la inicializacin de la variable a NIL un nombre de una variable normal. La siguiente forma LET, por ejemplo, une las tres variables x , y e z , con valores iniciales de 10, 20, y NIL :
(let ((x 10) (y 20) z) ...)

4 Como una optimizacin de ciertos tipos de objetos, tales como enteros por debajo de cierto tamao y caracteres, pueden ser
representados directamente en memoria donde otros objetos seran representados mediante un puntero al objeto real. Sin embargo, ciertos enteros y caracteres son inmutables, sin importar de que pueda haber mltiples copias del mismo" objeto en diferentes variables. Esta es la raz de la diferencia entre EQ y EQL en el captulo 4. 5 En cuanto al compilador-editor de Common Lisp las funciones son "pasadas por valor." Sin embargo, los valores que se transmiten son las referencias a objetos. Esto es similar a la forma de trabajo de Java y Python.

Cuando se evala la forma LET, todas las formas de valor inicial se evalan en primer lugar. Luego, se crean nuevos enlaces y se inicializan con los valores iniciales apropiados antes de que el cuerpo de la forma sea ejecutado. Dentro del cuerpo de la LET, los nombres de variables se refieren a los nuevos enlaces creados. Despus de LET, los nombres se refieren a cualquier cosa, que se haya referido antes de LET . El valor de la ltima expresin en el cuerpo es devuelto como el valor de expresin LET. Asi como con los parmetros de funcin, las variables introducidas con LET son reiniciadas cada vez que el LET es introducido.6 El mbito de aplicacin de los parmetros de funcin y las variables LET - el rea del programa donde se encuentra el nombre de variable se utiliza para referirse al enlace de la variable est delimitada por la forma en que se introduce la variable. Esta forma - la definicin de funcin o el LET - se llama la forma de enlace. Como vers en breve, los dos tipos de variables - lxico y dinmico - el uso de dos mecanismos de mbito ligeramente distintos, pero en ambos casos, el mbito est delimitado por la forma de enlace. Si anida formas de enlace que introducen variables con el mismo nombre, los enlaces de las variables ms profundos ocultan a los enlaces externos. Por ejemplo, cuando la siguiente funcin se llama, se crea un enlace para el parmetro x para sostener el argumento de la funcin. A continuacin, el primer LET crea un nuevo enlace con el valor inicial de 2, y en el interior LET crea otro enlace, esta vez con el valor inicial de 3. Las barras de la derecha marcan el mbito de cada enlace.
(defun foo (x) (format t "Parmetros: ~a ~%" x) (let ((x 2)) (format t "exterior LET: ~a ~%" x) (let ((x 3)) (format t "Interior LET: ~ a ~%" x)) (format t "exterior LET: ~a ~%" x)) (format t "Parmetros: ~a ~%" x)) |<--------- x es el argumento | | |<------- x vale 2 | | | | |<----- x vale 3 | | |

Cada referencia a x se refiere al enlace con el mbito ms pequeo que lo contiene. Una vez que el control deja al mbito de una forma de enlace, el enlace del mbito de clausura inmediato es develado y x se refiere a l en su lugar. Por lo tanto, llamar a foo da resultados en la siguiente salida:
CL-USUARIO> (foo 1) Parmetro: 1 LET exterior: 2 LET interior: 3 LET exterior: 2 Parmetro: 1 NIL

6 Las variables de LET formas y parmetros de la funcin son creados por exactamente el mismo mecanismo. De hecho, en
algunos dialectos Lisp - aunque no Common Lisp - LET no es ms que una macro que se expande en una llamada a una funcin annima. Es decir, en los dialectos, las siguientes: (let ((x 10)) (format t "~a" x)) es una forma de macro que se expande a lo siguiente: ((Lambda (x) (format t "~ a" x)) 10)

En los prximos captulos hablar sobre otras construcciones que tambin sirven como formas de enlace - cualquier construccin que introduce un nuevo nombre de variable que es utilizable slo dentro de la construccin es una forma de enlace. Por ejemplo, en el captulo 7 te encontrars con el bucle DOTIMES, un bucle de cuenta bsica. Se introduce una variable que contiene el valor de un contador que se incrementa cada vez que pasa travs del bucle. El siguiente bucle, por ejemplo, que imprime los nmeros del 0 al 9, enlaza la variable x:
(dotimes (x 10) (format t "~d" x))

Otra forma de enlace es una variante de LET, LET *. La diferencia es que en un LET, los nombres de las variables slo se pueden utilizar en el cuerpo de la LET - la parte del LET despus de la lista de variables - pero en un LET*, los valores iniciales de la forma para cada variable pueden referirse a variables introducidas anteriormente en la lista de variables. Por lo tanto, usted puede escribir lo siguiente:
(let* ((x 10) (y (+ x 10))) (list x y))

pero no esto:
(let ((x 10) (y (+ x 10))) (list x y))

Sin embargo, usted podra conseguir el mismo resultado con LETs anidados.
(let ((x 10)) (let ((y (x + 10))) (list x y)))

Variables lxicas y clausuras


Por defecto todas las formas de enlace en Common Lisp introducen variables con mbito lxico. Las variables con mbito lxico pueden hacer referencia solamente a cdigo que est textualmente dentro de la forma de enlace. El mbito lxico debe ser familiar para cualquiera que haya programado en Java, C, Perl o Python, ya que todas ofrecen variables con un mbito lxico "local". Por lo dems, los programadores de Algol tambin debe sentirse como en casa, ya que Algol introdujo por primera vez el mbito lxico en la dcada de 1960. Sin embargo, las variables lxicas de Common Lisp son variables lxicas con un giro, al menos en comparacin con el modelo del Algol original. El giro es proporcionado por la combinacin de mbito lxico con funciones anidadas. Segn las reglas de mbito lxico, slo el cdigo textual dentro de la forma de enlace puede referirse a una variable lxica. Pero, qu sucede cuando una funcin annima contiene una referencia a una variable lxica en un mbito que lo contiene? Por ejemplo, en esta expresin:
(let ((contador 0)) #'(lambda () (setf contador (1+ contador))))

la referencia a contador dentro de la forma LAMBDA debe ser legal de acuerdo con la normativa del mbito lxico. Sin embargo, la funcin annima que contiene la referencia ser devuelta como el valor de la forma LET y se puede invocar, a travs de funcall, por cdigo que no est en el mbito de la LET. Entonces, qu sucede? Pues resulta que, como contador es una variable lxica, simplemente funciona. El enlace de contador creado cuando el flujo de control entr en la forma LET se mantendr tanto tiempo como sea necesario, en este caso por el tiempo en que alguien mantenga una referencia al objeto devuelto por la funcin de la forma LET. La funcin annima es llamada clausura, ya que "se cierra sobre" el enlace creado por la LET. La clave para entender sobre la clausura es que es el enlace, no el valor de la variable, el que es capturado. Por lo tanto, la clausura no slo puede acceder al valor de las variables que se cierran, sino que tambin puede asignar nuevos valores que persisten entre las llamadas a la clausura. Por ejemplo, usted puede capturar la clausura creada por la expresin anterior en una variable global de esta manera:
(defparameter *fn* (let ((contador 0)) #'(lambda() (setf contador (1+ contador))))) Entonces cada vez que se lo invoca, el valor de contador se incrementar en uno. CL-USUARIO> (funcall *fn*) 1 CL-USUARIO> (funcall *fn*) 2 CL-USUARIO> (funcall *fn*) 3

Una clausura simple puede encerrar muchas ms asignaciones de variables simplemente haciendo referencia a ellas. O mltiples clausuras pueden capturar el mismo enlace. Por ejemplo, la siguiente expresin devuelve una lista de tres clausuras, una que incrementa el valor de contador encerrado en el enlace, otra que lo disminuye, y otra que devuelve el valor actual:
(let ((contador 0)) (list #'(lambda() (incf contador)) #'(lambda() (decf contador)) #'(lambda() (contador)))

Variables Dinmicas, tambin conocidas como variables especiales


Los enlaces de mbito lxico ayudan a mantener el cdigo entendible por la limitacin del mbito, literalmente, en el que un nombre tiene un significado. Esta es la razn por la que la mayora de los idiomas modernos utilizan mbito lxico para las variables locales. A veces, sin embargo, usted realmente desea una variable global - una variable a la que se pueda referir en cualquier parte de su programa. Si bien es cierto que el uso indiscriminado de variables globales puede convertir el cdigo en espaguetis casi tan rpido como el uso incontrolado de goto, las variables globales tienen usos legtimos y existen en una forma u otra en casi

cualquier lenguaje de programacin.7 Y como se ver en un momento, la versin de Lisp de variables globales, las variables dinmicas, son ms tiles y ms manejables. Common Lisp ofrece dos formas de crear variables globales: defvar y DEFPARAMETER . Ambas formas tienen un nombre de variable, un valor inicial, y una cadena de documentacin opcional. Despus de haber sido defvarado o DEFPARAMETERado, el nombre puede ser utilizado en cualquier lugar para referirse a la enlace actual de la variable global. Como hemos visto en captulos anteriores, las variables globales que convencionalmente se denominan con nombres que comienzan y terminan con *. Ver ms adelante en esta seccin por que es muy importante que siga esa convencin de nomenclatura. Ejemplos de defvar y DEFPARAMETER en este aspecto:
(defvar *contador* 0 "Contador de artefactos hechos hasta la fecha.") (defparameter *brecha-tolerancia* 0,001 "La tolerancia que se permite en las brechas entre artefactos")

La diferencia entre las dos formas es que DEFPARAMETER siempre asigna el valor inicial de la variable nombrada, mientras defvar lo hace slo si la variable no est definida. Una forma defvar tambin se puede utilizar sin ningn valor inicial para definir una variable global sin darle un valor. Se dice que estas variables son no enlazadas. En trminos prcticos, se debe utilizar defvar para definir las variables que contienen los datos que te gustara tener, incluso si realiza un cambio en el cdigo fuente que utiliza la variable. Por ejemplo, supongamos que las dos variables definidas anteriormente son parte de una aplicacin para el control de una fbrica de artefactos. Es preciso determinar la variable *contador* con defvar porque el nmero de artefactos hechos hasta la fecha no se invalidarn porque se hagan algunos cambios en el cdigo de construccin del artefacto. 8 Por otro lado, la variable *brecha-tolerancia* presumiblemente tiene algn efecto sobre el comportamiento del cdigo mismo de construccin del artefacto. Si usted decide que necesita una tolerancia ms ajustada o suelta y cambiar el valor de la forma DEFPARAMETER, le gustara que el cambio surta efecto cuando vuelva a compilar y cargar el archivo. Despus de definir una variable con defvar o DEFPARAMETER, puede hacer referencia a ella desde cualquier lugar. Por ejemplo, podra definir esta funcin para incrementar la cuenta de artefactos hechos a medida:
(defun incrementar-cuenta-artefacto() (incf *contador*))

La ventaja de las variables globales es que usted no tiene que pasar todo. La mayora de los idiomas almacenan los medios estndar de entrada y de salida de las variables globales exactamente por esta razn - nunca se sabe cuando vas a querer imprimir algo en la salida

7 Java disfraza a las variables globales como campos pblicos estticos, C utiliza variables extern, Python a nivel de mdulo y
Perl las variables a nivel de paquete que tambin pueden accederse desde cualquier lugar.

Si usted desea especficamente restablecer una variable defvarada, puede darle valores directamente con SETF o hacerla no enlazada utilizando MAKUNBOUND y luego reevaluar la forma defvar.

estndar, y usted no desea que todas las funciones acepten y pase argumentos conteniendo esos medios, slo en casos en que alguien ms abajo de la lnea lo necesite. Sin embargo, una vez que un valor, como el medio estndar de salida, se almacena en una variable global y has escrito cdigo que hace referencia a la variable global, es tentador tratar de modificar temporalmente el comportamiento de ese cdigo cambiando el valor de la variable. Por ejemplo, supongamos que est trabajando en un programa que contiene algunas de las funciones registro de bajo nivel que imprime al medio en la variable global *standard-output* (salida estndar/monitor/pantalla). Ahora supongamos que en una parte del programa desea capturar toda la salida generada por esas funciones en un archivo. Puede abrir un archivo y asignar el medio resultante a *standard-output*. Ahora las funciones de bajo nivel enviarn sus resultados al archivo. Esto funciona bien hasta que se olvide de establecer *standard-output* al valor original cuando haya terminado. Si usted se olvida de reiniciar *standard-output* todos los otros cdigos en el programa que utilizan *standard-output* tambin le enviaran su resultado al archivo.9 Lo que realmente busca, al parecer, es una forma de envolver un trozo de cdigo en algo que dice: "Todo el cdigo aqu - todas las funciones que llama, todas las funciones que ellos llaman, y as sucesivamente, hasta las funciones de mas bajo nivel - debe utilizar este valor de la variable global *standard-output*. Luego, cuando vuelve de la funcin de alto nivel, el valor antiguo de *standard-output* debe ser automticamente restablecido. Resulta que eso es exactamente lo que le permite hacer otro tipo de variables de Common Lisp las variables dinmicas -. Al enlazar una variable dinmica - por ejemplo, con una variable LET o un parmetro de funcin - el enlace que se crea al entrar en la forma de enlace reemplaza la enlace global por la duracin de la forma de enlace. A diferencia de un enlace lxico, que puede ser referenciado por el cdigo slo en el mbito lxico de la forma de enlace, un enlace dinmico puede hacer referencia a cualquier cdigo que sea invocado durante la ejecucin de forma vinculante. variables dinmicas. Por lo tanto, si desea volver a definir temporalmente *standard-output*, la forma de hacerlo es simplemente volver a enlazarla, por ejemplo, con un LET.
(let ((*standard-output* *algunos-otros-medios*)) (cosas))
10

Y resulta que todas las variables globales son de hecho,

9 La estrategia de reasignar temporalmente *standard-output* tambin se rompe si el sistema es multiproceso - si hay mltiples hilos de control intentando imprimir en diferentes medios al mismo tiempo, todos ellos intentarn establecer la variable global al medio que desean utilizar, pisotendose unos a otros. Se puede usar un bloqueo para controlar el acceso a la variable global, pero en realidad no ests tomando el beneficio de mltiples subprocesos simultneos, ya que el hilo de impresin tiene que bloquear todos los dems hilos hasta que haya finalizado, incluso si desea imprimir a un medio diferente.

10 El trmino tcnico para el intervalo durante el cual se pueden hacer referencias a una enlace es su

extensin. Por lo tanto, mbito y extensin son conceptos complementarios - mbito se refiere al espacio, mientras que extensin se refiere al tiempo. Las variables lxicas tienen mbito lxico, pero extensin indefinida, lo que significa que se quedan por un intervalo de tiempo indefinido, determinada por el tiempo que sea necesario. Las variables dinmicas, por el contrario, tienen un mbito indefinido, ya que pueden ser referidos desde cualquier lugar, pero una extensin dinmica. A fin de confundir las cosas, la combinacin de mbito indefinido y extensin dinmica se refiere con frecuencia por el nombre equivocado mbito dinmico.

En cualquier cdigo que se ejecuta como resultado de la llamada a (cosas), referencias a *standard-output* utilizarn el enlace establecido por la LET. Y cuando (cosas) vuelva y deja el control la LET, el nuevo enlace de *standard-output* va a desaparecer y las referencias posteriores a *standard-output* vern el enlace que estaba vigente antes de la LET. En un momento dado, el ms reciente enlace establecido oculta a todos los otros enlaces. Conceptualmente, cada nuevo enlace de una variable dinmica se inserta en una pila de enlaces para esa variable, y las referencias a la variable siempre utilizan el enlace ms reciente. Como el retorno de formas de enlace, los enlaces creados se extraen de la pila, exponiendo a los enlaces previos.11 Un sencillo ejemplo muestra cmo funciona esto.
(defvar *x* 10) (defun foo () (format t "X: ~d~%" *x*))

El defvar crea un enlace global para la variable *x* con el valor 10. La referencia a *x* en foo buscar el actual enlace de forma dinmica. Si usted llama a foo desde el nivel superior, la enlace global creado por el defvar es el nico enlace disponible, por lo que imprime 10.
CL-USUARIO> (foo) X: 10 NIL

Pero usted puede usar LET para crear un nuevo enlace que oculta temporalmente el enlace global, y foo imprimir un valor diferente.
CL-USUARIO> (let ((*x* 20)) (foo)) X: 20 NIL

Ahora llama a foo otra vez, sin LET , y vuelve a ver la enlace global.
CL-USUARIO> (foo) X: 10 NIL

Ahora defina otra funcin.


(defun bar() (foo) (let ((*x* 20)) (foo)) (foo))

Tenga en cuenta que la llamada del medio de foo est envuelta en un LET que une a *x* con el nuevo valor 20. Cuando se ejecuta bar, se obtiene este resultado:
CL-USUARIO> (bar) X: 10 X: 20 X: 10 NIL

11 Aunque el estndar no especifica cmo incorporar multihilo en Common Lisp, las implementaciones que brindan multiprocesamiento siguen la prctica establecida en las mquinas Lisp y crean enlaces dinmicos en funcin de cada hilo. Una referencia a una variable global encontrar el enlace de ms reciente creacin en el subproceso actual, o el enlace global.

Como puede ver, la primera llamada a foo ve el enlace global, con su valor de 10. La llamada del medio, sin embargo, considera el nuevo enlace, con el valor 20. Pero despus de la LET, foo, una vez ms ve el enlace global. Como con los enlaces de lxico, asignar un nuevo valor slo afecta a la consolidacin actual. Para ver esto, puede volver a definir foo para incluir una asignacin a *x*.
(defun foo () (format t "Antes de la asignacin~18tX: ~d~%" *x*) (setf *x* (+ 1 *x*)) (format t "Despus de la asignacin~18tX: ~d~%" *x*))

Ahora foo imprime el valor de *x*, la incrementa, y lo imprime de nuevo. Si usted simplemente debe ejecutar foo, ver lo siguiente:
CL-USUARIO> (foo) Antes de la asignacin X: 10 Despus de la asignacin X: 11 NIL

No es demasiado sorprendente. Ahora ejecute bar .


CL-USUARIO> (bar) Antes de la asignacin X: 11 Despus de la asignacin X: 12 Antes de la asignacin X: 20 Despus de la asignacin X: 21 Antes de la asignacin X: 12 Despus de la asignacin X: 13 NIL

Tenga en cuenta que *x* comenz con 11 - la llamada anterior a foo realmente cambia el valor global. La primera llamada a foo desde bar incrementa el enlace global a 12. La llamada del medio no ve el enlace global a causa de la LET. Luego de la ltima llamada se puede ver el enlace global de nuevo y lo incrementa de 12 a 13. Entonces, cmo funciona esto? Cmo puede saber LET cuando el enlace a *x* debe ser un enlace dinmico en lugar de un enlace lxico normal? Se sabe ya que el nombre ha sido declarado especial.12 El nombre de cada variable definida con defvar y DEFPARAMETER se declara automticamente como especial. Esto significa que cada vez que utilice un nombre en una forma de enlace - en un LET o como un parmetro de funcin o cualquier otra construccin que cree una nueva variable vinculante - el enlace que se crea ser un enlace dinmico. Esta es la razn porque la *convencin* de *nombres* es tan importante - esto ser una mala noticia si se ha utilizado un nombre para lo que usted pensaba que era una variable lxica y la variable pas a ser especial. Por un lado, el cdigo que se llama podra cambiar el valor del enlace por debajo de ti, por el otro, es posible que oculte un enlace establecido por el cdigo ms arriba en la pila. Si tu siempre nombras las variables globales de acuerdo con la * convencin de nombres, nunca en forma accidental usar un enlace dinmico donde intentes establecer un enlace lxico.

12

Esta es la razn por la que las variables dinmicas son tambin llamadas a veces variables especiales.

Tambin es posible declarar un nombre a nivel local especial. Si, en una forma vinculante, se declara un nombre especial, entonces el enlace creado para esa variable ser dinmico en lugar de lxico. Otro cdigo local puede declarar un nombre especial para referirse a la asignacin dinmica. Sin embargo, las variables locales especiales son relativamente raras, por lo que no tiene que preocuparse por ellas.13 Los enlaces dinmicos hacen a las variables globales mucho ms manejables, pero es importante tener en cuenta que todava permiten una accin a distancia. Enlazar una variable global tiene dos efectos a distancia - puede cambiar el comportamiento del cdigo mas abajo, y adems abre la posibilidad de que el cdigo de abajo le asignar un nuevo valor a un enlace establecido ms arriba en la pila. Usted debe utilizar las variables dinmicas slo cuando sea necesario para tomar ventaja de una o ambas de estas caractersticas.

Constantes ++++ Un otro tipo de variables que no he mencionado a todos es el oxmoron "variable constante". Todas las constantes son globales y se definen con DEFCONSTANT . La forma bsica deDEFCONSTANT es como DEFPARAMETER .
(Defconstant nombre inicial-forma-valor [ documentacin de cadena ])

Al igual que con defvar y DEFPARAMETER , DEFCONSTANT tiene un efecto global sobre el nombre que se utiliza - a partir de entonces el nombre slo se puede utilizar para referirse a la constante, no puede ser utilizado como un parmetro de funcin o de rebote con cualquier forma de encuadernacin. Por lo tanto, muchos programadores de Lisp sigue una convencin de nomenclatura de la utilizacin de los nombres que comienzan y terminan con + de las constantes. Esta convencin es un poco menos universalmente seguida de la *convencin de nomenclatura para los nombres de todo el mundo especial, pero es una buena idea por la misma razn.
14

Otra cosa a notar acerca DEFCONSTANT es que mientras que el lenguaje le permite redefinir una constante reevaluacin de un DEFCONSTANT con un valor inicial diferente forma, qu pasa despus de la redefinicin no est definido. En la prctica, la mayora de las implementaciones ser necesario que volver a evaluar cualquier cdigo que se refiere a la constante con el fin de ver el nuevo valor ya que el valor de edad bien pudo haber sido entre lneas. Por lo tanto, es una buena idea usar DEFCONSTANT slo para definir las cosas que son realmente constantes, tales como el valor de cero. Para todo aquello que alguna vez puede ser que desee cambiar, se debe utilizar DEFPARAMETER lugar.
13
Si quieres saberlo, puedes buscar DECLARE, SPECIAL , y LOCALLY en el HyperSpec.

Asignacin Una vez que has creado un enlace, se pueden hacer dos cosas con l: obtener el valor actual y la puso a un nuevo valor. Como se vio en el captulo 4, un smbolo como resultado el valor de la variable que nombra, para que pueda obtener el valor actual simplemente haciendo referencia a la variable. Para asignar un nuevo valor a un enlace, se utiliza el SETFmacro, de propsito general Common Lisp del operador de asignacin. La forma bsica de SETF es el siguiente:
(Setf lugar de valor )

Porque SETF es una macro, se puede examinar la forma de la lugar es la asignacin y ampliar en los correspondientes niveles inferiores de operaciones para manipular a ese lugar.Cuando el lugar es una variable, que se expande a una llamada al operador especial setq , que, como un operador especial, tiene acceso a enlaces tanto en lxico y dinmico. 15 Por ejemplo, para asignar el valor 10 a la variable x , se puede escribir lo siguiente:
(Setf x 10)

Como ya he comentado anteriormente, la asignacin de un nuevo valor a una enlace no tiene ningn efecto en cualquier otro enlace de dicha variable. Y no tiene ningn efecto sobre el valor que se almacena en la enlace antes de la asignacin. Por lo tanto, la SETF en esta funcin:
(Defun foo (x) (setf x 10))

no tendr ningn efecto en cualquier valor fuera de foo . La enlace que se cre cuando foo fue llamada se establece en 10, sustituyendo inmediatamente cualquier valor que se pasa como argumento. En particular, una forma como las siguientes:
(Let ((y 20)) (Foo y) (Impresin y))

se imprimirn 20, no 10, ya que es el valor de y que se pasa a foo donde es brevemente el valor de la variable x antes de la SETF da x un nuevo valor. SETF Tambin puede asignar a varios lugares en la secuencia. Por ejemplo, en lugar de los siguientes:
(Setf x 1) (Setf y 2)

usted puede escribir lo siguiente:

(Setf x 1 y 2)

SETF devuelve el valor asignado, por lo que tambin se pueden anidar las llamadas a SETF como en la siguiente expresin, que asigna tanto x , y y el valor aleatorio mismo:
(Setf x (setf y (random 10)))

Asignacin generalizada Asignaciones de variables, por supuesto, no son los nicos lugares que pueden contener valores. Common Lisp soporta las estructuras de datos compuestos, tales como arreglos, tablas y listas, as como definidos por el usuario las estructuras de datos, todos los cuales consisten en varios lugares que cada uno puede contener un valor. Voy a cubrir las estructuras de datos en los prximos captulos, pero ya que estamos en el tema de la asignacin, debe tener en cuenta que SETF puede asignar un valor en cualquier lugar. A medida que cubren las diferentes estructuras de datos compuestos, voy a sealar las funciones que pueden servir como " SETF lugares capaces. " La versin corta, sin embargo, es si es necesario asignar un valor a un lugar, SETF es casi seguro que la herramienta a utilizar. Es incluso posible extender SETF para permitir que se asignan a los lugares definidos por el usuario, aunque no voy a cubrir eso. 16 En este sentido SETF no es diferente de la = operador de asignacin en la mayora de los C-derivados idiomas. En esos idiomas, el = operador asigna nuevos valores a las variables, elementos de la matriz, y los campos de las clases. En lenguajes como Perl y Python que soporta tablas hash como un built-in tipo de datos, = tambin puede establecer los valores de cada uno de entradas de la tabla hash. La Tabla 6-1 resume las diversas formas = se utiliza en esos idiomas.
Tabla 1.6. Asignacin con = en otros idiomas

Asignar a los ... ... variable ... elemento de la matriz ... entrada de la tabla de hash ... campo en el objeto

Java, C, C + + x = 10; a [0] = 10; o.field = 10;

Perl $ X = 10; $ A [0] = 10; $ Hash {'clave'} = 10; $ O-> {'campo'} = 10;

Pitn x = 10 a [0] = 10 hash ['clave'] = 10 o.field = 10

SETF funciona del mismo modo - el primer "argumento" para SETF es un lugar para almacenar el valor, y el segundo argumento proporciona el valor. Al igual que con el = operador en estos idiomas, se utiliza la misma forma para expresar el lugar en

que se usa normalmente para recuperar el valor. 17 De este modo, los equivalentes de Lisp de las asignaciones en la Tabla 6-1 - ya que AREF es el funcin de acceso a una matriz, GetHash hace una bsqueda en la tabla hash, y el campo podra ser una funcin que accede a un espacio llamadocampo de un objeto definido por el usuario - son las siguientes:
Variable simple: (setf x 10) Array: (setf (aref a 0) 10) Tabla hash: (setf (clave hash GetHash ') 10) Ranura llamado "campo": (setf (o campo) 10)

Tenga en cuenta que SETF ING un lugar que es parte de un objeto ms grande tiene la misma semntica que SETF cin de una variable: el lugar se ha modificado sin ningn efecto sobre el objeto que estaba previamente almacenado en el lugar. Una vez ms, esto es similar a la forma = se comporta en Java, Perl y Python. 18 Otras maneras de modificar Lugares Mientras que todas las asignaciones se puede expresar con SETF , ciertos patrones de participacin de asignar un nuevo valor basado en el valor actual son lo suficientemente comunes como para justificar sus propios operadores. Por ejemplo, mientras que podra incrementar un nmero con SETF , de esta manera:
(Setf x (x + 1))

o disminuir con esto:


(Setf x (- x 1))

que es un poco tedioso, en comparacin con el C-estilo + + x y - x . En su lugar, puede utilizar las macros INCF y DECF , que incrementar y disminuir un lugar por una cierta cantidad que por defecto es 1.
(Incf x) === (setf x (x + 1)) (DECF x) === (setf x (- x 1)) (Incf x 10) === (setf x (x + 10))

INCF y DECF son ejemplos de una especie de macro llamado macros modificar . Modificar macros son macros construidas en la parte superior de SETF que modifican los lugares mediante la asignacin de un nuevo valor basado en el valor actual del lugar. El principal beneficio de modificar macros es que son ms concisa que la modificacin del mismo por escrito a cabo utilizando SETF . Adems, las macros se definen modificar de una manera que los hace seguros para usar con los

lugares donde debe ser la expresin lugar evala slo una vez. Un ejemplo tonto es esta expresin, que incrementa el valor de un elemento arbitrario de una matriz:
(Incf (aref matriz * * (longitud aleatoria (* array *))))

Una traduccin ingenua de que en un SETF expresin podra tener este aspecto:
(Setf (aref matriz * * (longitud aleatoria (* array *))) (1 + (Aref matriz * * (longitud aleatoria (* array *)))))

Sin embargo, esto no funciona, porque las dos llamadas a AZAR no necesariamente devolver el mismo valor - esta expresin es probable que tome el valor de un elemento de la matriz, se incrementar, a continuacin, guardar de nuevo como el nuevo valor de un elemento diferente. El INCF expresin, sin embargo, hace lo correcto, ya que sabe cmo desarmar esta expresin:
(Aref matriz * * (longitud aleatoria (* array *)))

para extraer las partes que podran tener efectos secundarios para asegurarse de que estn evala slo una vez. En este caso, probablemente se ampliar en algo ms o menos equivalente a lo siguiente:
(Let ((tmp (longitud aleatoria (* array *)))) (Setf (aref matriz * * tmp) (1 + (aref matriz * * tmp))))

En general, las macros modificar estn garantizados para evaluar tanto sus argumentos y los subformularios de la forma de colocar una sola vez cada uno, de izquierda a derecha. La macro EMPUJE , que utiliz en el mini-base de datos para agregar elementos a la db * * variable, es otra macro modificar. Le echaremos un vistazo ms de cerca cmo l y sus homlogos de POP y PUSHNEW trabajo en el captulo 12, cuando hablo de cmo se representan las listas en Lisp. Por ltimo, dos macros modificar ligeramente esotrico, pero son tiles ROTATEF y SHIFTF . ROTATEF gira valores entre los lugares. Por ejemplo, si tiene dos variables, una y b , la presente convocatoria:
(Rotatef ab)

intercambia los valores de las dos variables y devuelve NIL . Desde una y b son variables y no tiene que preocuparse de los efectos secundarios, la anterior ROTATEF expresin es equivalente a esto:
(Let ((tmp a)) (setf tmp ABB) nil)

Con otro tipo de lugares, la expresin equivalente usando SETF sera un poco ms complejo. SHIFTF es similar excepto que en lugar de la rotacin de los valores que les desplaza a la izquierda - el ltimo argumento proporciona un valor que se traslad al segundo argumento a la ltima, mientras que el resto de los valores se mueven una hacia la izquierda. El valor original del primer argumento se devuelve. Por lo tanto, lo siguiente:
(Shiftf ab 10)

es equivalente - de nuevo, ya que no tiene que preocuparse sobre los efectos secundarios - a esto:
(Let ((tmp a)) (setf abb 10) tmp)

Ambos ROTATEF y SHIFTF se puede utilizar con cualquier nmero de argumentos y, al igual que todas las macros modificar, estn garantizados para evaluar exactamente una vez, en orden de izquierda a derecha. Con los conceptos bsicos de las funciones de Common Lisp y las variables bajo su cinturn, ahora ests listo para pasar a la funcin que sigue diferenciando Lisp de otros idiomas: macros.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 varias constantes fundamentales definidos por el propio lenguaje no siguen esta convencin - no

menos importante de los cuales son T y NIL . Esto es a veces molesto cuando uno quiere usar t como

un nombre de variable local. Otra es la PI , que tiene el mejor tiempo de flotacin aproximacin de la constante matemtica pi.
15 Algunos Lispers la vieja escuela, prefiere usar setq con variables, pero de estilo moderno tiende a

utilizar SETF para todas las asignaciones.


16 Busque DEFSETF , define-SETF-expansor para ms informacin. 17 La prevalencia de Algol derivados de la sintaxis para la asignacin con el "lugar" en el lado

izquierdo del = y el nuevo valor en el lado derecho ha dado lugar a la terminologa lvalue , abreviatura de "valor de la izquierda", algo que significa que se pueden asignar a y valor- , algo por lo que proporciona un valor. Un hacker compilador dice: " SETF trata su primer argumento como un valor-i ".
18 programadores de C puede desear pensar de variables y de otros lugares como la celebracin de

un puntero al objeto real, asignar a una variable simplemente cambia qu objeto se seala, mientras que la asignacin de una parte de un objeto compuesto es similar a indirecting a travs del puntero a la objeto real. C + + debe tener en cuenta que el comportamiento de = en C + + cuando se trata de objetos - es decir, una copia miembro a miembro - es bastante peculiar.

7. Macros: Constructores de control estndar


Aunque muchas de las ideas que se origin en Lisp, a partir de la expresin condicional para la recoleccin de basura, se han incorporado a otros idiomas, la caracterstica del lenguaje que sigue marcando Common Lisp aparte es su sistema de macro. Por desgracia, la palabra macro describe un montn de cosas en la informtica a la que las macros de Common Lisp es tener slo una vaga semejanza y metafrico. Esto provoca un sinfn de malentendidos cuando Lispers tratar de explicar a los no Lispers qu macros gran caracterstica son. 1 Para entender las macros de Lisp, que realmente necesita para llegar a los frescos, sin ideas preconcebidas sobre la base de otras cosas que tambin son llamados macros . As que vamos a iniciar nuestra discusin de macros Lisp por dar un paso atrs y mirar a la extensibilidad de apoyo de varias maneras idiomas. Todos los programadores deben acostumbrarse a la idea de que la definicin de un lenguaje puede incluir una biblioteca estndar de funciones que pusieron en prctica en trminos del "ncleo" del lenguaje - la funcionalidad que podra haber llevado a cabo por cualquier programador en la parte superior de la lengua si hubieras 't se define como parte de la biblioteca estndar. De la biblioteca estndar C, por ejemplo, se puede implementar casi en su totalidad en la porttil de C. Del mismo modo, la mayor parte del conjunto cada vez mayor de las clases e interfaces que se incluyen con el estndar de Java Java Development Kit (JDK) estn escritos en el "puro" de Java. Una de las ventajas de la definicin de las lenguas en trminos de un ncleo adems de una biblioteca estndar es que las hace ms fciles de entender y aplicar. Pero el beneficio real es, en trminos de expresividad - ya que gran parte de lo que consideramos como "la lengua" es en realidad una biblioteca - el lenguaje es fcil de extender. Si C no tiene una funcin para hacer una cosa u otra que usted necesita, usted puede escribir esa funcin, y ahora tiene una versin ligeramente ms rica de C. De manera similar, en un lenguaje como Java o Smalltalk, donde casi todas las interesantes partes de la "lengua" se definen en trminos de clases, mediante la definicin de nuevas clases de ampliar el lenguaje, por lo que es ms adecuado para escribir programas para hacer lo que es lo que estamos tratando de hacer.

Mientras que Common Lisp soporta ambos mtodos para extender el idioma, macros dar Common Lisp pero de otra manera. Como ya he discutido brevemente en el captulo 4, cada macro define su propia sintaxis, la determinacin de cmo las s-expresiones se pasa se convierten en formas Lisp. Con macros como parte del ncleo del lenguaje es posible construir una nueva sintaxis - control de construcciones tales como AL , dolist y LOOP , as como las formas de definicin como DEFUN y DEFPARAMETER - como parte de la "librera estndar" en lugar de tener que cableado que en el ncleo. Esto tiene implicaciones para la forma en que el lenguaje en s mismo se lleva a cabo, pero como un programador de Lisp te importa ms que te da otra manera de ampliar el lenguaje, por lo que es un lenguaje para expresar mejor las soluciones a sus problemas de programacin en particular. Ahora, puede parecer que los beneficios de tener otra manera de extender el lenguaje sera fcil de reconocer. Pero por alguna razn una gran cantidad de gente que no se han utilizado macros Lisp - gente que piensa que nada de pasar su da la creacin de nuevas abstracciones funcionales o la definicin de jerarquas de clases para resolver sus problemas de programacin - se asust por la idea de poder para definir nuevas abstracciones sintcticas. La causa ms comn de macrophobia parece ser malas experiencias con otros "macro" de sistemas. Simple miedo a lo desconocido, sin duda juega un papel tambin. Para evitar el desencadenamiento de las reacciones macrophobic, voy a la facilidad en el tema por discutir varios de los estndares de control de la construccin de macros definidas por el Common Lisp. Estas son algunas de las cosas que, si Lisp no tiene macros, tendra que ser integrado en el ncleo del lenguaje. Cuando se utilizan, no tiene que preocuparse de que se implementen como macros, pero son un buen ejemplo de algunas de las cosas que puede hacer con macros. 2 En el captulo siguiente, te voy a mostrar cmo se puede definir sus propias macros. CUANDO MENOS y Como ya has visto, la forma ms bsica de ejecucin condicional - si x , hacer y , en caso contrario hacer z - es proporcionado por el SI operador especial, que tiene esta forma bsica:
(Si condicin entonces de forma [ ms forma ])

La condicin es evaluada y, si su valor no es NIL , el entonces forma se evala y se devuelve el valor resultante. De lo contrario, los dems de forma , en su caso, se evala y se devuelve su valor. Si condicin es NIL y no hay otra forma , el SI devuelve NIL .
(If (> 2 3) "S" "No.") ==> "No." (If (> 2 3) "S") ==> NIL (If (> 3 2) "S" "No.") ==> "S"

Sin embargo, SI no es en realidad como una construccin sintctica muy bien, porque la continuacin, de forma y de otra forma, cada uno limitado a ser una sola forma de Lisp. Esto significa que si usted desea llevar a cabo una secuencia de acciones, ya sea en la clusula, es necesario envolverlos en algunos otros elementos de sintaxis. Por ejemplo, supongamos que en medio de un programa de filtrado de spam que quera tanto a archivar un mensaje como spam y actualizar la base de datos de spam cuando un mensaje es spam. No se puede escribir lo siguiente:
(Si (spam-p corriente de mensajes) (Archivo en la carpeta de spam actuales-mensaje) (Update-spam-de mensajes de base de datos actual))

porque la llamada a update-spam base de datos sern tratados como la clusula de los dems, no como parte de la clusula de entonces. Otro operador especial, progn , ejecuta cualquier nmero de formas en el orden y devuelve el valor de la ltima forma. Por lo que podra conseguir el comportamiento deseado por escrito lo siguiente:
(Si (spam-p corriente de mensajes) (Progn (Archivo en la carpeta de spam actuales-mensaje) (Update-spam-de mensajes de base de datos actual)))

Que no es demasiado horrible. Sin embargo, dado el nmero de veces que probablemente tendr que usar este lenguaje, no es difcil imaginar que se cansara de ella despus de un tiempo. "Por qu", usted puede preguntarse, "no Lisp proporciona una manera de decir lo que realmente quiero, es decir," cuando x es cierto, hacer esto, aquello y lo otro '? " En otras palabras, despus de un tiempo que dara cuenta el patrn de un SI ms una progn y deseo de una manera de abstraer los detalles en lugar de escribirlos cada vez. Esto es exactamente lo que proporcionan las macros. En este caso, Common Lisp viene con un macro nivel, CUANDO , que le permite escribir lo siguiente:
(Cuando (spam-p actual-mensaje)

(Archivo en la carpeta de spam actuales-mensaje) (Update-spam-de mensajes de base de datos actual))

Pero si no se construy en la biblioteca estndar, se podra definir CUANDO usted mismo con una macro como esta, usando la notacin de acento grave que en el captulo 3: 3
(Defmacro cuando (condicin y el resto del cuerpo) `(Si, la condicin (progn, el cuerpo de @)))

Una contrapartida de la CUANDO macro es MENOS , que invierte la condicin, la evaluacin de su cuerpo forma slo si la condicin es falsa. En otras palabras:
(Defmacro menos (condicin y el resto del cuerpo) `(Si (no enfermedad) (progn, el cuerpo de @)))

Ciertamente, estos son macros bastante trivial. No hay magia aqu, negro profundo, sino que slo abstraer algunos nivel de lenguaje detalles de contabilidad, lo que le permite expresar sus verdaderas intenciones un poco ms claramente. Sin embargo, su trivialidad muy hace un punto importante: debido a que el sistema de macro est integrada en el lenguaje, puede escribir macros triviales como AL y MENOS que le dan pequeas ganancias, pero real, en la claridad que se multiplica por los miles de veces que los utilizan . En los captulos 24, 26, 31 y vers cmo las macros tambin se pueden utilizar en mayor escala, la creacin de toda lenguajes especficos de dominio incrustado. Pero primero vamos a terminar nuestra discusin de la norma de control de la construccin de macros. COND Otra vez primas SI expresiones puede poner feo es cuando usted tiene un multibranch condicional: si uno no x , de lo contrario, si b hacerlo y , otra cosa z . No hay ningn problema lgico escribir una cadena de expresiones condicionales con un solo SI , pero no es bastante.
(Si es un (Do-x) (Si b (Hacer-y) (Do-z)))

Y sera an peor si se deba incluir las mltiples formas en las clusulas de entonces, que requieren progn s. Por lo tanto, no es de extraar, Common Lisp proporciona una macro para expresar condicionales multibranch: COND . Este es el esqueleto bsico:

(Cond ( prueba de un formulario *) . . . ( prueba N formulario *))

Cada elemento del cuerpo representa una rama de la condicional y se compone de una lista que contiene un formulario de estado y cero o ms formas de ser evaluada si esa rama que se elija. Las condiciones se evalan en el orden de las ramas aparecen en el cuerpo hasta que uno de ellos se evala como verdadera. En ese momento, las restantes formas en que la rama se evalan, y el valor de la ltima forma en la rama se devuelve como el valor de la COND en su conjunto. Si la rama no contiene ninguna forma despus de la enfermedad, el valor de la condicin se devuelve en su lugar. Por convencin, la rama en representacin de la clusula final ms en un if / else, si la cadena se escribe con una condicin de T . Cualquier no-NIL valor trabajo, sino una T sirve como un punto de referencia til cuando la lectura del cdigo. Por lo tanto, se puede escribir la anterior anidado IF expresin utilizando CONDcomo esto:
(Cond (a) (do-x) (B (do-y)) (T (do-z)))

AND, OR y NOT Al escribir las condiciones de SI , CUANDO , A MENOS , y COND formas, los tres operadores que ser muy til son los operadores de la lgica booleana, Y , O y NO . NO es una funcin que en sentido estricto no pertenece a este captulo, pero estrechamente vinculado a E y O . Se toma un solo argumento e invierte su valor de verdad, volviendo T si el argumento es NIL y NIL lo contrario. Y y O , sin embargo, son las macros. Que poner en prctica la conjuncin lgica y disyuncin de cualquier nmero de subformularios y se definen como macros para que puedancortocircuito . Es decir, evaluar slo como muchos de sus subformularios - de izquierda a derecha - si es necesario para determinar el valor de verdad en general. Por lo tanto, y se detiene y devuelve NIL tan pronto como uno de sus subformularios evala a NIL . Si todos los subformularios evaluar a los noNIL , devuelve el valor de la ltima subformulario. O , por el contrario, se detiene tan pronto como uno de sus subformularios evala a los no- NIL y devuelve el valor

resultante. Si ninguno de los subformularios como resultado true, oretorna NIL . He aqu algunos ejemplos:
(Por no decir nulas) ==> T (No (= 1 1)) ==> NIL (Y (= 1 2) (3 = 3)) ==> NIL (O (= 1 2) (3 = 3)) ==> T

Looping Las construcciones de control son el otro tipo principal de construcciones de bucles. Instalaciones de bucle Common Lisp son - adems de ser muy potente y flexible una leccin interesante en el que-su-pastel y comer-l-tambin el estilo de programacin de macros que proporcionan. Como resultado, ninguno de los 25 operadores especiales Lisp apoyan directamente la estructura de bucle. Todas las construcciones de control de bucle Lisp son macros construidas en la parte superior de un par de operadores especiales que proporcionan una primitiva instalacin goto. 4 Al igual que muchas abstracciones bueno, sintcticas o de lo contrario, las macros de bucle Lisp se construyen como un conjunto de abstracciones en capas a partir de la base de siempre por los dos operadores especiales. En la parte inferior (dejando a un lado los operadores especiales) es una construccin de bucle muy general, DO . Si bien es muy poderoso, DO sufre, al igual que muchos de propsito general abstracciones, de ser una exageracin para situaciones simples. As Lisp tambin proporciona dos otras macros, dolist y DOTIMES , que son menos flexibles que NO , pero proporcionan apoyo prctico para los casos comunes de recorrer los elementos de una lista y contar los bucles. Mientras que una aplicacin puede poner en prctica estas macros sin embargo, quiere, son normalmente se implementa como macros que se expanden en un equivalente DO bucle. Por lo tanto, NO proporciona una estructura bsica de bucle estructurado en la parte superior de las primitivas correspondientes, presentada por los operadores especiales Common Lisp, y dolist y DOTIMES proporcionar dos ms fcil de usar, aunque menos general, las construcciones. Y, como veremos en el prximo captulo, usted puede construir su propia construcciones de bucles en la parte superior de la DO para situaciones en las dolist y DOTIMES no satisfacen sus necesidades.

Por ltimo, el LOOP macro proporciona un verdadero mini-lenguaje para expresar estructuras de bucle en un no-lispy, Ingls-like (o al menos similar a Algol) del lenguaje. Algunos hackers de Lisp amor LOOP , otros lo odian. LOOP aficionados s les gusta porque ofrece una manera concisa de expresar cierta frecuencia necesaria construcciones de bucles. Sus detractores no les gusta porque no es lo suficientemente lispy. Pero lo que por un lado se reduce en, es un ejemplo notable de la potencia de las macros de aadir nuevas construcciones de la lengua. Dolist y DOTIMES Voy a empezar con la facilidad de uso dolist y DOTIMES macros. Dolist bucles a travs de los elementos de una lista, la ejecucin del cuerpo del bucle con una variable que contiene los puntos sucesivos de la lista. 5 Este es el esqueleto bsico (dejando de lado algunas de las opciones ms esotricas):
(Dolist ( var lista de forma ) del cuerpo de forma *)

Cuando el ciclo se inicia, la lista de forma se evala una vez para producir una lista. Entonces, el cuerpo del bucle se evala una vez para cada elemento de la lista con la variable varque contiene el valor del elemento. Por ejemplo:
CL-USUARIO> (dolist (x '(1 2 3)) (print x)) 1 2 3 NIL

Utilizado de esta manera, el dolist forma en su conjunto da como resultado NIL . Si desea salir de una dolist bucle antes de la final de la lista, puede utilizar RETORNO .
CL-USUARIO> (dolist (x '(1 2 3)) (print x) (if (evenp x) (volver))) 1 2 NIL

DOTIMES es la construccin de bucle de alto nivel para bucles de contar. El modelo bsico es muy similar al dolist s. '
(Dotimes ( var contar de forma ) del cuerpo de forma *)

El conteo de forma debe ser un nmero entero. Cada vez que a travs del bucle var contiene enteros sucesivos de 0 a uno menos que ese nmero. Por ejemplo:
CL-USUARIO> (dotimes (i 4) (print i))

0 1 2 3 NIL

Al igual que con dolist , puede utilizar RETORNO para salir del bucle inicial. Debido a que el cuerpo de ambos dolist y DOTIMES bucles puede contener cualquier tipo de expresiones, tambin puede bucles nido. Por ejemplo, para imprimir las tablas de multiplicar del 1 x 1 = 1 a 20 20 = 400 , usted puede escribir este par de anidado DOTIMES bucles:
(Dotimes (x 20) (Dotimes (y 20) (Formato t "~ 3d" (* (1 + x) (1 + y)))) (Formato t "~%"))

DO Mientras dolist y DOTIMES son convenientes y fciles de usar, no son lo suficientemente flexibles para uso de todos los bucles. Por ejemplo, qu pasa si usted quiere dar un paso mltiples variables en paralelo? O usar una expresin arbitraria de la prueba para el final del bucle? Si ninguno de los dolist ni DOTIMES satisfacer sus necesidades, usted todava tiene acceso a la ms general DO bucle. Donde dolist y DOTIMES proporcionan slo una variable de bucle, NO permite enlazar cualquier nmero de variables y le da un control completo sobre cmo cambian en cada paso a travs del bucle. Tambin llegar a definir la prueba que determina cundo debe finalizar el bucle y puede proporcionar una forma de evaluar al final del bucle para generar un valor a cambio de la DO expresin en su conjunto. La plantilla bsica es la siguiente:
(Hacer ( variable definicin *) ( final de la prueba de forma Declaracin *) resultado de forma *)

Cada definicin de la variable- introduce una variable que va a estar en el mbito en el cuerpo del bucle. La forma completa de una definicin nica variable es una lista que contiene tres elementos.
( var init-forma paso a forma )

La forma de inicio- sern evaluados al principio del bucle y los valores resultantes vinculados a la variable var . Antes de cada iteracin siguiente del bucle, el paso a forma sern evaluados y el nuevo valor asignado a la var . El paso a la forma es

opcional, si se deja, la variable mantendr su valor de iteracin a iteracin a menos que explcitamente se le asigna un nuevo valor en el cuerpo del bucle. Al igual que con la definicin de las variables en un LET , si la forma de inicio, se queda fuera, la variable est obenlazado a NIL . Tambin como con LET , puede utilizar un nombre simple variable como forma abreviada de una lista que contiene slo el nombre. Al comienzo de cada iteracin, despus de todas las variables de bucle ha dado sus nuevos valores, el final de la prueba de forma se evala. Mientras se evala como NIL , el producto iteracin, la evaluacin de la declaraciones en orden. Cuando el final de la prueba de forma como resultado true, el resultado de las formas que se evalan, y el valor de la forma del ltimo resultado se devuelve como el valor de la DOexpresin. En cada paso de la iteracin de las formas a paso para todas las variables que se evalan antes de asignar cualquiera de los valores a las variables. Esto significa que puede referirse a cualquiera de las variables de bucle, en las formas paso. 6 Es decir, en un circuito como este:
(Do ((n 0 (1 + n)) (Act 0 siguiente) (Al lado de 1 (+ act siguiente))) ((= 10 n) act))

las formas de paso (1 + n) , al lado , y (+ act siguiente) se evalan con los viejos valores de n , act , y la prxima . Slo despus de todas las formas de paso han sido evaluadas son las variables dadas sus nuevos valores. (Lectores inclinaciones matemticas puede notar que esta es una forma particularmente eficaz de la computacin en el undcimo nmero de Fibonacci.) Este ejemplo ilustra tambin otra caracterstica de la DO - porque usted puede caminar varias variables, a menudo no se necesita un cuerpo en absoluto. Otras veces, usted puede dejar el formulario de resultado, sobre todo si ests utilizando el bucle como un control de la construccin. Esta flexibilidad, sin embargo, es la razn por la cual NO expresiones pueden ser un poco crptico. Dnde es exactamente lo que todos los parntesis ir? La mejor manera de entender un DO expresin es a tener en cuenta la plantilla bsica.
( ver ( variable definicin * ) ( final de la prueba de forma Declaracin * ) resultado de forma * )

Los seis parntesis en la plantilla son los nicos requeridos por el NO en s. Usted necesita un par de incluir las declaraciones de variables, un par de adjuntar la prueba final y formas resultado, y un par de incluir toda la expresin. Otras formas dentro de la DO pueden exigir a sus propios parntesis - definicin de las variables suelen ser las listas, por ejemplo. Y el formulario de la prueba es a menudo una llamada a la funcin. Pero el esqueleto de una DO bucle siempre ser el mismo. Aqu estn algunos ejemplos de DO bucles con el esqueleto en negrita:
( ver ( (i 0 (1 + i)) ) ( (> = i 4) ) (print i) )

Observe que el formulario de resultados se ha omitido. Esta es, sin embargo, no es un uso particular idiomtica de DO , ya que este circuito es mucho ms simple escrito usandoDOTIMES . 7
(Dotimes (i 4) (print i))

Como otro ejemplo, este es el incorpreo de Fibonacci de computacin en bucle:


( ver ( (n 0 (1 + n)) (Act 0 siguiente) (Al lado de 1 (+ act siguiente)) ) ( (= 10 n) act ))

Finalmente, el ciclo siguiente se muestra una DO bucle que une a ninguna variable. Se realiza un bucle mientras que la hora actual es menor que el valor de una variable global, la impresin de "espera" una vez por minuto. Tenga en cuenta que incluso cuando no hay variables de bucle, usted todava necesita la lista de variables vacas.
( ver () ( (> (get-universal-tiempo) * algunos-futuro-la fecha *) ) (Formato t "Waiting ~%") (Sleep 60) )

El LOOP Poderoso Para los casos simples que usted tiene dolist y DOTIMES . Y si no se ajustan a sus necesidades, usted puede volver a caer en la general, completamente DO . Qu ms se puede pedir? Bueno, resulta que un puado de frases hechas bucle llegar una y otra vez, como recorrer varias estructuras de datos: listas, vectores, tablas y paquetes. O la acumulacin de los valores de varias maneras, mientras que un bucle: recoger,

contar, sumar, minimizar o maximizar. Si usted necesita un bucle para hacer una de estas cosas (o varias a la vez), el LOOP macro puede darle una forma ms fcil de expresar. El LOOP macro en realidad viene en dos sabores - sencilla y extendida . La versin simple es tan simple como puede ser - un bucle infinito que no es vinculante para todas las variables. El esqueleto se parece a esto:
(Bucle cuerpo de forma *)

Las formas en el cuerpo son evaluados cada iteracin del bucle, que se repetir por siempre a menos que utilice RETORNO para salir. Por ejemplo, se podra escribir la anterior DO con un simple bucle de LOOP .
(Bucle (Cuando (> (get-universal-tiempo) * algunos-futuro-la fecha *) (Ida y vuelta)) (Formato t "Waiting ~%") (Sleep 60))

La extendida LOOP es bastante bestia diferente. Se distingue por el uso de ciertas palabras clave de bucle que implementan un lenguaje de propsito especial para expresar expresiones de bucle. Vale la pena sealar que no todos los Lispers encanta la extendida LOOP idioma. Al menos uno de los Comunes diseadores originales Lisp es lo odiaba. LOOP 'detractores s se quejan de que su sintaxis es totalmente no lispy (en otras palabras, no lo suficiente entre parntesis). LOOP 'fans s responden que ese es el punto: complicadas construcciones de bucles son lo suficientemente fuertes de entender sin envolverlos en DO sintaxis 's crptico. Es mejor, dicen, para tener un poco ms detallada la sintaxis que le da algunas pistas de qu diablos est pasando. Por ejemplo, aqu hay un idiomticas DO bucle que recoge los nmeros del 1 al 10 en una lista:
(Do ((nums nil) (i 1 (1 + i))) ((> I 10) (nums nreverse)) (Push nums i)) ==> (1 2 3 4 5 6 7 8 9 10)

Un avezado Lisper no tendr problemas para comprender que el cdigo - es slo una cuestin de entender la forma bsica de una DO de bucle y el reconocimiento de la EMPUJE /NREVERSE idioma para la creacin de una lista. Pero no es exactamente transparente. El LOOP versin, por el contrario, es casi comprensible, ya que una sentencia de Ingls.

(Bucle para i desde 1 hasta 10 recogiendo i) ==> (1 2 3 4 5 6 7 8 9 10)

Los siguientes son algunos ejemplos de los usos ms simples de LOOP . Esto resume las diez primeras plazas:
(Circuito de x 1 a 10 sumando (expt x 2)) ==> 385

Esta cuenta el nmero de vocales en una cadena:


(Circuito de x a travs de "la rpida zorro marrn salta sobre el perro perezoso" contar (encontrar x "aeiou")) ==> 11

Esto calcula el undcimo nmero de Fibonacci, similar a la DO loop utilizado anteriormente:


(Loop for i por debajo de 10 y = 0, entonces b y b = 1, entonces (ba +) finalmente (ida y vuelta a))

Los smbolos a travs de , y , a continuacin , recoger , contar , finalmente , para , desde , sumando , entonces , y que son algunas de las palabras clave de bucle, cuya presencia identifica a estos como ejemplos de la extensin del LOOP . 8 Voy a guardar los detalles de LOOP para el Captulo 22, pero vale la pena sealar aqu como otro ejemplo de la forma de macros se pueden utilizar para ampliar el lenguaje de base.Mientras LOOP ofrece su propio lenguaje para expresar construcciones de bucles, no se cort del resto de Lisp. Las palabras clave de bucle se analizan de acuerdo a la gramtica del bucle, pero el resto del cdigo en un LOOP es regular el cdigo Lisp. Y vale la pena destacar una vez ms que mientras que el LOOP macro es bastante ms complicado de lo que las macros como CUANDO o MENOS , que es slo otra macro. Si no hubiera sido incluida en la biblioteca estndar, se podra aplicar a s mismo oa obtener una biblioteca de terceros que lo haga. Con que voy a concluir nuestro recorrido por la construccin bsica de control de macros. Ahora est listo para echar un vistazo ms de cerca la forma de definir sus propias macros.

1 Para ver lo que este malentendido se parece, encontrar algo largo hilo de Usenet cross-publicado

entre comp.lang.lisp y cualquier otro .* comp.lang grupo con macro en el tema. Una parfrasis aproximada es la siguiente: Lispnik: "Lisp es el mejor debido a sus macros";

Othernik: "Crees que Lisp es bueno porque de macros, pero las macros son horribles y el mal; Lisp debe ser horrible y el mal? ".
2 Otra clase importante de construcciones del lenguaje que se definen mediante macros son todas las

construcciones de definicin como DEFUN , DEFPARAMETER , defvar , y otros. En el captulo 24 va a definir sus propias macros de definicin que le permiten escribir de forma concisa el cdigo para leer y escribir datos binarios.
3 De hecho no puede alimentar a esta definicin de Lisp, ya que es ilegal para redefinir los nombres

en el common-lisp paquete donde CUANDO viene. Si usted realmente quiere intentar escribir una macro, que haba necesidad de cambiar el nombre a algo ms, como mi-cuando .
4 Los operadores especiales, si quieres saberlo, se TAGBODY y GO . No hay necesidad de hablar de

ellos ahora, pero voy a cubrir en el captulo 20.


5 dolist es similar a la de Perl foreach o Python para . Java aade un tipo similar de construccin de

bucle con el "mejor" para bucle en Java 1.5, como parte de JSR-201. Note lo que una diferencia de hacer macros. Un programador de Lisp que nota un patrn comn en su cdigo se puede escribir una macro para darse una abstraccin a nivel de fuente de ese patrn. Un programador de Java que se da cuenta el mismo patrn que tiene que convencer a Sol, que esta abstraccin particular, vale la pena aadir a la lengua. A continuacin, Sun ha de publicar un JSR y convocar a una industria en todo el "grupo de expertos" de todo lo hash. Ese proceso - de acuerdo con Sun - toma un promedio de 18 meses. Despus de eso, los autores de compiladores todos tenemos que ir a actualizar sus compiladores para apoyar la nueva funcin. E incluso una vez compilador favorito del programador de Java compatible con la nueva versin de Java, es probable que todava no se puede utilizar la funcin de nuevo hasta que se les permite romper la compatibilidad de cdigo fuente con las versiones antiguas de Java. As que una molestia que Common Lisp programadores pueden resolver por s mismos a los cinco minutos plagas programadores de Java desde hace aos.
6 Una variante de la DO , DO * , asigna a cada variable de su valor antes de evaluar la forma de paso

para las variables siguientes. Para ms detalles, consulte a su referencia favorita Common Lisp.
7 El DOTIMES Tambin es preferible debido a que la expansin de la macro probablemente incluya

declaraciones que permiten que el compilador generar un cdigo ms eficiente.


8 palabras de bucle es un nombre poco apropiado, ya que no son smbolos de palabras clave. De

hecho, LOOP no le importa lo que el paquete de smbolos. Cuando el LOOP macro analiza su cuerpo, se considera que cualquier smbolo apropiado nombre equivalente. Incluso puede utilizar palabras clave true si quieres - : a , : a travs , y as sucesivamente - porque ellos tambin tienen el nombre correcto. Pero la mayora de la gente slo tiene que utilizar smbolos sin formato. Debido a que el bucle de palabras clave slo se utilizan como marcadores sintcticos, no importa si son utilizados para otros fines - como la funcin o los nombres de variable.

8. Macros: definicin de sus propios


Ahora es el momento para empezar a escribir sus propias macros. Las macros estndar que cubre en el captulo anterior pista a algunas de las cosas que puede hacer con macros, pero eso es slo el principio. Common Lisp no es compatible con macros para todos los programadores de Lisp puede crear sus propias variantes de control estndar construye ms de lo que C es compatible con funciones para todos los programadores C puede escribir variantes triviales de las funciones en la biblioteca de C estndar. Las macros son parte del lenguaje que le permite crear abstracciones en la parte superior del ncleo del lenguaje y de la biblioteca estndar que se mueven ms cerca hacia la posibilidad de expresar directamente las cosas que quiere expresar. Tal vez el mayor obstculo para una comprensin adecuada de las macros es, irnicamente, que estn tan bien integrados en el lenguaje. En muchos sentidos, parece simplemente un tipo divertido de la funcin - they're escrito en Lisp, que toman argumentos y devolver los resultados, y que le permiten abstraer los detalles de distraccin. Sin embargo, a pesar de estas similitudes, las macros funcionan a un nivel diferente que las funciones y crear un tipo totalmente diferente de abstraccin. Una vez que entiendas la diferencia entre macros y funciones, la estrecha integracin de las macros en el idioma ser un gran beneficio. Pero mientras tanto, es una fuente frecuente de confusin para Lispers nuevo. La siguiente historia, aunque no es cierto en un sentido histrico o tcnico, trata de aliviar la confusin que le da una manera de pensar acerca de cmo las macros de trabajo. La historia de Mac: Una historia de Just-As rase una vez, hace mucho tiempo, haba una compaa de programadores de Lisp. Fue hace mucho tiempo, de hecho, que Lisp no macros. Todo lo que no se puede definir con una funcin o el resultado de un operador especial tuvo que ser escrito en su totalidad cada momento, que era ms bien un lastre. Por desgracia, los programadores de esta empresa - aunque brillante - tambin eran bastante flojos. A menudo, en medio de sus programas - cuando el tedio de escribir un montn de cdigo tiene que ser demasiado - en lugar de otro que escriba una nota que describe el cdigo que tenan que escribir en ese lugar en el programa. An ms,

lamentablemente, porque eran perezosos, los programadores tambin odiaba ir hacia atrs y realmente escribir el cdigo se describe en las notas. Pronto la empresa tena una gran pila de programas que nadie poda correr porque estaba lleno de notas sobre el cdigo que todava tena que ser escrito. En su desesperacin, los grandes jefes contratado a un programador junior, Mac, cuyo trabajo consista en encontrar las notas, escribir el cdigo necesario, y la inserta en el programa en lugar de las notas. Mac nunca haba corrido los programas - que no se realizaron, sin embargo, por supuesto, para que no pudiera. Pero incluso si se hubieran cumplido, Mac no hubiera sabido lo que las entradas para darles de comer. Por lo que acabo de escribir su cdigo basado en el contenido de las notas y lo envi de nuevo a la programacin original. Con la ayuda de Mac, todos los programas que se completar en breve, y la compaa hizo un montn de dinero vendiendo ellos - tanto dinero que la compaa podra duplicar el tamao de su personal de programacin. Pero por alguna razn nadie se le ocurri contratar a alguien para ayudar a Mac, y pronto estaba sola mano ayudar a los programadores de varias decenas. Para evitar gastar todo su tiempo en busca de notas en el cdigo fuente, Mac hizo una pequea modificacin en el compilador de los programadores utilizan. A partir de entonces, cada vez que el compilador de golpear una nota, que le e-mail la nota y esperar a que e-mail con el cdigo de reempbucle. Desafortunadamente, incluso con este cambio, Mac tena un tiempo difcil mantenerse al da con los programadores. Ha trabajado con tanto cuidado como pudo, pero a veces - sobre todo cuando las notas no eran claros - que se cometen errores. Los programadores de cuenta, sin embargo, que la forma ms precisa que escribi sus notas, lo ms probable era que Mac le devolver el cdigo correcto. Un da, uno de los programadores, teniendo un tiempo difcil describir en palabras el cdigo que quera, incluido en una de sus notas un programa Lisp que genera el cdigo que l quera. Eso estaba bien para Mac, que acaba de ejecutar el programa y enva el resultado al compilador. La siguiente innovacin lleg cuando un programador de poner una nota en la parte superior de uno de sus programas contienen una definicin de funcin y un comentario que deca: "Mac, no escribir ningn cdigo aqu, pero mantener esta funcin para ms adelante, me voy para su uso en algunas de mis otras notas. "

Otras notas en el mismo programa, dijo cosas como: "Mac, vuelva a colocar esta nota con el resultado de la ejecucin que otra funcin con los smbolos x , y y como argumentos. " Esta tcnica capturado tan rpidamente que a los pocos das, la mayora de los programas contenidos docenas de notas de la definicin de las funciones que se han utilizado slo por el cdigo en otras notas. Para hacer ms fcil para Mac para recoger las notas que contiene definiciones nico que no requiere respuesta inmediata, los programadores marcado con el prefacio de serie: "Definicin de Mac, de slo lectura." Esto - como los programadores siguen siendo bastante flojos - se redujo rpidamente a "DEF MAC R / S.." Y luego "defmacro". Muy pronto, no haba dejado Ingls reales en las notas de Mac. Todo lo que hizo durante todo el da era leer y responder a mensajes de correo electrnico del compilador que contiene las notas defmacro y las llamadas a las funciones definidas en el DEFMACROs. Dado que los programas de Lisp en las notas hizo todo el trabajo real, mantenerse al da con los e-mails no era un problema. Mac de repente haba un montn de tiempo en sus manos y se sentaba en su oficina soando con playas de arena blanca, aguas claras del ocano azul, y las bebidas con sombrillas de papel poco en ellos. Varios meses despus, los programadores se dio cuenta de que nadie haba visto a Mac desde hace bastante tiempo. Cuando fueron a su oficina, se encontraron con una fina capa de polvo sobre todo, una mesa llena de folletos de viajes para varios lugares tropicales, dentro y fuera del equipo. Sin embargo, el compilador trabaj como no poda ser? Result que Mac haba hecho un ltimo cambio en el compilador: en lugar de las notas de correo electrnico para Mac, el compilador ahora se guardan las funciones definidas por las notas defmacro y sali corriendo cuando llama por las otras notas. Los programadores decidieron que no haba ninguna razn para decir la Big Mac jefes no iba a venir a la oficina ms. As que para el da de hoy, Mac recibe un salario y de vez en cuando enva a los programadores una postal de un lugar tropical u otra. Macro vs Tiempo de expansin de tiempo de ejecucin La clave para la comprensin de las macros es tener muy claro la distincin entre el cdigo que genera cdigo (macros) y el cdigo que finalmente hace que el programa (todo lo dems). Al escribir macros, que est escribiendo programas que sern

utilizados por el compilador para generar el cdigo que luego ser compilado. Slo despus de que todas las macros han sido totalmente ampliado y el cdigo compilado resultante se puede ejecutar el programa en realidad se. El momento en que las macros de ejecucin se denomina tiempo de expansin de la macro , lo que es distinto del tiempo de ejecucin , cuando el cdigo regular, incluyendo el cdigo generado por las macros, se ejecuta. Es importante tener presente esta distincin muy en cuenta porque el cdigo de ejecucin en el momento de expansin de la macro se ejecuta en un entorno muy diferente al cdigo que se ejecuta en tiempo de ejecucin. Es decir, en el momento de expansin macro, no hay forma de acceder a los datos que existen en tiempo de ejecucin. Al igual que Mac, que no podan correr los programas que estaba trabajando, porque no saba lo que eran las entradas correctas, cdigo que se ejecuta en el momento de expansin de la macro se puede tratar slo con los datos que es inherente en el cdigo fuente. Por ejemplo, supongamos el siguiente cdigo fuente aparece en alguna parte de un programa:
(Defun foo (x) (Cuando (> x 10) (print 'grande)))

Lo normal sera pensar en x como una variable que contendr el argumento que se pasa en una llamada a foo . Pero en el momento de expansin de la macro, por ejemplo, cuando el compilador est ejecutndose la CUANDO macro, la nica informacin disponible es el cdigo fuente. Desde que el programa no se est ejecutando, sin embargo, no hay ninguna llamada a foo y por lo tanto no tiene valor asociado a x . En cambio, los valores del compilador pasa a AL estn las listas de Lisp que representa el cdigo fuente, es decir,(> x 10) y (print 'grande) . Supongamos que CUANDO se define, como se vio en el captulo anterior, con algo como la siguiente macro:
(Defmacro cuando (condicin y el resto del cuerpo) `(Si, la condicin (progn, el cuerpo de @)))

Cuando el cdigo de foo se compila, el CUANDO macro se llevar a cabo con las dos formas como argumentos. El parmetro de estado estarn obenlazados a la forma (> x 10) , y la forma (print 'grande) se recogern en una lista que se convertir en el valor de la y el resto del cuerpo de los parmetros. La expresin backquote entonces generar este cdigo:
(If (> x 10) (progn (print 'grande)))

por interpolacin en el valor de la condicin de empalme y el valor de cuerpo en el progn . Cuando Lisp se interpreta, en vez de compilado, la distincin entre el tiempo de expansin de la macro y tiempo de ejecucin no est tan claro porque estn temporalmente relacionados. Adems, el lenguaje estndar no especifica exactamente cmo un intrprete debe manejar macros - se podra ampliar todas las macros en la forma de ser interpretados y luego interpretar el cdigo resultante, o podra empezar en la forma de interpretar y ampliar macros cuando los golpea. En cualquier caso, las macros siempre se pasan los objetos sin evaluar Lisp que representan los subformularios de forma macro, y el trabajo de la macro sigue siendo para producir un cdigo que va a hacer algo en vez de hacer nada directamente. Defmacro Como se vio en el captulo 3, macros realmente se definen con defmacro formas, a pesar de que est - por supuesto - para Definir macro no, la definicin de Mac. El esqueleto bsico de un defmacro es muy similar al esqueleto de un DEFUN .
(Defmacro nombre ( parmetro *) "Cadena de documentacin opcional." cuerpo de forma *)

Como una funcin, una macro consiste en un nombre, una lista de parmetros, una cadena de documentacin opcional, y un conjunto de expresiones Lisp. 1 Sin embargo, como acabamos de comentar, el trabajo de una macro no es hacer nada directamente - su trabajo consiste en generar el cdigo que ms tarde har lo que usted desea. Las macros se pueden usar todo el poder de Lisp para generar su expansin, lo que significa que en este captulo slo se puede rayar la superficie de lo que puede hacer con macros.Puedo, sin embargo, describe un proceso general para la escritura de macros que funcione para todas las macros desde las ms sencillas hasta las ms complejas. El trabajo de un macro es traducir una forma macro - en otras palabras, una expresin Lisp, cuyo primer elemento es el nombre de la macro - en el cdigo que hace algo en particular. A veces se escribe una macro comienza con el cdigo que le gustara ser capaz de escribir, es decir, con una forma macro de ejemplo. En otras

ocasiones, se decidi a escribir una macro despus de haber escrito el mismo patrn de cdigo varias veces y se da cuenta que puede hacer su cdigo ms claro, abstrayendo el modelo. A pesar de lo cual se parte de, es necesario calcular el otro extremo antes de poder empezar a escribir una macro: lo que necesita saber tanto de dnde vienes ya dnde vas antes de que se puede esperar que escribir cdigo para hacer de forma automtica. Por lo tanto, el primer paso de escribir una macro es escribir por lo menos un ejemplo de una llamada a la macro y el cdigo en el que la llamada debe ampliar. Una vez que haya una llamada de ejemplo y la expansin deseada, estar listo para el segundo paso: escribir el cdigo de la macro actual. Para las macros simples, esto ser un asunto trivial de la escritura de una plantilla backquoted con los parmetros macro enchufado en los lugares correctos. Macros complejas sern programas importantes por derecho propio, con funciones de ayuda y estructuras de datos. Despus de haber escrito el cdigo para traducir la llamada ejemplo para la expansin adecuada, es necesario asegurarse de que la abstraccin de la macro ofrece no "filtrar" detalles de su implementacin. Abstracciones con fugas macro no tendrn ningn problema para ciertos argumentos, pero no otros, o van a interactuar con el cdigo en el entorno de la llamada de manera indeseable. Como resultado, las macros pueden escaparse en un pequeo puado de formas, todas las cuales son fciles de evitar, siempre y cuando usted sabe que las revisa.Voy a discutir cmo en la seccin "Conexin de los gasoductos." En resumen, los pasos para redactar una macro son los siguientes: 1.Escriba un ejemplo de llamada a la macro y el cdigo que debe expandirse hacia, o viceversa. 2.Escribir cdigo que genera la expansin de mano de los argumentos en la llamada muestra. 3.Asegrese de que la abstraccin macro no se "fuga". Una macro de ejemplo: do-primos Para ver cmo funciona este proceso de tres pasos funciona, voy a escribir una macro no-primos que proporciona un bucle de construccin similar a DOTIMES y dolist excepto que en lugar de iterar sobre nmeros enteros o elementos de una

lista, que se repite en sucesivos nmeros primos. Esto no pretende ser un ejemplo de una macro particularmente til - es slo un vehculo para demostrar el proceso. En primer lugar, tendr dos funciones de utilidad, uno para poner a prueba si un nmero dado es primo y otro que devuelve el siguiente nmero primo mayor o igual que su argumento.En ambos casos se puede utilizar un simple, pero eficiente, de fuerza bruta enfoque.
(Defun primep (nmero) (Cuando (numero> 1) (Bucle de factor de 2 a (nmero isqrt) nunca (zerop (mod nmero de factores ))))) (Defun siguiente-prime (el nmero) (Bucle de n de nmero cuando (primep n) return n))

Ahora usted puede escribir la macro. Siguiendo el procedimiento descrito anteriormente, se necesitan al menos un ejemplo de una llamada a la macro y el cdigo en el que se debe ampliar. Suponga que usted comienza con la idea de que desea ser capaz de escribir esto:
(No-primos (p 0 19) (Formato t "~ d" p))

para expresar un bucle que se ejecuta el cuerpo una vez para cada nmero primo mayor o igual a 0 e inferior o igual a 19, con la variable p tiene el nmero de primos. Tiene sentido este modelo macro en el formulario de la norma dolist y DOTIMES macros, macros que siguen el patrn de las macros existentes son ms fciles de entender y utilizar macros de sintaxis que introducir gratuitamente novela. Sin el no-primos macro, se podra escribir un bucle con DO (y las dos funciones de utilidad definidas previamente) como esta:
(Do ((p (al lado de alto riesgo 0) (al lado de alto riesgo (1 + p)))) ((> P 19)) (Formato t "~ d" p))

Ahora ya ests listo para comenzar a escribir el cdigo de macro que se traducir en la primera a la segunda. Parmetros Macro Dado que los argumentos pasados a una macro Lisp son objetos que representan el cdigo fuente de la llamada a la macro, el primer paso en cualquier macro para extraer cualquier parte de los objetos son necesarios para calcular la expansin.

Para las macros que simplemente interpolar sus argumentos directamente a una plantilla, este paso es trivial: basta con definir los parmetros adecuados para mantener los diferentes argumentos es suficiente. Pero este enfoque, al parecer, no ser suficiente para hacer-los nmeros primos . El primer argumento de la no-primos llamada es una lista que contiene el nombre de la variable de bucle, p ; el lmite inferior, 0 , y el lmite superior, 19 . Pero si nos fijamos en la expansin, la lista en su conjunto no aparece en la expansin, los tres ltimos elementos se dividen y poner en diferentes lugares. Se podra definir -se prepara con dos parmetros, uno para mantener la lista y uno y el resto de parmetros para mantener la forma del cuerpo, y luego desmontar la lista a mano, algo como esto:
(Defmacro do-primos (var y mbito y el resto del cuerpo) (Let ((var (var primero y mbito)) (Start (segundo var y mbito)) (Al final (tercer var y mbito))) `(Do ((, var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var)))) ((>, Var, final)) , @ Cuerpo)))

En un momento voy a explicar cmo el cuerpo genera la expansin correcta, por ahora slo se puede observar que las variables var , inicio y final de cada contener un valor, extrado de var y mbito , que se interpolan en la expresin de acento grave que genera no-primos 's de expansin. Sin embargo, no es necesario desmontar var y mbito "a mano" porque las listas de parmetros macro son lo que se llama la desestructuracin listas de parmetros. La desestructuracin, como su nombre indica, consiste en tomar distancia de una estructura - en este caso la estructura de la lista de las formas pasan a una macro. Dentro de una lista de parmetros desestructuracin, un nombre de parmetro simple puede ser reemplazada por una lista de parmetros anidados. Los parmetros de la lista de parmetros anidados se toman sus valores de los elementos de la expresin que se han enlazado al parmetro de la lista reemplazado. Por ejemplo, puede reemplazarvar y mbito de una lista (de inicio y trmino var) , y los tres elementos de la lista ser automticamente desestructurado en estos tres parmetros. Otra caracterstica especial de las listas de parmetros macro que puede utilizar y el cuerpo como sinnimo de descanso y . Semnticamente y el cuerpo y ely el resto

son equivalentes, pero muchos entornos de desarrollo a utilizar la presencia de un cuerpo y el parmetro a modificar la forma en que guin usos de la macro normalmente y el cuerpo parmetros se utilizan para mantener una lista de formularios que componen el cuerpo de la macro. Por lo que puede simplificar la definicin de do-primos y dar una pista tanto para los lectores humanos y sus herramientas de desarrollo sobre el uso previsto por la definicin que de esta manera:
(Defmacro do-primos del cuerpo del cuerpo ((final var start) y) `(Do ((, var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var)))) ((>, Var, final)) , @ Body))

Adems de ser ms conciso, desestructuracin listas de parmetros tambin le dan la comprobacin de errores automtica - con do-primos definido de esta manera, Lisp ser capaz de detectar una llamada cuyo primer argumento no es un elemento de la lista de tres y le dar una mensaje de error significativo al igual que si hubiera llamado a una funcin con argumentos muy pocos o demasiados. Adems, en entornos de desarrollo como baba que indican qu argumentos se espera que tan pronto como se escriba el nombre de una funcin o macro, si se utiliza una lista de parmetros desestructuracin, el medio ambiente ser capaz de decirle ms especficamente, la sintaxis de la macro llamada. Con la definicin original, el limo le diras a hacer-los nmeros primos se llama as:
(No-primos-var y mbito y el resto del cuerpo)

Pero con la nueva definicin, se puede decir que una llamada debe tener este aspecto:
(No-primos (a finales var start) y el cuerpo del cuerpo)

La desestructuracin de las listas de parmetros puede contener y opcional , y clave , y el resto y los parmetros y puede contener listas anidadas desestructuracin. Sin embargo, usted no necesita ninguna de esas opciones para escribir no-primos . La generacin de la expansin Debido a que no-primos es una macro bastante simple, despus de haber desestructurado los argumentos, lo nico que queda es interpolar en una plantilla para conseguir la expansin.

Para macros sencillas como hacer-primos , la sintaxis especial acento grave es perfecto. Para revisar, una expresin backquoted es similar a una expresin citada excepto que usted puede "fin de la cita" subexpresiones particular anterior con una coma, posiblemente seguido de un signo de arroba (@). Sin un signo de los casos, la coma hace que el valor de la subexpresin que se incluirn como es. Con un signo, el valor - que debe ser una lista - es "empalmados" en la lista de inclusin. Otra manera til de pensar acerca de la sintaxis backquote es como una forma particularmente concisa de escribir el cdigo que genera listas. Esta forma de pensar en ello tiene la ventaja de ser ms o menos exactamente lo que est sucediendo bajo las sbanas - cuando el lector lee una expresin backquoted, lo que se traduce en un cdigo que genera la estructura de la lista correspondiente. Por ejemplo, `(AB) podra leerse como (lista A, B ') . La lengua estndar no especifica exactamente qu cdigo que el lector debe producir siempre y cuando se genera la estructura de lista de la derecha. La Tabla 8-1 muestra algunos ejemplos de expresiones backquoted junto con la creacin de la lista equivalente de cdigo y el resultado se obtendra si se evaluarse la expresin backquoted o el cdigo equivalente. 2
Tabla 8.1. Ejemplos backquote

Backquote Sintaxis `(A (1 + 2) c) `(A, (1 + 2) c) `(A, C (lista 1 2)) `(A, (lista 1 2) c)

Lista equivalente-del Cdigo de Construccin (La lista 'A' (+ 1 2) c) (Lista A (+ 1 2) c) (La lista 'A' (lista 1 2) c) (Lista A (lista 1 2) c) (Append (list 'a) (lista 1 2) (lista' `(A, @ (lista 1 2) c) c))

Resultado (A (1 + 2) c) (A, c 3) (A, c (lista 1 2)) (A, C (1 2)) (A 1 2 c)

Es importante sealar que backquote es slo para su conveniencia. Pero es una gran comodidad. Para apreciar cun grande es, comparar la versin backquoted de do-primos a la versin siguiente, que utiliza explcitamente la creacin de la lista de cdigos:
(Defmacro do-primos-un rgano del cuerpo ((final var start) y) (Append '(hacer) (List (list (lista de var (Lista 'al lado de alto riesgo de inicio) (Lista 'al lado de alto riesgo (lista de '1 + var ))))) (List (list (lista "> var final))) cuerpo))

Como se ver en un momento, la implementacin actual de hacer-los primos no maneja ciertos casos el borde correctamente. Pero primero que debe verificar que

al menos funciona para el ejemplo original. Puedes probarlo de dos maneras. Usted puede probar indirectamente, simplemente usando - presumiblemente, si la conducta resultante es correcta, la expansin es la correcta. Por ejemplo, puede escribir el ejemplo original de hacer-los primos de la REPL y ver que realmente imprime la serie correcta de los nmeros primos.
USUARIO CL-> (do-primos (p 0 19) (formato t "~ d" p)) 2 3 5 7 11 13 17 19 NIL

O usted puede comprobar la macro directamente mirando a la expansin de una llamada en particular. La funcin MACROEXPAND-1 tiene una expresin Lisp como un argumento y devuelve el resultado de hacer un nivel de expansin de la macro. 3 Porque MACROEXPAND-1 es una funcin, para pasar a una forma macro literal debe citarlo. Se puede utilizar para ver la expansin de la llamada anterior. 4
CL-USUARIO> (macroexpand-1 "(no-primos (p 0 19) (formato t" ~ d "p))) (DO ((P (NEXT-PRIME 0) (NEXT-PRIME (1 + P)))) ((> P 19)) (FORMATO T "~ d" P)) T

O, ms convenientemente, en el fango se puede comprobar la expansin de la macro, coloque el cursor en el parntesis de apertura de una forma macro en el cdigo fuente y escribirCc RET para invocar la funcin Emacs limo macroexpand-1 , que pasar a la forma macro a MACROEXPAND-1 y "Pretty Print" el resultado en un bfer temporal. Sin embargo, llegar a ella, se puede ver que el resultado de la expansin de la macro es la misma que la expansin original escrito a mano, as que parece que no-primos obras. Conectar el Fugas En su ensayo "La Ley de abstracciones con fugas", Joel Spolsky acu el trmino abstraccin con fugas para describir a una abstraccin que las "filtraciones" detalles que se supone que es la abstraccin de distancia. Desde que escrib una macro es una forma de crear una abstraccin, lo que necesita para asegurarse de que las macros no se escapan sin necesidad. 5 Como resultado, una macro puede filtrarse detalles de su funcionamiento interno de tres maneras. Afortunadamente, es muy fcil saber si una macro dada sufre de cualquiera de las fugas y repararlas.

La definicin actual sufre de una de las tres fugas de macro es posible: es decir, se evala la final subformulario demasiadas veces. Supongamos que usted fuera a llamar alos nmeros primos no- con, en lugar de un nmero literal, como 19 , una expresin como (al azar 100) en el final su posicin.
(No-primos (p 0 (al azar 100)) (Formato t "~ d" p))

Es de suponer que la intencin aqu es un bucle sobre los nmeros primos de cero a cualquier nmero al azar es devuelto por (al azar 100) . Sin embargo, esto no es lo que la implementacin actual es, como MACROEXPAND-1 muestra.
CL-USUARIO> (macroexpand-1 "(no-primos (p 0 (random 100)) (formato t" ~ d "p))) (DO ((P (NEXT-PRIME 0) (NEXT-PRIME (1 + P)))) ((> P (random 100))) (FORMATO T "~ d" P)) T

Cuando este cdigo se ejecute la ampliacin, AZAR se llamar cada vez que se evala la prueba final para el bucle. As, en lugar de un bucle hasta que p es mayor que el nmero inicialmente elegido al azar, este bucle se repetir hasta que ocurra sacar un nmero aleatorio menor o igual al valor actual de la p . Mientras que el nmero total de iteraciones seguir siendo al azar, se extrae de una distribucin muy diferente a la distribucin uniforme AZAR devoluciones. Se trata de una fuga en la abstraccin, ya que, al utilizar la macro correctamente, la persona que llama tiene que ser consciente de que la final se forma va a ser evaluado ms de una vez. Una forma de conectar esta fuga sera definir simplemente como la conducta de los do-primos . Pero eso no es muy satisfactorio debe tratar de observar el principio de mnimo asombro en la aplicacin de las macros. Y los programadores normalmente esperan que las formas pasan a macros para ser evaluados sin veces ms de lo absolutamente necesario. 6 Por otra parte, ya que se prepara- se basa en el modelo de las macros estndar, DOTIMES y dolist , ni de lo que hace que cualquiera de las formas excepto aquellos en el cuerpo para ser evaluados ms de una vez, la mayora de los programadores esperan que se prepara- a comportarse de manera similar. Usted puede fijar la evaluacin mltiple con bastante facilidad, slo tiene que generar cdigo que evala fin de una vez y guarda el valor de una variable que se utilizar ms tarde.Recordemos que en una DO bucle, las variables definidas con una forma de inicializacin y de ninguna forma paso no cambian de iteracin a

iteracin. As que usted puede solucionar el problema de la evaluacin de mltiples con esta definicin:
(Defmacro do-primos del cuerpo del cuerpo ((final var start) y) `(Do ((terminacin-valor, final) (Var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var)))) ((> Var terminacin-valor)) , @ Body))

Por desgracia, esta solucin presenta dos nuevas filtraciones a la abstraccin macro. Una nueva fuga es similar a la evaluacin de varias fugas que acaba de arreglar. Porque las formas de inicializacin de variables en una DO bucle se evalan en el orden de las variables se definen, cuando la expansin de la macro se evala la expresin que se pasa como final ser evaluado antes de la expresin pasada como inicio , frente al orden en que aparecen en el macro llamada. Esta fuga no causa ningn problema al inicio y final son valores literales como 0 y 19. Sin embargo, cuando son formas que pueden tener efectos secundarios, evaluacin de las mismas fuera de orden puede volver a ir en contra del principio de mnima sorpresa. Esta fuga es trivialmente conectado mediante el canje de la orden de las dos definiciones de las variables.
(Defmacro do-primos del cuerpo del cuerpo ((final var start) y) `(Do ((, var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var))) (Terminacin-valor final)) ((> Var terminacin-valor)) , @ Body))

La fuga ltimo que usted necesita para conectar se cre con el nombre de la variable final-valor . El problema es que el nombre, que debera ser un detalle puramente interna de la ejecucin de macros, puede llegar a interactuar con el cdigo pasa a la macro o en el contexto donde se llama a la macro. La siguiente llamada, aparentemente inocente, queno primos- no funciona correctamente debido a esta filtracin:
(No-primos (que termina el valor 0 10) (Impresin final-valor))

Tampoco esta:
(Let ((terminacin-valor 0)) (No-primos (p 0 10) (Incf terminacin-valor p)) terminacin-valor)

Una vez ms, MACROEXPAND-1 puede mostrar el problema. La primera llamada se expande a lo siguiente:
(Do ((terminacin-valor (al lado de alto riesgo 0) (al lado de alto riesgo (1 + sufijo-valor))) (Terminacin-valor 10)) ((> Final-valor final-valor)) (Impresin final-valor))

Algunos balbucea puede rechazar este cdigo, ya que termina el valor se utiliza el doble de un nombre de variable en la misma DO bucle. Si no es rechazada de plano, el cdigo se repetir siempre, ya poner fin a un valor nunca ser mayor que s mismo. El llamado segundo problema se expande a lo siguiente:
(Let ((terminacin-valor 0)) (Do ((p (al lado de alto riesgo 0) (al lado de alto riesgo (1 + p))) (Terminacin-valor 10)) ((> P-valor final)) (Incf terminacin-valor p)) terminacin-valor)

En este caso el cdigo generado es perfectamente legal, pero el comportamiento no es en absoluto lo que quieres. Debido a la enlace de la terminacin-el valor establecido por el LET fuera del circuito est marcado por la variable con el mismo nombre dentro de la DO , la forma (incf terminacin-valor p) incrementos de la variable de buclefinal de valor en lugar de la variable externa con la mismo nombre, la creacin de otro bucle infinito. 7 Est claro que lo que usted necesita para reparar esta fuga es un smbolo que nunca sern usados fuera del cdigo generado por la macro. Usted podra intentar usar un nombre muy poco probable, pero eso no es garanta. Usted tambin puede protegerse, hasta cierto punto mediante el uso de paquetes, tal como se describe en el captulo 21. Pero hay una solucin mejor. La funcin GENSYM devuelve un nico smbolo cada vez que se llama. Este es un smbolo que nunca ha sido ledo por el lector de Lisp y nunca lo ser, porque no es internado en un paquete. As, en lugar de utilizar un nombre literal como valor final , puede generar un nuevo smbolo cada vez que se prepara- se ampla.
(Defmacro do-primos del cuerpo del cuerpo ((final var start) y) (Let ((terminacin-valor-nombre (gensym))) `(Do ((, var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var))) (, Poniendo fin a un valor de nombre, el final)) ((>, Var, final-valor-nombre))

, @ Cuerpo)))

Tenga en cuenta que el cdigo que llama GENSYM no es parte de la expansin, sino que se ejecuta como parte de la expansin de macro y por lo tanto crea un nuevo smbolo cada vez que se ampli el macro. Esto puede parecer un poco extrao al principio - que termina el valor del nombre es una variable cuyo valor es el nombre de otra variable.Pero en realidad no es diferente del parmetro var cuyo valor es el nombre de una variable - la diferencia es el valor de var fue creado por el lector cuando la forma macro se ley, y el valor de la terminacin-valor-nombre se genera mediante programacin cuando el cdigo de macro se ejecuta. Con esta definicin las dos formas anteriormente problemtico ampliar en un cdigo que funciona de la forma que desee. La primera forma:
(No-primos (que termina el valor 0 10) (Impresin final-valor))

se expande a lo siguiente:
(Do ((terminacin-valor (al lado de alto riesgo 0) (al lado de alto riesgo (1 + sufijo-valor))) (#: G2141 10)) ((> Final-valor #: g2141)) (Impresin final-valor))

Ahora, la variable que se utiliza para mantener el valor final es el smbolo gensymed, #: g2141 . El nombre del smbolo, G2141 , fue generado por GENSYM pero no es significativa, lo que importa es la identidad del objeto del smbolo. Gensymed smbolos estn impresos en la sintaxis normal de smbolos uninterned, con una de las principales #: . La otra forma previamente problemtica:
(Let ((terminacin-valor 0)) (No-primos (p 0 10) (Incf terminacin-valor p)) terminacin-valor)

se parece a esto si se reemplaza el do-primos formulario con su expansin:


(Let ((terminacin-valor 0)) (Do ((p (al lado de alto riesgo 0) (al lado de alto riesgo (1 + p))) (#: G2140 10)) ((> P #: g2140)) (Incf terminacin-valor p)) terminacin-valor)

Una vez ms, no hay fuga desde el final de valor variable enlazada por el LET que rodean la prepara do- loop ya no es la sombra de las variables introducidas en el cdigo de ampliacin. No todos los nombres literal utilizado en una expansin de la macro ser necesariamente causar un problema - a medida que ms experiencia con las formas de enlace diferentes, usted podr determinar si un nombre se est utilizando en una posicin que podra causar una fuga en una abstraccin macro. Pero no hay ningn inconveniente real de utilizar un nombre gensymed slo para estar seguro. Con esa solucin, que ha conectado todas las fugas en la aplicacin de la do-primos . Una vez que hayas conseguido un poco de experiencia en macro-escrito debajo de su correa, usted aprender a escribir macros con este tipo de fugas preplugged. En realidad es bastante simple si usted sigue estas reglas generales: A menos que haya una razn especial para hacerlo de otra manera, incluir subformularios en la expansin de las posiciones que sern evaluados en el mismo orden que los subformularios aparecen en la llamada a la macro. A menos que haya una razn especial para hacerlo de otra manera, hacer que se subformularios evala slo una vez mediante la creacin de una variable en la expansin para mantener el valor de la evaluacin de la forma del argumento y luego usar esa variable en cualquier otro lugar del valor que se necesita en la expansin. Use GENSYM en el momento de la expansin de macros para crear nombres de las variables utilizadas en la expansin. Escribir Macro-Macros Por supuesto, no hay razn para que usted debera ser capaz de tomar ventaja de las macros slo cuando la escritura de funciones. El trabajo de las macros es abstraer patrones sintcticos de distancia comn, y ciertos patrones aparecen una y otra vez en la escritura de macros que tambin pueden beneficiarse de ser una abstraccin. De hecho, ya has visto un patrn como - macros muchos, al igual que la ltima versin de do-primos , comience con un LET , que introduce algunas variables sosteniendo smbolos gensymed para ser utilizados en la expansin de la macro. Dado que este es un patrn comn, por qu no se abstrae de su propio macro? En esta seccin voy a escribir una macro, con-gensyms , que hace precisamente eso. En otras palabras, que voy a escribir una macro macro-escritura: una macro que genera un cdigo que genera el cdigo. Mientras que la escritura compleja macro-

macros pueden ser un poco confuso hasta que se acostumbre a mantener los distintos niveles de cdigo claro en su mente, con-gensyms es bastante sencillo y servir como un ejercicio de hacer entrar en calor til, pero no muy extenuante mental. Quieres ser capaz de escribir algo como esto:
(Defmacro do-primos del cuerpo del cuerpo ((final var start) y) (Con-gensyms (terminacin-valor-nombre) `(Do ((, var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var))) (, Poniendo fin a un valor de nombre, el final)) ((>, Var, final-valor-nombre)) , @ Cuerpo)))

y tiene que ser equivalente a la versin anterior de no-primos . En otras palabras, la con-gensyms necesita ampliar en un LET que une a cada variable llamada,finalvalor-nombre en este caso, a un smbolo gensymed. Eso es bastante fcil de escribir con una plantilla de backquote simple.
(Defmacro con-gensyms cuerpo cuerpo ((y dems nombres) y) `(Vamos, (bucle de n en los nombres de recoger '(, n (gensym))) , @ Body))

Tenga en cuenta cmo se puede utilizar una coma para interpolar el valor de la LOOP expresin. El bucle genera una lista de formas de enlace en la que cada forma de enlace consiste en una lista que contiene uno de los nombres dados a congensyms y el cdigo literal (gensym) . Usted puede probar lo que el cdigo de la LOOP expresin podra generar en el REPL mediante la sustitucin de los nombres con una lista de smbolos.
CL-USUARIO> (bucle de n en (abc) reunir `(, n (gensym))) ((A (GENSYM)) (B (GENSYM)) (C (GENSYM)))

Despus de la lista de las formas de enlace, el argumento de cuerpo con-gensyms se empalma en el cuerpo de la LET . Por lo tanto, en el cdigo que se coloca en uncongensyms puede referirse a cualquiera de las variables con nombre en la lista de variables pasadas al con-gensyms . Si macro-ampliar el con-gensyms forma en la nueva definicin de lo-primos , debera ver algo como esto:
(Let ((terminacin-valor-nombre (gensym))) `(Do ((, var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var))) (, Poniendo fin a un valor de nombre, el final)) ((>, Var, final-valor-nombre)) , @ Body))

Se ve bien. Si bien esta macro es bastante trivial, es importante tener claro que las macros son expandidas diferentes: al compilar el defmacro dehacer-los nmeros primos , la con-gensyms forma se expande en el cdigo mostrado anteriormente y compilado. Por lo tanto, la versin compilada dehacer-los nmeros primos es lo mismo que si hubiera escrito el exterior LET a mano. Al compilar una funcin que utiliza no-primos , el cdigo generado por elcon-gensyms va generando el no-primos de expansin, pero con-gensyms s misma no es necesaria para elaborar un doprimos forma puesto que ya se ha ampliado, de nuevo cuando se prepara- fue compilado.
Otro clsico macro-escritura MACRO: una sola vez
Otro clsico macro-macro es escribir una sola vez , que se utiliza para generar cdigo que evala ciertos argumentos macro por una sola vez y en un orden particular. Utilizandouna sola vez , podra escribir no-primos casi tan simple como la versin original de fugas, de esta manera: (Defmacro do-primos del cuerpo del cuerpo ((final var start) y) (Una sola vez (de inicio y trmino) `(Do ((, var (al lado de alto riesgo, de inicio) (al lado de alto riesgo (1 +, var)))) ((>, Var, final)) , @ Cuerpo))) Sin embargo, la implementacin de una sola vez es un poco demasiado complicado para una explicacin golpe por golpe, ya que se basa en mltiples niveles de backquoting y unquoting.Si realmente quieres mejorar tus chuletas de macro, puede tratar de averiguar cmo funciona. Se parece a esto: (Defmacro una sola vez ((y dems nombres) y el cuerpo del cuerpo) (Let ((gensyms (bucle de n en los nombres de recoger (gensym)))) `(Let (, @ (bucle de g en gensyms recoger '(, g (gensym)))) `(Let loop (,,@( de g en gensyms para n en los nombres de recoger ``(,, g, n))) , (Let (, @ (bucle de n en los nombres de g en gensyms recoger `(, n, g))) , @ Body )))))

Ms all de simples macros Yo podra, por supuesto, decir mucho ms acerca de las macros. Todas las macros que hemos visto hasta ahora han sido ejemplos bastante simple que guarde un poco de escribir, pero no proporcionan radical nuevas formas de expresar las cosas. En los prximos captulos se ver ejemplos de macros que le permiten expresar las cosas de manera que sera prcticamente imposible sin macros. Vamos a empezar en el siguiente captulo, en el que podrs crear un marco de unidad de prueba simple pero eficaz.

1 Al igual que con las funciones, macros pueden contener declaraciones, pero no es necesario

preocuparse por los de ahora.

2 APPEND , que no hemos discutido todava, es una funcin que toma cualquier nmero de lista de

argumentos y devuelve el resultado del empalme de ellos juntos en una sola lista.
3 Otra de las funciones, MACROEXPAND , sigue ampliando el resultado, siempre y cuando el primer

elemento de la expansin resultante es el nombre de la macro. Sin embargo, esto a menudo le mostrar una vista mucho ms bajo nivel de lo que hace el cdigo que quieras, ya que el control bsico construcciones tales como DO , tambin se aplican a las macros. En otras palabras, aunque tambin puede ser educativo para ver lo que la macro se expande en ltima instancia, no es una visin muy til en lo que sus propias macros estn haciendo.
4 Si la expansin de la macro se muestra en una sola lnea, es probable que sea debido a la variable *

IMPRESIN * BONITO es NIL . Si es as, la evaluacin (setf * impresin bastante * t) debe hacer la expansin de la macro ms fcil de leer.
5 Esto es de Joel on Software por Joel Spolsky, tambin disponible en los artculos

http://www.joelonsoftware.com/ / LeakyAbstractions.html . Spolsky punto en el ensayo es que todas las abstracciones de fuga, en cierta medida, es decir, no hay abstracciones perfecto. Pero eso no quiere decir que deben tolerar las fugas que pueden conectar.
6 Por supuesto, ciertas formas se supone que deben ser evaluados ms de una vez, como las formas

en el cuerpo de un no-primos bucle.


7 No puede ser obvio que este circuito es necesariamente infinita dado los acontecimientos no

uniforme de los nmeros primos. El punto de partida para una prueba de que es infinito en realidad es el postulado de Bertrand, que dice que para cualquier n > 1, existe un nmero primo p , n < p < 2n . Desde all se puede demostrar que para cualquier nmero primo, P menor que la suma de los nmeros primos anteriores, el prximo primer, P ', es tambin menor que la suma original y P.

9. Prctica: Creacin de un marco de pruebas unitarias


En este captulo se volver a cortar el cdigo y desarrollar un marco de unidad de prueba simple para Lisp. Esto le dar la oportunidad de utilizar algunas de las caractersticas que ha aprendido sobre ya que el captulo 3, incluyendo macros y variables dinmicas, en el cdigo real. El principal objetivo de diseo del framework de pruebas ser la de hacer lo ms fcil posible para agregar nuevas pruebas, para ejecutar diversas suites de pruebas, y para localizar fallos de las pruebas. Por ahora nos centraremos en el diseo de una infraestructura que puede usar durante el desarrollo interactivo. La caracterstica clave de un marco de pruebas automatizadas es que el marco es responsable de decir que si todas las pruebas pasadas. Usted no quiere gastar su tiempo penosamente a travs de respuestas de los exmenes comprobar la salida cuando el equipo lo puede hacer mucho ms rpida y precisa. En consecuencia, cada caso de prueba debe ser una expresin que produce un valor boolean verdadero o falso, aprobar o reprobar. Por ejemplo, si estuviera escribiendo ensayos para el built-in + funcin, estos podran ser los casos de prueba razonable: 1
(= (+ 1 2) 3) (= (+ 1 2 3) 6) (= (+ -1 -3) -4)

Las funciones que tienen efectos secundarios se pondr a prueba un poco diferente - usted tiene que llamar a la funcin y verificar la evidencia de los efectos secundarios esperados. 2Pero al final, cada caso de prueba tiene que reducirse a una expresin booleana, los dedos pulgares arriba o hacia abajo. Dos primeros intentos Si usted estaba haciendo pruebas ad hoc, se puede introducir estas expresiones en el REPL y comprobar que volver T . Pero usted quiere un marco que hace que sea fcil de organizar y ejecutar estos casos de prueba en cualquier momento. Si quieres empezar con la cosa ms simple que podra funcionar, usted puede escribir una funcin que evala los casos de prueba y Y s resultados del conjunto.
(Defun prueba + () (Y (= (+ 1 2) 3)

(= (+ 1 2 3) 6) (= (+ -1 -3) -4)))

Cada vez que desee ejecutar este conjunto de casos de prueba, puede llamar a la prueba-+ .
CL-USUARIO> (prueba +) T

Siempre y cuando regresa T , ya sabes los casos de prueba estn pasando. Esta forma de organizacin de las pruebas es tambin agradablemente concisa - usted no tiene que escribir un montn de cdigo de prueba de la contabilidad. Sin embargo, como usted descubrir la primera vez que un caso de prueba falla, el resultado de informacin deja mucho que desear.Cuando la prueba-+ vuelve NIL , sabr algo fall, pero usted no tiene idea de que caso de prueba que era. As que vamos a probar con otro simple - incluso ingenuo - enfoque. Para saber lo que pasa con cada caso de prueba, podra escribir algo como esto:
(Defun prueba + () (Formato t "~: [FAIL ~; pasar ~] ... ~ a ~%" (= (+ 1 2) 3) "(= (+ 1 2) 3)) (Formato t "~: [FAIL ~; pasar ~] ... ~ a ~%" (= (+ 1 2 3) 6) "(= (+ 1 2 3) 6)) (Formato t "~: [FAIL ~; pasar ~] ... ~ a ~%" (= (+ -1 -3) -4) '(= (+ -1 -3) -4)))

Ahora, cada caso de prueba se informar de forma individual. El ~: [FAIL ~; pasar ~] parte de la FORMATO directiva hace FORMATO para imprimir "FAIL" si el argumento primer formato es falsa y "pasar" de lo contrario. 3 A continuacin, la etiqueta del resultado con la expresin de la prueba en s. Ahora correr la prueba-+ muestra exactamente lo que est pasando.
CL-USUARIO> (prueba +) pasar ... (= (+ 1 2) 3) pasar ... (= (+ 1 2 3) 6) pasar ... (= (+ -1 -3) -4) NIL

Esta vez los informes de resultados es ms parecido a lo que quiere, pero el cdigo es bastante grave. Los repetidos llamamientos a FORMATO , as como la duplicacin aburrida de la expresin de prueba claman por ser reprogramado. La duplicacin de la expresin de la prueba es particularmente reja, porque si lo escribe mal, los resultados de la prueba ser mal etiquetados. Otro problema es que usted no recibe un solo indicador, si todos los casos ha pasado la prueba. Es bastante fcil, con slo tres casos de prueba, para buscar la salida en busca de "FAIL", sin embargo, cuando tienes cientos de casos de prueba, que ser ms de una molestia.

Refactoring Lo que realmente me gustara es una manera de escribir las funciones de prueba lo ms eficaz que la primera prueba + que devuelven un nico T o NIL de valor, pero que tambin informe sobre los resultados de casos de prueba individuales, como la segunda versin. Desde la segunda versin es similar a lo que desea en trminos de funcionalidad, su mejor apuesta es para ver si se puede factorizar algunos de los duplicacin molesto. La forma ms sencilla de deshacerse de los reiterados llamamientos similares a FORMATO es crear una nueva funcin.
(Informe-resultado defun (forma de resultados) (Formato t "~: [FAIL ~; pasar ~] ... ~ a ~%" formulario de resultados))

Ahora usted puede escribir de prueba + con llamadas a informe-resultado en lugar de FORMATO . No es una gran mejora, pero al menos ahora, si usted decide cambiar la forma de informar de los resultados, slo hay un lugar que tiene que cambiar.
(Defun prueba + () (Informe-resultado (= (+ 1 2) 3) "(= (+ 1 2) 3)) (Informe-resultado (= (+ 1 2 3) 6) "(= (+ 1 2 3) 6)) (Informe-resultado (= (+ -3) -4 -1) '(= (+ -1 -3) -4)))

Lo siguiente que necesita para deshacerse de la duplicacin de la expresin de caso de prueba, con su consiguiente riesgo de etiquetado incorrecto de los resultados. Lo que realmente me gustara es ser capaz de tratar la expresin como los de cdigo (para obtener el resultado) y datos (que se utilizar como etiqueta). Siempre que quiera tratar el cdigo como de datos, que es un signo seguro de que necesita una macro. O, visto de otra manera, lo que usted necesita es una manera de automatizar la escritura propenso a erroresresultado del informe- las llamadas. Que le gustara poder decir algo como esto:
(Marque (= (+ 1 2) 3))

y tienen que decir lo siguiente:


(Informe-resultado (= (+ 1 2) 3) "(= (+ 1 2) 3))

Escribir una macro para hacer esta traduccin es trivial.


(Defmacro cheque (formulario) `(Informe-resultado, la forma, la forma))

Ahora usted puede cambiar de prueba + de usar cheque .

(Defun prueba + (Marque (= (+ (Marque (= (+ (Marque (= (+

() 1 2) 3)) 1 2 3) 6)) -1 -3) -4)))

Puesto que usted est en la bsqueda de la duplicacin, por qu no deshacerse de los repetidos llamamientos a ver ? Puede definir comprobar que un nmero arbitrario de las formas y se envuelven cada uno en una llamada al informeresultado .
(Defmacro comprobar (y las formas del cuerpo) `(Progn , @ (Bucle de f en los formularios reunir `(informe-resultado, f, f))))

Esta definicin utiliza un lenguaje de macros comn de envolver un progn en torno a una serie de formularios con el fin de convertirlos en un solo formulario. Note tambin cmo se puede utilizar , @ para empalmar en el resultado de una expresin que devuelve una lista de expresiones que son ellos mismos generan con una plantilla de acento grave. Con la nueva versin de comprobar que usted puede escribir una nueva versin de prueba + de esta manera:
(Defun (Ver (= (= (= prueba + () (+ 1 2) 3) (+ 1 2 3) 6) (+ -1 -3) -4)))

que es equivalente al siguiente cdigo:


(Defun prueba + () (Progn (Informe-resultado (= (+ 1 2) 3) "(= (+ 1 2) 3)) (Informe-resultado (= (+ 1 2 3) 6) "(= (+ 1 2 3) 6)) (Informe-resultado (= (+ -3) -4 -1) '(= (+ -1 -3) -4))))

Gracias a ver , esta versin es tan conciso como la primera versin de prueba + sino que se expande en el cdigo que hace lo mismo que la segunda versin. Y ahora los cambios que desea realizar en la forma de prueba + se comporta, se puede hacer por cambiar cheque . La fijacin del valor de retorno Usted puede comenzar con la fijacin de prueba + por lo que su valor de retorno indica si todos los casos ha pasado la prueba. Desde cheque es responsable de generar el cdigo que se ejecuta en ltima instancia, los casos de prueba, slo tiene

que cambiar para generar cdigo que tambin realiza un seguimiento de los resultados. Como primer paso, usted puede hacer un pequeo cambio en el informe-resultado por lo que devuelve el resultado del caso de prueba es la presentacin de informes.
(Informe-resultado defun (forma de resultados) (Formato t "~: [FAIL ~; pasar ~] ... ~ a ~%" forma de resultados) resultado)

Ahora que el informe-resultado devuelve el resultado de su caso de prueba, podra parecer que slo podra cambiar la progn a una Y para combinar los resultados. Por desgracia, y no hace absolutamente lo que quiere en este caso debido a su comportamiento en cortocircuito: tan pronto como un caso de prueba falla, y se omita el resto. Por otro lado, si haba una construccin que funcionaba como Y sin el cortocircuito, se podra utilizar en el lugar de progn , y le haba hecho ser. Common Lisp no proporciona una construccin, pero eso no es razn para que no se puede utilizar: es un asunto trivial para escribir una macro para dar a ti mismo. Dejando a un lado los casos de prueba por un momento, lo que quiere es una macro - vamos a llamarlo combinar los resultados - que le permiten decir lo siguiente:
(Combinar los resultados (Foo) (Bar) (BAZ))

y tienen que decir algo como esto:


(Let ((resultado t)) (A menos que (foo) (setf resultado nulo)) (A menos que (bar) (setf resultado nulo)) (A menos que (BAZ) (setf resultado nulo)) resultado)

La nica parte difcil de escribir esta macro es que es necesario introducir una variable - resultado en el cdigo anterior - en la expansin. Como se vio en el captulo anterior, utilizando un nombre literal de las variables en la expansin de macro puede introducir una fuga en su abstraccin macro, por lo que tendrs que crear un nombre nico. Este es un trabajo para con-gensyms . Puede definir combinar los resultados de esta manera:
(Defmacro combinar los resultados (y las formas del cuerpo) (Con-gensyms (el resultado) `(Let ((, resultado t)) , @ (Bucle de f en los formularios reunir `(a menos que, f (setf, resultado nil))) , Resultado)))

Ahora usted puede fijar comprobar con un simple cambio de la expansin de usar se combinan los resultados en lugar de progn .
(Defmacro comprobar (y las formas del cuerpo) `(Se combinan los resultados , @ (Bucle de f en los formularios reunir `(informe-resultado, f, f))))

Con esa versin de comprobar , prueba + debe emitir los resultados de sus expresiones prueba de tres y luego regresar T para indicar que todo lo que pasa. 4
CL-USUARIO> (prueba +) pasar ... (= (+ 1 2) 3) pasar ... (= (+ 1 2 3) 6) pasar ... (= (+ -1 -3) -4) T

Y si se cambia uno de los casos de prueba por lo que falla, 5 los cambios finales valor de retorno a NIL .
CL-USUARIO> (prueba +) pasar ... (= (+ 1 2) 3) pasar ... (= (+ 1 2 3) 6) FALLO ... (= (+ -1 -3) -5) NIL

Mejor resultado de informes Siempre y cuando usted tiene slo una funcin de prueba, los informes de resultados actual es bastante clara. Si un caso de prueba en particular no, todo lo que tienes que hacer es encontrar el caso de prueba en el cheque y la forma de averiguar por qu est fallando. Pero si usted escribe un montn de pruebas, es probable que desee para organizar de alguna manera, en lugar de empujar todos en una sola funcin. Por ejemplo, supongamos que quera aadir algunos casos de prueba para la * funcin. Usted puede escribir una funcin de prueba.
(Defun prueba * () (Ver (= (* 2 2) 4) (= (* 3 5) 15)))

Ahora que tiene dos funciones de prueba, usted probablemente querr otra funcin que se ejecuta todas las pruebas. Eso es bastante fcil.
(Defun prueba de la aritmtica () (Combinar los resultados (Test-+) (Test-*)))

En esta funcin se utiliza combinar los resultados en lugar de comprobar , ya que ambos test-+ y prueba *- se encargar de informar de sus propios

resultados.Cuando se ejecuta la prueba de aritmtica , obtendr los siguientes resultados:


CL-USUARIO> (prueba de la aritmtica) pasar ... (= (+ 1 2) 3) pasar ... (= (+ 1 2 3) 6) pasar ... (= (+ -1 -3) -4) pasar ... (= (* 2 2) 4) pasar ... (= (* 3 5) 15) T

Ahora imagine que uno de los casos de prueba y no es necesario localizar el problema. Con slo cinco casos de prueba y dos funciones de prueba, no ser demasiado difcil encontrar el cdigo del caso de prueba falla. Pero supongamos que usted tena 500 casos de prueba repartidos en 20 funciones. Sera bueno si los resultados le dijo lo que la funcin de cada caso de prueba vino. Dado que el cdigo que imprime los resultados se centraliza en el informe-resultado , se necesita una manera de pasar informacin acerca de lo que las pruebas de funcin est en que el informe-resultado . Usted puede agregar un parmetro al informe-resultado de pasar esta informacin, pero ver , lo que genera las llamadas alinforme-resultado , no sabe cul es la funcin que est siendo llamado desde, lo que significa que tambin tendra que cambiar la forma de llamar a comprobar , pasndole un argumento que simplemente pasa a informe-resultado . Este es exactamente el tipo de variables del problema dinmico fueron diseados para resolver. Si se crea una variable dinmica que cada funcin de prueba se une al nombre de la funcin antes de llamar a comprobar , a continuacin, el informeresultado se puede utilizar sin comprobar tener que saber nada al respecto. El primer paso es declarar la variable en el nivel superior.
(Defvar * Prueba de nombre * nil)

Ahora tiene que hacer otro pequeo cambio en el informe-resultado de incluir * Prueba de nombre * en el FORMATO salida.
(Formato t "~: [FAIL ~; pasar ~] ... ~ a: ~ a ~%" nmero * Prueba de nombre de forma *)

Con estos cambios, las funciones de prueba seguir funcionando, pero se producir la siguiente salida, porque * prueba * El nombre no es de rebote:
CL-USUARIO> (prueba de la aritmtica) pasar ... NIL: (= (+ 1 2) 3) pasar ... NIL: (= (+ 1 2 3) 6) pasar ... NIL: (= (+ -1 -3) -4) pasar ... NIL: (= (2 * 2) 4)

pasar ... NIL: (= (3 * 5) 15) T

Por el nombre que se inform correctamente, es necesario cambiar las dos funciones de prueba.
(Defun prueba + () (Let ((* prueba * El nombre 'test-+)) (Ver (= (+ 1 2) 3) (= (+ 1 2 3) 6) (= (+ -1 -3) -4)))) (Defun prueba * () (Let ((* prueba * El nombre 'test-*)) (Ver (= (* 2 2) 4) (= (* 3 5) 15))))

Ahora los resultados estn correctamente etiquetados.


CL-USUARIO> (prueba de la aritmtica) pasar ... TEST +: (= (+ 1 2) 3) pasar ... TEST +: (= (+ 1 2 3) 6) pasar ... TEST +: (= (+ -1 -3) -4) pasar ... TEST *: (= (2 * 2) 4) pasar ... TEST *: (= (3 * 5) 15) T

Emerge una abstraccin En la fijacin de las funciones de prueba, que ha introducido varios nuevos trozos de la duplicacin. No slo cada funcin tiene que incluir el nombre de la funcin dos veces - una vez que el nombre de la DEFUN y una vez en la enlace de la prueba * nombre * - pero el mismo patrn de tres lneas de cdigo se duplica entre las dos funciones . Se puede eliminar la duplicacin simplemente por el hecho de que la duplicacin es malo. Pero si se mira ms de cerca la causa de la duplicacin, usted puede aprender una importante leccin acerca de cmo utilizar las macros. La razn de estas dos funciones de arranque de la misma manera se debe a que ambos son funciones de prueba. La duplicacin se debe a que, en este momento, las pruebas de funcines slo la mitad de una abstraccin. La abstraccin existe en su mente, pero en el cdigo no hay forma de expresar "esto es una funcin de prueba" que no sea la escritura de cdigo que sigue un patrn particular. Por desgracia, las abstracciones parciales son un instrumento de mala muerte para la construccin de software. Debido a que una abstraccin de un medio se expresa en el cdigo por una manifestacin de la modelo, tiene la garanta de que la duplicacin de cdigo masivo con todas las consecuencias negativas normales que

implica para el mantenimiento. De manera ms sutil, porque la abstraccin slo existe en la mente de los programadores, no hay un mecanismo para asegurarse de que los programadores diferentes (o incluso el mismo programador que trabaja en diferentes momentos) entienden realmente la abstraccin de la misma manera. Para hacer una completa abstraccin, se necesita una manera de expresar "esto es una funcin de prueba" y que todo el cdigo requerido por el patrn se genera automticamente. En otras palabras, se necesita una macro. Debido a que el patrn que se est tratando de capturar un DEFUN adems de algunos cdigo repetitivo, es necesario crear una macro que se expandir en una DEFUN . A continuacin, utilizar esta macro, en lugar de una llanura DEFUN para definir las funciones de prueba, as que tiene sentido llamarlo deftest .
(Defmacro deftest (parmetros de nombre y el cuerpo del cuerpo) `(Defun, nombre y parmetros (Let ((* prueba * Nombre ', nombre)) , @ Cuerpo)))

Con esta macro se puede reescribir la prueba de + de la siguiente manera:


(Deftest prueba + () (Ver (= (+ 1 2) 3) (= (+ 1 2 3) 6) (= (+ -1 -3) -4)))

Una jerarqua de las pruebas Ahora que ya ha establecido las funciones de prueba, como ciudadanos de primera clase, podra plantearse la cuestin, si la prueba aritmtica de ser una funcin de prueba? Como estn las cosas, que en realidad no importa - si se ha definido con deftest , la enlace de la prueba * Nombre * sera la sombra de los enlaces en laprueba de + y * prueba- antes de que los resultados son reportados. Pero ahora imagina que tienes miles de casos de prueba para la organizacin. El primer nivel de organizacin es proporcionada por las funciones de test como prueba de + y* prueba que llamar directamente a comprobar . Sin embargo, con miles de casos de prueba, es probable que tengas otros niveles de organizacin. Funciones tales como laprueba de la aritmtica se pueden agrupar las funciones relacionadas con la prueba en bancos de pruebas. Ahora supongamos que algunas funciones de prueba de bajo nivel son llamados de conjuntos de pruebas mltiples. No es raro para un caso de prueba para pasar en un contexto pero no en otro. Si eso

sucede, usted probablemente querr saber ms de lo que baja el nivel de funcin de prueba contiene el caso de prueba. Si se definen las funciones de banco de pruebas tales como prueba de la aritmtica con deftest y hacer un pequeo cambio en el * nombre de la prueba de contabilidad, puede hacer que los resultados reportados con un "completo" el camino para el caso de prueba, algo como esto:
pasar ... (TEST-TEST-ARITMTICA +): (= (+ 1 2) 3)

Debido a que ya ha abstrado el proceso de definir una funcin de prueba, puede cambiar los datos de contabilidad sin modificar el cdigo de las funciones de prueba. 6 Para hacerla prueba-* Nombre * mantener una lista de nombres de las funciones de prueba en lugar de slo el nombre de la funcin de prueba del ltimo contrato celebrado, slo tiene que cambiar esta forma de enlace:
(Let ((* prueba * Nombre ', nombre))

a lo siguiente:
(Let ((* prueba * El nombre (se aade a la prueba-* Nombre * (lista, nombre))))

Desde APPEND devuelve una nueva lista formada por los elementos de sus argumentos, esta versin se unir * prueba * El nombre de una lista con el contenido anterior de la prueba-* Nombre * con el nuevo nombre pegado en el final. 7 Cuando cada prueba funcin, el valor antiguo de la * prueba * El nombre ser restaurada. Ahora puede volver a definir la prueba de aritmtica con deftest lugar de DEFUN .
(Deftest prueba de la aritmtica () (Combinar los resultados (Test-+) (Test-*)))

Los resultados muestran ahora exactamente cmo lleg a cada expresin de la prueba.
CL-USUARIO> (prueba de la aritmtica) pasar ... (TEST-TEST-ARITMTICA +): (= pasar ... (TEST-TEST-ARITMTICA +): (= pasar ... (TEST-TEST-ARITMTICA +): (= pasar ... (TEST-TEST-ARITMTICA *): (= pasar ... (TEST-TEST-ARITMTICA *): (= T (+ (+ (+ (2 (3 1 2) 3) 1 2 3) 6) -1 -3) -4) * 2) 4) * 5) 15)

A medida que el conjunto de pruebas crece, puede agregar nuevas capas de funciones de prueba, siempre y cuando est definida con deftest , los resultados sern reportados correctamente. Por ejemplo, las siguientes:
(Deftest prueba de matemticas () (Prueba de la aritmtica))

generar los siguientes resultados:


CL-USUARIO> (prueba de matemticas) pasar ... (TEST-MATH-TEST TEST-ARITMTICA pasar ... (TEST-MATH-TEST TEST-ARITMTICA pasar ... (TEST-MATH-TEST TEST-ARITMTICA pasar ... (TEST-MATH-TEST TEST-ARITMTICA pasar ... (TEST-MATH-TEST TEST-ARITMTICA T +): +): +): *): *): (= (= (= (= (= (+ (+ (+ (2 (3 1 2) 3) 1 2 3) 6) -1 -3) -4) * 2) 4) * 5) 15)

Conclusin Usted puede seguir adelante, agregando ms caractersticas de este marco de pruebas. Sino como un marco para la escritura de pruebas con un mnimo de busywork y fcilmente que va desde el REPL, esto es un comienzo razonable. Aqu est el cdigo completo, las 26 lneas de la misma:
(Defvar * Prueba de nombre * nil) (Defmacro deftest (parmetros de nombre y el cuerpo del cuerpo) "Definir una funcin de prueba. Dentro de una funcin de prueba que podemos llamar otras funciones de prueba o uso de control para ejecutar pruebas individuales los casos. " `(Defun, nombre y parmetros (Let ((* prueba * El nombre (se aade a la prueba-* Nombre * (lista, nombre)))) , @ Cuerpo))) (Defmacro comprobar (y las formas del cuerpo) "Ejecutar cada expresin 'formas' como un caso de prueba". `(Se combinan los resultados , @ (Bucle de f en los formularios reunir `(informe-resultado, f, f)))) (Defmacro combinar los resultados (y las formas del cuerpo) "Al combinar los resultados (como booleanos) para evaluar las" formas "en orden". (Con-gensyms (el resultado) `(Let ((, resultado t)) , @ (Bucle de f en los formularios reunir `(a menos que, f (setf, resultado nil))) , Resultado))) (Informe-resultado defun (forma de resultados) "Informe de los resultados de un solo caso de prueba. Llamado por" pasar "." (Formato t "~: [FAIL ~; pasar ~] ... ~ a: ~ a ~%" nmero * Prueba de nombre de forma *) resultado)

Vale la pena revisar cmo has llegado aqu porque es ilustrativo de cmo programar en Lisp va a menudo.

Empezamos definiendo una versin simple de su problema - cmo evaluar un conjunto de expresiones booleanas y averiguar si todos volvieron realidad. Slo Y ING juntos funcionaba y estaba limpio, pero sintcticamente revel la necesidad de informar mejor resultado. As que escribi un cdigo muy ingenuo, lleno de duplicacin y propenso a errores modismos que inform de los resultados de la manera que quera. El siguiente paso fue ver si se puede refactorizar la segunda versin en algo tan limpio como el primero. Que comenz con una tcnica estndar de extraccin de refactorizacin algn cdigo en una funcin, el informe-resultado . Por desgracia, se poda ver que el uso de informe-resultado iba a ser tedioso y propenso a errores ya que tena que pasar la expresin de prueba dos veces, una para el valor y los datos una vez citado. Por lo que escribi el cheque macro para automatizar los detalles de la llamadainforme-resultado correctamente. Mientras que la escritura de verificacin , se dio cuenta, siempre y cuando se genera el cdigo, se puede hacer una sola llamada a revisar la generacin de mltiples llamadas al informe-resultado , que te llevan de nuevo a una versin de prueba de + de lo ms conciso que el original y la versin. En ese momento usted tena el control de la API clavado, lo que le ha permitido comenzar limpiando con la forma en que trabaj en el interior. La siguiente tarea consiste en fijarcomprobar que el cdigo se genera devolvera un booleano que indica si todos los casos de prueba haba pasado. En lugar de buscar una solucin en forma inmediata cheque , usted hizo una pausa para disfrutar de un lenguaje de diseo poco por la fantasa. Qu pasa si - que fantaseado - que ya era un no corto circuito y la construccin. A continuacin se fijan cheque sera trivial. Al regresar de fantasa que se dio cuenta de que no haba tal, sino la construccin de que se poda escribir una en unas pocas lneas. Despus de escribirse combinan los resultados , la solucin para comprobar fue hecho trivial. En ese momento lo nico que quedaba era hacer algunas mejoras ms a la forma en que informaron resultados de la prueba. Una vez que comenz a hacer cambios en las funciones de prueba, se dio cuenta de esas funciones represent una categora especial de funcin que se mereca su propia abstraccin. Por lo que escribi deftest para abstraer el modelo de cdigo que convierte a una funcin normal en una funcin de prueba.

Con deftest proporciona una barrera de abstraccin entre las definiciones de la prueba y la maquinaria subyacente, que fueron capaces de mejorar el resultado de informes sin el tacto de las funciones de prueba. Ahora, con los conceptos bsicos de las funciones, variables y macros dominado, y un poco de experiencia prctica con ellos, ests listo para comenzar a explorar la rica biblioteca estndar Common Lisp de funciones y tipos de datos.

1 Esto es slo para fines ilustrativos - obviamente, escribir casos de prueba para las funciones

integradas tales como + es un poco tonto, ya que si tales cosas bsicas que no estn funcionando, las posibilidades de las pruebas se ejecutan de la forma esperada es bastante delgado. Por otro lado, balbucea ms comunes se aplican en gran medida en Common Lisp, por lo que no es una locura imaginar la escritura de pruebas en Common Lisp para probar las funciones de la biblioteca estndar.
2 Los efectos secundarios pueden incluir cosas tales como la sealizacin de los errores, voy a

discutir el sistema Common Lisp de gestin de errores en el captulo 19. Es posible que, despus de leer este captulo, quiero pensar en cmo incorporar las pruebas que comprueban si una funcin cumple o no una seal de error en particular en ciertas situaciones.
3 Voy a discutir esto y otros FORMATO directivas con ms detalle en el captulo 18. 4 Si la prueba-+ ha sido elaborado - lo cual puede suceder de manera implcita en ciertas

implementaciones de Lisp - puede que tenga que reevaluar la definicin de la prueba + para obtener la definicin cambi de comprobar que afectan al comportamiento de prueba + . Cdigo interpretado, por otro lado, normalmente se expande macros de nuevo cada vez que se interpreta el cdigo, permitiendo que los efectos de la redefinicin de macro que se observan de inmediato.
5 Hay que cambiar la prueba para asegurarse de que no ya que no puede cambiar el

comportamiento de + .
6 Aunque, de nuevo, si las funciones de prueba se han compilado, usted tiene que volver a compilar

despus de cambiar la macro.


7 Como se ver en el captulo 12, APPEND cin hasta el final de una lista no es la forma ms eficaz de

construir una lista. Pero por ahora esto es suficiente - siempre y cuando las jerarquas de prueba no son muy profundas, que debera estar bien. Y si se convierte en un problema, todo lo que tienes que hacer es cambiar la definicin de deftest .

10. Nmeros, caracteres y cadenas


Mientras que las funciones, variables, macros, y 25 operadores especiales proporcionan los elementos bsicos de la propia lengua, los componentes bsicos de sus programas sern las estructuras de datos que utiliza. Como Fred Brooks observ en The Mythical Man-Month , "La representacin es la esencia de la programacin. "
1

Common Lisp proporciona soporte integrado para la mayora de los tipos de datos se encuentran tpicamente en las lenguas modernas: los nmeros (enteros, punto flotante, y complejo), caracteres, cadenas, matrices (incluyendo matrices multidimensionales), listas, tablas hash, flujos de entrada y de salida , y una abstraccin para representar los nombres de archivo portable. Las funciones son tambin un tipo de primera clase de datos en Lisp - pueden ser almacenados en variables, pasados como argumentos, devuelve como valor de retorno, y ha creado en tiempo de ejecucin. Y estos tipos integrados son slo el comienzo. Estn definidos en el estndar del lenguaje que los programadores puedan contar con ellos y estar disponible debido a que tienden a ser ms fcil de implementar de manera eficiente cuando se integra perfectamente con el resto de la aplicacin. Pero, como veremos en captulos posteriores, Common Lisp tambin ofrece varias formas de definir nuevos tipos de datos, definir las operaciones en ellos, e integrarlos con los tipos de datos incorporados. Por ahora, sin embargo, usted puede comenzar con los tipos de datos incorporados. Debido a que Lisp es un lenguaje de alto nivel, los detalles exactos de cmo los diferentes tipos de datos se implementan son en gran parte oculto. Desde su punto de vista como usuario de la lengua, la incorporada en los tipos de datos definidos por las funciones que operan sobre ellos. As que para aprender un tipo de datos, slo tienes que aprender sobre las funciones que se pueden utilizar con l. Adems, la mayora de los incorporados en los tipos de datos tienen una sintaxis especial que el lector entiende Lisp y que la impresora utiliza Lisp. Es por eso que, por ejemplo, puede escribir cadenas como "foo" , nmeros 123 , 1.23 , y1.23 y en las listas como (abc) . Voy a describir la sintaxis de los diferentes tipos de objetos cuando se describen las funciones para la manipulacin de ellos.

En este captulo, voy a cubrir el built-in "escalar" los tipos de datos: nmeros, caracteres y cadenas. Tcnicamente, las cadenas no son escalares cierto - una cadena es una secuencia de caracteres, y se puede acceder a los caracteres individuales y manipular cadenas con una funcin que opera en las secuencias. Pero voy a hablar de cadenas de aqu porque la mayora de las funciones especficas de cadenas manipularlos como valores individuales y tambin debido a la estrecha relacin entre varias de las funciones de cadena y sus homlogos de carcter. Nmeros Matemticas, como Barbie, dice, es difcil. 2 Common Lisp no puede hacer la parte de matemticas sea ms fcil, pero s tienden a ponerse en el camino mucho menos que otros lenguajes de programacin. Eso no es sorprendente teniendo en cuenta su herencia matemtica. Lisp fue diseado por un matemtico como una herramienta para el estudio de las funciones matemticas. Y uno de los proyectos principales del proyecto MAC del MIT fue el sistema de Macsyma lgebra simblica, escrita en Maclisp, uno de los predecesores inmediatos de Common Lisp. Adems, Lisp ha sido utilizado como lengua de enseanza en lugares como el MIT, donde incluso los profesores de ciencias de la computacin se estremecen con la idea de decirle a sus alumnos que 04/10 = 2 , lo que ayuda Lisp para proporciones exactas. Y en varias ocasiones Lisp ha sido llamado a competir con FORTRAN en el campo de la informtica de alto rendimiento numrico. Una de las razones Lisp es un lenguaje bueno para las matemticas es su nmero se comportan como verdaderos nmeros matemticos de las aproximaciones de nmeros que son fciles de implementar en hardware limitado. Por ejemplo, los nmeros enteros en Common Lisp puede ser casi arbitrariamente grande en lugar de estar limitado por el tamao de una palabra de la mquina. 3 y dividir dos nmeros enteros resultados en una proporcin exacta, no un valor truncado. Y desde relaciones se representan como pares de enteros de tamao arbitrario, las relaciones pueden representar fracciones arbitrariamente precisa. 4 Por otro lado, de alto rendimiento de programacin numrica, usted puede estar dispuesto a cambiar la exactitud de los nmeros racionales de la velocidad ofrecida por el uso del hardware subyacente operaciones de punto flotante. Por lo tanto, Common Lisp tambin ofrece varios tipos de nmeros de punto flotante, que se

asignan mediante la aplicacin a la correspondiente apoyo de hardware de punto flotante de las representaciones. 5 Flota tambin se utilizan para representar los resultados de un clculo matemtico, cuyo verdadero valor sera un nmero irracional. Por ltimo, Common Lisp admite los nmeros complejos - los nmeros que resultan de hacer las cosas como la toma de races cuadradas y logaritmos de nmeros negativos. El Common Lisp estndar, incluso va tan lejos como para especificar los valores principales y cortes de la rama de funciones irracionales y trascendentales en el dominio complejo. Numricos Literales Usted puede escribir literales numricos en una variedad de formas, que vio unos pocos ejemplos en el captulo 4. Sin embargo, es importante tener en cuenta la divisin del trabajo entre el lector y el evaluador Lisp Lisp - el lector es responsable de traducir el texto en objetos de Lisp, y el evaluador Lisp se ocupa slo de los objetos. Para un determinado nmero de un tipo determinado, puede haber muchas representaciones textuales, todo lo cual se traduce a la representacin del objeto mismo por el lector Lisp. Por ejemplo, puede escribir el nmero entero 10, 10 , 20 / 2 , # xA , o cualquiera de una serie de otras maneras, pero el lector se traducir todo esto al mismo objeto. Cuando los nmeros se imprimen de vuelta - por ejemplo, en el REPL - they're impresas en una sintaxis textual cannico que puede ser diferente de la sintaxis utilizada para introducir el nmero. Por ejemplo:
CL-USUARIO> 10 10 CL-USUARIO> 20 / 2 10 CL-USUARIO> # xa 10

La sintaxis de los valores enteros es un signo opcional ( + o - ) seguido de uno o ms dgitos. Las razones se escribe como un signo opcional y una secuencia de dgitos, lo que representa el numerador, una barra inclinada ( / ), y otra secuencia de dgitos que representan el denominador. Todos los nmeros racionales son "cannica", como son leer - por eso es10 y 20 / 2 se leen como el mismo nmero, al igual que 3 / 4 y 6 / 8 . Racionales se imprimen en "reducir" la forma - valores enteros se imprimen en la sintaxis de entero y las relaciones con el numerador y el denominador reducido a la mnima expresin.

Tambin es posible escribir racionales en otras bases de 10. Si van precedidos de # B o b # , una racional literal se lee como un nmero binario con 0 y 1 como los dgitos slo legal.Un # O o o # indica un nmero octal (dgitos legal 0 - 7 ), y # X o # x indica hexadecimal (dgitos legal 0 - F o 0 - f ). Usted puede escribir en otras bases racionales 2 a 36 con# nR , donde n es la base (siempre escrito en decimal). Adicionales "cifras" ms all del 9 se han tomado de las letras A - Z o un - z . Tenga en cuenta que estos indicadores de base se aplican a todo racional - no es posible escribir una relacin con el numerador y el denominador de una base en otro. Tambin, puede escribir valores enteros, pero no relaciones, como dgitos decimales termina con un punto decimal. 6 Algunos ejemplos de los nmeros racionales, con su representacin cannica, decimal son las siguientes:
123 ==> 123 123 ==> 123 -123 ==> -123 123. ==> 123 2.3 ==> 2 / 3 -2 / 3 ==> -2 / 3 4.6 ==> 2 / 3 6.3 ==> 2 # B10101 ==> 21 # B1010/1011 ==> 10/11 # O777 ==> 511 # XDADA ==> 56026 # 36rABCDEFGHIJKLMNOPQRSTUVWXYZ ==> 8337503854730415241050377135811259267835

Tambin puede escribir nmeros de punto flotante en una variedad de maneras. A diferencia de los nmeros racionales, la sintaxis utilizada para anotar un nmero de punto flotante puede afectar el tipo real de leer el nmero. Common Lisp define cuatro subtipos de nmero de punto flotante: corto, simple, doble y largo pbucle. Cada subtipo puede utilizar un nmero diferente de bits en su representacin, lo que significa que cada subtipo puede representar valores con un rango diferente y con diferente precisin. Ms bits ofrece una gama ms amplia y ms precisin. 7 El formato bsico de nmeros de punto flotante es un signo opcional seguido de una secuencia no vaca de dgitos decimales, posiblemente, con un punto decimal incrustado. Esta secuencia puede ser seguido por un marcador de exponente para "notacin cientfica informticos." 8 El marcador exponente consta de una sola letra seguida de un signo opcional y una secuencia de dgitos, que se interpretan como la potencia de diez en el que el nmero antes de la marcador exponente debe multiplicarse. La carta tiene una doble funcin: marca el comienzo del exponente e indica qu representacin de punto flotante se debe utilizar para el nmero. Los marcadores exponente s , f , d , l (y sus equivalentes en maysculas) indican baos

cortos, simples, dobles, y largo, respectivamente. La carta e indica que la representacin por defecto (inicialmente de un solo flotador) debe ser utilizado. Los nmeros con ningn marcador exponente se leen en la representacin por defecto y debe contener un punto decimal seguido por al menos un dgito para distinguirlos de los nmeros enteros uno. Los dgitos de un nmero de punto flotante se tratan siempre como base de 10 dgitos - el B # , # X , # O , y # R sintaxis slo funciona con nmeros racionales. Los siguientes son algunos ejemplos de nmeros de punto flotante, junto con su representacin cannica:
1.0 ==> 1.0 1E0 ==> 1.0 1D0 ==> 1.0d0 123.0 ==> 123,0 123e0 ==> 123,0 0.123 ==> 0,123 0.123 ==> 0,123 123 sexto-3 ==> 0,123 123E-3 ==> 0,123 0.123e20 ==> 1.23e 19 123d23 ==> 1.23d 25

Por ltimo, los nmeros complejos estn escritos en su propia sintaxis, es decir, C # o C # seguido de una lista de dos nmeros reales que representan la parte real e imaginaria del nmero complejo. En realidad, hay cinco tipos de nmeros complejos, porque las partes real e imaginaria o bien ambos deben ser racionales o ambos el mismo tipo de nmero de punto flotante. Pero usted puede escribir como quiera - si un complejo est escrito con una racional y otra parte de punto flotante, lo racional se convierte en un flotador de la representacin adecuada.Del mismo modo, si las partes reales e imaginarias son flotadores de diferentes representaciones, la de la representacin ms pequea ser actualizado . Sin embargo, no los nmeros complejos tienen un componente racional, real y una parte imaginaria a cero - ya que tales valores son, matemticamente hablando, racional, que estn representados por el valor racional adecuado. El argumento matemtico mismo podra hacerse para los nmeros complejos de punto flotante componentes, pero para los tipos complejos un nmero con una parte imaginaria a cero es siempre un objeto diferente que el nmero de punto flotante que representa el componente real. Estos son algunos ejemplos de nmeros escritos la sintaxis de nmeros complejos:
# C (2 1) ==> # c (2 1) # C (2 / 3 3 / 4) ==> # c (2 / 3 3 / 4)

# # # # # # #

C C C C C C C

(2 1,0) ==> # c (2,0 1,0) (2,0 1.0d0) ==> # c (2.0d0 1.0d0) (1 / 2 1.0) ==> # c (0,5 1,0) (3 0) ==> 3 (3,0 0,0) ==> # c (3,0 0,0) (2.1 0) ==> 1 / 2 (-6 / 3 0) ==> -2

Matemticas bsicas Las operaciones aritmticas bsicas - suma, resta, multiplicacin y divisin - son compatibles con todos los diferentes tipos de nmeros de Lisp con las funciones + , - , * y / . Llamar a cualquiera de estas funciones con ms de dos argumentos es equivalente a llamar a la misma funcin en los dos primeros argumentos y luego llamar de nuevo en el valor resultante y el resto de los argumentos. Por ejemplo, (+ 1 2 3) es equivalente a (+ (+ 1 2) 3) . Con un nico argumento, + y * devuelve el valor; - devuelve su negacin y / . su recproco 9
(+ (+ (+ (+ ((((* (* (/ (/ (/ (/ 1 2) ==> 3 1 2 3) ==> 6 10,0 3,0) ==> 13,0 C # (1 2) C # (3 4)) ==> C # (4 6) 5 4) ==> 1 2) ==> -2 10 3 5) ==> 2 2 3) ==> 6 2 3 4) ==> 24 10 5) ==> 2 10 5 2) ==> 1 2 3) ==> 2 / 3 4) ==> 1 / 4

Si todos los argumentos son del mismo tipo de nmero (punto racional, flotante o complejo), el resultado ser el mismo tipo, salvo en el caso de que el resultado de una operacin con nmeros complejos con componentes racionales se obtiene un nmero con un cero imaginario parte, en cuyo caso el resultado ser un racional. Sin embargo, los nmeros de coma flotante y complejos son contagiosas - si todos los argumentos son reales, pero uno o ms nmeros de punto flotante, los otros argumentos se convierten en la ms cercana valor de punto flotante en un "grande" representacin de punto flotante de el real de punto flotante argumentos. Nmeros de punto flotante en una "pequea" representacin tambin se convierten en la mayor representacin. Del mismo modo, si alguno de los argumentos son complejos, verdaderos argumentos se convierten en el equivalente complejo.
(+ (/ (+ (+ (+ 1 2 C C # 2,0) 3.0) # (1 # (1 C (1 ==> 3.0 ==> 0,6666667 2) 3) ==> # c (4 2) 2) 3 / 2) ==> # c (2.5 2) 1) # c (2 -1)) ==> 3

Debido a que / no trunca, Common Lisp ofrece cuatro tipos distintos de truncar y redondear para la conversin de un nmero real (punto flotante o racional) a un nmero entero:PISO trunca hacia el infinito negativo, volviendo el mayor entero menor o igual al argumento. TECHO trunca hacia el infinito positivo, devolver el entero ms pequeo mayor o igual al argumento. TRUNCATE trunca hacia cero, por lo que es equivalente a la PLANTA de argumentos positivos y TECHO de argumentos negativos. Y ROUND redondea al entero ms cercano. Si el argumento es exactamente a medio camino entre dos enteros, redondea al entero par ms cercano. Dos son las funciones relacionadas con MOD y REM , que devuelven el mdulo y el resto de una divisin de truncar en los nmeros reales. Estas dos funciones estn relacionadas con elSUELO y TRUNCATE funciones de la siguiente manera:
(+ (* (Piso (/ xy)) y) (mod xy)) === x (+ (* (Truncar (/ xy)) y) (rem xy)) === x

As, por cocientes positivos son equivalentes, pero de cocientes negativos que producen resultados diferentes. 10 Las funciones 1 + y 1 - proporcionar una forma abreviada de expresar la suma y resta uno de un nmero. Tenga en cuenta que estos son diferentes de las macros INCF y DECF .1 + y 1 - son las funciones que devuelven un valor nuevo, pero INCF y DECF modificar un lugar. Las equivalencias siguientes muestran la relacin entre INCF / DECF , 1 + /1 - , y + / - :
(Incf (DECF (Incf (DECF x) === (setf x (1 x) === (setf x (1 x 10) === (setf x x 10) === (setf x + x)) === (setf x (x + 1)) - x)) === (setf x (- x 1)) (x + 10)) (- x 10))

Las comparaciones numricas La funcin = es el predicado de igualdad numrica. En l se compara el nmero por el valor matemtico, haciendo caso omiso de las diferencias en el tipo. Por lo tanto, = se consideran los valores matemticamente equivalente de tipos diferentes, mientras que el equivalente de la igualdad predicado genrico EQL considerara no equivalentes por la diferencia en el tipo.(El predicado la igualdad genrica EQUALP , sin embargo, usa = para comparar nmeros.) Si se llama con ms de dos argumentos, devuelve true slo si todos tienen el mismo valor.Por lo tanto:
(1 = 1) ==> T (10 = 20 / 2) ==> T

(= 1 # 1.0 c (1,0 0,0) C # (1 0)) ==> T

El / = funcin, por el contrario, devuelve true slo si todos sus argumentos son valores diferentes.
(/ (/ (/ (/ (/ = = = = = 1 1 1 1 1 1) ==> NIL 2) ==> T 2 3) ==> T 2 3 1) ==> NIL 2 3 1,0) ==> NIL

Las funciones < , > , <= y > = racionales y para nmeros de punto flotante (en otras palabras, los nmeros reales). Al igual = y / = , estas funciones se pueden llamar con ms de dos argumentos, en cuyo caso cada argumento se compara con el argumento de la derecha.
(<2 3) ==> T (> 2 3) ==> NIL (> 3 2) ==> T (<2 3 4) ==> T (<2 3 3) ==> NIL (<= 2 3 3) ==> T (<= 2 3 3 4) ==> T (<= 2 3 4 3) ==> NIL

Para elegir el ms pequeo o ms grande de varios nmeros, puede utilizar la funcin Min o Max , que toma cualquier nmero de argumentos de un nmero real y devuelve el valor mximo o mnimo.
(Mx. 10 11) ==> 11 (Min -12 -10) ==> -12 (Max -1 2 -3) ==> 2

Algunas funciones tiles otros ZEROP , MINUSP y PLUSP que comprobar si un nmero real solo es igual, menor o mayor que cero. Dos otros predicados, EVENP y ODDP , comprobar si un argumento de tipo entero es par o impar. El P sufijo en los nombres de estas funciones es una convencin de nomenclatura estndar para funciones de predicado, las funciones que ponen a prueba una condicin y devuelve un booleano. Superior de Matemticas Las funciones que hemos visto hasta ahora son el comienzo de las funciones incorporadas en matemticas. Lisp tambin es compatible con logaritmos: LOG ; exponencial: EXP yEXPT , las funciones trigonomtricas bsicas: SIN , COS y TAN , sus inversos: ASIN , ACOS , y ATAN , las funciones hiperblicas: SENOH , COSH y TANH y sus inversas:ASENOH , ACOSH y ATANH . Tambin ofrece funciones para llegar a los bits individuales de un entero y para extraer las partes de una relacin o

un nmero complejo. Para obtener una lista completa, consulte cualquier referencia comunes Lisp. Personajes Common Lisp personajes son un tipo distinto de objeto a partir de los nmeros. Eso es como debe ser - los personajes son no . nmeros, y los idiomas que los tratan como si fueran tienden a tener problemas cuando el personaje de cambiar la codificacin, por ejemplo, de 8-bit ASCII y 21-bit Unicode 11 Debido a que el Common Lisp estndar no impona una representacin particular de los personajes, en la actualidad varias implementaciones de Lisp utilizar Unicode como su codificacin "nativos" a pesar de carcter Unicode que slo un destello en los ojos de un organismo de normalizacin en el momento de ser propio de normalizacin Common Lisp est envuelta. La sintaxis de la lectura para los objetos de los personajes es simple: # \ seguido por el carcter deseado. Por lo tanto, # \ x es el carcter x . Cualquier personaje puede ser utilizado despus de la # \ , incluidos los caracteres especiales, como otra manera " , ( y los espacios en blanco Sin embargo, escribir espacios en blanco de esta manera no es muy (humanos) legible;. una sintaxis alternativa para ciertos caracteres es # \ seguido por el nombre del personaje. Exactamente lo que los nombres son compatibles depende del conjunto de caracteres y sobre la aplicacin de Lisp, pero todas las implementaciones de apoyo a los nombres de espacio y de nueva lnea . Por lo tanto, debe escribir # \ espacio en lugar de # \ , aunque esta ltima es tcnicamente legal. Otros nombres semistandard (que las implementaciones debe usar si el conjunto de caracteres tiene los caracteres apropiados) sonTab , pgina , rubout , avance de lnea , retorno , y la tecla de retroceso . Las comparaciones de carcter Lo ms importante que puedes hacer con los personajes, que no sea puesta en cadenas (que voy a llegar a ms adelante en este captulo), es compararlos con otros personajes. Dado que los personajes no son nmeros, no puede utilizar las funciones de comparacin numrica, como por ejemplo < y > . En su lugar, dos juegos de funciones proporcionan carcter especfico-anlogos a los comparadores numricos, un juego entre maysculas y minsculas y el otro maysculas y minsculas.

El anlogo de maysculas y minsculas para el teclado = es la funcin CHAR = . Al igual = , = CHAR puede tener cualquier nmero de argumentos y devuelve true slo si todos son el mismo personaje. La versin de maysculas y minsculas es CHAREQUAL . El resto de los comparadores de caracteres siguen este mismo esquema de denominacin: la comparacin entre maysculas y minsculas se nombran anteponiendo el comparador anlogo numrico con CHAR , las versiones entre maysculas y minsculas deletrear el nombre de comparacin, separado de la CAR con un guin. Tenga en cuenta, sin embargo, que<= y > = son "enunciados" con los equivalentes lgicos NO-GREATERP y NO LESSP- en lugar de la ms detallado LESSP-O-EQUALP y GREATERP-O-EQUALP . Al igual que sus homlogos numricos, todas estas funciones pueden tomar uno o ms argumentos. La Tabla 10-1 resume la relacin entre las funciones de comparacin numrica y su carcter.
Tabla 10-1. Funciones de comparacin de caracteres

Numrico analgico = / = < > <= > =

Entre maysculas y minsculas CHAR = CHAR / = CHAR < CHAR> CHAR <= CHAR> =

Entre maysculas y minsculas CHAR-IGUALDAD CHAR-NO-IGUALDAD CHAR-LESSP CHAR-GREATERP CHAR-NO-GREATERP CHAR-NO-LESSP

Otras funciones que tienen que ver con los personajes proporcionan funciones para, entre otras cosas, comprobar si un determinado carcter es alfabtico o un dgito, las pruebas del caso de un personaje, la obtencin de un carcter correspondiente en un caso diferente, y la traduccin entre los valores numricos que representan los cdigos de caracteres y objetos de carcter real. Una vez ms, para obtener ms detalles, consulte a su referencia favorita Common Lisp. Cadenas Como se mencion anteriormente, las cadenas de Common Lisp son en realidad un tipo de datos compuesto, es decir, una matriz unidimensional de caracteres. En consecuencia, voy a cubrir muchas de las cosas que usted puede hacer con las cadenas en el prximo captulo, cuando hable de las numerosas funciones para la manipulacin de secuencias, de los que las cadenas son slo un tipo. Sin embargo, las cadenas tambin tienen su propia sintaxis literal y una biblioteca de funciones para realizar operaciones especficas de cadena. Voy a hablar de estos aspectos de las cadenas en este captulo y dejar las otras para el Captulo 11.

Como hemos visto, las cadenas literales se escriben entre comillas dobles. Usted puede incluir cualquier carcter apoyado por el conjunto de caracteres en una cadena literal, excepto comillas ( " ) y la barra invertida (\). Y usted puede incluir estos dos y si se les escape con una barra invertida. De hecho, la barra invertida escapa siempre a los siguientes carcter, lo que sea, aunque esto no es necesario para cualquier carcter salvo " y l mismo. Tabla 10-2 muestra cmo varias cadenas literal ser ledo por el lector de Lisp.
Tabla 10-2. Las cadenas literales

Literal "Que tal" "Foo \" bar " "Foo \ \ bar" "\" Pepe \ "" "Foo \ bar"

Contenido foobar foo "bar foo \ bar "Que tal" foobar

Comentario Cadena de formato. La barra invertida escapa cotizacin. La barra invertida primera barra invertida escapa segundo. Las barras invertidas escapar comillas. La barra invertida "se escapa" b

Tenga en cuenta que el REPL normalmente se imprimen las cadenas en forma legible, la adicin de las comillas encierran y las barras es necesario escapar, as que si quieres ver el contenido real de una cadena, es necesario utilizar la funcin como FORMATO diseado para imprimir fcilmente legible salida. Por ejemplo, aqu est lo que se ve si escribe una cadena que contiene una comilla incrustada en el REPL:
CL-USUARIO> "foo \" bar " "Foo \" bar "

FORMATO , por el contrario, le mostrar el contenido de la cadena actual: 12


CL-USUARIO> (formato t "foo \" bar ") foo "bar NIL

Comparaciones de cadenas Usted puede comparar cadenas utilizando un conjunto de funciones que siguen la convencin al igual que las funciones de carcter comparacin con excepcin CADENA como prefijo en lugar de CHAR (ver Tabla 10-3).
Tabla 10-3. Funciones de comparacin de cadenas

Numrico analgico = / = < > <= > =

Entre maysculas y minsculas CADENA = CUERDA / = Cadena < > String CADENA <= > String =

Entre maysculas y minsculas CUERDA-IGUALDAD CADENA NO-IGUALDADCUERDA-LESSP CUERDA-GREATERP CUERDA-NOT-GREATERP CUERDA-NOT-LESSP

Sin embargo, a diferencia del carcter y el nmero de comparadores, los comparadores de cadenas se pueden comparar slo dos cuerdas. Eso es porque ellos tambin tienen argumentos de palabras clave que le permiten restringir el anlisis a una subcadena de una o de ambas cadenas. Los argumentos - : start1 , : ao1 , : start2 , y : End2 - especificar el punto de partida (inclusive) y final (en exclusiva) los ndices de subcadenas de la cadena de argumentos primero y segundo. Por lo tanto, lo siguiente:
(Cadena = "foobarbaz" "quuxbarfoo": start1 3: ao1 6: start2 4: End2 7)

compara la subcadena "bar" en los dos argumentos y devuelve cierto. El : ao1 y : End2 argumentos pueden ser NIL (o el argumento de palabra clave se omite por completo) para indicar que la subcadena correspondiente se extiende hasta el final de la cadena. La comparacin que el regreso de cierto cuando sus argumentos difieren - es decir, todos ellos, excepto = STRING y CADENA-IGUALDAD - devolver el ndice en la primera cadena en donde se detect la falta de correspondencia.
(Cadena / = "ceceo", "gil") ==> 3

Si la primera cadena es un prefijo de la segunda, el valor de retorno ser la longitud de la primera cadena, es decir, mayor que el mayor ndice vlido en la cadena de uno.
(String <"ceceo", "lisper") ==> 4

Al comparar las subcadenas, el valor resultante es todava un ndice en la cadena como un todo. Por ejemplo, el siguiente se comparan las subcadenas "bar" y "Baz", pero devuelve 5, ya que es el ndice de la r en la primera cadena:
(String <"foobar" "Abaz": start1 3: start2 1) ==> 5, NB no 2

Otras funciones de cadena le permiten convertir el caso de cadenas de caracteres y ajuste de uno o ambos extremos de una cuerda. Y, como he mencionado anteriormente, ya que las cadenas son en realidad una especie de secuencia, todas las funciones de secuencia voy a discutir en el prximo captulo se puede utilizar con cadenas. Por ejemplo, usted puede descubrir la longitud de una cadena con la LONGITUD funcin y se puede obtener y establecer los caracteres individuales de una cadena con la funcin de acceso secuencia genrica elemento, ELT , o el elemento genrico amplia funcin de acceso, AREF . O puede utilizar la cadena de

acceso especficos, CHAR . Pero esas funciones, y otros, son el tema del siguiente captulo, as que vamos a seguir adelante.

1 Fred Brooks, The Mythical Man-Month , 20 Anniversary Edition (Boston: Addison-Wesley, 1995), p.

103. nfasis en el original.


2 Teen Talk Barbie de Mattel 3 Obviamente, el tamao de un nmero que puede representarse en un equipo con memoria finita

es todava limitada en la prctica y, adems, la representacin real de bignums utilizados en una determinada implementacin de Common Lisp pueden poner otros lmites en el tamao del nmero que se puede representados. Pero esos lmites van a ser mucho ms all de "astronmica" un gran nmero. Por ejemplo, el nmero de tomos en el universo se estima en menos de 2 ^ 269; comn actual implementaciones de Lisp puede manejar fcilmente los nmeros hasta y ms all de 2 ^ 262 144.
4 La gente interesada en el uso de Common Lisp para el clculo numrico intensivo debe tener en

cuenta que la comparacin ingenua de la ejecucin de cdigo numrico en Common Lisp y lenguajes como C o FORTRAN probablemente mostrar Common Lisp a ser mucho ms lento. Esto se debe a algo tan simple como (+ ab) en Common Lisp est haciendo mucho ms de lo que pareca equivalente a + b en uno de esos idiomas. A causa de tipado dinmico de Lisp y el apoyo para cosas tales como los racionales de precisin arbitraria y nmeros complejos, adems de una aparentemente simple es hacer mucho ms que una suma de dos nmeros que son conocidos por ser representados por las palabras de la mquina. Sin embargo, puede utilizar las declaraciones para dar la informacin Lisp comunes acerca de los tipos de nmeros que usted est usando que le permitir generar cdigo que funciona slo lo que el cdigo que se generara por una C o el compilador FORTRAN. Cdigo numrico de ajuste para este tipo de actuacin est fuera del mbito de este libro, pero es ciertamente posible.
5 Si bien la norma no lo exige, muchas implementaciones de Lisp comunes compatibles con el

estndar IEEE para aritmtica de punto flotante, Norma IEEE para binario aritmtica de punto flotante, ANSI / IEEE Std. 754 a 1,985 (Institute of Electrical and Electronics Engineers, 1985) .
6 Tambin es posible cambiar la base por defecto el lector utiliza para los nmeros, sin un marcador

especfico de base, cambiando el valor de la variable global * LECTURA * BASE . Sin embargo, no est claro que es el camino a otra cosa que la locura completa.
7 Puesto que el propsito de nmeros de punto flotante es hacer un uso eficiente del hardware de

punto flotante, cada implementacin Lisp se le permite asignar estos cuatro subtipos en el nativo de tipos de punto flotante, segn corresponda. Si el hardware es compatible con menos de cuatro representaciones distintas, una o ms de los tipos puede ser equivalente.
8 "notacin cientfica computarizada" es entre comillas porque, si bien de uso comn en los

lenguajes de programacin desde los das de FORTRAN, en realidad es muy diferente de la notacin cientfica real. En particular, algo as como 1.0e4 significa 10,000.0 , pero en notacin cientfica

verdadera que se escribira como 1.0 x 10 ^ 4. Y para confundir ms las cosas, en notacin cientfica verdadera de la letra erepresenta la base del logaritmo natural, as que algo como 1,0 x e ^ 4, mientras que superficialmente similar a 1.0e4 , es un valor completamente diferente, aproximadamente del 54,6.
9 Para mantener la coherencia matemtica, + y * tambin puede ser llamado sin argumentos, en

cuyo caso devolver la identidad correspondiente: 0 para + y 1 para * .


10 En trminos generales, MOD es equivalente al % del operador en Perl y Python, y REM es

equivalente al% de C y Java. (Tcnicamente, el comportamiento exacto de% en C no se ha especificado hasta que la norma C99).
11 Incluso Java, que fue diseado desde el principio el uso de caracteres Unicode en la teora de que

fue el Unicode va a ser la codificacin de caracteres en el futuro, ha tenido problemas desde Java personajes se definen como una cantidad de 16-bit Unicode y la 3.1 estndar ampliado la gama del conjunto de caracteres Unicode para exigir una representacin de 21 bits. Ooops.
12 Ntese, sin embargo, que no todas las cadenas literales se pueden imprimir al pasar como el

segundo argumento FORMATO ya que algunas secuencias de caracteres tienen un significado especial para FORMATO .Para imprimir de forma segura una cadena arbitraria - por ejemplo, el valor de una variable s - con FORMATO , debe escribir (formato t "~ a" s).

10. Nmeros, caracteres y cadenas


Mientras que las funciones, variables, macros, y 25 operadores especiales proporcionan los elementos bsicos de la propia lengua, los componentes bsicos de sus programas sern las estructuras de datos que utiliza. Como Fred Brooks observ en The Mythical Man-Month , "La representacin es la esencia de la programacin. "
1

Common Lisp proporciona soporte integrado para la mayora de los tipos de datos se encuentran tpicamente en las lenguas modernas: los nmeros (enteros, punto flotante, y complejo), caracteres, cadenas, matrices (incluyendo matrices multidimensionales), listas, tablas hash, flujos de entrada y de salida , y una abstraccin para representar los nombres de archivo portable. Las funciones son tambin un tipo de primera clase de datos en Lisp - pueden ser almacenados en variables, pasados como argumentos, devuelve como valor de retorno, y ha creado en tiempo de ejecucin. Y estos tipos integrados son slo el comienzo. Estn definidos en el estndar del lenguaje que los programadores puedan contar con ellos y estar disponible debido a que tienden a ser ms fcil de implementar de manera eficiente cuando se integra perfectamente con el resto de la aplicacin. Pero, como veremos en captulos posteriores, Common Lisp tambin ofrece varias formas de definir nuevos tipos de datos, definir las operaciones en ellos, e integrarlos con los tipos de datos incorporados. Por ahora, sin embargo, usted puede comenzar con los tipos de datos incorporados. Debido a que Lisp es un lenguaje de alto nivel, los detalles exactos de cmo los diferentes tipos de datos se implementan son en gran parte oculto. Desde su punto de vista como usuario de la lengua, la incorporada en los tipos de datos definidos por las funciones que operan sobre ellos. As que para aprender un tipo de datos, slo tienes que aprender sobre las funciones que se pueden utilizar con l. Adems, la mayora de los incorporados en los tipos de datos tienen una sintaxis especial que el lector entiende Lisp y que la impresora utiliza Lisp. Es por eso que, por ejemplo, puede escribir cadenas como "foo" , nmeros 123 , 1.23 , y1.23 y en las listas como (abc) . Voy a describir la sintaxis de los diferentes tipos de objetos cuando se describen las funciones para la manipulacin de ellos.

En este captulo, voy a cubrir el built-in "escalar" los tipos de datos: nmeros, caracteres y cadenas. Tcnicamente, las cadenas no son escalares cierto - una cadena es una secuencia de caracteres, y se puede acceder a los caracteres individuales y manipular cadenas con una funcin que opera en las secuencias. Pero voy a hablar de cadenas de aqu porque la mayora de las funciones especficas de cadenas manipularlos como valores individuales y tambin debido a la estrecha relacin entre varias de las funciones de cadena y sus homlogos de carcter. Nmeros Matemticas, como Barbie, dice, es difcil. 2 Common Lisp no puede hacer la parte de matemticas sea ms fcil, pero s tienden a ponerse en el camino mucho menos que otros lenguajes de programacin. Eso no es sorprendente teniendo en cuenta su herencia matemtica. Lisp fue diseado por un matemtico como una herramienta para el estudio de las funciones matemticas. Y uno de los proyectos principales del proyecto MAC del MIT fue el sistema de Macsyma lgebra simblica, escrita en Maclisp, uno de los predecesores inmediatos de Common Lisp. Adems, Lisp ha sido utilizado como lengua de enseanza en lugares como el MIT, donde incluso los profesores de ciencias de la computacin se estremecen con la idea de decirle a sus alumnos que 04/10 = 2 , lo que ayuda Lisp para proporciones exactas. Y en varias ocasiones Lisp ha sido llamado a competir con FORTRAN en el campo de la informtica de alto rendimiento numrico. Una de las razones Lisp es un lenguaje bueno para las matemticas es su nmero se comportan como verdaderos nmeros matemticos de las aproximaciones de nmeros que son fciles de implementar en hardware limitado. Por ejemplo, los nmeros enteros en Common Lisp puede ser casi arbitrariamente grande en lugar de estar limitado por el tamao de una palabra de la mquina. 3 y dividir dos nmeros enteros resultados en una proporcin exacta, no un valor truncado. Y desde relaciones se representan como pares de enteros de tamao arbitrario, las relaciones pueden representar fracciones arbitrariamente precisa. 4 Por otro lado, de alto rendimiento de programacin numrica, usted puede estar dispuesto a cambiar la exactitud de los nmeros racionales de la velocidad ofrecida por el uso del hardware subyacente operaciones de punto flotante. Por lo tanto, Common Lisp tambin ofrece varios tipos de nmeros de punto flotante, que se

asignan mediante la aplicacin a la correspondiente apoyo de hardware de punto flotante de las representaciones. 5 Flota tambin se utilizan para representar los resultados de un clculo matemtico, cuyo verdadero valor sera un nmero irracional. Por ltimo, Common Lisp admite los nmeros complejos - los nmeros que resultan de hacer las cosas como la toma de races cuadradas y logaritmos de nmeros negativos. El Common Lisp estndar, incluso va tan lejos como para especificar los valores principales y cortes de la rama de funciones irracionales y trascendentales en el dominio complejo. Numricos Literales Usted puede escribir literales numricos en una variedad de formas, que vio unos pocos ejemplos en el captulo 4. Sin embargo, es importante tener en cuenta la divisin del trabajo entre el lector y el evaluador Lisp Lisp - el lector es responsable de traducir el texto en objetos de Lisp, y el evaluador Lisp se ocupa slo de los objetos. Para un determinado nmero de un tipo determinado, puede haber muchas representaciones textuales, todo lo cual se traduce a la representacin del objeto mismo por el lector Lisp. Por ejemplo, puede escribir el nmero entero 10, 10 , 20 / 2 , # xA , o cualquiera de una serie de otras maneras, pero el lector se traducir todo esto al mismo objeto. Cuando los nmeros se imprimen de vuelta - por ejemplo, en el REPL - they're impresas en una sintaxis textual cannico que puede ser diferente de la sintaxis utilizada para introducir el nmero. Por ejemplo:
CL-USUARIO> 10 10 CL-USUARIO> 20 / 2 10 CL-USUARIO> # xa 10

La sintaxis de los valores enteros es un signo opcional ( + o - ) seguido de uno o ms dgitos. Las razones se escribe como un signo opcional y una secuencia de dgitos, lo que representa el numerador, una barra inclinada ( / ), y otra secuencia de dgitos que representan el denominador. Todos los nmeros racionales son "cannica", como son leer - por eso es10 y 20 / 2 se leen como el mismo nmero, al igual que 3 / 4 y 6 / 8 . Racionales se imprimen en "reducir" la forma - valores enteros se imprimen en la sintaxis de entero y las relaciones con el numerador y el denominador reducido a la mnima expresin.

Tambin es posible escribir racionales en otras bases de 10. Si van precedidos de # B o b # , una racional literal se lee como un nmero binario con 0 y 1 como los dgitos slo legal.Un # O o o # indica un nmero octal (dgitos legal 0 - 7 ), y # X o # x indica hexadecimal (dgitos legal 0 - F o 0 - f ). Usted puede escribir en otras bases racionales 2 a 36 con# nR , donde n es la base (siempre escrito en decimal). Adicionales "cifras" ms all del 9 se han tomado de las letras A - Z o un - z . Tenga en cuenta que estos indicadores de base se aplican a todo racional - no es posible escribir una relacin con el numerador y el denominador de una base en otro. Tambin, puede escribir valores enteros, pero no relaciones, como dgitos decimales termina con un punto decimal. 6 Algunos ejemplos de los nmeros racionales, con su representacin cannica, decimal son las siguientes:
123 ==> 123 123 ==> 123 -123 ==> -123 123. ==> 123 2.3 ==> 2 / 3 -2 / 3 ==> -2 / 3 4.6 ==> 2 / 3 6.3 ==> 2 # B10101 ==> 21 # B1010/1011 ==> 10/11 # O777 ==> 511 # XDADA ==> 56026 # 36rABCDEFGHIJKLMNOPQRSTUVWXYZ ==> 8337503854730415241050377135811259267835

Tambin puede escribir nmeros de punto flotante en una variedad de maneras. A diferencia de los nmeros racionales, la sintaxis utilizada para anotar un nmero de punto flotante puede afectar el tipo real de leer el nmero. Common Lisp define cuatro subtipos de nmero de punto flotante: corto, simple, doble y largo pbucle. Cada subtipo puede utilizar un nmero diferente de bits en su representacin, lo que significa que cada subtipo puede representar valores con un rango diferente y con diferente precisin. Ms bits ofrece una gama ms amplia y ms precisin. 7 El formato bsico de nmeros de punto flotante es un signo opcional seguido de una secuencia no vaca de dgitos decimales, posiblemente, con un punto decimal incrustado. Esta secuencia puede ser seguido por un marcador de exponente para "notacin cientfica informticos." 8 El marcador exponente consta de una sola letra seguida de un signo opcional y una secuencia de dgitos, que se interpretan como la potencia de diez en el que el nmero antes de la marcador exponente debe multiplicarse. La carta tiene una doble funcin: marca el comienzo del exponente e indica qu representacin de punto flotante se debe utilizar para el nmero. Los marcadores exponente s , f , d , l (y sus equivalentes en maysculas) indican baos

cortos, simples, dobles, y largo, respectivamente. La carta e indica que la representacin por defecto (inicialmente de un solo flotador) debe ser utilizado. Los nmeros con ningn marcador exponente se leen en la representacin por defecto y debe contener un punto decimal seguido por al menos un dgito para distinguirlos de los nmeros enteros uno. Los dgitos de un nmero de punto flotante se tratan siempre como base de 10 dgitos - el B # , # X , # O , y # R sintaxis slo funciona con nmeros racionales. Los siguientes son algunos ejemplos de nmeros de punto flotante, junto con su representacin cannica:
1.0 ==> 1.0 1E0 ==> 1.0 1D0 ==> 1.0d0 123.0 ==> 123,0 123e0 ==> 123,0 0.123 ==> 0,123 0.123 ==> 0,123 123 sexto-3 ==> 0,123 123E-3 ==> 0,123 0.123e20 ==> 1.23e 19 123d23 ==> 1.23d 25

Por ltimo, los nmeros complejos estn escritos en su propia sintaxis, es decir, C # o C # seguido de una lista de dos nmeros reales que representan la parte real e imaginaria del nmero complejo. En realidad, hay cinco tipos de nmeros complejos, porque las partes real e imaginaria o bien ambos deben ser racionales o ambos el mismo tipo de nmero de punto flotante. Pero usted puede escribir como quiera - si un complejo est escrito con una racional y otra parte de punto flotante, lo racional se convierte en un flotador de la representacin adecuada.Del mismo modo, si las partes reales e imaginarias son flotadores de diferentes representaciones, la de la representacin ms pequea ser actualizado . Sin embargo, no los nmeros complejos tienen un componente racional, real y una parte imaginaria a cero - ya que tales valores son, matemticamente hablando, racional, que estn representados por el valor racional adecuado. El argumento matemtico mismo podra hacerse para los nmeros complejos de punto flotante componentes, pero para los tipos complejos un nmero con una parte imaginaria a cero es siempre un objeto diferente que el nmero de punto flotante que representa el componente real. Estos son algunos ejemplos de nmeros escritos la sintaxis de nmeros complejos:
# C (2 1) ==> # c (2 1) # C (2 / 3 3 / 4) ==> # c (2 / 3 3 / 4)

# # # # # # #

C C C C C C C

(2 1,0) ==> # c (2,0 1,0) (2,0 1.0d0) ==> # c (2.0d0 1.0d0) (1 / 2 1.0) ==> # c (0,5 1,0) (3 0) ==> 3 (3,0 0,0) ==> # c (3,0 0,0) (2.1 0) ==> 1 / 2 (-6 / 3 0) ==> -2

Matemticas bsicas Las operaciones aritmticas bsicas - suma, resta, multiplicacin y divisin - son compatibles con todos los diferentes tipos de nmeros de Lisp con las funciones + , - , * y / . Llamar a cualquiera de estas funciones con ms de dos argumentos es equivalente a llamar a la misma funcin en los dos primeros argumentos y luego llamar de nuevo en el valor resultante y el resto de los argumentos. Por ejemplo, (+ 1 2 3) es equivalente a (+ (+ 1 2) 3) . Con un nico argumento, + y * devuelve el valor; - devuelve su negacin y / . su recproco 9
(+ (+ (+ (+ ((((* (* (/ (/ (/ (/ 1 2) ==> 3 1 2 3) ==> 6 10,0 3,0) ==> 13,0 C # (1 2) C # (3 4)) ==> C # (4 6) 5 4) ==> 1 2) ==> -2 10 3 5) ==> 2 2 3) ==> 6 2 3 4) ==> 24 10 5) ==> 2 10 5 2) ==> 1 2 3) ==> 2 / 3 4) ==> 1 / 4

Si todos los argumentos son del mismo tipo de nmero (punto racional, flotante o complejo), el resultado ser el mismo tipo, salvo en el caso de que el resultado de una operacin con nmeros complejos con componentes racionales se obtiene un nmero con un cero imaginario parte, en cuyo caso el resultado ser un racional. Sin embargo, los nmeros de coma flotante y complejos son contagiosas - si todos los argumentos son reales, pero uno o ms nmeros de punto flotante, los otros argumentos se convierten en la ms cercana valor de punto flotante en un "grande" representacin de punto flotante de el real de punto flotante argumentos. Nmeros de punto flotante en una "pequea" representacin tambin se convierten en la mayor representacin. Del mismo modo, si alguno de los argumentos son complejos, verdaderos argumentos se convierten en el equivalente complejo.
(+ (/ (+ (+ (+ 1 2 C C # 2,0) 3.0) # (1 # (1 C (1 ==> 3.0 ==> 0,6666667 2) 3) ==> # c (4 2) 2) 3 / 2) ==> # c (2.5 2) 1) # c (2 -1)) ==> 3

Debido a que / no trunca, Common Lisp ofrece cuatro tipos distintos de truncar y redondear para la conversin de un nmero real (punto flotante o racional) a un nmero entero:PISO trunca hacia el infinito negativo, volviendo el mayor entero menor o igual al argumento. TECHO trunca hacia el infinito positivo, devolver el entero ms pequeo mayor o igual al argumento. TRUNCATE trunca hacia cero, por lo que es equivalente a la PLANTA de argumentos positivos y TECHO de argumentos negativos. Y ROUND redondea al entero ms cercano. Si el argumento es exactamente a medio camino entre dos enteros, redondea al entero par ms cercano. Dos son las funciones relacionadas con MOD y REM , que devuelven el mdulo y el resto de una divisin de truncar en los nmeros reales. Estas dos funciones estn relacionadas con elSUELO y TRUNCATE funciones de la siguiente manera:
(+ (* (Piso (/ xy)) y) (mod xy)) === x (+ (* (Truncar (/ xy)) y) (rem xy)) === x

As, por cocientes positivos son equivalentes, pero de cocientes negativos que producen resultados diferentes. 10 Las funciones 1 + y 1 - proporcionar una forma abreviada de expresar la suma y resta uno de un nmero. Tenga en cuenta que estos son diferentes de las macros INCF y DECF .1 + y 1 - son las funciones que devuelven un valor nuevo, pero INCF y DECF modificar un lugar. Las equivalencias siguientes muestran la relacin entre INCF / DECF , 1 + /1 - , y + / - :
(Incf (DECF (Incf (DECF x) === (setf x (1 x) === (setf x (1 x 10) === (setf x x 10) === (setf x + x)) === (setf x (x + 1)) - x)) === (setf x (- x 1)) (x + 10)) (- x 10))

Las comparaciones numricas La funcin = es el predicado de igualdad numrica. En l se compara el nmero por el valor matemtico, haciendo caso omiso de las diferencias en el tipo. Por lo tanto, = se consideran los valores matemticamente equivalente de tipos diferentes, mientras que el equivalente de la igualdad predicado genrico EQL considerara no equivalentes por la diferencia en el tipo.(El predicado la igualdad genrica EQUALP , sin embargo, usa = para comparar nmeros.) Si se llama con ms de dos argumentos, devuelve true slo si todos tienen el mismo valor.Por lo tanto:
(1 = 1) ==> T (10 = 20 / 2) ==> T

(= 1 # 1.0 c (1,0 0,0) C # (1 0)) ==> T

El / = funcin, por el contrario, devuelve true slo si todos sus argumentos son valores diferentes.
(/ (/ (/ (/ (/ = = = = = 1 1 1 1 1 1) ==> NIL 2) ==> T 2 3) ==> T 2 3 1) ==> NIL 2 3 1,0) ==> NIL

Las funciones < , > , <= y > = racionales y para nmeros de punto flotante (en otras palabras, los nmeros reales). Al igual = y / = , estas funciones se pueden llamar con ms de dos argumentos, en cuyo caso cada argumento se compara con el argumento de la derecha.
(<2 3) ==> T (> 2 3) ==> NIL (> 3 2) ==> T (<2 3 4) ==> T (<2 3 3) ==> NIL (<= 2 3 3) ==> T (<= 2 3 3 4) ==> T (<= 2 3 4 3) ==> NIL

Para elegir el ms pequeo o ms grande de varios nmeros, puede utilizar la funcin Min o Max , que toma cualquier nmero de argumentos de un nmero real y devuelve el valor mximo o mnimo.
(Mx. 10 11) ==> 11 (Min -12 -10) ==> -12 (Max -1 2 -3) ==> 2

Algunas funciones tiles otros ZEROP , MINUSP y PLUSP que comprobar si un nmero real solo es igual, menor o mayor que cero. Dos otros predicados, EVENP y ODDP , comprobar si un argumento de tipo entero es par o impar. El P sufijo en los nombres de estas funciones es una convencin de nomenclatura estndar para funciones de predicado, las funciones que ponen a prueba una condicin y devuelve un booleano. Superior de Matemticas Las funciones que hemos visto hasta ahora son el comienzo de las funciones incorporadas en matemticas. Lisp tambin es compatible con logaritmos: LOG ; exponencial: EXP yEXPT , las funciones trigonomtricas bsicas: SIN , COS y TAN , sus inversos: ASIN , ACOS , y ATAN , las funciones hiperblicas: SENOH , COSH y TANH y sus inversas:ASENOH , ACOSH y ATANH . Tambin ofrece funciones para llegar a los bits individuales de un entero y para extraer las partes de una relacin o

un nmero complejo. Para obtener una lista completa, consulte cualquier referencia comunes Lisp. Personajes Common Lisp personajes son un tipo distinto de objeto a partir de los nmeros. Eso es como debe ser - los personajes son no . nmeros, y los idiomas que los tratan como si fueran tienden a tener problemas cuando el personaje de cambiar la codificacin, por ejemplo, de 8-bit ASCII y 21-bit Unicode 11 Debido a que el Common Lisp estndar no impona una representacin particular de los personajes, en la actualidad varias implementaciones de Lisp utilizar Unicode como su codificacin "nativos" a pesar de carcter Unicode que slo un destello en los ojos de un organismo de normalizacin en el momento de ser propio de normalizacin Common Lisp est envuelta. La sintaxis de la lectura para los objetos de los personajes es simple: # \ seguido por el carcter deseado. Por lo tanto, # \ x es el carcter x . Cualquier personaje puede ser utilizado despus de la # \ , incluidos los caracteres especiales, como otra manera " , ( y los espacios en blanco Sin embargo, escribir espacios en blanco de esta manera no es muy (humanos) legible;. una sintaxis alternativa para ciertos caracteres es # \ seguido por el nombre del personaje. Exactamente lo que los nombres son compatibles depende del conjunto de caracteres y sobre la aplicacin de Lisp, pero todas las implementaciones de apoyo a los nombres de espacio y de nueva lnea . Por lo tanto, debe escribir # \ espacio en lugar de # \ , aunque esta ltima es tcnicamente legal. Otros nombres semistandard (que las implementaciones debe usar si el conjunto de caracteres tiene los caracteres apropiados) sonTab , pgina , rubout , avance de lnea , retorno , y la tecla de retroceso . Las comparaciones de carcter Lo ms importante que puedes hacer con los personajes, que no sea puesta en cadenas (que voy a llegar a ms adelante en este captulo), es compararlos con otros personajes. Dado que los personajes no son nmeros, no puede utilizar las funciones de comparacin numrica, como por ejemplo < y > . En su lugar, dos juegos de funciones proporcionan carcter especfico-anlogos a los comparadores numricos, un juego entre maysculas y minsculas y el otro maysculas y minsculas.

El anlogo de maysculas y minsculas para el teclado = es la funcin CHAR = . Al igual = , = CHAR puede tener cualquier nmero de argumentos y devuelve true slo si todos son el mismo personaje. La versin de maysculas y minsculas es CHAREQUAL . El resto de los comparadores de caracteres siguen este mismo esquema de denominacin: la comparacin entre maysculas y minsculas se nombran anteponiendo el comparador anlogo numrico con CHAR , las versiones entre maysculas y minsculas deletrear el nombre de comparacin, separado de la CAR con un guin. Tenga en cuenta, sin embargo, que<= y > = son "enunciados" con los equivalentes lgicos NO-GREATERP y NO LESSP- en lugar de la ms detallado LESSP-O-EQUALP y GREATERP-O-EQUALP . Al igual que sus homlogos numricos, todas estas funciones pueden tomar uno o ms argumentos. La Tabla 10-1 resume la relacin entre las funciones de comparacin numrica y su carcter.
Tabla 10-1. Funciones de comparacin de caracteres

Numrico analgico = / = < > <= > =

Entre maysculas y minsculas CHAR = CHAR / = CHAR < CHAR> CHAR <= CHAR> =

Entre maysculas y minsculas CHAR-IGUALDAD CHAR-NO-IGUALDAD CHAR-LESSP CHAR-GREATERP CHAR-NO-GREATERP CHAR-NO-LESSP

Otras funciones que tienen que ver con los personajes proporcionan funciones para, entre otras cosas, comprobar si un determinado carcter es alfabtico o un dgito, las pruebas del caso de un personaje, la obtencin de un carcter correspondiente en un caso diferente, y la traduccin entre los valores numricos que representan los cdigos de caracteres y objetos de carcter real. Una vez ms, para obtener ms detalles, consulte a su referencia favorita Common Lisp. Cadenas Como se mencion anteriormente, las cadenas de Common Lisp son en realidad un tipo de datos compuesto, es decir, una matriz unidimensional de caracteres. En consecuencia, voy a cubrir muchas de las cosas que usted puede hacer con las cadenas en el prximo captulo, cuando hable de las numerosas funciones para la manipulacin de secuencias, de los que las cadenas son slo un tipo. Sin embargo, las cadenas tambin tienen su propia sintaxis literal y una biblioteca de funciones para realizar operaciones especficas de cadena. Voy a hablar de estos aspectos de las cadenas en este captulo y dejar las otras para el Captulo 11.

Como hemos visto, las cadenas literales se escriben entre comillas dobles. Usted puede incluir cualquier carcter apoyado por el conjunto de caracteres en una cadena literal, excepto comillas ( " ) y la barra invertida (\). Y usted puede incluir estos dos y si se les escape con una barra invertida. De hecho, la barra invertida escapa siempre a los siguientes carcter, lo que sea, aunque esto no es necesario para cualquier carcter salvo " y l mismo. Tabla 10-2 muestra cmo varias cadenas literal ser ledo por el lector de Lisp.
Tabla 10-2. Las cadenas literales

Literal "Que tal" "Foo \" bar " "Foo \ \ bar" "\" Pepe \ "" "Foo \ bar"

Contenido foobar foo "bar foo \ bar "Que tal" foobar

Comentario Cadena de formato. La barra invertida escapa cotizacin. La barra invertida primera barra invertida escapa segundo. Las barras invertidas escapar comillas. La barra invertida "se escapa" b

Tenga en cuenta que el REPL normalmente se imprimen las cadenas en forma legible, la adicin de las comillas encierran y las barras es necesario escapar, as que si quieres ver el contenido real de una cadena, es necesario utilizar la funcin como FORMATO diseado para imprimir fcilmente legible salida. Por ejemplo, aqu est lo que se ve si escribe una cadena que contiene una comilla incrustada en el REPL:
CL-USUARIO> "foo \" bar " "Foo \" bar "

FORMATO , por el contrario, le mostrar el contenido de la cadena actual: 12


CL-USUARIO> (formato t "foo \" bar ") foo "bar NIL

Comparaciones de cadenas Usted puede comparar cadenas utilizando un conjunto de funciones que siguen la convencin al igual que las funciones de carcter comparacin con excepcin CADENA como prefijo en lugar de CHAR (ver Tabla 10-3).
Tabla 10-3. Funciones de comparacin de cadenas

Numrico analgico = / = < > <= > =

Entre maysculas y minsculas CADENA = CUERDA / = Cadena < > String CADENA <= > String =

Entre maysculas y minsculas CUERDA-IGUALDAD CADENA NO-IGUALDADCUERDA-LESSP CUERDA-GREATERP CUERDA-NOT-GREATERP CUERDA-NOT-LESSP

Sin embargo, a diferencia del carcter y el nmero de comparadores, los comparadores de cadenas se pueden comparar slo dos cuerdas. Eso es porque ellos tambin tienen argumentos de palabras clave que le permiten restringir el anlisis a una subcadena de una o de ambas cadenas. Los argumentos - : start1 , : ao1 , : start2 , y : End2 - especificar el punto de partida (inclusive) y final (en exclusiva) los ndices de subcadenas de la cadena de argumentos primero y segundo. Por lo tanto, lo siguiente:
(Cadena = "foobarbaz" "quuxbarfoo": start1 3: ao1 6: start2 4: End2 7)

compara la subcadena "bar" en los dos argumentos y devuelve cierto. El : ao1 y : End2 argumentos pueden ser NIL (o el argumento de palabra clave se omite por completo) para indicar que la subcadena correspondiente se extiende hasta el final de la cadena. La comparacin que el regreso de cierto cuando sus argumentos difieren - es decir, todos ellos, excepto = STRING y CADENA-IGUALDAD - devolver el ndice en la primera cadena en donde se detect la falta de correspondencia.
(Cadena / = "ceceo", "gil") ==> 3

Si la primera cadena es un prefijo de la segunda, el valor de retorno ser la longitud de la primera cadena, es decir, mayor que el mayor ndice vlido en la cadena de uno.
(String <"ceceo", "lisper") ==> 4

Al comparar las subcadenas, el valor resultante es todava un ndice en la cadena como un todo. Por ejemplo, el siguiente se comparan las subcadenas "bar" y "Baz", pero devuelve 5, ya que es el ndice de la r en la primera cadena:
(String <"foobar" "Abaz": start1 3: start2 1) ==> 5, NB no 2

Otras funciones de cadena le permiten convertir el caso de cadenas de caracteres y ajuste de uno o ambos extremos de una cuerda. Y, como he mencionado anteriormente, ya que las cadenas son en realidad una especie de secuencia, todas las funciones de secuencia voy a discutir en el prximo captulo se puede utilizar con cadenas. Por ejemplo, usted puede descubrir la longitud de una cadena con la LONGITUD funcin y se puede obtener y establecer los caracteres individuales de una cadena con la funcin de acceso secuencia genrica elemento, ELT , o el elemento genrico amplia funcin de acceso, AREF . O puede utilizar la cadena de

acceso especficos, CHAR . Pero esas funciones, y otros, son el tema del siguiente captulo, as que vamos a seguir adelante.

1 Fred Brooks, The Mythical Man-Month , 20 Anniversary Edition (Boston: Addison-Wesley, 1995), p.

103. nfasis en el original.


2 Teen Talk Barbie de Mattel 3 Obviamente, el tamao de un nmero que puede representarse en un equipo con memoria finita

es todava limitada en la prctica y, adems, la representacin real de bignums utilizados en una determinada implementacin de Common Lisp pueden poner otros lmites en el tamao del nmero que se puede representados. Pero esos lmites van a ser mucho ms all de "astronmica" un gran nmero. Por ejemplo, el nmero de tomos en el universo se estima en menos de 2 ^ 269; comn actual implementaciones de Lisp puede manejar fcilmente los nmeros hasta y ms all de 2 ^ 262 144.
4 La gente interesada en el uso de Common Lisp para el clculo numrico intensivo debe tener en

cuenta que la comparacin ingenua de la ejecucin de cdigo numrico en Common Lisp y lenguajes como C o FORTRAN probablemente mostrar Common Lisp a ser mucho ms lento. Esto se debe a algo tan simple como (+ ab) en Common Lisp est haciendo mucho ms de lo que pareca equivalente a + b en uno de esos idiomas. A causa de tipado dinmico de Lisp y el apoyo para cosas tales como los racionales de precisin arbitraria y nmeros complejos, adems de una aparentemente simple es hacer mucho ms que una suma de dos nmeros que son conocidos por ser representados por las palabras de la mquina. Sin embargo, puede utilizar las declaraciones para dar la informacin Lisp comunes acerca de los tipos de nmeros que usted est usando que le permitir generar cdigo que funciona slo lo que el cdigo que se generara por una C o el compilador FORTRAN. Cdigo numrico de ajuste para este tipo de actuacin est fuera del mbito de este libro, pero es ciertamente posible.
5 Si bien la norma no lo exige, muchas implementaciones de Lisp comunes compatibles con el

estndar IEEE para aritmtica de punto flotante, Norma IEEE para binario aritmtica de punto flotante, ANSI / IEEE Std. 754 a 1,985 (Institute of Electrical and Electronics Engineers, 1985) .
6 Tambin es posible cambiar la base por defecto el lector utiliza para los nmeros, sin un marcador

especfico de base, cambiando el valor de la variable global * LECTURA * BASE . Sin embargo, no est claro que es el camino a otra cosa que la locura completa.
7 Puesto que el propsito de nmeros de punto flotante es hacer un uso eficiente del hardware de

punto flotante, cada implementacin Lisp se le permite asignar estos cuatro subtipos en el nativo de tipos de punto flotante, segn corresponda. Si el hardware es compatible con menos de cuatro representaciones distintas, una o ms de los tipos puede ser equivalente.
8 "notacin cientfica computarizada" es entre comillas porque, si bien de uso comn en los

lenguajes de programacin desde los das de FORTRAN, en realidad es muy diferente de la notacin cientfica real. En particular, algo as como 1.0e4 significa 10,000.0 , pero en notacin cientfica

verdadera que se escribira como 1.0 x 10 ^ 4. Y para confundir ms las cosas, en notacin cientfica verdadera de la letra erepresenta la base del logaritmo natural, as que algo como 1,0 x e ^ 4, mientras que superficialmente similar a 1.0e4 , es un valor completamente diferente, aproximadamente del 54,6.
9 Para mantener la coherencia matemtica, + y * tambin puede ser llamado sin argumentos, en

cuyo caso devolver la identidad correspondiente: 0 para + y 1 para * .


10 En trminos generales, MOD es equivalente al % del operador en Perl y Python, y REM es

equivalente al% de C y Java. (Tcnicamente, el comportamiento exacto de% en C no se ha especificado hasta que la norma C99).
11 Incluso Java, que fue diseado desde el principio el uso de caracteres Unicode en la teora de que

fue el Unicode va a ser la codificacin de caracteres en el futuro, ha tenido problemas desde Java personajes se definen como una cantidad de 16-bit Unicode y la 3.1 estndar ampliado la gama del conjunto de caracteres Unicode para exigir una representacin de 21 bits. Ooops.
12 Ntese, sin embargo, que no todas las cadenas literales se pueden imprimir al pasar como el

segundo argumento FORMATO ya que algunas secuencias de caracteres tienen un significado especial para FORMATO .Para imprimir de forma segura una cadena arbitraria - por ejemplo, el valor de una variable s - con FORMATO , debe escribir (formato t "~ a" s).

11. Colecciones
Al igual que los lenguajes de programacin, Common Lisp proporciona tipos de datos estndar que recogen los valores mltiples en un nico objeto. Todos los trozos del lenguaje el problema de recogida un poco diferente, pero los tipos de coleccin bsica por lo general se reducen a un tipo de matriz de enteros indexada y un tipo de tabla que puede ser utilizado para asignar las teclas ms o menos arbitraria de valores. Los primeros son distintas denominaciones, matrices , listas o tuplas , este ltimo a los nombres tablas hash , arrays asociativos , mapas y diccionarios . Lisp es, por supuesto, famoso por su lista de estructura de datos, y la mayora de los libros de Lisp, siguiendo el principio de la ontogenia recapitula-, la filogenia de la enseanza de idiomas, iniciar la discusin de Lisp de colecciones con las listas. Sin embargo, este enfoque conduce a menudo a los lectores a la conclusin errnea de que las listas son Lisp slo tipo de coleccin. Para empeorar las cosas, porque las listas Lisp son como una estructura de datos flexible, es posible utilizarlos para muchas de las cosas arrays y tablas hash se utilizan en otros idiomas. Pero es un error centrarse demasiado en las listas, mientras que son una estructura de datos fundamental para la representacin de datos de cdigo Lisp como Lisp, en muchas situaciones, otras estructuras de datos son ms apropiadas. Para mantener las listas de robar el show, en este captulo me centrar en Common Lisp de otros tipos de coleccin:. Vectores y tablas hash 1 Sin embargo, los vectores y las listas tienen caractersticas bastante que Common Lisp los trata como dos subtipos de una abstraccin ms general, la secuencia. Por lo tanto, puede utilizar muchas de las funciones voy a discutir en este captulo con los dos vectores y listas. Vectores Los vectores son bsicas comunes Lisp entero indexados a la coleccin, y vienen en dos sabores. De tamao fijo vectores son muy parecidos a las matrices en un lenguaje como Java:. Una fina capa sobre un trozo de memoria contigua que tiene elementos del vector dos vectores de tamao variable, en cambio, son ms como arrays en Perl o Ruby, listas en Python, o la clase ArrayList en Java: que el resumen real de almacenamiento, permitiendo que el vector de crecer y disminuye a medida que los elementos se agregan y quitan.

Usted puede hacer de tamao fijo vectores que contienen valores especficos con la funcin VECTOR , que toma cualquier nmero de argumentos y devuelve un recin asignados de tamao fijo vector que contiene los argumentos.
(Vector) ==> # () (Vector 1) ==> # (1) (Vector 1 2) ==> # (1 2)

El #(...) sintaxis es la notacin literal de los vectores utilizados por la impresora y lector de Lisp. Esta sintaxis le permite guardar y restaurar los vectores por IMPRIMIR cin hacia fuera y LEA cin de nuevo pulg Usted puede utilizar el #(...) sintaxis para incluir vectores literales en el cdigo, pero como los efectos de la modificacin de objetos literal aren ' t definida, usted debe utilizar siempre VECTOR o la funcin ms general MAKE-ARRAY para crear vectores va a modificar. MAKE-ARRAY es ms general que VECTOR ya que se puede utilizar para crear matrices de cualquier dimensin, as como los dos vectores de tamao fijo y puede cambiar de tamao.El argumento que se requiere para MAKE-ARRAY es una lista que contiene las dimensiones de la matriz. Desde un vector es una matriz unidimensional, esta lista contiene un nmero, el tamao del vector. Para mayor comodidad, HAZ-ARRAY tambin aceptar un simple nmero en el lugar de una lista de un elemento. Con ningn otro argumento, HAZ-ARRAY . crear un vector con elementos no inicializada que se deben establecer antes de que se puede acceder a 3 Para crear un vector con todos los elementos a un valor particular, puede pasar una : pgina inicial-elemento de discusin. Por lo tanto, para hacer un vector de cinco elementos con sus elementos inicializados a NIL , puede escribir lo siguiente:
(Make-matriz 5: pgina inicial-elemento nulo) ==> # (NIL NIL NIL NIL NIL)

MAKE-ARRAY es tambin la funcin a utilizar para hacer un vector de tamao variable. Un vector de tamao variable es un objeto un poco ms complicado que un vector de tamao fijo, adems de mantener un registro de la memoria utilizada para contener los elementos y el nmero de plazas disponibles, un vector de tamao variable tambin realiza un seguimiento del nmero de elementos que realmente se almacena en el vector. Este nmero se almacena en el vector de puntero llenar , llamado as porque es el ndice de la siguiente posicin a cubrir cuando se agrega un elemento al vector.

Para que un vector con un puntero de relleno, pasa MAKE-ARRAY uno : llenar triple argumento. Por ejemplo, la siguiente llamada a MAKE-ARRAY hace un vector con espacio para cinco elementos, pero lo que parece vaco porque el puntero de relleno es cero:
(Make-serie 5: llenar triple 0) ==> # ()

Para agregar un elemento al final de un vector de tamao variable, puede utilizar la funcin VECTOR-PUSH . Aade el elemento en el valor actual del puntero de relleno y luego se incrementa el puntero del relleno por uno, la devolucin del ndice en el que se aadi un nuevo elemento. La funcin VECTOR-POP devuelve el elemento ms recientemente empujados, decrementando el puntero complete el proceso.
(Defparameter * x * (make-array 5: llenar triple 0)) (Vector de empuje 'a * * X * ==> # (A) (Vector de empuje 'b * * X * ==> # (AB) (Vector de empuje 'c * * X * ==> # (ABC) (Vector-pop * x *) ==> * X * ==> # (AB) (Vector-pop * x *) ==> * X * ==> # (A) (Vector-pop * x *) ==> * X * ==> # () x *) ==> 0 x *) ==> 1 x *) ==> 2 C B A

Sin embargo, incluso un vector con un puntero de relleno no es completamente ajustable. El vector * x * puede contener un mximo de cinco elementos. Para hacer un vector de tamao variable arbitraria, tiene que pasar MAKE-ARRAY otro argumento clave: : ajustable .
(Make-serie 5: relleno puntero 0: regulable t) ==> # ()

La presente convocatoria se hace un ajuste vector cuyo subyacente de memoria se puede cambiar de tamao segn sea necesario. Para agregar elementos a un vector de ajuste, se utilizaVECTOR-PUSH-EXTENDER , que funciona como VECTOR-PUSH , excepto que se expande automticamente la matriz, si tratas de empujar un elemento en un vector completo - un puntero cuyo relleno es igual a el tamao del almacenamiento subyacente. 4 Subtipos de vectores Todos los vectores que ha tratado hasta ahora han sido en general vectores que pueden contener cualquier tipo de objeto. Tambin es posible crear especializados

vectores que estn restringidos a la celebracin de ciertos tipos de elementos. Una razn para utilizar vectores especializados es que se puede almacenar de forma ms compacta y puede proporcionar un acceso un poco ms rpido a sus elementos de vectores en general. Sin embargo, por el momento vamos a centrarnos en un tipo par de vectores especializados que son los tipos de datos son importantes por derecho propio. Uno de esos que ya hemos visto - las cadenas son vectores especializados para mantener los caracteres. Las cadenas son lo suficientemente importantes como para conseguir su propia sintaxis de lectura / print (comillas dobles) y el conjunto de cadena especfica de las funciones que discut en el captulo anterior. Sino porque son tambin vectores, todas las funciones voy a discutir en las prximas secciones que toman argumentos vector tambin se puede utilizar con cadenas. Estas funciones se llene la biblioteca de cadenas con las funciones para las cosas tales como la bsqueda de una cadena de una subcadena, la bsqueda de apariciones de un carcter dentro de una cadena, y mucho ms. Las cadenas literales, como "foo" , son como vectores literal por escrito con el # () sintaxis - su tamao es fijo, y no debe ser modificado. Sin embargo, puede utilizarMAKE-ARRAY para hacer que las cadenas de tamao variable mediante la adicin de otro de los argumentos de palabras clave, : tipo de elemento . Este argumento toma untipo de descriptor. No voy a discutir todos los descriptores de posibles que puede utilizar aqu, por ahora es suficiente para saber que usted puede crear una cadena que pasa por el smbolo CARCTER como : tipo de elemento argumento. Tenga en cuenta que es necesario citar el smbolo para evitar que sea tratado como un nombre de variable. Por ejemplo, para hacer una cadena vaca inicialmente, pero puede cambiar de tamao, usted puede escribir lo siguiente:
(Make-serie 5: relleno puntero 0: t ajustable: el carcter de elemento de tipo ') ""

Vectores de bits - vectores cuyos elementos son todos ceros o unos - tambin tienen un tratamiento especial. Ellos tienen una lectura especial / sintaxis de impresin que se ve como# * 00001111 y una biblioteca bastante amplia de funciones, que no voy a discutir, para llevar a cabo las operaciones de bit-jugueteando como "AND" a dos matrices de bits. El descriptor de tipo para pasar como : tipo de elemento para crear un vector de bits es el smbolo de BIT .

Vectores como las secuencias de Como se mencion anteriormente, los vectores y las listas son los dos subtipos concretos de tipo abstracto secuencia . Todas las funciones voy a discutir en las prximas secciones son las funciones de secuencia, adems de ser aplicable a los vectores - tanto generales como especializados - que tambin se puede utilizar con las listas. Las dos funciones ms bsicas son la secuencia LONGITUD , que devuelve la longitud de una secuencia, y el ELT , que le permite acceder a elementos individuales a travs de un ndice de enteros. LONGITUD toma una secuencia como su nico argumento y devuelve el nmero de elementos que contiene. Para los vectores con un puntero de relleno, ste ser el valor del puntero de relleno. ELT , siglas de elemento , tiene una secuencia y un ndice de nmero entero entre cero (inclusive) y la longitud de la secuencia (en exclusiva) y devuelve el elemento correspondiente. ELT ser una seal de error si el ndice est fuera de lmites. Al igual que LONGITUD , ELT trata de un vector con un puntero como llenar con la longitud especificada por el puntero de relleno.
(Defparameter * x * (vector 1 2 3)) (Largo (Elt * (Elt * (Elt * (Elt * * x x x x x * * * * *) 0) 1) 2) 3) ==> ==> ==> ==> ==> 3 1 2 3 error

ELT es tambin un SETF lugar capaz de, por lo que puede establecer el valor de un elemento particular de esta manera:
(Setf (elt * x * 0) 10) * X * ==> # (10 2 3)

Secuencia de iteracin de funciones Si bien en teora todas las operaciones en las secuencias se reducen a una combinacin de LONGITUD , ELT , y SETF de ELT operaciones, Common Lisp proporciona una amplia biblioteca de funciones de secuencia. Un grupo de funciones de secuencia le permite expresar ciertas operaciones en secuencias como la bsqueda o filtrado de los elementos especficos sin necesidad de escribir bucles explcito. La Tabla 11-1 resume los.
Tabla 11-1.Basic funciones de secuencia

Nombre CUENTA ENCONTRAR POSICIN QUITAR

Argumentos necesarios Elemento y la secuencia Elemento y la secuencia Elemento y la secuencia Elemento y la secuencia Elemento nuevo, el tema, y la SUSTITUTO secuencia

Devoluciones Nmero de elemento veces aparece en la secuencia Artculo o NIL ndice en secuencia o NIL Secuencia con las instancias de elemento eliminado Secuencia con las instancias de elemento sustituido con nuevo tema

Aqu hay algunos ejemplos sencillos de cmo utilizar estas funciones:


(Cargo 1 # (1 2 1 2 3 1 2 3 4)) ==> 3 (Quitar # 1 (1 2 1 2 3 1 2 3 4)) ==> # (2 2 3 2 3 4) (Quitar 1 '(1 2 1 2 3 1 2 3 4)) ==> (2 2 3 2 3 4) (Remove # \ a "foobarbaz") ==> "foobrbz" (Sustituir 10 # 1 (1 2 1 2 3 1 2 3 4)) ==> # (10 2 10 2 3 10 2 3 4) (Suplente 10 1 '(1 2 1 2 3 1 2 3 4)) ==> (10 2 10 2 3 10 2 3 4) (Sustituye # \ x # \ b "foobarbaz") ==> "fooxarxaz" (Encontrar un # (1 2 1 2 3 1 2 3 4)) ==> 1 (Encontrar 10 # (1 2 1 2 3 1 2 3 4)) ==> NIL (Posicin # 1 (1 2 1 2 3 1 2 3 4)) ==> 0

Observe cmo QUITAR y SUSTITUTO siempre devuelven una secuencia del mismo tipo como argumento la secuencia. Puede modificar el comportamiento de estas cinco funciones en una variedad de formas, utilizando argumentos de palabras clave. Por ejemplo, estas funciones, por defecto, buscar elementos en la secuencia que el mismo objeto que el argumento de partida. Usted puede cambiar de dos maneras: En primer lugar, puede utilizar el : prueba de palabra clave para pasar a una funcin que acepta dos argumentos y devuelve un booleano. Si se provee, se utilizar para comparar punto a cada elemento en lugar de la prueba por defecto igualdad de objetos, luces ssmicas . 5 En segundo lugar, con la tecla: palabra clave que usted puede pasar a una funcin de un argumento que se pidi a cada elemento de la secuencia para extraer una clave de valor, que luego ser comparado con el elemento en el lugar del propio elemento. Ntese, sin embargo, que las funciones tales como ENCONTRARque los elementos de retorno de la secuencia de seguir para devolver el elemento real, no slo la clave extrada.
(Recuento de "foo" # ("foo" "bar" "baz"): cadena de prueba # '=) ==> 1 (Find 'c # ((a 10) (b 20) (c 30) (d 40)): tecla # "primero) ==> (C 30)

Para limitar los efectos de estas funciones a una sucesin particular de la secuencia de argumentos, que pueden proporcionar los ndices de seleccin con : comienzo y : fin delos argumentos. Pasando NIL para : final o la omisin es el mismo que especifica la longitud de la secuencia. 6

Si un no- NIL : a partir de fin de argumento es siempre, entonces los elementos de la secuencia se examinarn en el orden inverso. Por s mismo : de-final puede afectar a los resultados de slo ENCONTRAR y POSICIN . Por ejemplo:
(Encontrar: en primer lugar 'a # ((a 10) (B 20) (a 30) (b 40)) tecla #') ==> (A 10) (Encontrar: en primer lugar 'a # ((a 10) (B 20) (a 30) (b 40)) tecla # ": a partir de fin de t) ==> (A 30)

Sin embargo, la : a partir de fin de argumento puede afectar QUITE y SUSTITUTO junto con otro parmetro de palabra clave, : contar , que se utiliza para especificar el nmero de elementos para eliminar o sustituir. Si se especifica un : contar con ms bajo que el nmero de elementos coincidentes, entonces es obvio que las cuestiones que acaban de empezar a partir de:
(Remove # \ a "foobarbaz": cargo 1) ==> "foobrbaz" (Remove # \ a "foobarbaz": contar con un: a partir de fin de t) ==> "foobarbz"

Y mientras : a partir de fines no puede cambiar los resultados de la CUENTA funcin, que afecta al orden de los elementos se pasan a cualquiera : la prueba yla tecla: funciones, lo que podra tener efectos secundarios. Por ejemplo:
CL-USUARIO> (defparameter * v * # ((a 10) (b 20) (A 30) (40 b))) * V * CL-USUARIO> (defun detallado y uno (x) (formato t "Mirando ~ s ~%" x) (primero x)) VERBOSE PRIMERA CL-USUARIO> (cuenta 'a * v *: tecla #' verbose primero) En cuanto a (A 10) En cuanto a (B 20) En cuanto a (A 30) En cuanto a (B 40) 2 CL-USUARIO> (cuenta 'a * v *: tecla #' verbose-en primer lugar: a partir de fin de t) En cuanto a (B 40) En cuanto a (A 30) En cuanto a (B 20) En cuanto a (A 10) 2

Tabla 11-2 resume estos argumentos.


Tabla 11-2. Secuencia estndar argumentos de funcin de palabras clave

Argumento : Prueba : Clave : Inicio : Final : A partir de fin de : Contar

Significado Dos argumentos funcin que se utiliza para comparar el punto (o valor extrado por : Tecla de funcin) a los elementos. De un argumento de la funcin de extraer valor de la clave de elemento de la secuencia actual. NIL elemento de uso de medios como es. ndice inicial (inclusive) de la subsecuencia. Terminando ndice (exclusiva) de la subsecuencia. NIL indica el final de la secuencia. Si es cierto, la secuencia se recorre en sentido inverso, desde el final de partida.

Defecto EQL NIL 0 NIL NIL

Nmero que indica el nmero de elementos para eliminar o sustituir o NIL para indicar a NIL todos ( QUITAR y SUSTITUTO solamente).

De orden superior variantes de funcin Para cada una de las funciones que acabamos de mencionar, Common Lisp proporciona dos funciones de orden superior variantes que, en el lugar de la discusin el punto, tomar una funcin a ser llamada en cada elemento de la secuencia. Una serie de variantes con el mismo nombre que la funcin bsica de un SI- aade. Estas funciones se cuentan, encontrar, eliminar y sustituir los elementos de la secuencia para que el argumento de la funcin devuelve el valor true. El otro conjunto de variantes se denominan con un -SI-NO sufijo y contar, encontrar, eliminar y sustituir los elementos para que el argumento de la funcin es no devolver true.
(Cuenta-si # 'evenp # (1 2 3 4 5)) ==> 2 (Cuenta-si-no # 'evenp # (1 2 3 4 5)) ==> 3 (Posicin-si # 'dgito-char-p "abcd0001") ==> 4 (Quitar-si-no # '(lambda (x) (char = (elt x 0) # \ f)) # ("Foo" "bar" "baz" "foom")) ==> # ("foo" "foom")

De acuerdo con el estndar del lenguaje, la -SI-NO variantes estn en desuso. Sin embargo, que desprecio general, se considera que han sido en s misma una mala idea. Si la norma es cada vez revisado, es ms probable que la depreciacin ser removido de la -IF-no funciona. Por un lado, la QUITAR-SI-NO variante es, probablemente, utilizados con ms frecuencia que QUITAR-SI . A pesar de su negativa-que suena el nombre, QUITAR-SI-NO es en realidad la variante positiva que devuelve los elementos que no cumplen el predicado. 7 El SI- y -si no- variantes de aceptar todos los argumentos de palabras clave que sus contrapartes de vainilla con excepcin de : prueba , que no es necesario, ya que el principal argumento es ya una funcin. 8 Con una tecla: el argumento, el valor extrado por el : clave de la funcin se pasa a la funcin en lugar del propio elemento.
(Cuenta-si # "# evenp ((1 a) (2 b) (3 c) (4 d) (5 e)): tecla #" primero) ==> 2 (Cuenta-si-no # 'evenp # ((1 a) (2 b) (3 c) (4 d) (5 e)): tecla # "primero) ==> 3 (Quitar-si-no # 'alfa-char-p # ("Foo" "bar" "1baz"): tecla # '(lambda (x) (x elt 0))) ==> # ("foo" "bar")

El QUITAR familia de funciones de apoyo tambin una cuarta variante, RemoveDUPLICADOS , que slo tiene un argumento necesario, una secuencia, de la cual se elimina todo menos una instancia de cada elemento duplicado. Se necesita la misma

palabra clave como argumentos QUITAR , a excepcin de : contar , ya que siempre elimina todos los duplicados.
(Quitar-duplicados # (1 2 1 2 3 1 2 3 4)) ==> # (1 2 3 4)

Manipulaciones de toda la secuencia Un puado de funciones realizan operaciones en toda una secuencia (o secuencias) de una vez. Estos tienden a ser ms simple que las otras funciones que he descrito hasta ahora. Por ejemplo, COPY-SEC y REVERSE cada uno tome un solo argumento, una secuencia, y cada uno regresa una nueva secuencia del mismo tipo. La secuencia devuelta por COPY-SECcontiene los mismos elementos como argumento, mientras que la secuencia devuelta por REVERSE contiene los mismos elementos pero en orden inverso. Tenga en cuenta que ni la funcin de copias de los mismos elementos - solamente la secuencia se devuelve un nuevo objeto. El CONCATENAR funcin crea una nueva secuencia que contiene la concatenacin de cualquier nmero de secuencias. Sin embargo, a diferencia de REVERSE y COPYSEC , que devuelva una secuencia del mismo tipo que su nico argumento, CONCATENAR debe decir explcitamente qu tipo de secuencia para producir en caso de que los argumentos son de diferentes tipos. Su primer argumento es un descriptor de tipos, como el : elemento de tipo argumento MAKE-ARRAY . En este caso, los descriptores de tipo que va a utilizar ms probables son los smbolos VECTOR , LISTA , o CUERDA . 9 Por ejemplo:
(Concatenar 'vector # (1 2 3) (4 5 6)) ==> # (1 2 3 4 5 6) (Concatenar '# lista (1 2 3) (4 5 6)) ==> (1 2 3 4 5 6) ("Cadena" abc "" concatenar (# \ d # \ # e \ f)) ==> "abcdef"

Clasificacin e intercalacin Las funciones SORT y ESTABLE SORT- ofrecen dos formas de ordenar una secuencia. Ambos tomar una secuencia y un predicado de dos argumentos y devuelve una versin ordenada de la secuencia.
(Ms o menos (vector "foo" "bar" "baz") # 'cadena) <==> # ("bar" "baz" "foo")

La diferencia es que ordenacin estable- est garantizado para no volver a ordenar los elementos considerados equivalentes por el predicado, mientras SORT slo garantiza que el resultado se clasifica y se pueden reordenar los elementos equivalentes.

Ambas funciones son ejemplos de lo que se llama destructiva funciones. Funciones destructivas se les permite - por lo general por razones de eficiencia - para modificar sus argumentos de manera ms o menos arbitrarias. Esto tiene dos implicaciones: una, siempre hay que hacer algo con el valor de retorno de estas funciones (por ejemplo, asignar a una variable o pasar a otra funcin), y, dos, a menos que haya terminado con el objeto que est pasando a la funcin destructiva, debe pasar una copia en su lugar. Voy a decir algo ms acerca de las funciones destructivas en el prximo captulo. Por lo general no se preocupan por la versin sin clasificar de una secuencia despus de haberlo ordenado, as que tiene sentido para permitir SORT y ESTABLESORT para destruir el orden en el curso de la clasificacin de la misma. Pero s significa que usted necesita recordar al escribir lo siguiente: 10
(Setf mi-secuencia (tipo mi-secuencia # cadena '<))

y no slo esto:
(Ms o menos mi secuencia # '<cadena)

Ambas funciones tambin tienen un argumento de palabra clave, : claves , que, como el : clave en el argumento de las funciones de otra secuencia, debe ser una funcin y se utilizar para extraer los valores que se transferirn al predicado de ordenacin en el lugar de los elementos reales . Las claves extradas slo se utilizan para determinar el orden de los elementos, la secuencia devuelta contendr los elementos reales de la secuencia del argumento. El MERGE funcin toma dos secuencias y un predicado, y devuelve una secuencia producida por la fusin de las dos secuencias, de acuerdo con el predicado. Est relacionado con las dos funciones de ordenacin en que si cada secuencia ya est ordenado por el mismo predicado, entonces la secuencia devuelta por MERGE tambin sern ordenados. Al igual que las funciones de ordenacin, MERGE toma : clave de discusin. Al igual que CONCATENAR y por la misma razn, el primer argumento de MERGE debe ser un descriptor de tipo especifica el tipo de secuencia para producir.
(Merge 'vector # (1 3 5) # (2 4 6) #') <==> # (1 2 3 4 5 6) (Merge 'lista # (1 3 5) # (2 4 6) #') <==> (1 2 3 4 5 6)

Manipulaciones subsecuencia Otro conjunto de funciones le permite manipular las subsecuencias de las secuencias existentes. La ms bsica de ellas es SUBSEQ , que extrae una sucesin a partir de un ndice concreto y siguiendo con un ndice final en particular o el final de la secuencia. Por ejemplo:
(Subseq "foobarbaz" 3) ==> "barbaz" (Subseq "foobarbaz" 3 6) ==> "bar"

SUBSEQ tambin SETF poder, pero no va a ampliar o reducir una secuencia, si el nuevo valor y la subsecuencia que ser sustituido son de distinta longitud, la ms corta de las dos determina cuntos caracteres se cambian.
(Defparameter * x * (copy-seq "foobarbaz")) (Setf (subseq * x * 3 6) "xxx"); subsecuencia y el nuevo valor es la misma longitud * X * ==> "fooxxxbaz" (Setf (subseq * x * 3 6) "abcd"); nuevo valor demasiado largo, personaje extra ignorado. * X * ==> "fooabcbaz" (Setf (subseq * x * 3 6) "xx"), el nuevo valor demasiado corto, slo dos caracteres cambiados * X * ==> "fooxxcbaz"

Usted puede utilizar el LLENAR funcin para establecer mltiples elementos de una secuencia a un solo valor. Los argumentos necesarios son una secuencia y el valor con que llenarlo.Por defecto, cada elemento de la secuencia se establece en el valor; : inicio y : fin de los argumentos de palabras clave puede limitar los efectos de una sucesin determinada. Si usted necesita encontrar una sucesin dentro de una secuencia, el BUSCAR funcin trabaja como POSICIN , excepto el primer argumento es una secuencia en lugar de un solo elemento.
(Posicin # \ b "foobarbaz") ==> 3 (Bsqueda de "bar" "foobarbaz") ==> 3

Por otro lado, para encontrar en dos secuencias con un prefijo comn primero divergen, se puede utilizar el NO COINCIDE funcin. Se necesitan dos secuencias y devuelve el ndice del primer par de elementos que no coinciden.
(Desajuste "foobarbaz" "foom") ==> 3

Devuelve NIL si el partido cadenas. DESCALCE tambin tiene muchos de los argumentos de palabra clave estndar: una tecla: el argumento para especificar una funcin de utilizar para extraer los valores que se compararn, una : prueba de

argumento para especificar la funcin de comparacin, y : start1 , : ao1 , : start2 , y : End2argumentos para especificar subsecuencias dentro de las dos secuencias. Y : a partir de fin de argumento de T especifica las secuencias deben ser buscados en el orden inverso, lo que NO COINCIDE para devolver el ndice, en la primera secuencia, donde lo comn el sufijo comparten dos secuencias comienza.
(Desequilibrio "que tal" "bar": a partir de fin de t) ==> 3

Secuencia de predicados Cuatro funciones tiles otros CADA , ALGUNOS , NOTANY y NOTEVERY , que iterar a travs de secuencias de prueba de un predicado booleano. El primer argumento de todas estas funciones es el predicado, y el resto de argumentos son secuencias. El predicado debe tener tantos argumentos como el nmero de secuencias pasado. Los elementos de las secuencias se pasan con el predicado - un elemento de cada secuencia - hasta que una de las secuencias se queda sin elementos o la prueba de la terminacin total de cumplirse es: TODOS LOStermina, devolviendo falso, tan pronto como el predicado falla. Si el predicado siempre se cumple, se devuelve el valor true. ALGUNOS devuelve el primer no- NIL valor devuelto por el predicado o devuelve false si el predicado no est nunca satisfecho. NOTANY declaraciones falsas en cuanto el predicado es verdadero si cumplen o no lo es. Y NOTEVERYdevuelve true tan pronto como el predicado falla o falso si el predicado es siempre satisfechos. Estos son algunos ejemplos de las pruebas de una sola secuencia:
(Evenp (Evenp (Evenp (Evenp todos los # '# (1 2 3 4 5)) ==> NIL algunos # '# (1 2 3 4 5)) ==> T notany # '# (1 2 3 4 5)) ==> NIL notevery # '# (1 2 3 4 5)) ==> T

Estas llamadas comparar los elementos de dos secuencias de pares:


(> Todos los # '# (1 2 3 4) # (5 4 3 2)) ==> NIL (> Algunos # '# (1 2 3 4) # (5 4 3 2)) ==> T (Notany # "> # (1 2 3 4) # (5 4 3 2)) ==> NIL (Notevery # "> # (1 2 3 4) # (5 4 3 2)) ==> T

Funciones de secuencia de asignacin Finalmente, la ltima de las funciones de secuencia son las funciones de asignacin genrica. MAPA , al igual que las funciones de secuencia de predicado, tiene un n funcin de argumento y n secuencias. Pero en lugar de un valor booleano, MAPA devuelve una secuencia nueva que contiene el resultado de aplicar la funcin a los

elementos posteriores de las secuencias. Al igual que CONCATENAR y MERGE , MAPA necesita que le digan qu tipo de secuencia para crear.
(* Mapa "vector # '# (1 2 3 4 5) # (10 9 8 7 6)) ==> # (10 18 24 28 30)

MAP-EN es como MAPA excepto que en lugar de producir una nueva secuencia de un tipo determinado, se coloca los resultados en una secuencia que se pasa como primer argumento.Esta secuencia puede ser la misma que una de las secuencias de proporcionar los valores de la funcin. Por ejemplo, para sumar varios vectores - un , b , y c - en una sola, se podra escribir lo siguiente:
(Mapa-en un # + 'abc)

Si las secuencias son de distinta longitud, MAP-EN afecta slo a tantos elementos como se presentan en la secuencia ms corta, incluida la secuencia que se est asignando en. Sin embargo, si la secuencia que se asignan a es un vector con un puntero de relleno, el nmero de elementos afectados no est limitado por el puntero del relleno, sino ms bien por el tamao actual del vector. Despus de una llamada a la MAP-EN , el puntero del relleno se establece en el nmero de elementos asignados. MAP-EN no, sin embargo, extender un vector ajustable. La funcin ltima secuencia es REDUCIR , lo que hace otro tipo de asignacin: se asigna a una sola secuencia, la aplicacin de una funcin de dos argumentos primero en los dos primeros elementos de la secuencia y el valor devuelto por la funcin y los elementos posteriores de la secuencia. Por lo tanto, la expresin siguiente resume los nmeros del uno al diez:
(Reducir el # '+ # (1 2 3 4 5 6 7 8 9 10)) ==> 55

REDUCIR es una funcin sorprendentemente til - siempre que sea necesario extraer una secuencia a un valor nico, lo ms probable es que usted puede escribir con REDUCE , y muchas veces ser una manera muy concisa de expresar lo que quieres. Por ejemplo, para encontrar el valor mximo de una secuencia de nmeros, se puede escribir(reducir el nmero # 'max) . REDUCIR tambin tiene un complemento completo de los argumentos de la palabra clave ( la tecla: , : de-final , : inicio , y : fin ) y una nica para REDUCIR ( : valor inicial ). Este ltimo establece un valor que es lgicamente colocados antes del primer elemento de la secuencia (o despus de la ltima, si tambin se especifica un cierto : a partir de fin de argumento).

Tablas Hash El otro propsito general coleccin proporcionada por Common Lisp es la tabla hash. Donde los vectores proporcionar una estructura enteros indexados a los datos, las tablas hash permiten utilizar objetos arbitrarios como los ndices o claves. Cuando se agrega un valor a una tabla hash, lo almacena en una clave particular. Ms tarde se puede utilizar la misma clave para recuperar el valor. O se puede asociar un nuevo valor con la misma clave - cada uno de los principales mapas para un solo valor. Sin argumentos MAKE-tabla hash hace una tabla hash que considera dos teclas equivalentes si son el mismo objeto de acuerdo a EQL . Este es un buen valor por defecto a menos que desee usar cadenas como claves, ya que dos cadenas con el mismo contenido que no son necesariamente EQL . En ese caso, usted querr un llamado IGUALDAD tabla hash, que usted puede conseguir pasando el smbolo de EQUAL como : prueba de argumento clave para MAKE-tabla hash . Otros dos valores posibles para la : Prueba dediscusin son los smbolos de EQ y EQUALP . Estos son, por supuesto, los nombres de las funciones estndar de comparacin de objetos, que en el captulo 4. Sin embargo, a diferencia de : prueba de los argumentos pasados a las funciones de secuencia, MAKE-tabla hash s ' : la prueba no puede utilizarse para especificar una funcin arbitraria - nicamente los valores de EQ , EQL , EQUAL , y EQUALP . Esto se debe a las tablas hash en realidad necesitamos dos funciones, una funcin de equivalencia y un hash dela funcin que calcula el cdigo hash de la clave numrica de una manera compatible con la forma en la funcin de equivalencia en ltima instancia, comparar dos claves. Sin embargo, aunque el estndar del lenguaje proporciona slo para las tablas hash que utilizan las funciones estndar de equivalencia, la mayora de las implementaciones de proporcionar un mecanismo para la definicin de las tablas hash personalizado. El GetHash funcin le permite acceder a los elementos de una tabla hash. Tiene dos argumentos - una llave y la tabla hash - y devuelve el valor, en su caso, almacenados en la tabla hash bajo esa clave o NIL . 11 Por ejemplo:
(Defparameter * h * (make-hash-table)) (GetHash 'fu * h *) ==> NIL (Setf (GetHash 'fu * h *)' quux) (GetHash 'fu * h *) ==> quux

Desde GetHash vuelve NIL si la clave no est presente en la mesa, no hay manera de saber el valor de retorno de la diferencia entre una clave no est en una tabla hash en todo y estar en la tabla con el valor NIL . GetHash resuelve este problema con una caracterstica que no hemos discutido todava - de varios valores de retorno. GetHash en realidad devuelve dos valores: el valor principal es el valor almacenado en la clave dada o NIL . El valor de secundaria es un booleano que indica si la clave est presente en la tabla hash.Debido al trabajo de la forma en varios valores, el valor de retorno adicional es silenciosamente descartado a menos que la persona que llama explcitamente lo maneja con una forma que puede "ver" varios valores. Voy a hablar de los mltiples valores de retorno en mayor detalle en el captulo 20, pero por ahora te voy a dar un adelanto de cmo utilizar el multiple-value-BIND macro para tomar ventaja de GetHash valor 's rendimiento extra. mltiples valores -BIND crea enlaces de variables como el LET hace, llenndolos de los mltiples valores devueltos por un formulario. La funcin siguiente muestra cmo puede utilizar varios valores-BIND , las variables que se une es el valor y la actualidad :
(Defun show-valor (tecla de almohadilla de mesa) (Multiple-value-bind (valor actual) (GetHash tecla de almohadilla de mesa) (Si est presente (Formato nulo "Valor ~ una realidad presente." Valor) (Formato nulo "Valor ~ a, ya que no clave que se encuentran." Valor)))) (Setf (GetHash 'bar * h *) nil); proporcionar un valor explcito de NIL (Show-valor 'fu * h *) ==> "Valor quux realidad presente." (Show-valor 'bar * h *) ==> "valor nulo en realidad presente." (Show-valor 'baz * h *) ==> "NIL de valor porque no clave que se encuentran."

Desde la creacin de valor en una clave de NIL deja la llave en la tabla, tendr otra funcin para eliminar por completo un par de clave / valor. REMHASH toma los mismos argumentos que GetHash y elimina la entrada especificada. Tambin puede del todo claro una tabla hash de todos sus pares clave / valor con CLRHASH . Iteracin tabla hash Common Lisp proporciona un par de formas para repetir las entradas en una tabla hash. La ms sencilla de ellas es a travs de la funcin MAPHASH . Anloga a la MAPA funcin,MAPHASH toma una funcin de dos argumentos y una tabla hash, y llama a la funcin una vez por cada par clave / valor en la tabla hash. Por ejemplo,

para imprimir todos los pares clave / valor en una tabla hash, se puede utilizar MAPHASH de esta manera:
(Maphash # '(lambda (kv) (formato t "~ a => ~ a ~%" kv)) * h *)

Las consecuencias de aadir o eliminar elementos de una tabla hash, mientras que la iteracin en que no se especifican (y es probable que sean malos), con dos excepciones: se puede usar SETF con GetHash para cambiar el valor de la entrada actual, y puede usar REMHASH para eliminar la entrada actual. Por ejemplo, para eliminar todas las entradas cuyo valor sea inferior a diez, se podra escribir lo siguiente:
(Maphash # '(lambda (kv) (cuando (<v 10) (remhash k * h *))) * h *)

La otra manera de iterar sobre una tabla hash con la ampliacin del LOOP macro, que hablar en el captulo 22. 12 El LOOP equivalente a la primera MAPHASH expresin se vera as:
(Bucle de k son la clave hash de * h * con (hash-value v) hacer (formato t "~ a => ~ a ~%" kv))

Podra decir mucho ms acerca de las colecciones nonlist con el apoyo de Common Lisp. Por ejemplo, no he hablado de matrices multidimensionales en todo o en la biblioteca de funciones para la manipulacin de matrices de bits. Sin embargo, lo que hemos cubierto en este captulo debera bastar para la mayora de sus necesidades de programacin de propsito general. Ahora es el momento de ver la estructura del mismo nombre Lisp de datos: listas.

Una vez que est familiarizado con todos los tipos de datos comn ofrece Lisp, usted tambin ver

que las listas pueden ser tiles para los datos de creacin de prototipos de estructuras que ms tarde ser reemplazado por algo ms eficiente una vez que est claro cmo exactamente los datos que se va a utilizar .
Dos vectores se llaman vectores , no matrices como sus anlogos en otros idiomas, debido a Common

Lisp soporta cierto matrices multidimensionales. Es igualmente correcto, aunque ms complicado, para referirse a ellos como arrays de una dimensin .
Tres elementos de la matriz "debe" ajustarse antes de ser visitada en el sentido de que el

comportamiento es indefinido; Lisp no necesariamente te detendr.


4 Si bien con frecuencia se usan juntos, el : relleno puntero y : ajustable argumentos son

independientes - usted puede hacer una serie ajustable sin necesidad de llenar un puntero. Sin embargo, puede utilizar VECTOR-PUSH y POP-VECTOR slo con vectores que tienen un puntero de relleno y VECTOR-PUSH-EXTENDER slo con vectores que tienen un puntero de relleno y son

ajustables. Tambin puede usar la funcin AJUSTAR-ARRAY para modificar matrices ajustables en una variedad de maneras ms all de extender la longitud de un vector.
5 Otro parmetro : la prueba no- parmetro, se especifica un predicado de dos argumentos para ser

utilizado como un : prueba de razonamiento, excepto con el resultado booleano lgica invertida. Este parmetro es obsoleto, sin embargo, con preferencia para el uso del COMPLEMENTO funcin. COMPLEMENTO toma una funcin argu-mento y devuelve una funcin que toma el mismo nmero de argumentos que el original y devuelve el complemento lgico de la funcin original. Por lo tanto, puede y debe escribir lo siguiente:
(Nmero de secuencia x: test (complemento # 'alguna prueba))

en lugar de lo siguiente:
(Nmero de secuencia x: test-no # 'alguna prueba) 6 Ntese, sin embargo, que el efecto de : inicio y : fin de QUITE y SUSTITUTO es slo para limitar los

elementos que consideran para la eliminacin o sustitucin, los elementos antes de : comenzar y despus de : final se pasa a travs de tocar.
7 Esta misma funcionalidad se conoce con el nombre de grep en Perl y filtro en Python. 8 La diferencia entre los predicados pasado como : prueba de los argumentos y los argumentos de

funcin en la SI- y -IF-no es que las funciones : prueba de predicados son predicados de dos argumentos utilizados para comparar los elementos de la secuencia para el tema especfico, mientras que el SI- y -IF-NO predicados son un argumento de las funciones que el simple prueba de los elementos individuales de la secuencia. Si las variantes de vainilla no existe, se podra aplicar en trminos de las versiones-IF mediante la incorporacin de un elemento especfico de la funcin de prueba.
(Recuento de cadena de caracteres) === (Cuenta-si # '(lambda (c) (eql char c)) de la cadena) (Recuento de cadena de caracteres: test # 'CHAR-EQUAL) === (Cuenta-si # '(lambda (c) (char-la igualdad de char c)) de la cadena) 9 Si usted le dice CONCATENAR para devolver un vector especializados, tales como una cadena,

todos los elementos de las secuencias de argumentos deben ser instancias de tipo de elemento del vector.
10 Cuando la secuencia pasa a las funciones de clasificacin es un vector, la "destruccin"

Actualmente se garantiza que implica permutar los elementos en su lugar, por lo que podra salir sin guardar el valor devuelto. Sin embargo, es un buen estilo de hacer siempre algo con el valor de retorno ya que las funciones de ordenacin puede modificar las listas de maneras mucho ms arbitraria.
11 Por un accidente de la historia, el orden de los argumentos a GetHash es lo contrario de ELT - ELT

toma la primera coleccin y el ndice, mientras que GetHash toma la primera y despus de la coleccin.

12 LOOP iteracin 's tabla hash es tpicamente implementado en la parte superior de una forma ms

primitiva, CON-HASH-TABLE-ITERATOR , que usted no necesita preocuparse, sino que fue introducido en el lenguaje especficamente para apoyar la aplicacin de las cosas tales como LOOP y es de poca utilidad a menos que necesite escribir de forma totalmente nuevas construcciones de control para iterar sobre las tablas hash.

12. Lo llamaron LISP por una razn: el proceso de listas


Listas de desempear un papel importante en Lisp - tanto por razones histricas y prcticas. Histricamente, las listas eran originales Lisp tipo de datos compuesto, a pesar de que ha pasado dcadas desde que eran su nico tipo de datos tales. En estos das, un programador de Lisp Comn es la probabilidad de utilizar un vector, una tabla hash, o una estructura definida por el usuario de clase o en cuanto a utilizar una lista. En trminos prcticos, las listas permanecern en el idioma, porque son una excelente solucin para ciertos problemas. Uno de estos problemas - la forma de representar el cdigo como los datos a fin de apoyar la transformacin de cdigo y las macros de generacin de cdigo - es particular de Lisp, lo que puede explicar por qu los otros idiomas no se siente la falta de listas Lisp estilo. En trminos ms generales, las listas son una estructura de datos excelente para representar cualquier tipo de datos heterogneos y / o jerrquico. Tambin son muy ligeros y apoyar un estilo funcional de programacin que es otra parte importante del patrimonio de Lisp. Por lo tanto, es necesario comprender las listas en sus propios trminos, a medida que adquiera una mejor comprensin de cmo las listas de trabajo, usted estar en una mejor posicin para apreciar cuando se debe y no debe usarlas. "No hay una lista"
Boy Cuchara : No trate de doblar la lista. Eso es imposible. En lugar. . . slo tratan de darse cuenta de la verdad. Neo : Qu verdad? Boy Cuchara : No hay una lista. Neo : No hay una lista? Boy Cuchara : Entonces vers que no es la lista que se dobla, sino que es slo a ti mismo. 1

La clave para la comprensin de las listas es entender que ellos son en gran medida una ilusin construida en la parte superior de los objetos que son instancias de un

tipo de datos ms primitivo. Esos objetos ms simples son los pares de valores denominados clulas contras , despus de la funcin CONS utilizados para crearlos. CONS toma dos argumentos y devuelve una nueva clula contras que contiene los dos valores. 2 Estos valores pueden ser las referencias a cualquier tipo de objeto. A menos que el segundo valor es NIL o de otra celda cons, un cons se imprime como los dos valores entre parntesis separados por un punto, un par de puntos llamada.
(Cons 1 2) ==> (1. 2)

Los dos valores en una celda de prisioneros se llama el CAR y el CDR despus de que los nombres de las funciones que se utilizan para acceder a ellos. En los albores del tiempo, estos nombres eran mnemnico, por lo menos a la gente de la ejecucin del Lisp por primera vez en un IBM 704. Pero aun as, se levantaron slo de los mnemnicos de ensamblaje utilizados para implementar las operaciones. Sin embargo, no todo es malo que estos nombres son un poco de sentido - al considerar las clulas individuales de los contras, lo mejor es pensar en ellos simplemente como un par cualquiera de los valores sin una semntica particular. As:
(Car (cons 1 2)) ==> 1 (Cdr (cons 1 2)) ==> 2

Tanto el CAR y el CDR tambin setf lugares capaces - dada una celda cons existente, es posible asignar un nuevo valor a cualquiera de sus valores. 3
(Defparameter * Contra * (cons 1 2)) * Contra * ==> (1. 2) (Setf (coche * Contra *) 10) ==> 10 * Contra * ==> (10. 2) (Setf (cdr * Contra *) 20) ==> 20 * Contra * ==> (10. 20)

Debido a que los valores de una celda contras pueden ser las referencias a cualquier tipo de objeto, se pueden construir estructuras ms grandes fuera de las clulas por los contras que los une. Las listas se construyen uniendo las clulas de los contras en una cadena. Los elementos de la lista se llevan a cabo en el CAR s de las clulas de los contras, mientras que los enlaces a las clulas contra posteriores se llevan a cabo en el CDR s. La ltima celda de la cadena tiene un CDR de NIL , que - como ya he dicho en el captulo 4 - representa la lista vaca, as como el valor falso booleano. Esta disposicin no es en absoluto exclusiva de Lisp, se llama una lista enlazada . Sin embargo, pocas lenguas fuera de la familia Lisp proporcionar ese apoyo extenso para este tipo de datos humilde.

As que cuando digo un determinado valor es una lista, lo que realmente quieren decir es que es bien NIL , o una referencia a una celda cons. El CAR de la clula contras es el primer elemento de la lista, y el CDR es una referencia a otra lista, es decir, en otra celda cons o NIL , que contiene los elementos restantes. La impresora Lisp entiende esta convencin e imprime las cadenas de este tipo de clulas como los contras listas entre parntesis y no como pares de puntos.
(Cons 1 nulo) ==> (1) (Cons 1 (cons 2 nil)) ==> (1 2) (Cons 1 (cons 2 (cons 3 nil))) ==> (1 2 3)

Cuando se habla de las estructuras construidas a partir de clulas de los contras, algunos diagramas pueden ser de gran ayuda. De caja y las flechas representan los diagramas de las clulas de los contras como un par de cajas de este tipo:

La caja de la izquierda representa la CAR , y el cuadro de la derecha es el CDR . Los valores almacenados en una celda contras particular, estn ya sea dibujado en la casilla correspondiente o representado por una flecha desde la caja a una representacin del valor referenciado. 4 Por ejemplo, la lista (1 2 3) vinculado, que consta de tres clulas contras unidos por su CDR s, se esquematiza as:

Sin embargo, la mayor parte del tiempo que trabaja con una lista que usted no tendr que hacer frente a las clulas individuales de los contras - las funciones para crear y manipular listas de hacerse cargo de eso para usted. Por ejemplo, la LISTA funcin construye un contras las clulas bajo las sbanas para usted y enlaza a todas ellas, las siguientes LISTAexpresiones son equivalentes a las anteriores CONTRAS expresiones:
(Lista 1) ==> (1) (Lista 1 2) ==> (1 2) (Lista 1 2 3) ==> (1 2 3)

Del mismo modo, cuando usted est pensando en trminos de las listas, usted no tiene que usar el sentido nombres CAR y el CDR ; PRIMERA y REST son sinnimos de CAR y CDRque se deben utilizar cuando se trata con las clulas contra las listas.
(Defparameter lista * * (lista 1 2 3 4)) (Primera lista * *) ==> 1 (El resto * la lista *) ==> (2 3 4)

(El primero (el resto * lista *)) ==> 2

Dado que las clulas contras puede tener cualquier tipo de valores, por lo que las listas pueden. Y una sola lista puede contener objetos de diferentes tipos.
(Lista de "foo" (lista 1 2) 10) ==> ("foo" (1 2) 10)

La estructura de la lista quedara as:

Dado que las listas pueden tener otras listas como elementos, tambin se pueden utilizar para representar los rboles de la profundidad y la complejidad arbitraria. Por lo tanto, hacen excelentes representaciones de datos heterogneos y jerrquicos. Lisp procesadores basados en XML, por ejemplo, suelen representar documentos XML internamente como listas. Otro ejemplo evidente de la estructura de rbol de datos es propio cdigo Lisp. En los captulos 30 y 31 que voy a escribir una biblioteca de generacin de HTML que utiliza listas de listas para representar el cdigo HTML que se generar. Voy a hablar ms prximo captulo sobre el uso de clulas contras para representar a otras estructuras de datos. Common Lisp ofrece todo una gran biblioteca de funciones para la manipulacin de las listas. En las secciones "manipulacin de la lista de-funciones" y "Cartografa", usted se ver en algunos de los ms importantes de estas funciones. Sin embargo, ser ms fcil de entender en el contexto de algunas ideas tomadas de la programacin funcional. Programacin Funcional y listas La esencia de la programacin funcional es que los programas se construyen totalmente de las funciones que no tienen efectos secundarios que calculan sus resultados basados nicamente en los valores de sus argumentos. La ventaja del estilo funcional es que hace que los programas ms fcil de entender. La eliminacin de los efectos secundarios elimina casi todas las posibilidades de accin a distancia. Y puesto que el resultado de una funcin est determinada slo por los valores de sus argumentos, su comportamiento es ms fcil de entender y poner a prueba. Por ejemplo, cuando ves a una expresin como (+ 3 4) , ya sabes el resultado es determinado nicamente por la definicin de la + funcin y los valores3 y 4 . Usted no tiene que preocuparse de lo que pudo haber sucedido antes en la ejecucin

del programa ya que no hay nada que pueda cambiar el resultado de evaluar la expresin. Las funciones que tienen que ver con los nmeros son por naturaleza funcional, ya que los nmeros son inmutables. Una lista, por el contrario, pueden mutar, como acabamos de ver, por SETF cin de la CAR y la s CDR s de las clulas de los contras que componen su columna vertebral. Sin embargo, las listas pueden ser tratados como un tipo de datos funcional si se tiene en cuenta su valor que ser determinado por los elementos que contienen. Por lo tanto, cualquier lista de la forma (1 2 3 4) es funcionalmente equivalente a cualquier otra lista que contiene los cuatro valores, independientemente de lo que las clulas de los contras se utilizan realmente para representar a la lista. Y cualquier otra funcin que toma una lista como argumento y devuelve un valor basado exclusivamente en el contenido de la lista del mismo modo se puede considerar funcional. Por ejemplo, el INVERSA funcin de sucesiones, dada la lista (1 2 3 4) , siempre devuelve una lista (4 3 2 1) . Diferentes convocatorias de REVERSE funcionalmente equivalentes a las listas de la discusin volver funcionalmente las listas de resultados equivalentes. Otro aspecto de la programacin funcional, que discutiremos en la seccin "Asignacin", es el uso de funciones de orden superior: las funciones que tratan a otras funciones como los datos, teniendo como argumentos o que regresan como resultados. La mayor parte de Common Lisp de manipulacin de la lista las funciones estn escritas en un estilo funcional. Voy a discutir ms adelante la forma de mezclar estilos de codificacin funcionales y de otro, pero primero debe entender algunos sutilezas del estilo funcional tal como se aplica a las listas. La razn la mayora de las funciones de lista se escriben funcionalmente es que les permite devolver resultados que las clulas comparten desventajas con sus argumentos. Para tomar un ejemplo concreto, la funcin APPEND toma cualquier nmero de argumentos de la lista y devuelve una nueva lista que contenga los elementos de todos sus argumentos. Por ejemplo:
(Append (lista 1 2) (lista 3 4)) ==> (1 2 3 4)

Desde el punto de vista funcional, APPEND trabajo 's es para devolver la lista (1 2 3 4) , sin modificar ninguna de las clulas de los contras en las listas (1 2) y (3 4) . Una manera obvia de lograr ese objetivo es crear una lista completamente nueva que consta de cuatro celdas nuevas contras. Sin embargo, eso es ms trabajo de lo

necesario. En su lugar,APPEND en realidad hace tan slo dos clulas nuevas contras para mantener los valores 1 y 2 , que los une y sealando el CDR de la clula contras segundo a la cabeza del ltimo argumento, la lista (3 4) . A continuacin, devuelve la celda contras que contiene el 1 . Ninguna de las clulas contras originales ha sido modificado, y el resultado es de hecho la lista (1 2 3 4) . La arruga slo es que la lista devuelta por APPEND comparte algunas clulas contras con la lista (3 4) . La estructura resultante es la siguiente:

En general, APPEND debe copiar todo, pero su ltimo argumento, pero siempre se puede devolver un resultado de que la estructura de acciones con el ltimo argumento. Otras funciones aprovechar similar de la capacidad de compartir listas de estructura. Algunos, como APPEND , se especifica que devuelva siempre resulta que la estructura social de una manera particular. Otros simplemente se le permiti regresar estructura compartida, a discrecin de la aplicacin. "Destructiva" Operaciones Si Common Lisp era un lenguaje puramente funcional, que sera el final de la historia. Sin embargo, debido a que es posible modificar una celda cons despus de haber sido creado porSETF cin de su coche o CDR , hay que pensar un poco acerca de cmo los efectos secundarios y la mezcla de compartir la estructura. Debido a la herencia funcionales Lisp, las operaciones que modifican objetos ya existentes se les llama destructivo - en la programacin funcional, el cambio de estado de un objeto "destruye" que puesto que ya no representa el mismo valor. Sin embargo, utilizando el mismo trmino para describir todas las operaciones de modificacin del estado-lleva a una cierta confusin, ya que hay dos tipos muy diferentes de las operaciones de destruccin, para la de efectos secundarios y operaciones de reciclaje de las operaciones. 5 Por el lado de efecto operaciones son los que se utilizan especficamente para sus efectos secundarios. Todos los usos de SETF son destructivas, en este sentido, al igual que las funciones que utilizan SETF debajo de las sbanas para cambiar el estado de un objeto ya existente, como VECTOR-PUSH o POP-VECTOR . Pero es un poco injusto para describir estas operaciones tan destructivo - they're no destinados

a ser utilizados en el cdigo escrito en un estilo funcional, por lo que no debe ser descrito utilizando la terminologa funcional.Sin embargo, si se mezclan no funcionales, con fines de efectos secundarios con las operaciones de distribucin de las funciones que devuelven la estructura-resultados, entonces usted necesita tener cuidado de no modificar sin darse cuenta de la estructura compartida. Por ejemplo, considere estas tres definiciones:
(* Defparameter lista-1 * (lista 1 2)) (Defparameter lista * 2 * (lista 3 4)) (Defparameter lista * 3 * (se aade a la lista * 1 **-list-2 *))

Despus de la evaluacin de estas formas, usted tiene tres listas, pero * la lista-3 * y la * lista-2 * estructura accionaria al igual que las listas en el diagrama anterior.
* Lista-1 * ==> (1 2) * Lista-2 * ==> (3 4) * Listar-3 * ==> (1 2 3 4)

Ahora consideremos lo que sucede cuando se modifica la lista * 2 * .


(Setf (primera lista * 2 *) 0) ==> 0 * Lista-2 * ==> (0 4), como se esperaba * Lista-3 * ==> (1 2 0 4), tal vez no lo que quera

El cambio a la lista * 2 *- tambin cambia lista * 3 * debido a la estructura comn: la primera clula en la cons -lista * 2 * es tambin la tercera celda en la cons-lista * 3 * . SETF cin del PRIMER de * Lista-2 * cambia el valor en el CAR de esa celda cons, que afecta a ambas listas. Por otro lado, el otro tipo de operaciones destructivas, el reciclaje de operaciones, estn destinados a ser utilizados en el cdigo funcional. Ellos usan efectos secundarios slo como una optimizacin. En particular, se vuelva a usar las clulas contra algunos de sus argumentos en la construccin de su resultado. Sin embargo, a diferencia de funciones tales comoAPPEND que las clulas de reutilizacin de los contras de su inclusin, sin modificar, en la lista de su regreso, el reciclaje de las funciones de las clulas contra reutilizar como materia prima, la modificacin de la CAR y el CDR es necesario para crear el resultado deseado. Por lo tanto, las funciones de reciclaje se puede utilizar de forma segura slo cuando las listas originales no van a ser necesarias despus de la llamada a la funcin de reciclaje. Para ver cmo funciona una funcin de reciclaje, vamos a comparar REVERSO , la funcin no destructiva que devuelve una versin invertida de una secuencia, para NREVERSE , una versin de reciclaje de la misma funcin. Debido a REVERSE no

modifica su argumento, se debe asignar una nueva clula contras de cada elemento en la lista se invierte. Pero supongamos que escribir algo como esto:
(Setf * lista * (* lista inversa *))

Al asignar el resultado de REVERSE de nuevo a la lista * * , que ha eliminado la referencia al valor original de la lista * * . Suponiendo que las clulas de los contras en la lista original no se hace referencia en ningn otro lugar, ahora son elegibles para el recolector de basura. Sin embargo, en muchas implementaciones de Lisp que sera ms eficiente a la reutilizacin de inmediato las clulas existentes en lugar de contra la asignacin de los nuevos y dejar que los viejos convertirse en basura. NREVERSE te permite hacer exactamente eso. La N significa no Consing , lo que significa que no es necesario asignar las clulas contra los nuevos. Los efectos secundarios exactas deNREVERSE se especifica que no sea intencionalmente - le est permitido modificar cualquier coche o CDR de cualquier clula contras en la lista -, pero una implementacin tpica podra caminar por la lista cambiando el CDR de cada clula contras para que apunte a la anterior celular contra, el tiempo de regresar la clula contras que antes era la clula contras ltimo en la lista de edad y ahora es la cabeza de la lista invertida. No hay clulas nuevas contras deben ser asignados, y no la basura se crea. La mayora de las funciones de reciclaje, como NREVERSE , tienen su contraparte no destructivos que calculan el mismo resultado. En general, las funciones de reciclaje tienen nombres que son los mismos que sus no-destructivas contrapartes excepto con un lder N . Sin embargo, no todos lo hacen, incluyendo varias de las funciones de reciclaje ms comnmente usados, tales como NCONC , la versin de reciclaje de APPEND y DELETE , DELETE-SI , SUPR-SI-NO , y DELETE, DUPLICADOS , las versiones de reciclaje de la REMOVE familia de las funciones de secuencia. En general, el uso de funciones de reciclaje de la misma manera que utilizan sus contrapartes no destructivos, excepto que es seguro para usar slo cuando se conocen los argumentos no se van a utilizar despus de la funcin devuelve. Los efectos secundarios de la mayora de las funciones de reciclaje no se especifican con suficiente fuerza para confiar en ella. Sin embargo, las aguas se enturbiaron an ms por un puado de reciclaje de funciones con efectos secundarios especficos que pueden ser invocados. Son

NCONC , la versin de reciclaje de APPEND y NSUBSTITUTE y su -SI y NO-IFvariantes, las versiones de reciclaje de la secuencia de funciones SUSTITUTO y amigos. Al igual que APPEND , NCONC devuelve una concatenacin de sus argumentos de la lista, sino que se basa su resultado en la siguiente forma: por cada lista no vaca que ha pasado,NCONC establece el CDR de la ltima celda de la lista de contras para que apunte a la primera celda contras del vaco al lado lista. A continuacin, devuelve la primera lista, que ahora es la cabeza de los resultados de empalmarjuntos. As:
(Defparameter * x * (lista 1 2 3)) (Nconc * x * (lista 4 5 6)) ==> (1 2 3 4 5 6) * X * ==> (1 2 3 4 5 6)

NSUBSTITUTE y las variantes se puede confiar en que caminar por la estructura de la lista de la lista de argumentos y setf del CAR s de las clulas que sostienen contra el valor antiguo por el nuevo valor y salir de lo contrario la lista intacto. A continuacin, devuelve la lista original, que ahora tiene el mismo valor que hubiera sido calculada por SUSTITUTO .6 La clave a recordar sobre NCONC y NSUBSTITUTE es que son las excepciones a la regla de que no se puede confiar en los efectos secundarios de las funciones de reciclaje. Es perfectamente aceptable - y el estilo podra decirse que buena - hacer caso omiso de la fiabilidad de sus efectos secundarios y los utilizan, como cualquier otra funcin de reciclaje, slo por el valor que retornan. La combinacin de reciclaje con estructura compartida Aunque puede utilizar las funciones de reciclaje cada vez que los argumentos de la funcin de reciclaje no se utilizar despus de la llamada a la funcin, vale la pena sealar que cada funcin de reciclaje es un arma cargada seal footward: si accidentalmente utilizar una funcin de reciclaje en un argumento que se utiliza ms tarde, usted es el riesgo de perder algunos dedos de los pies. Para empeorar las cosas, comparten las funciones de la estructura y el reciclaje tienden a trabajar con propsitos cruzados. Funciones no destructivos lista de devolver las listas que la estructura de participacin en el supuesto de que las clulas de los contras no se modifican, pero las funciones de reciclaje de trabajar

por la violacin de esa suposicin. O, dicho de otro modo, la estructura de intercambio se basa en la premisa de que no le importa exactamente lo que las clulas contras hacer una lista, mientras que el uso de funciones de reciclaje requiere que usted sepa exactamente lo que las clulas se hace referencia a los contras de dnde. En la prctica, las funciones de reciclaje tienden a ser utilizados en unas formas idiomticas pocos. Con mucho, el idioma de reciclaje ms comn es la de construir una lista que deber de regresar de una funcin "Consing" en la parte frontal de una lista, por lo general PUSH Ing. elementos en una lista almacenada en una variable local y luego devolver el resultado deNREVERSE Ing ella. 7 Esta es una manera eficaz de construir una lista, ya que cada PUSH tiene que crear una sola clula contras y modificar una variable local y la NREVERSE slo tiene que comprimir la lista reasignar el CDR s. Debido a que la lista se crea por completo dentro de la funcin, no hay peligro cualquier cdigo fuera de la funcin tiene una referencia a cualquiera de sus clulas contra. Aqu hay una funcin que utiliza este lenguaje para crear una lista de los primeros n nmeros, a partir de cero: 8
(Defun hasta (mximo) (Let ((resultado nil)) (Dotimes (I max) (Push i resultado)) (Resultado nreverse))) (Hasta 10) ==> (0 1 2 3 4 5 6 7 8 9)

La expresin ms comn de reciclaje 9 es reasignar de inmediato el valor devuelto por la funcin de reciclaje de vuelta al lugar que contiene el valor potencialmente reciclable. Por ejemplo, usted ver a menudo expresiones como la siguiente, usando DELETE , la versin de reciclaje de QUITE :
(Setf foo (eliminar nula foo))

Esto establece el valor de foo a su valor anterior, excepto con todo el NIL s eliminado. Sin embargo, incluso este lenguaje se debe utilizar con cuidado - si foo estructura de acciones con las listas de referencia en otras secciones, usando DELETE en lugar de REMOVE puede destruir la estructura de las otras listas. Por ejemplo, considere las dos listaslista *-2 * y la * lista-3 * desde antes que compartir sus ltimos dos clulas contras.
* Lista-2 * ==> (0 4) * Lista-3 * ==> (1 2 0 4)

Puede eliminar cuatro de la lista * 3 * de esta manera:


(Setf lista * 3 * (4 * Eliminar la lista-3 *)) ==> (1 2 0)

Sin embargo, DELETE es probable que sea necesario realizar la eliminacin mediante el establecimiento de la CDR de la tercera celda contras de NIL , desconectar la cuarta celda cons, la celebracin de la 4 , de la lista. Debido a que la tercera celda de la cons -lista * 3 * es tambin la primera celda en la cons -lista * 2 * , el siguiente se modificalista *-2 * , as:
* Lista-2 * ==> (0)

Si se hubiera usado QUITAR lugar de DELETE , se ha construido una lista que contiene los valores 1 y 2 , y 0 , la creacin de clulas nuevas contras, segn sea necesario en lugar de modificar cualquiera de las clulas de los contras en la lista * 3 * . En ese caso, lista *-2 * no se habra visto afectado. El PUSH / NREVERSE y SETF / DELETE idiomas, probablemente el 80 por ciento de los usos de reciclaje de funciones. Otros usos son posibles, pero requieren un seguimiento cuidadoso de que las funciones devuelven la estructura compartida y que no lo hacen. En general, cuando la manipulacin de las listas, lo mejor es escribir su propio cdigo en un estilo funcional - las funciones que dependen slo de los contenidos de sus argumentos de la lista y no se debe modificar. A raz de esta regla, por supuesto, descartar el uso de cualquiera de las funciones destructivas, el reciclaje o de otra manera. Una vez que haya el cdigo de trabajo, si muestra perfiles que necesita para optimizar, puede reemplazar las operaciones no destructivas de la lista con sus homlogos de reciclaje, pero slo si est seguro de las listas de argumentos no se hace referencia a cualquier otro lugar. Una Gotcha ltima a tener en cuenta es que la clasificacin de las funciones SORT , ESTABLE-SORT , y MERGE se mencion en el captulo 11 tambin el reciclaje de las funciones cuando se aplica a las listas. 10 Sin embargo, estas funciones no tienen su contraparte no destructivos, as que si usted necesita ordenar una lista sin destruirla, tiene que pasar la funcin de clasificacin de una copia hecha con el copy-list . En cualquier caso, usted necesita estar seguro de guardar el resultado de la funcin de clasificacin debido a que el argumento original es probable que sea por los suelos. Por ejemplo:
CL-> USER (defparameter lista * * (lista 4 3 2 1))

* LISTA * CL-> USER (ms o menos lista * # * '<) (1 2 3 4); se ve bien CL-USUARIO> * la lista * (4), hala!

Manipulacin de la lista de Funciones Con eso fuera el fondo del camino, usted est listo para buscar en la biblioteca de funciones de Common Lisp provee para la manipulacin de las listas. Ya hemos visto las funciones bsicas para llegar a los elementos de una lista: PRIMERA y REST . Aunque usted puede conseguir en cualquier elemento de una lista mediante la combinacin de las llamadas suficientes para RESTO (para bajar la lista) con una PRIMERA (para extraer el elemento), que puede ser un poco tedioso. As Common Lisp proporciona funciones con nombre de los ordinales de otros SEGUNDO al DECIMO que devuelven el elemento apropiado. En trminos ms generales, la funcin NTH toma dos argumentos, un ndice y una lista, y devuelve el n (cero) elemento de la lista. Del mismo modo, NTHCDR tiene un ndice y una lista y devuelve el resultado de la llamada CDR n veces. (Por lo tanto,(nthcdr 0 ...) simplemente devuelve la lista original, y (nthcdr 1 ...) es equivalente a REST .) Ntese, sin embargo, que ninguna de estas funciones es ms eficiente, en trminos de trabajo realizado por la computadora, que las combinaciones equivalentes de PRIMERA s y REST s - no hay manera de llegar al n -simo elemento de una lista sin seguir n CDR referencias. 11 Los 28 compuestos CAR / CDR funciones son otra familia de funciones que se pueden ver utiliza de vez en cuando. Cada funcin se nombra mediante la colocacin de una secuencia de hasta cuatro Un s y D s entre un C y R , con cada una representando una llamada a la CAR y cada D una llamada a la RDC . As:
(Lista de Caar) === (car (coche de la lista)) (Lista de CADR) === (car (cdr lista)) (Lista de cadadr) === (car (cdr (car (cdr lista))))

Tenga en cuenta, sin embargo, que muchas de estas funciones slo tienen sentido cuando se aplica a las listas que contienen otras listas. Por ejemplo, CAAR extrae el CAR de la CAR de la lista que se le da, por lo que la lista que ha pasado debe contener otra lista como primer elemento. En otras palabras, estos son realmente las funciones de los rboles en lugar de las listas:
(Caar (lista 1 2 3)) ==> Error (Caar (list (lista 1 2) 3)) ==> 1 (CADR (list (lista 1 2) (lista 3 4))) ==> (3 4)

(Caadr (list (lista 1 2) (lista 3 4))) ==> 3

Estas funciones no se utilizan tan a menudo ahora como en los viejos tiempos. E incluso los ms acrrimos de la vieja escuela los hackers de Lisp tienden a evitar las combinaciones ms. Sin embargo, se usan un poco de mayor cdigo Lisp, por lo que vale la pena, al menos la comprensin de cmo funcionan. 12 La PRIMERA - DCIMA y CAR , CADR , y as sucesivamente, las funciones tambin se puede utilizar como setf lugares capaces de si usted est utilizando las listas de nonfunctionally. La Tabla 1.12 resume algunas de las funciones de la lista de otros que no voy a cubrir en detalle.
Tabla 12-1. Otras funciones de lista

Funcin

Descripcin Devuelve la ltima celda de contras en una lista. Con un nmero entero, el argumento devuelve los LTIMA ltimos n clulas contras. Devuelve una copia de la lista, con exclusin de la clula contras pasado. Con un argumento entero, BUTLAST excluye a los ltimos n las clulas. La versin de reciclaje de BUTLAST , puede modificar y devolver la lista de argumentos, pero no tiene NBUTLAST efectos secundarios confiables. LDIFF Devuelve una copia de una lista a una celda determinada contras. TAILP Devuelve true si un objeto dado, es una clula contras que eso es parte de la estructura de una lista. Construye una lista que contenga todos excepto el ltimo de sus argumentos y luego hace que el ltimo LISTA * argumento de la CDR de la ltima celda en la lista. En otras palabras, un cruce entre LISTA y APPEND . Construye un n elemento de la lista. Los elementos iniciales de la lista son NIL o el valor especificado MAKE-LISTA con la : pgina inicial-elemento de argumento de palabra clave. La combinacin de REVERSE y APPEND , se invierte el primer argumento como con INVERSA agrega y REVAPPEND luego el segundo argumento. Reciclaje versin de REVAPPEND ; invierte primer argumento como por NREVERSE agrega y entonces NRECONC el segundo argumento. No tiene efectos secundarios confiables. CONSP Predicado para comprobar si un objeto es una clula contras. ATOM Predicado para comprobar si un objeto es no una clula contras. Listp Predicado para comprobar si un objeto es o bien una celda cons o NIL . Predicado para comprobar si un objeto es NIL . Funcionalmente equivalente a NO , pero estilsticamente NULL preferible cuando las pruebas de una lista vaca en comparacin con booleano falso.

Mapeo Otro aspecto importante del estilo funcional es el uso de funciones de orden superior, funciones que toman otras funciones como argumentos o funciones de retorno como valores. Ya has visto varios ejemplos de funciones de orden superior, tales como MAP , en el captulo anterior. A pesar de MAPA se puede utilizar con ambas listas y vectores (es decir, con cualquier tipo de secuencia), Common Lisp tambin ofrece seis funciones de asignacin especfica para las listas. Las diferencias entre las seis funciones tienen que ver con la forma en que construir su

resultado y si se aplica la funcin a los elementos de la lista o las clulas de los contras de la estructura de la lista. MAPCAR es la funcin ms como el MAPA . Debido a que siempre se devuelve una lista, que no requiere el argumento de resultado de tipo MAP hace. En su lugar, su primer argumento es la funcin de aplicar, y los argumentos siguientes son las listas cuyos elementos se proporcionan los argumentos para la funcin. De lo contrario, se comporta como MAP : la funcin se aplica a elementos sucesivos de los argumentos de la lista, teniendo un elemento de cada lista por la aplicacin de la funcin. Los resultados de cada llamada de funcin se recogen en una nueva lista. Por ejemplo:
(Mapcar # '(lambda (x) (* 2 x)) (lista 1 2 3)) ==> (2 4 6) (+ Mapcar # '(lista 1 2 3) (lista 10 20 30)) ==> (11 22 33)

Maplist es justo como MAPCAR excepto en lugar de pasar los elementos de la lista a la funcin, que pasa a las clulas contras reales. 13 As, la funcin tiene acceso no slo al valor de cada elemento de la lista (a travs de la CAR de los contras celular), sino tambin para el resto de la lista (a travs del CDR ). MAPCAN y MAPCON trabajo como MAPCAR y maplist a excepcin de la forma en que construir su resultado. Mientras MAPCAR y maplist crear una lista completamente nueva para almacenar los resultados de las llamadas a funciones, MAPCAN y MAPCON construir su resultado por corte y empalme en conjunto los resultados - que deben ser las listas - como por NCONC . As, cada invocacin de la funcin puede proporcionar cualquier nmero de elementos que se incluirn en el resultado. 14 MAPCAN , como MAPCAR , pasa a los elementos de la lista a la funcin asignada, mientras que MAPCON , como maplist , pasa a las clulas contra. Por ltimo, las funciones MAPC y MAPL son las construcciones de control disfrazados de funciones - que devuelva la lista de argumentos en primer lugar, por lo que son tiles slo cuando los efectos secundarios de la funcin asignada hacer algo interesante. MAPC es el primo de MAPCAR y MAPCAN , mientras que MAPL se encuentra en el maplist / MAPCONfamilia. Otras estructuras Mientras que las clulas contras y las listas son tpicamente considerados como sinnimo, que no tiene toda la razn - como he mencionado antes, puede utilizar listas de listas para representar los rboles. Al igual que las funciones descritas en

este captulo le permiten tratar a las estructuras construidas fuera de las clulas contra las listas, otras funciones le permiten utilizar las clulas contras para representar rboles, juegos, y dos tipos de mapas de clave / valor. Voy a discutir algunas de esas funciones en el prximo captulo.

1 Adaptado de The Matrix ( http://us.imdb.com/Quotes?0133093 ) 2 CONS fue originalmente una para el verbo construccin . 3 Cuando el lugar dado a SETF es un CAR o CDR , que se expande en una llamada a la funcin

RPLACA o RPLACD ; algunos Lispers de la vieja escuela, las mismas que todava utilizan setq - an utilizan RPLACAy RPLACD directamente, pero estilo moderno es utilizar SETF de la CAR o el CDR .
4 Normalmente, los objetos simples, tales como nmeros se dibujan dentro de la casilla

correspondiente, y los objetos ms complejos sern el exterior de la caja con una flecha de la casilla que indica la referencia. En realidad, esto se corresponde bien con la cantidad de implementaciones de Lisp Comn de trabajo - a pesar de todos los objetos se almacenan conceptualmente como referencia, algunos simples objetos inmutables pueden almacenarse directamente en una celda cons.
5 La frase de-efecto secundario se utiliza en el lenguaje estndar, pero el reciclaje es una invencin

ma, la mayora de la literatura Lisp simplemente utiliza el trmino destructiva para ambos tipos de operaciones, lo que lleva a la confusin que estoy tratando de disipar.
6 Las funciones de cadena nSTRING-CAPITALIZAR , nSTRING downcase- , y nSTRING-upcase son

similares - que devolver los mismos resultados que sus contrapartes menos-N, pero se especifican a modificar su argumento de cadena en su lugar.
7 Por ejemplo, en un examen de todos los usos de reciclaje de las funciones de la coleccin de Cdigo

Abierto de Common Lisp (CLOCC), un conjunto diverso de bibliotecas escritas por varios autores, las instancias de la PUSH / NREVERSE idioma representaron casi la mitad de todos los usos del reciclaje funciones.
8 Hay, por supuesto, otras maneras de hacer esta misma cosa. La extendida LOOP macro, por

ejemplo, hace que sea especialmente fcil y probablemente genera el cdigo que es incluso ms eficiente que el PUSH /NREVERSE versin.
9 Esta expresin representa el 30 por ciento de los usos de reciclaje en la base de cdigo CLOCC. 10 SORT y ESTABLE SORT- se puede utilizar como operaciones con fines de efectos secundarios en

los vectores, pero dado que todava devuelven el vector ordenado, que debe pasar por alto este hecho y los utilizan para los valores devueltos por el bien de la coherencia.
11 NTH es aproximadamente equivalente a la secuencia de la funcin ELT , pero slo funciona con

las listas. Tambin, de manera confusa, NTH toma el ndice como el primer argumento, lo contrario de ELT . Otra diferencia es que ELT ser una seal de error si se intenta acceder a un elemento en un ndice mayor o igual a la longitud de la lista, pero NTH devolver NIL .

12 En particular, ellos usaron a ser utilizado para extraer las diversas partes de expresiones pasados

a las macros antes de la invencin de desestructuracin listas de parmetros. Por ejemplo, usted podra tomar, aparte de la siguiente expresin:
(Cuando (> x 10) (print x))

De esta manera:
;; La condicin (CARD '(cuando (> x 10) (print x))) ==> (> X 10) ;; El cuerpo, como una lista (Cddr '(cuando (> x 10) (print x))) ==> ((IMPRIMIR X)) 13 Por lo tanto, maplist es la ms primitiva de las dos funciones - si slo tena maplist , usted podra

construir MAPCAR en la parte superior de la misma, pero no se poda construir maplist en la parte superior de la MAPCAR .
14 En los dialectos de Lisp que no se han filtrado funciona como REMOVE , la forma idiomtica para

filtrar una lista estaba con MAPCAN .


(Mapcan # '(lambda (x) (if (= x 10) nula (la lista x))) lista) === (quitar lista de los 10)

13. Ms all de Listas: Otros usos de las clulas Contras


Como se vio en el captulo anterior, el tipo de datos lista es una ilusin creada por un conjunto de funciones que manipulan las clulas de los contras. Common Lisp tambin proporciona funciones que permiten el tratamiento de las estructuras de datos construidas a partir de las clulas contra los rboles, series y tablas de consulta. En este captulo te voy a dar un rpido recorrido por algunas de esas otras estructuras de datos y las funciones para la manipulacin de ellos. Al igual que con las funciones de manipulacin de la lista-, muchas de estas funciones ser de utilidad cuando se inicia la escritura de macros ms complejas y la necesidad de manipular el cdigo Lisp como de datos. Los rboles El tratamiento de las estructuras construidas a partir de las clulas contra los rboles es casi tan natural como tratarlos como listas. Qu es una lista de listas, despus de todo, pero otra manera de pensar de un rbol? La diferencia entre una funcin que trata a un montn de clulas contras como una lista y una funcin que trata el mismo grupo de clulas contras como un rbol tiene que ver con el cual las clulas contra las funciones de recorrer para encontrar los valores de la lista o rbol. Las clulas contras atravesados por una funcin de lista, llama la estructura de la lista , se encuentran comenzando en la celda cons primera vez y tras CDR referencias hasta llegar a un NIL . Los elementos de la lista son los objetos referenciados por la CAR s de las clulas de los contras en la estructura de la lista. Si una celda contras en la estructura de lista tiene un CAR que hace referencia a otra celda contras, la celda referenciada contras se considera para ser la cabeza de una lista que es un elemento de la lista exterior. 1 estructura de rbol , por otro lado, est atravesado por siguiendo tanto CAR yCDR referencias durante tanto tiempo como ellos sealan a las clulas contras otros. Los valores de un rbol son as los atmica valores que no son los contras de las clulas de referencia ya sea por el CAR s o el CDR s de las clulas de los contras en la estructura de rbol. Por ejemplo, el siguiente cuadro y la flecha diagrama muestra las clulas de los contras que conforman la lista de listas: ((1 2) (3 4) (5 6)) . La estructura de la lista slo incluye las tres celdas de los contras en el interior del cuadro de lneas

discontinuas, mientras que la estructura de rbol incluye todas las clulas de los contras.

Para ver la diferencia entre una funcin y una funcin de lista de rbol, se puede considerar cmo funciona la lista de copias- y -COPIA DE RBOLES copiar este grupo de clulas contras. copy-list , como una funcin de lista, copia de las clulas que componen los contras la estructura de lista. Es decir, se hace una nueva celda contras correspondiente a cada una de las clulas contras en el interior del cuadro de lneas discontinuas. El CAR s de cada una de estas nuevas clulas contras hacen referencia al objeto mismo que el CAR s de las clulas originales de los contras en la estructura de la lista. Por lo tanto, copy-list no copia el sublistas (1 2) , (3 4) , o (5 6) , como se muestra en este diagrama:

COPIA-RBOL , por otro lado, hace una nueva celda contras para cada una de las clulas contras en el diagrama y los vincula juntos en la misma estructura, como se muestra en este diagrama:

Cuando una clula contras en el original referenciado un valor atmico, la clula contras correspondiente en la copia har referencia al mismo valor. De este modo, los objetos slo se hace referencia en comn por el rbol original y la copia presentada por COPY-TREE son los nmeros 5, 6, y el smbolo NIL . Otra funcin que se acerca tanto a la CAR s y el CDR s de un rbol de las clulas de los contras es RBOL-EQUAL , que compara dos rboles, teniendo en cuenta lo mismo si la estructura del rbol es la misma forma y si las hojas son EQL (o, si cumplen la prueba se suministra con el : Prueba argumento de palabra clave). Algunas otras funciones centradas en el rbol son los anlogos de los rboles a la SUSTITUTO y NSUBSTITUTE funciones de secuencia y su -SI y NO-IF- variantes. La funcinSUBST , como SUSTITUTO , toma un nuevo elemento, un elemento de edad, y un rbol (en comparacin con una secuencia), junto con la tecla: y : prueba de los argumentos de palabras clave, y devuelve un nuevo rbol con la misma forma que

el original rbol, pero con todas las instancias del elemento antiguo reemplazado por el nuevo elemento. Por ejemplo:
CL-> USER (subst 10 1 '(1 2 (3 2 1) ((1 1) (2 2)))) (10 2 (3 2 10) ((10 10) (2 2)))

SUBST-IF es anloga a sustituir-SI . En lugar de un elemento de edad, se necesita una funcin de un argumento - la funcin se llama con cada valor atmico en el rbol, y cada vez que devuelve el valor true, la posicin en el nuevo rbol se llena con el nuevo valor. SUBST-IF- NO es lo mismo, excepto los valores en los retornos de prueba NIL se sustituyen. NSUBST , NSUBST-SI , y NSUBST-si no- son las versiones de reciclaje de los SUBST funciones. Como con la mayora de las funciones de reciclaje de otros, usted debe utilizar estas funciones slo como gotaen los reempbucles para sus contrapartes no destructivos en las situaciones donde se sabe que no hay peligro de modificar una estructura compartida. En particular, usted debe seguir para guardar el valor de retorno de estas funciones ya que no tienen ninguna garanta de que el resultado ser EQ al rbol original. 2 Juegos Conjuntos tambin puede ser implementado en trminos de clulas contras. De hecho, usted puede tratar a cualquier lista como un conjunto - Common Lisp proporciona varias funciones para realizar operaciones de teora de conjuntos en las listas. Sin embargo, usted debe tener en cuenta que por la forma en las listas estn estructuradas, estas operaciones vez menos eficiente es el ms grande de los conjuntos de conseguir. Dicho esto, utilizando el incorporado en las funciones de juego hace que sea fcil de escribir conjunto de manipulacin de cdigo. Y para los conjuntos pequeos que bien puede ser ms eficiente que las alternativas. Si el perfil que muestra que estas funciones son un cuello de botella en el cdigo, siempre puede colgar las listas con los conjuntos construidos en la cima de las tablas hash o vectores de bits. Para crear un conjunto, puede utilizar la funcin colindan . colindan toma un elemento y una lista que representa un conjunto y devuelve una lista que representa el conjunto que contiene el elemento y todos los elementos del conjunto original. Para determinar si el elemento est presente, se debe examinar la lista, si el artculo no se encuentra, colindancrea una nueva clula contras mantener el

tema y que apunta a la lista original y lo devuelve. De lo contrario, devuelve la lista original. Lindan tambin tiene : clave y la prueba: los argumentos de palabras clave, que se utilizan para determinar si el elemento est presente en la lista original. Al igual que CONS, colindan no tiene ningn efecto en la lista original - si usted desea modificar una lista en particular, tiene que asignar el valor devuelto por colindan con el lugar donde la lista de vino. La modificacin macro PUSHNEW lo hace por usted de forma automtica.
CL-> USER (defparameter * juego * ()) * SET * CL-> USER (colindan 1 * juego *) (1) CL-USUARIO> * juego * NIL CL-USER> (setf * set * (1 * colindan conjunto *)) (1) CL-> USER (pushnew 2 * juego *) (2 1) CL-USUARIO> * juego * (2 1) CL-> USER (pushnew 2 * juego *) (2 1)

Se puede comprobar si un determinado artculo se encuentra en un conjunto con el miembro y las funciones relacionadas Si el Miembro- y miembro-si no- . Estas funciones son similares a las funciones de secuencia de FIND , FIND-IF , y FIND-si no- , excepto que slo se puede utilizar con las listas. Y en lugar de devolver el artculo si est presente, regresan de la clula que contiene el elemento contras - en otras palabras, la sublista a partir de la opcin deseada. Cuando el elemento deseado no est en la lista, las tres funciones devuelven NIL . El resto de teora de conjuntos funciones permiten que las operaciones a granel: INTERSECCIN , enlace , la diferencia de la- y -SET-O EXCLUSIVO . Cada una de estas funciones tiene dos listas y la tecla: y : prueba de los argumentos de palabras clave y devuelve una nueva lista que representa el conjunto resultante de la realizacin de la correspondiente operacin de la teora de conjuntos en las dos listas: INTERSECCIN devuelve una lista que contiene todos los elementos que se encuentran en ambos argumentos. enlace devuelve una lista que contenga una instancia de cada elemento nico de los dos argumentos. 3 set-difference devuelve una lista que contiene todos los elementos del primer argumento que no aparecen en el segundo argumento. Y SET-EXCLUSIVA-O devuelve una lista que contiene los elementos que aparecen slo en uno u otro de las dos listas de argumentos, pero no

en ambos. Cada una de estas funciones tambin tiene una contraparte de reciclaje, cuyo nombre es el mismo salvo con una N prefijo. Por ltimo, la funcin subsetp toma dos listas y la costumbre : clave y : prueba verdadera argumentos de palabras clave y devuelve si la primera lista es un subconjunto de la segunda - si cada elemento en la primera lista tambin est presente en la segunda lista. El orden de los elementos en las listas no importa.
CL-> USER (subsetp '(3 2 1) (1 2 3 4)) T CL-> USER (subsetp '(1 2 3 4) (3 2 1)) NIL

Tablas de bsqueda: Alists y plists Adems de los rboles y conjuntos, se pueden construir las tablas que asignar teclas a los valores fuera de las clulas contra. Dos sabores de las tablas de bsqueda basadas en contras son de uso general, tanto de los que he mencionado de pasada en captulos anteriores. Son las listas de la enlace , tambin llamados alists y listas de propiedades , tambin conocidos como plists . Mientras que no se usar ya sea alists o plists para tablas grandes - para que usted tendra que utilizar una tabla hash - vale la pena saber cmo trabajar con ellos, tanto por las tablas pequeas pueden ser ms eficientes que las tablas hash y porque tienen algunas propiedades tiles de los suyos. Un alist es una estructura de datos que se asigna a los valores claves y tambin es compatible con la resolucin inversa, la bsqueda de la clave cuando se le da un valor. Alists tambin apoyan la adicin de clave / valor mapeos que las asignaciones de sombra existentes en tal manera que el mapeo remedo ms tarde puede ser removido y las asignaciones originales expuestos de nuevo. Debajo de las sbanas, una alist es esencialmente una lista cuyos elementos son en s mismas clulas contras. Cada elemento puede ser pensado como un par clave / valor con la tecla en la celda contras del CAR y el valor en el CDR . Por ejemplo, el siguiente es un diagrama de la caja-y-flecha de un alist mapear el smbolo Una para el nmero 1, B a 2, y C a 3:

A menos que el valor en el CDR es una lista, las clulas de los contras que representan los pares clave / valor ser pares de puntos en la notacin s-expresin.

El alist esquematizado en la figura anterior, por ejemplo, se imprimen de esta forma:


((Un. 1) (B. 2) (C. 3))

La funcin de bsqueda principal de alists es ASOC , que tiene una clave y un nalista y devuelve la primera celda cuya cons CAR coincide con la clave o NIL si no se encuentra una coincidencia.
CL-USER> (assoc 'a' ((a. 1) (b. 2) (c. 3))) (Un. 1) CL-USER> (assoc 'c' ((a. 1) (b. 2) (c. 3))) (C. 3) CL-USER> (assoc 'd' ((a. 1) (b. 2) (c. 3))) NIL

Para obtener el valor correspondiente a una clave determinada, slo tiene que pasar el resultado de ASOC al CDR .
CL-USER> (cdr (assoc 'a' ((a. 1) (b. 2) (c. 3)))) 1

Por defecto, la clave dada en comparacin con las llaves en el alist usando EQL , pero usted puede cambiar eso con la combinacin estndar de : clave y la prueba: los argumentos de palabras clave. Por ejemplo, si desea utilizar claves de cadena, puede escribir lo siguiente:
CL-USER> (assoc "a" "((" a "1) (" b "2) (" C "3)):... Test # 'cadena =) ("A". 1)

Sin especificar : Prueba a ser = CUERDA , que ASOC probablemente volvera NIL ya que dos cadenas con el mismo contenido que no son necesariamente EQL .
CL-USER> (assoc "a" "((" a ". 1) (" b ". 2) (" c ". 3))) NIL

Debido a ASOC busca en la lista de exploracin de la parte delantera de la lista, un par clave / valor en una alist puede remedar otras parejas con la misma clave ms adelante en la lista.
CL-USER> (assoc 'a' ((a. 10) (a. 1) (b. 2) (c. 3))) (Un. 10)

Usted puede agregar un par al frente de un alista con CONS como esta:
(Cons (cons 'new-key' nuevo-valor) ALIST)

Sin embargo, para su conveniencia, Common Lisp proporciona las funciones ACONS , que le permite escribir lo siguiente:
('New-key "nuevo valor acons alist)

Al igual que CONS , ACONS es una funcin y por lo tanto no puede modificar el lugar de celebracin de la alist que ha pasado. Si desea modificar una alist, tiene que escribir sea la siguiente:
(Setf alist ('new-key' acons nuevo valor alist))

o este:
(Push (cons 'new-key' nuevo-valor) alist)

Obviamente, el tiempo que toma para realizar la bsqueda una alist con ASOC es una funcin de qu tan profundo en la lista el par coincidente se encuentra. En el peor de los casos, la determinacin de que ningn par de partidos requiere ASOC para escanear todos los elementos de la alist. Sin embargo, como el mecanismo bsico para alists es tan ligero, por pequeas mesas un alist puede superar a una tabla hash. Adems, alists le dar ms flexibilidad a la hora hacer la bsqueda. Ya he mencionado que ASOC tiene : clave y: prueba los argumentos de palabras clave. Cuando los que no se ajustan a sus necesidades, usted puede ser capaz de utilizar los Assoc-IF y Assoc-si no- funciones, que devuelven el primer par clave / valor cuya CAR cumple (o no, en el caso de Assoc-IF- NO ), la funcin ha pasado la prueba en el lugar de un elemento especfico. Y tres funciones:RASSOC , RASSOC-SI , y RASSOC-si no- - funcionan igual que los correspondientes ASSOC las funciones, excepto que utilizan el valor de la CDR de cada elemento como la clave, realizando una bsqueda inversa. La funcin COPY-ALIST es similar a COPY-TREE , excepto, en lugar de copiar la estructura del rbol entero, copia slo las clulas contras que componen la estructura de la lista, adems de las clulas contra referencia directamente de la CAR s de esas clulas. En otras palabras, el alist original y la copia se contienen los mismos objetos que las claves y valores, incluso si esas claves o valores resultan ser formado por clulas contras. Por ltimo, usted puede construir un alista a partir de dos listas separadas de las claves y valores con la funcin de PAIRLIS . El alist resultante puede contener los pares ya sea en el mismo orden que las listas originales o en orden inverso. Por ejemplo, usted puede obtener este resultado:
CL-> USER (pairlis '(abc)' (1 2 3)) ((C. 3) (B. 2) (A. 1))

O usted podra del mismo modo que sale esto:

CL-> USER (pairlis '(abc)' (1 2 3)) ((Un. 1) (B. 2) (C. 3))

El otro tipo de tabla de bsqueda es la lista de propiedades, o plist, que se utiliza para representar las filas de la base de datos en el captulo 3. Estructuralmente un plist es slo una lista regular con las claves y valores como son la alternancia valores. Por ejemplo, un plist mapeo Un , B , y C , para 1, 2, y 3 es simplemente la lista (A 1 B 2 C 3) . En cajas y flechas forma, se parece a esto:

Sin embargo, plists son menos flexibles que alists. De hecho, plists apoyar una sola operacin de bsqueda fundamental, la funcin GETF , que tiene un plist y una clave y devuelve el valor asociado o NIL si la clave no se encuentra. GETF tambin tiene un tercer argumento opcional, el cual ser devuelto en el lugar de NIL si la clave no se encuentra. A diferencia de ASOC , que utiliza EQL como prueba por defecto y permite una funcin de prueba diferente a ser suministrado con un : prueba de razonamiento, GETF siempre utiliza la EQ para comprobar si la clave proporcionada coincide con las llaves en el plist. Por lo tanto, nunca debe usar nmeros o caracteres como claves en un plist, como se vio en el captulo 4, el comportamiento de EQ para esos tipos es esencialmente indefinido. En la prctica, las claves de un plist son casi siempre los smbolos, lo cual tiene sentido ya plists se inventaron para implementar simblicos "Propiedades" asignaciones arbitrarias entre los nombres y valores. Usted puede utilizar SETF con GETF para establecer el valor asociado con una determinada clave. SETF tambin trata GETF un poco sobre todo en que el primer argumento deGETF se trata como el lugar de modificar. Por lo tanto, puede utilizar SETF de GETF para agregar un nuevo par clave / valor de un plist existente.
CL-> USER (* defparameter plist * ()) * PLIST * CL-USUARIO> * plist * NIL CL-> USER (setf (GETF * plist *: a) 1) 1 CL-USUARIO> * plist * (: Un 1) CL-> USER (setf (GETF * plist *: a) 2) 2 CL-USUARIO> * plist * (: Un 2)

Para quitar un par clave / valor de un plist, se utiliza la macro REMF , que establece el lugar dado como primer argumento a un plist que contiene todos los pares clave / valor, excepto el que se especifica. Devuelve true si la clave dada en realidad se encuentran.
CL-> USER (* remf plist *: a) T CL-USUARIO> * plist * NIL

Al igual que GETF , REMF siempre utiliza EQ para comparar la clave dada a las teclas en el plist. Desde plists se utilizan a menudo en situaciones en las que desea extraer varias propiedades de la misma plist, Common Lisp proporciona una funcin,Obtener propiedades de los , que hace que sea ms eficiente para extraer los valores mltiples de una sola plist. Se necesita un plist y una lista de claves para la bsqueda y vuelve, como varios valores, la primera clave encontrada, el valor correspondiente, y la cabeza de la lista que comienza con la clave que se encuentran. Esto le permite procesar una lista de propiedades, la extraccin de las propiedades deseadas, sin depender constantemente volver a analizar desde el frente de la lista. Por ejemplo, las siguientes funciones de manera eficiente procesos, utilizando la funcin hipottica proceso de la propiedad - todos los pares clave / valor en un plist para una lista determinada de teclas:
(Defun proceso de las propiedades (plist teclas) (Bucle while plist hacer (Mltiple-se unen de valor (valor de la clave de cola) (get-propiedades claves plist) (Cuando la llave (proceso de la propiedad valor de la clave)) (Setf plist (cddr cola)))))

Lo ltimo de especial plists es la relacin que tienen con los smbolos: cada objeto tiene un smbolo asociado plist que se puede utilizar para almacenar informacin sobre el smbolo. El plist se pueden obtener a travs de la funcin SMBOLO-PLIST . Sin embargo, rara vez se preocupan por el plist todo, ms a menudo vamos a usar las funciones GET , que tiene un smbolo y una clave, y es la abreviatura de un GETF de la misma clave en los smbolos PLIST SMBOLO- .
(Obtener la clave de "smbolo") === (GETF ('smbolo)' smbolo plist clave)

Al igual que GETF , GET es SETF poder, por lo que se puede adjuntar informacin arbitraria a un smbolo como este:
(Setf (get 'un smbolo-' mi-key) "informacin")

Para eliminar una propiedad de plist de un smbolo, puede utilizar cualquiera REMF de SYMBOL-PLIST o la funcin de la conveniencia REMPROP . 4
(Tecla de 'smbolo' remprop) === (remf (tecla de smbolo plist smbolo '))

Ser capaz de adjuntar informacin arbitraria de nombres es muy til cuando se hace cualquier tipo de programacin simblica. Por ejemplo, una de las macros que usted escribe en el captulo 24 se adjuntar la informacin a los nombres que otras instancias de las mismas macros va a extraer y utilizar al generar sus expansiones. La desestructuracin-BIND Una ltima herramienta para cortar y rebanar las listas que tengo que cubrir ya que lo necesitar en los prximos captulos es la desestructuracin-BIND macro. Esta macro proporciona una forma de desestructurar las listas arbitrarias, de forma similar a las listas de los parmetros macro puede desarmar a su lista de argumentos. El esqueleto bsico de unadesestructurada-de BIND es el siguiente:
(Desestructuracin-bind ( el parmetro *) lista de forma corporal *)

La lista de parmetros pueden incluir cualquiera de los tipos de parmetros que admite en el parmetro de macro enumera tales como y opcional , y resto , y y claveparmetros. 5 Y, como en las listas de parmetros macro, cualquier parmetro puede ser reemplazado con una lista desestructuracin parmetro anidada, que toma aparte la lista que de otra manera se han consolidado con el parmetro reemplazado. La lista de la forma se evala una vez y debe devolver una lista, que es entonces desestructurado y los valores adecuados estn vinculados a las variables en la lista de parmetros. A continuacin, las formas del cuerpo se evalan en orden con los enlaces en vigor. Algunos ejemplos sencillos seguir:
(Desestructuracin-bind (xyz) (lista 1 2 3) (Lista: xx: yy: zz)) ==> (X 1: Y 2: Z 3) (Desestructuracin-bind (xyz) (lista 1 (lista 2 20) 3) (Lista: xx: yy: zz)) ==> (X 1: Y (2 20): Z 3) (Desestructuracin-bind (x (y1 y2) z) (lista 1 (lista 2 20) 3) (Lista: xx: y1 y1: y2 y2: zz)) ==> (X 1: Y1 2: Y2 20: Z 3) (Desestructuracin-bind (x (y1 + y2 opcional) z) (lista 1 (lista 2 20) 3) (Lista: xx: y1 y1: y2 y2: zz)) ==> (X 1: Y1 2: Y2 20: Z 3) (Desestructuracin-bind (x (y1 + y2 opcional) z) (lista 1 (lista 2) 3) (Lista: xx: y1 y1: y2 y2: zz)) ==> (X 1: Y1 2: NIL Y2: Z 3) (Desestructuracin-bind (tecla & xyz) (lista: x 1: y 2: z 3)

(Lista: xx: yy: zz)) ==> (X 1: Y 2: Z 3) (Desestructuracin-bind (tecla & xyz) (lista: z 1: y 2: x 3) (Lista: xx: yy: zz)) ==> (X 3: Y 2: Z 1)

Un tipo de parmetro que se puede utilizar con la desestructuracin-BIND y tambin en las listas de parmetros macro, aunque no lo mencion en el captulo 8, es unay todo parmetro. Si se especifica, debe ser el primer parmetro en una lista de parmetros, y est enlazado a la forma de lista completa. 6 Despus de una y todo parmetro, los parmetros pueden aparecer otros como de costumbre y que va a extraer partes concretas de la lista tal como lo haran si el y su conjunto parmetro no estaban all. Un ejemplo del uso y todo con la desestructuracinBIND es el siguiente:
(Desestructuracin-bind (y todo su conjunto y la llave xyz) (lista: z 1: y 2: x 3) (Lista: xx: yy: zz: toda entera)) ==> (X 3: Y 2: Z 1: TODO (: Z 1: y 2: X 3))

Vamos a usar una y todo parmetro en una de las macros que es parte de la biblioteca de la generacin de HTML que va a desarrollar en el captulo 31. Sin embargo, tengo unos cuantos temas ms para cubrir antes de poder llegar a eso. Despus de dos captulos sobre el tema en lugar de las clulas lispy contras, ahora se puede dar vuelta a la cuestin ms prosaica de cmo tratar con los archivos y nombres de archivo.

1 Es posible construir una cadena de clulas de los contras en el CDR de la clula contras ltimo no

es NIL , pero algn otro tomo. Esto se conoce como puntos lista debido a los inconvenientes ltimos es un par de puntos.
2 Puede parecer que el NSUBST familia de funciones se puede y de hecho no modifica el rbol en su

lugar. Sin embargo, hay un caso borde: cuando el "rbol" se pasa, de hecho, un tomo, no puede ser modificado en su lugar, por lo que el resultado de NSUBST ser un objeto diferente que el argumento: ('x' nsubst y "y) X .
3 enlace tiene slo un elemento de cada lista, pero si cualquiera de las listas contiene elementos

duplicados, el resultado tambin puede contener duplicados.


4 Tambin es posible directamente SETF SMBOLO-PLIST . Sin embargo, eso es una mala idea, como

un cdigo diferente puede haber aadido propiedades diferentes a plist del smbolo por diferentes razones. Si una pieza de cdigo anula el plist conjunto del smbolo, se puede romper otro cdigo que aade sus propias caractersticas a la plist.

5 listas de parmetros macro Apoyar a un tipo de parmetro, el medio ambiente y los parmetros,

que desestructuracin-BIND no lo hace. Sin embargo, no discuti que tipo de parmetro en el captulo 8, y usted no tendr que preocuparse de eso ahora tampoco.
6 Cuando una y toda parmetro se utiliza en una lista de parmetros macro, la forma en que est

destinado a es la forma macro conjunto, incluido el nombre de la macro.

14. Los archivos y archivos de E / S


Common Lisp proporciona una amplia biblioteca de funcionalidad para trabajar con archivos. En este captulo me centrar en unos pocos bsicos relacionados con el archivo de las tareas: la lectura y escritura de archivos y archivos de inclusin en el sistema de archivos. Para estas tareas bsicas, de E / S Common Lisp las instalaciones son similares a aquellos en otros idiomas. Common Lisp proporciona una abstraccin de flujo para leer y escribir datos y la extraccin de uno, llamado nombres de ruta , para la manipulacin de nombres de archivo en un sistema operativo independiente del camino. Adems, Common Lisp ofrece otros fragmentos de una funcionalidad nica de Lisp, como la capacidad de leer y escribir S-expresiones. Lectura de datos de archivos La ms bsica archivo E / S tarea es leer el contenido de un archivo. Se puede obtener una corriente desde donde se puede leer el contenido de un archivo con el OPEN funcin. De forma predeterminada OPEN devuelve un flujo de entrada basada en caracteres se puede pasar a una variedad de funciones que leer uno o ms caracteres de texto: READ-CHAR lee un solo carcter; READ-LINE lee una lnea de texto, volviendo como una cadena con el carcter de fin de lnea (s) que se retir, y READ lee una sola s-expresin, devolviendo un objeto Lisp. Cuando haya terminado con la corriente, se puede cerrar con la CERRAR funcin. El nico argumento necesario para OPEN es el nombre del archivo a leer. Como se ver en la seccin "Los nombres de archivo," Common Lisp proporciona un par de maneras de representar un nombre de archivo, pero la ms sencilla es utilizar una cadena que contiene el nombre en el archivo local de nombres de sintaxis. As que asumiendo que/ alguna / archivo / nombre.txt es un archivo, puede abrir de esta manera:
(Abierto "/ algun / archivo / nombre.txt")

Usted puede utilizar el objeto devuelto como primer argumento a cualquiera de las funciones de lectura. Por ejemplo, para imprimir la primera lnea del archivo, usted puede combinarOPEN , READ-LINE , y CERRAR de la siguiente manera:
(Let (((abierta "/ algun / archivo / nombre.txt"))) (Formato t "~ a ~%" (lase en lnea))

(Cerca de))

Por supuesto, una serie de cosas pueden salir mal al tratar de abrir y leer desde un archivo. El archivo no puede existir. O usted puede inesperadamente golpe el final del archivo mientras se lee. De forma predeterminada OPEN y los * Read- funciones ser una seal de error en estas situaciones. En el captulo 19, voy a hablar sobre cmo recuperarse de dichos errores. Por ahora, sin embargo, no hay una solucin ms ligera de peso: cada una de estas funciones acepta los argumentos que modifican su comportamiento en estas situaciones excepcionales. Si desea abrir un archivo inexistente, posiblemente, sin OPEN sealizacin de un error, puede utilizar el argumento de palabra clave : si-no-hay- para especificar un comportamiento diferente. Los tres valores posibles son : error , el valor predeterminado; : crear , que dice que para seguir adelante y crear el archivo y luego proceder como si ya exista, y NIL , que le dice que regrese NIL en lugar de un arroyo. Por lo tanto, puede cambiar el ejemplo anterior para hacer frente a la posibilidad de que el archivo no exista.
(Let (((abierta "/ algun / archivo / nombre.txt": si-no-hay-nil))) (Cuando en (Formato t "~ a ~%" (lase en lnea)) (Cerca de)))

Las funciones de lectura - READ-CHAR , READ-LINE , y READ - llevan todos un argumento opcional, que por defecto es true, que especifica si se debe sealar un error si se les llama al final del archivo. Si este argumento es NIL , que en lugar de devolver el valor de su tercer argumento, que por defecto es NIL . Por lo tanto, usted podra imprimir todas las lneas de un archivo como este:
(Let (((abierta "/ algun / archivo / nombre.txt": si-no-hay-nil))) (Cuando en (Bucle de la lnea = (lectura de la lnea de cero) mientras que la lnea lo hacen (en formato t "~ a ~%" line)) (Cerca de)))

De las tres lecturas de texto funciones, READ es exclusivo de Lisp. Esta es la misma funcin que proporciona la investigacin en el REPL y que se utiliza para leer el cdigo fuente de Lisp. Cada vez que llama, se lee una sola s-expresin, saltndose los espacios en blanco y los comentarios, y devuelve el objeto Lisp denotada por el sexpresin. Por ejemplo, supongamos que / un archivo / / nombre.txt tiene el siguiente contenido:
(1 2 3) 456

"Una cadena"; esto es un comentario ((Ab) (Cd))

En otras palabras, que contiene cuatro S-expresiones: una lista de nmeros, un nmero, una cadena, y una lista de listas. Usted puede leer las expresiones como esta:
CL-> USER (defparameter * s * (abierto "/ algun / archivo / nombre.txt")) * S * CL-> USER (lase * s *) (1 2 3) CL-> USER (lase * s *) 456 CL-> USER (lase * s *) "Una cadena" CL-> USER (lase * s *) ((AB) (CD)) CL-> USER (cerca de * s *) T

Como se vio en el captulo 3, se puede utilizar PRINT para imprimir objetos de Lisp en "leer" la forma. Por lo tanto, siempre que lo necesite para almacenar un bit de datos en un archivo, IMPRIMIR y LEA proporcionan una manera fcil de hacerlo sin tener que disear un formato de datos o escribir un parser. Incluso - como el ejemplo anterior se ha mostrado - le dan los comentarios de forma gratuita. Y debido a que s-expresiones fueron diseados para ser humana modificable, tambin es un buen formato para cosas como los archivos de configuracin. 1 Lectura de datos binarios De forma predeterminada OPEN . flujos de caracteres devoluciones, que se traducen los bytes subyacentes a los personajes de acuerdo a un determinado esquema de codificacin de caracteres 2 Para leer los bytes sin formato, tiene que pasar ABIERTA una : el elemento de tipo argumento de '(unsigned byte 8) . 3 Se puede pasar la corriente resultante de la funcin READ-BYTE , que devolver un nmero entero entre 0 y 255 cada vez que se llama. READ-BYTE , al igual que las funciones de lectura de caracteres, tambin acepta argumentos opcionales para especificar si se debe sealar una error si se llama al final del archivo y qu valor, si no regresa. En el captulo 24 que va a construir una biblioteca que le permite leer cmodamente los datos estructurados binarios usando READ-BYTE . 4 Lee a granel Una de las funciones ltima lectura, LECTURA DE SECUENCIA , funciona tanto con carcter y secuencias binarias. Usted debe drselo una secuencia (tpicamente un

vector) y corriente de una, y que intenta llenar la secuencia con datos de la secuencia. Se devuelve el ndice del primer elemento de la secuencia que no se llen o la longitud de la secuencia si era capaz de llenarlo completamente. Tambin se puede pasar : inicio y : finales argumentos clave para especificar una subsecuencia que debe ser llenado en su lugar. El argumento de la secuencia debe ser un tipo que puede contener elementos de tipo de elemento de la secuencia. Como la mayora de los sistemas operativos son compatibles con algn tipo de bloque de E / S, LECTURA SECUENCIA es probable que sea un poco ms eficiente que el llenado de una secuencia llamando repetidamente a READ-BYTE o READ-CHAR. Archivo de salida Para escribir datos en un archivo, usted necesita un flujo de salida, que se obtiene mediante una llamada OPEN con una : la direccin del argumento de palabra clave: la produccin . Al abrir un archivo para la salida, OPEN asume que el archivo no debera existir y sealar un error si lo hace. Sin embargo, puede cambiar ese comportamiento con el : si existe- argumento de palabra clave. Al pasar el valor : sustituyen dice ABRIR para reemplazar el archivo existente. Paso : append causaOPEN para abrir el archivo existente de tal manera que los nuevos datos se escriben al final del archivo, mientras que : sobrescribir devuelve una secuencia que va a sobrescribir los datos existentes a partir del principio del archivo. Y pasando NIL har OPEN para volver NIL en lugar de una corriente de si el archivo ya existe. Un uso tpico deOPEN para la salida se ve as:
(Abierto "/ algun / archivo / nombre.txt": direccin: salida: si existe-: reemplaza)

Common Lisp tambin ofrece varias funciones para la escritura de datos: WRITECHAR escribe un carcter nico a la corriente. ESCRIBIR-LINE escribe una cadena seguida de una nueva lnea, que se emitir el correspondiente final de la lnea de personaje o personajes de la plataforma . Otra funcin, ESCRBANOS CUERDAS , escribe una cadena sin agregar ningn caracteres de fin de lnea. Dos funciones diferentes pueden imprimir slo una nueva lnea: TERPRI - abreviatura de "terminar de impresin" - sin condiciones imprime un carcter de nueva lnea, y FRESH-LINE imprime un carcter de nueva lnea a menos que la corriente es al principio de una lnea. FRESH-LINE es til cuando se quiere evitar falsas lneas en blanco en la produccin textual generada por las distintas funciones llamadas en secuencia. Por ejemplo, suponga que tiene una funcin que genera una salida que

siempre debe ser seguido por un salto de lnea y otro que debe comenzar en una nueva lnea. Pero suponer que si las funciones se llaman uno tras otro, usted no quiere una lnea en blanco entre los dos bits de salida. Si utiliza FRESH-LINE en el comienzo de la segunda funcin, la salida siempre se iniciar en una nueva lnea, pero si se llama justo despus de la primera, no emitir un salto de lnea adicional. Varias funciones de salida de datos Lisp como S-expresiones: PRINT imprime una sexpresin precedida por un fin de lnea y seguido por un espacio. PRIN1 imprime slo la s-expresin. Y la funcin pprint imprime s-expresiones como PRINT y PRIN1 pero el uso de la "impresora bonito", que trata de imprimir su salida de una forma estticamente agradable. Sin embargo, no todos los objetos se pueden imprimir en una forma que READ va a entender. La variable * Los legible PRINT * Los controles de lo que ocurre si se intenta imprimir como un objeto con PRINT , PRIN1 o pprint . Cuando es NIL , estas funciones se imprimir el objeto en una sintaxis especial que est garantizado para causarLEA para sealar un error si trata de leer, de lo contrario ser una seal de error en lugar de imprimir el objeto. Otra funcin, Princ , tambin imprime Lisp objetos, pero en una manera diseada para el consumo humano. Por ejemplo, Princ imprime cadenas sin comillas. Puede generar la salida de texto ms elaborado con la increblemente flexible aunque algo misterioso FORMATO funcin. Voy a discutir algunos de los detalles ms importantes de FORMATO , que esencialmente define un mini-lenguaje para la emisin de la salida con formato, en el captulo 18. Para escribir datos binarios en un archivo, usted tiene que ABRIR el archivo con el mismo : el elemento de tipo argumento como lo hizo para leer que:"(sin firma-de 8 bytes) . A continuacin, puede escribir bytes individuales a la corriente con WRITEBYTE . La funcin de la produccin a granel de ESCRITURA DE SECUENCIA acepta binarios y secuencias de caracteres, siempre y cuando todos los elementos de la secuencia son de un tipo adecuado para la corriente, caracteres o bytes. Al igual que con READ-SECUENCIA , esta funcin es probable que sea bastante un poco ms eficiente que escribir los elementos de la secuencia de una a la vez.

Los archivos de clausura Como cualquiera que haya escrito el cdigo que se ocupa de un montn de archivos sabe, es importante cerrar los ficheros cuando hayas terminado con ellos, porque los identificadores de archivo tienden a ser un recurso escaso. Si abre los archivos y no las cierra, usted pronto descubrir que no puede abrir ms archivos. 5 Puede parecer bastante sencillo ser justo que cada OPEN tiene un juego CERRAR . Por ejemplo, siempre se puede estructurar el archivo usando un cdigo como este:
(Let ((corriente (abierto "/ algun / archivo / nombre.txt"))) ;; Hacer cosas con la corriente (Arroyo cercano))

Sin embargo, este enfoque adolece de dos problemas. Una de ellas es simplemente que los propenso a errores - si usted se olvida de la CLOSE , el cdigo se escapar un identificador de archivo cada vez que se ejecuta. El otro - y ms importante - el problema es que no hay ninguna garanta de que va a llegar a la CERRAR . Por ejemplo, si el cdigo antes de laCERRAR contiene un RETURN o RETURN FROM- , se puede salir de la LET , sin cerrar el flujo. O, como se ver en el captulo 19, si ninguno de los cdigos antes de que losCERRAR seales de un error, el control puede saltar fuera de la LET a un controlador de errores y no volver nunca ms para cerrar el flujo. Common Lisp proporciona una solucin general al problema de cmo asegurar que cierto cdigo siempre se ejecuta: el operador especial UNWIND-PROTECT , que voy a discutir en el captulo 20. Sin embargo, debido a que el patrn de la apertura de un archivo, hacer algo con el flujo resultante, a continuacin, cerrar el flujo es tan comn, Common Lisp proporciona una macro, CON-OPEN-FILE , construido en la cima de UNWIND-PROTECT , para encapsular este patrn . Esta es la forma bsica:
(Con-open-file ( flujo-var forma corporal * ) abierto argumento * )

Las formas en las formas del cuerpo son evaluados con la corriente var unido a una secuencia de archivo abierto por una llamada a OPEN con el abierto de argumentos como sus argumentos. CON-OPEN-FILE asegura entonces la corriente en chorro-var se cierra antes de que el CON- Archivo Abierto vuelve formulario. Por lo tanto, usted puede escribir esto para leer una lnea de un archivo:
(Con-open-file (flujo "/ algun / archivo / nombre.txt") (Formato t "~ a ~%" (lase lnea de flujo)))

Para crear un nuevo archivo, puede escribir algo como esto:

(Con-open-file (flujo "/ algun / archivo / nombre.txt": direccin: salida) (Secuencia de formato "Parte del texto."))

Es probable que use CON-OPEN-FILE de 90-99 por ciento de los archivos de E / S lo hace - la nica vez que es necesario utilizar primas OPEN y CLOSE llamadas es si usted necesita abrir un archivo en una funcin y mantener la corriente de alrededor despus devuelve la funcin. En ese caso, usted debe tener cuidado para cerrar finalmente la corriente de uno mismo, o te fugas de descriptores de fichero y, finalmente, puede terminar no se puede abrir ms archivos. Los nombres de archivo Hasta ahora ha utilizado para representar cadenas de nombres de archivo. Sin embargo, el uso de cadenas como nombres de archivo vincula el cdigo en un sistema operativo en particular y del sistema de archivos. Del mismo modo, si la construccin de los nombres de programacin de acuerdo a las reglas de un esquema de nombres en particular (que separa los directorios con /, por ejemplo), tambin atar su cdigo a un sistema de archivos en particular. Para evitar este tipo de nonportability, Common Lisp proporciona otra representacin de nombres de archivos: Los objetos de ruta. Rutas de acceso representan los nombres de archivo de una manera estructurada que les hace fciles de manipular sin atarlos a una sintaxis de nombre de fichero en particular. Y la carga de la traduccin de ida y vuelta entre las cadenas en la sintaxis localllamados namestrings - y los caminos se coloca sobre la aplicacin de Lisp. Desafortunadamente, como con muchas abstracciones diseadas para ocultar los detalles de los sistemas subyacentes fundamentalmente diferentes, la abstraccin ruta presenta sus propias complicaciones. Cuando se disearon rutas de acceso, el conjunto de los sistemas de archivos de uso general era un poco ms variado que los de uso comn hoy en da. En consecuencia, algunos rincones y grietas de la abstraccin ruta de acceso no tienen mucho sentido si lo nico que preocupa es que representa los nombres de archivo de Unix o Windows. Sin embargo, una vez que comprender qu partes de la abstraccin ruta de acceso que puede pasar por alto como artefactos de la historia evolutiva de los nombres de ruta ', proporcionan una manera conveniente de manipular los nombres de archivo. 6 La mayora de los lugares que se llama un nombre de archivo, puedes usar un namecadena o ruta de acceso a. Qu va a utilizar depende sobre todo del lugar

donde se origin el nombre. Los nombres de archivo proporcionados por el usuario - por ejemplo, como argumentos o valores en los archivos de configuracin normalmente ser namestrings, ya que el usuario sabe qu sistema operativo se est ejecutando en y no se debe esperar que se preocupan por los detalles de cmo Lisp representa los nombres de archivo. Sin embargo, los nombres de archivo generados mediante programacin ser rutas de acceso ya que se pueden crear portable. Una secuencia devuelta por OPEN tambin representa un nombre de archivo, es decir, el nombre del archivo que se utiliz originalmente para abrir el arroyo. En conjunto, estos tres tipos se conocen colectivamente como los designadores de nombre de ruta . Todas las funciones integradas que esperan un argumento de nombre de archivo de aceptar los tres tipos de designador de ruta. Por ejemplo, todos los lugares en la seccin anterior donde se utiliza una cadena para representar a un nombre de archivo, tambin podra haber pasado un objeto de ruta o un arroyo.
Cmo llegamos hasta aqu
La diversidad histrica de los sistemas de archivos en la existencia durante los aos 70 y 80 puede ser fcil de olvidar. Kent Pitman, uno de los editores principales tcnicas de la Norma comn Lisp, describi la situacin una vez en comp.lang.lisp (Message-ID: sfwzo74np6w.fsf @ world.std.com ) as:
Los sistemas de archivos dominante en la poca se llev a cabo el diseo [de Common Lisp] eran TOPS-10, TENEX, TOPS-20, VAX VMS, AT & T Unix, el MIT Multics, el MIT ITS, por no hablar de un montn de mainframe de [sistemas operativos]. Algunos eran slo de letras maysculas, algunos mixtos, algunos eran maysculas y minsculas, pero el caso-la traduccin (como CL). Algunos tenan directorios como archivos, no. Algunos tenan caracteres de comillas para caracteres de archivos divertidos, otros no. Algunos tenan comodines, otros no. Algunos tenan: en rutas relativas, otros no.Algunos tenan directorios namable races, otros no. Haba sistemas de archivos que no tienen directorios, sistemas de archivos con las organizaciones no jerrquicas de directorios, sistemas de archivos con ningn tipo de archivo, sistemas de archivos que no tienen las versiones, sistemas de archivos con ningn otro dispositivo, y as sucesivamente.

Si nos fijamos en la abstraccin ruta desde el punto de vista de cualquier sistema de archivo nico, parece barroco. Sin embargo, si se toma hasta dos de estos sistemas de archivos similares como Windows y Unix, que ya puede comenzar a ver las diferencias del sistema de nombre de ruta puede ayudar a abstraer - nombres de archivos de Windows contienen una letra de unidad, por ejemplo, nombres de ficheros Unix, mientras que no lo hacen. La otra ventaja de tener la abstraccin de ruta diseada para manejar la amplia variedad de sistemas de archivos que existan en el pasado es que es ms probable que sea capaz de manejar sistemas de archivos que puedan existir en el futuro. Si, por ejemplo, los sistemas de archivos de versiones vuelven a estar de moda, Common Lisp estarn listos.

Cmo representar los nombres de archivo nombres de rutas Una ruta es un objeto estructurado que representa un nombre de archivo con seis componentes: host, dispositivo, directorio, nombre, tipo y versin. La mayora de estos componentes adquieren valores atmicos, por lo general cadenas, y slo el componente de directorio es ms estructurado, que contiene una lista de nombres de directorios (como cadenas) precedido de la palabra clave : absoluta o : relativa . Sin embargo, no todos los componentes del nombre de ruta se necesitan en todas las plataformas - esta es una de las razones por las rutas de acceso huelga Lispers muchos nuevos gratuitamente complejo. Por otro lado, usted realmente no necesita preocuparse acerca de qu componentes puede o no puede ser utilizado para representar los nombres en un sistema de archivo en particular a menos que usted necesita para crear un objeto de ruta de acceso nuevo desde el principio, que casi nunca tendrs que hacer. En su lugar, lo ms probable conseguir el asimiento de objetos pathname ya sea dejando la implementacin analizar un archivo especfico del sistema namecadena en un objeto de ruta o por la creacin de una nueva ruta que toma la mayora de sus componentes de una ruta existente. Por ejemplo, para traducir un namecadena a una ruta, se utiliza la VA funcin. Se necesita un designador de ruta y devuelve un objeto ruta equivalente. Cuando el indicador es ya un nombre de ruta, es simplemente regresar. Cuando se trata de un arroyo, el nombre del archivo original, se extrae y se devuelve. Cuando el indicador es un namecadena, sin embargo, se analiza de acuerdo con la sintaxis de nombre de archivo local. El lenguaje estndar, como un documento independiente de la plataforma, no se especifica ninguna asignacin especial de namecadena de ruta, pero la mayora de las implementaciones de seguir las mismas convenciones que en un sistema operativo dado. En los sistemas de archivos de Unix, slo el directorio, el nombre y tipo de componentes se utilizan normalmente. En Windows, un componente ms - por lo general el dispositivo o host - tiene la letra de la unidad. En estas plataformas, una namecadena se analiza mediante la divisin por primera vez en los elementos en el separador de ruta - una sola barra en Unix y una barra o barra invertida en Windows. La letra de unidad en Windows ser colocado en el dispositivo o el componente de host. Todos, excepto el ltimo de los elementos de nombre otros se colocan en una lista que comienza con : absoluta o : relativa dependiendo de si el nombre (haciendo caso omiso de la letra de unidad, si los hay) se inici con un

separador de ruta. Esta lista se convierte en el componente de directorio de la ruta. El ltimo elemento se divide entonces en el punto ms a la derecha, si los hubiere, y las dos partes puso en el nombre y componentes de tipo del nombre de ruta. 7 Usted puede examinar estos componentes individuales de una ruta con las funciones DIRECTORIO VA- , la VA NOMBRE- y -VA TIPO .
(Ruta-directorio (ruta "/ foo / bar / baz.txt")) ==> (: ABSOLUTA "foo" "bar") (Path-name (nombre de ruta "/ foo / bar / baz.txt")) ==> "baz" (Ruta de acceso del tipo de (nombre de ruta "/ foo / bar / baz.txt")) ==> "txt"

Tres otras funciones, VA-HOST , VA DISPOSITIVO , y VA-VERSION - le permiten llegar a los otros tres componentes del nombre de ruta, aunque es poco probable que tengan valores interesantes en Unix. En Windows ya sea VA-HOST o VA DISPOSITIVO devolver la letra de la unidad. Al igual que muchos otros objetos integrados, rutas tienen su propia sintaxis de lectura, p # seguido de una cadena entre comillas dobles. Esto le permite imprimir y leer de nuevo S-expresiones que contienen objetos de ruta, sino porque la sintaxis depende del algoritmo de anlisis sintctico namecadena, estos datos no es necesariamente portable entre sistemas operativos.
(Nombre de ruta "/ foo / bar / baz.txt") ==> # p "/ foo / bar / baz.txt"

Para traducir un nombre de ruta de nuevo a un namecadena - por ejemplo, para presentar al usuario - usted puede utilizar la funcin namecadena , que tiene un designador de ruta y devuelve un namecadena. Otras dos funciones, DIRECTORIO namecadena- y -ARCHIVO namecadena , devolver un namecadena parcial. DIRECTORIO-namecadenacombina los elementos del componente de directorio en el nombre de un directorio local, y FILE-namecadena combina el nombre y tipo de componentes. 8
(Namecadena # p "/ foo / bar / baz.txt") ==> "/ foo / bar / baz.txt" (Directorio-namecadena # p "/ foo / bar / baz.txt") ==> "/ foo / bar /" (Archivo-namecadena # p "/ foo / bar / baz.txt") ==> "baz.txt"

La construccin de nuevos nombres de rutas Usted puede construir rutas arbitrarias utilizando el MARCA-VA funcin. Se necesita un argumento de palabra clave para cada componente en el camino y devuelve una ruta con todos los componentes suministrados llenos y el resto NIL . 9
(Make-ruta : El directorio '(: absoluta "foo" "bar") : Nombre de "baz"

: Tipo "txt") ==> # p "/ foo / bar / baz.txt"

Sin embargo, si usted quiere que sus programas para ser porttil, es probable que no quieren hacer rutas de acceso completamente desde cero: a pesar de que la abstraccin ruta de acceso que protege de la sintaxis de nombre de fichero portable, nombres de archivo puede ser portable en otros aspectos. Por ejemplo, el nombre del archivo/ home / peter / foo.txt no es bueno en una caja de OS X en / home / se llama / users / . Otra razn para no hacer rutas completamente desde cero, es que las distintas aplicaciones utilizar los componentes de nombre de ruta ligeramente diferente. Por ejemplo, como se mencion anteriormente, algunas implementaciones basadas en Windows Lisp guardar la letra de unidad en el componente de equipo mientras que otros lo guarde en el componente de host. Si usted escribe cdigo como este:
(Make-ruta: dispositivo de "c": el directorio '(: absoluta "foo" "bar"): el nombre de "baz")

ser correcta en algunas implementaciones, pero no en otros. En lugar de hacer nombres desde el principio, usted puede construir una nueva ruta basada en una ruta existente con MAKE-VA parmetro de palabra clave 's : por defecto .Con este parmetro puede proporcionar un indicador de ruta, que suministrar los valores de los componentes no especificados por otros argumentos. Por ejemplo, la expresin siguiente, se crea una ruta con un html. extensin y todos los dems componentes de la misma como la ruta en la variable de archivo de entrada :
(Make-ruta: tipo "html": valores predeterminados de archivo de entrada)

Suponiendo que el valor de la entrada de archivos era un nombre proporcionado por el usuario, este cdigo ser fuerte en la cara del sistema operativo y las diferencias de implementacin tales como si los nombres de archivo tienen letras de unidad en ellos y en el que se almacenarn en la ruta si lo hacen. 10 Puede utilizar la misma tcnica para crear una ruta con un componente de directorio diferente.
(Make-ruta: directorio '(: copias de seguridad "relativos"): por defecto de entrada y archivo)

Sin embargo, esto crear una ruta cuyo nico componente es el directorio en relacin copias de seguridad / , independientemente de cualquier componente de directoriode archivo de entrada puede haber tenido. Por ejemplo:
(Make-ruta: directorio '(: copias de seguridad "relativos") : Por defecto # P "/ foo / bar / baz.txt") ==> # p "copias de seguridad o baz.txt"

A veces, sin embargo, desea combinar dos rutas, por lo menos uno de los cuales tiene un componente de directorio relativa, mediante la combinacin de sus componentes de directorio.Por ejemplo, suponga que tiene un nombre de ruta relativa, como # p "foo / bar.html" que desea combinar con una ruta absoluta como # p "/ www / html /" para obtener # p "/ www / html / foo / bar.html " . En ese caso, HAZ-VA no va a hacer, sino que desea MERGE-nombres de rutas . MERGE-nombres de rutas tiene dos rutas de acceso y los combina, debera llenar los NIL componentes en la ruta por primera vez con el valor correspondiente de la ruta en segundo lugar, al igual que MAKE-VA rellena cualquiera de los componentes no especificados con los componentes de la : defaults argumento. Sin embargo,MERGE-nombres de rutas trata el componente de directorio especial: si el directorio de la ruta primero es relativo, el componente de directorio de la ruta resultante ser de directorio relativo al directorio de la ruta la ruta del segundo primera. As:
(Merge-rutas # p "foo / bar.html" # p "/ www / html /") ==> # p "/ www / html / foo / bar.html"

El nombre de ruta segundo tambin puede ser relativa, en cuyo caso el nombre de ruta resultante tambin ser relativa.
(Merge-rutas # p "foo / bar.html" # p "html /") ==> # p "html / foo / bar.html"

Para revertir este proceso y obtener un nombre de archivo relativo a un directorio raz en particular, puede utilizar la funcin prctica suficiente, namecadena .
(Lo suficiente-namecadena # p "/ www / html / foo / bar.html" # p "/ www /") ==> "html / foo / bar.html"

A continuacin, puede combinar SUFICIENTE-namecadena con los nombres de rutas MERGE para crear una ruta que representa el mismo nombre pero en una raz diferente.
(Merge-rutas (Lo suficiente-namecadena # p "/ www / html / foo / bar / baz.html" # p "/ www /") # P "/ www-las copias de seguridad /") ==> p # "/ wwwbackups/html/foo/bar/baz.html"

MERGE-nombres de rutas tambin se utiliza internamente por las funciones estndar que realmente acceder a los archivos en el sistema de archivos para llenar rutas incompletas. Por ejemplo, suponga que ha hecho una ruta de acceso con un solo nombre y un tipo.
(Make-ruta de acceso: el nombre de "foo": tipo "txt") ==> # p "foo.txt"

Si intenta utilizar esta ruta como un argumento para ABRIR , los componentes que faltan, como el directorio, debe ser llenado antes de Lisp ser capaz de traducir la ruta de acceso a un nombre de archivo real. Common Lisp va a obtener los valores de los componentes que faltan mediante la fusin de la ruta de acceso dada por el valor de la variable delas rutas de directorios * DEFAULT-defaults-* . El valor inicial de esta variable se determina por la aplicacin pero usualmente es un nombre de ruta con un componente de directorio que representa el directorio donde Lisp se inici y valores adecuados para los componentes de host y el dispositivo, si es necesario. Si se invoca con un solo argumento, MERGE-nombres de rutas se unirn el argumento con el valor de las rutas de directorios * DEFAULT-defaults-* . Por ejemplo, siPredeterminado *-VA-defaults * es # p "/ home / peter /" , entonces se obtendra lo siguiente:
(Merge-rutas # p "foo.txt") ==> # p "/ home / peter / foo.txt"

Dos representaciones de los nombres de directorio Cuando se trata de rutas que los directorios de nombres, es necesario estar al tanto de una arruga. Rutas separar el directorio y el nombre de los componentes, pero Unix y Windows en cuenta los directorios ms que otro tipo de archivo. Por lo tanto, en dichos sistemas, cada directorio tiene dos representaciones diferentes pathname. Una representacin, que voy a llamar a la forma de archivo , trata de un directorio como cualquier otro archivo y pone el ltimo elemento de la namecadena en el nombre y tipo de componentes. La otra representacin, forma de directorio , coloca todos los elementos del nombre en el componente de directorio, dejando el nombre y los componentes de tipo NIL .Si / foo / bar / es un directorio, a continuacin, tanto de las rutas siguientes que sea.
(Make-ruta: directorio '(: absoluta "foo"): el nombre de "bar"), la forma de archivo (Make-ruta: directorio '(: absoluta "foo" "bar")), la forma de directorio

Al crear rutas con MAKE-VA , se puede controlar la forma de llegar, pero hay que tener cuidado cuando se trata de namestrings. Todas las implementaciones actuales

de creacin de rutas de acceso de archivos de forma a menos que el namecadena termina con un separador de ruta. Pero no se puede confiar en suministrados por el usuario namestrings ser necesariamente de una forma u otra. Por ejemplo, supongamos que usted ha pedir al usuario un directorio para guardar un archivo en y entraron en "/ home / peter" . Si pasa ese valor como el : defaults argumento de MAKE-VA as:
(Make-ruta de acceso: el nombre de "foo": tipo "TXT": por defecto suministrada por el usuario de nombre)

acabar guardar el archivo en / home / foo.txt en lugar de la intencin / home / peter / foo.txt debido a que el "Pedro" en el namecadena se colocar en el componente de nombre cuando suministrado por el usuario de nombre se convierte a un nombre de ruta. En la biblioteca de portabilidad de ruta voy a discutir en el prximo captulo, que voy a escribir una funcin llamada ruta de acceso-como directorio- que convierte una ruta de acceso a la forma de directorio. Con esa funcin de forma fiable puede guardar el archivo en el directorio indicado por el usuario.
(Make-ruta : Nombre de "foo": tipo "TXT": por defecto (ruta-como-el directorio suministrado por el usuario-nombre))

La interaccin con el sistema de archivos Mientras que la interaccin ms frecuente con el sistema de archivo es, probablemente, OPEN ficheros restantes para leer y escribir, tambin de vez en cuando quiere comprobar si existe un archivo, la lista de los contenidos de un directorio, borrar y renombrar archivos, crear directorios, y obtener informacin acerca de un archivo, tales como quin es el propietario, cuando fue modificada por ltima vez, y su longitud. Aqu es donde la generalidad de la abstraccin ruta comienza a causar un poco de dolor: porque el lenguaje estndar no especifica cmo las funciones que interactan con el mapa del sistema de archivos a cualquier sistema de archivos especfico, los ejecutores se quedan con un poco de margen de maniobra. Dicho esto, la mayora de las funciones que interactan con el sistema de archivos siguen siendo bastante sencillo. Voy a discutir las funciones estndar aqu y sealar a los que sufren de nonportability entre las implementaciones. En el prximo

captulo que va a desarrollar una biblioteca de portabilidad de ruta para suavizar algunas de esas cuestiones nonportability. Para comprobar si un archivo existe en el sistema de archivos correspondiente a un designador de ruta - una ruta de acceso, namecadena, o secuencia de archivo - se puede utilizar la funcin de SONDA-FILE . Si el archivo con el nombre por el designador de ruta existe, SONDA-FILE devuelve el archivo truename , una ruta con cualquier archivo de traducciones a nivel de sistema, tales como la resolucin de los enlaces simblicos realizados. De lo contrario, devuelve NIL . Sin embargo, no todas las implementaciones de apoyar el uso de esta funcin para comprobar si existe un directorio. Adems, Common Lisp no proporciona una forma porttil para comprobar si un archivo determinado que existe es un archivo normal o un directorio. En el prximo captulo te envuelva SONDA-FILE con una nueva funcin, archivo-existe-p , que puede tanto comprobar si existe un directorio y decirle si un nombre es el nombre de un archivo o directorio. Del mismo modo, la funcin estndar para los archivos de inclusin en el sistema de archivos, DIRECTORIO , funciona bien para los casos simples, pero las diferencias entre las implementaciones que sea difcil de usar portable. En el prximo captulo va a definir una lista de directorio de la funcin que suaviza sobre algunas de estas diferencias. DELETE-FILE y File Rename- hacer lo que sugieren sus nombres. DELETE-FILE tiene un indicador de ruta y elimina el archivo llamado, devolviendo true si tiene xito. De lo contrario, seala un FILE-ERROR . 11 RENAME-FILE tiene dos designadores de ruta y renombra el archivo nombrado por el primer nombre que el segundo nombre. Puede crear directorios con la funcin de asegurar-directorios-EXISTE . Se tarda un designador de nombre de ruta y asegura que todos los elementos del componente directorio existen y son directorios, la creacin de ellos como sea necesario. Devuelve el nombre de ruta que fue aprobada, lo que lo hace cmodo de usar en lnea.
(Con-de archivos abiertos (de un total (garantizar los directorios existen-nombre): direccin: salida) ... )

Tenga en cuenta que si se pasa ASEGURAR-directorios-EXISTE un nombre de directorio, debe ser en forma de directorio, o en el directorio de la hoja no se crear. Las funciones ARCHIVO-fecha de cancelacin y de Autor, ambos toman un designador de ruta. ARCHIVO-ESCRITURA FECHA devuelve el tiempo en el nmero de segundos desde la medianoche del 01 de enero 1900, hora de Greenwich (GMT), que el archivo fue escrito el pasado, y Autor- devoluciones, en Unix y Windows, el propietario del archivo. 12 Para encontrar la longitud de un archivo, puede utilizar la funcin ARCHIVO DE LONGITUD . Por razones histricas ARCHIVO DE LONGITUD toma un flujo como un argumento en lugar de un nombre de ruta. En teora, esto permite ARCHIVO DE LONGITUD para devolver la longitud en trminos del tipo de elemento de la secuencia. Sin embargo, dado que en la mayora de los sistemas operativos de hoy en da, la nica informacin disponible acerca de la longitud de un archivo, a falta de realmente leer todo el archivo de medirlo, es su longitud en bytes, que es lo que la mayora de las implementaciones de regresar, aun cuando ARCHIVO DE LONGITUD se pasa un flujo de caracteres. Sin embargo, la norma no requiere este comportamiento, por lo que para obtener resultados predecibles, la mejor manera de obtener la longitud de un archivo es utilizar una secuencia binaria. 13
(Con-open-file (en nombre de archivo: Elemento de tipo "(unsigned byte 8)) (Archivo de longitud en el))

Una funcin relacionada que tambin tiene una secuencia de archivo abierto ya que su argumento es ARCHIVO-POSICIN . Cuando son llamadas con slo una corriente, esta funcin devuelve la posicin actual en el archivo - el nmero de elementos que se han ledo o escrito en el arroyo. Cuando se invoca con dos argumentos, el arroyo y un indicador de posicin, se establece la posicin de la corriente a la posicin designada. El indicador de posicin debe ser la palabra clave : inicio , la palabra clave : final , o un entero no negativo. Las dos palabras clave establecer la posicin de la corriente al inicio o al final del archivo mientras se mueve un nmero entero a la posicin indicada en el archivo. Con una secuencia binaria de la posicin es simplemente un desplazamiento de bytes en el archivo. Sin embargo, para flujos de caracteres las cosas son un poco ms complicado debido a problemas de codificacin de caracteres. Su mejor apuesta, si tiene que saltar dentro de un archivo de datos textuales, es solamente siempre pasa, como un segundo argumento de la versin de dos argumentos de FILE-POSICIN , un valor

devuelto previamente por la versin de un argumento de ARCHIVO POSICIONES con el argumento de misma corriente. Otros tipos de E / S Adems de secuencias de archivo, Common Lisp admite otros tipos de corrientes, que tambin pueden ser utilizados con la lectura diversos, la escritura, y la impresin de funciones de E / S. Por ejemplo, usted puede leer datos desde o escribir datos, utilizando una cadena STRING-STREAM s, que se pueden crear con las funcionesHAZ-serie-Flujo de entrada- y HAZ-serie-Output Stream- . HAZ-serie-corriente-entrada tiene un inicio de cadena y opcional, y los ndices finales para limitar el rea de la cadena de la que los datos deben ser ledos y devuelve una secuencia de caracteres que se puede pasar a cualquiera de las funciones de entrada basados en caracteres, como LEA -CHAR , READ-LINE , o LEA . Por ejemplo, si usted tiene una cadena que contiene un literal de coma flotante en la sintaxis de Common Lisp, puede convertirlo en un flotador de esta manera:
(Let ((s (hacer-serie-de entrada-stream "1,23"))) (Desconectar de proteccin (lase s) (Cerrar s)))

Del mismo modo, HAZ-serie-SALIDA-STREAM crea una corriente que puede utilizar con FORMATO , PRINT , WRITE-CHAR , escribir la lnea- , y as sucesivamente. No toma ningn argumento. Lo que escribes, una corriente de salida de cadena, se acumularn en una cadena que puede ser obtenido con la funcinGET-SALIDA-STREAM-STRING . Cada vez que se llama GET-SALIDASTREAM-STRING , la cadena de interno de la secuencia se borra para que pueda volver a utilizar una secuencia de cadena de salida existente. Sin embargo, rara vez vamos a usar estas funciones directamente, ya que las macros CON-INPUT-DE-STRING y CON SALIDA-a cadena proporcionar una interfaz ms conveniente. CON-INPUT-DE CUERDAS es similar a la CON-OPENFILE - se crea un flujo de entrada de cadena de una cadena y luego ejecuta las formas en su cuerpo con la corriente vinculada a la variable que usted proporcione. Por ejemplo, en lugar de la LET formulario con la explcita UNWIND-PROTECT , probablemente volvera a escribir esto:
(Con entrada-de-cadena (s "1,23") (Leer s))

El CON SALIDA-a cadena macro es similar: se une una corriente de salida de cadena de nueva creacin a una variable que el nombre y luego ejecuta su cuerpo. Despus de todas las formas del cuerpo han sido ejecutadas, CON SALIDA-a cadena devuelve el valor que ser devuelto por el GET-SALIDA-STREAM-STRING .
CL-> USER (con salida a cadena (a) (Formato de "hola, mundo") (Formato de salida "~ s" (lista 1 2 3))) "Hola, mundo (1 2 3)"

Los otros tipos de corrientes definidas en el lenguaje estndar de ofrecer varios tipos de flujo de "tubera", lo que le permite conectar juntos los arroyos en casi cualquier configuracin.Un BROADCAST-STREAM es un flujo de salida que enva los datos grabados en ellos a un conjunto de flujos de salida proporcionados como argumentos a su funcin de constructor,HAZ-BROADCAST-STREAM . 14 Por el contrario, un concatenado STREAM- es un flujo de entrada que tiene su entrada de un conjunto de flujos de entrada, pasando de un ro a otro, ya que llega al final de cada secuencia. concatenado-STREAM s se construyen con la funcin que enlazara HAZ-STREAM , que toma cualquier nmero de los flujos de entrada como argumentos. Hay dos tipos de corrientes bidireccionales que pueden conectarse juntos los arroyos en un par de maneras son TWO-WAY-STREAM y ECHO-STREAM . Sus funciones constructoras,HAZ-TWO-WAY-STREAM y HAZ-ECHO-STREAM , ambos toman dos argumentos, un flujo de entrada y un flujo de salida, y devolver una secuencia del tipo apropiado, que puede utilizar tanto con funciones de entrada y de salida . En un TWO-WAY-STREAM cada lectura se realiza a devolver los datos ledos de la secuencia de entrada subyacente, y todas las escrituras que enviar datos a la secuencia de salida subyacente. Un ECO-STREAM funciona bsicamente de la misma manera, excepto que todos los datos ledos de la secuencia de entrada subyacente tambin se hizo eco de la secuencia de salida. As, la secuencia de salida de una de ECHO-STREAM arroyo contendr una transcripcin de ambos lados de la conversacin. El uso de estos cinco tipos de flujos, se puede construir casi cualquier topologa de fontanera corriente que desee.

Por ltimo, si bien la Norma comn Lisp no dice nada acerca de las API de red, la mayora de las implementaciones de apoyo a la programacin del zcalo y por lo general la aplicacin tomas como otro tipo de corriente, as que usted puede usar todas las regulares funciones I / O con ellos. 15 Ahora est listo para pasar a la construccin de una biblioteca que alisa sobre algunas de las diferencias entre cmo funciona el pathname bsicos se comportan de diferentes implementaciones de Common Lisp.

Original Text: However, many implementations support some form of so-called Gray Streams, basing their API on Gray's draft proposal.
Mostrar traducciones alternativas 1 Nota, sin embargo, que mientras que el lector Lisp sabe cmo omitir comentarios, que

completamente les salta. Por lo tanto, si utiliza LEER para leer en un archivo de configuracin que contiene los comentarios y luego usar PRINT para guardar los cambios a los datos, si no se pierden los comentarios.
2 De manera predeterminada OPEN utiliza la codificacin de caracteres por defecto para el sistema

operativo, sino que tambin acepta un parmetro de palabra clave, : externa de formato , que puede pasar a la aplicacin definidos por los valores que especifican una codificacin diferente. Flujos de caracteres tambin se traducen especfico de la plataforma de fin de lnea de la secuencia con el carcter nico# \ nueva lnea .
3 El tipo (unsigned byte 8) indica un byte de 8 bits; Common Lisp "byte" tipos no son de un tamao

fijo desde Lisp se ha quedado en varias ocasiones en las arquitecturas con un tamao de bytes de 6 a 9 bits, por no hablar de el PDP-10, que tena dirigidos individualmente campos de longitud variable de bits de 1 a 36 bits.
4 En general, una corriente es o bien una secuencia de caracteres o de una secuencia binaria, por lo

que no se puede mezclar llamadas a READ-BYTE y READ-CHAR o de otro carcter, basado en las funciones de lectura. Sin embargo, algunas implementaciones, como Allegro, el apoyo a las llamadas corrientes de bivalentes, que apoyan tanto el carcter como binarios I / O.
5 Algunas personas esperan que esto no sera un problema en un lenguaje de recoleccin de

elementos tales como Lisp. Es el caso en la mayora de las implementaciones de Lisp que un arroyo que se convierte en basura se cerrar automticamente. Sin embargo, esto no es alguien en quien confiar - el problema es que los recolectores de basura que generalmente se ejecuta slo cuando la memoria es baja, no saben acerca de otros recursos escasos, como identificadores de archivo. Si hay un montn de memoria disponible, es fcil que se quede sin el archivo se encarga de mucho antes de que el recolector de basura se ejecuta.

6 Otra razn es considerado el sistema de ruta un tanto barroco, es debido a la inclusin de los

nombres de las rutas lgicas . Sin embargo, puede utilizar el resto del sistema de nombre de ruta perfectamente sin saber nada ms acerca de rutas de acceso lgicas que con seguridad puede ignorar. En pocas palabras, rutas lgicas permitir que los programas comunes de Lisp que contienen referencias a los nombres de las rutas sin nombrar a los archivos especficos. Rutas de acceso lgico, entonces, podra ser asignada a lugares especficos en un sistema de archivo real cuando el programa fue instalado por la definicin de una "traduccin ruta lgica", que traduce nombres de las rutas lgicas correspondientes comodines ciertas rutas que representan los archivos en el sistema de archivos, las llamadas rutas de acceso fsicas. Ellos tienen su utilidad en ciertas situaciones, pero se puede llegar muy lejos sin preocuparse por ellos.
7 Muchos basados en Unix implementaciones tratar los nombres de archivo cuyo ltimo elemento

comienza con un punto y no contienen ningn otros puntos especialmente, poniendo todo el elemento, con el punto, en el componente de nombre y dejando el componente de tipo de NIL .
(Path-name (nombre de ruta "/ foo / .emacs")) ==> ". Emacs" (Ruta de acceso del tipo de (nombre de ruta "/ foo / .emacs")) ==> NIL

Sin embargo, no todas las implementaciones de seguir este convenio, y algunos se crear una ruta con "" como el nombre y emacs como el tipo.
8 El nombre devuelto por FILE-namecadena tambin incluye el componente de la versin en

sistemas de archivos que lo utilizan.


9 El componente de acogida no puede por defecto a NIL , pero si no, ser una opaca la aplicacin

definida por el valor.


10 Para un transporte absolutamente mxima, que realmente debera escribir lo siguiente: (Make-ruta: tipo "html": Versin: ms reciente: por defecto de entrada y archivo)

Sin el : Versin argumento, en un sistema de archivos con una funcin de control de versiones, la ruta de salida iba a heredar su nmero de versin del archivo de entrada que no es probable que sea correcta - si el archivo de entrada se ha salvado muchas veces tendr que una versin mucho mayor nmero que el archivo HTML generado. En las implementaciones de versiones de archivos sin el : Versin argumento debe ser ignorada. Depende de usted si usted se preocupa mucho acerca de la portabilidad.
11 Vase el captulo 19 para ms informacin sobre errores de manipulacin. 12 Para las aplicaciones que necesitan acceder a otros atributos de archivo en un sistema operativo

en particular o sistema de archivos, las bibliotecas ofrecen enlaces a las llamadas al sistema C subyacentes. La biblioteca Osicat en http://common-lisp.net/project/osicat/ proporciona una API simple construida utilizando la interfaz de funciones extranjeras Universal (UFFI), que debera funcionar en la mayora de los Lisps comunes que se ejecutan en un sistema operativo POSIX.
13 El nmero de bytes y caracteres en un archivo puede ser diferente, incluso si usted no est

utilizando una codificacin de caracteres multibyte. Debido a las corrientes de caracteres tambin se traducen especficas de la plataforma los finales de lnea a un nico # \ nueva lnea de carcter, en

Windows (que utiliza CRLF como final de lnea) el nmero de caracteres por lo general ser ms pequeo que el nmero de bytes.Si usted realmente tiene que saber el nmero de caracteres en un archivo, usted tiene que morder la bala y escribir algo como esto:
(Con-open-file (en nombre de archivo) (While (read-char en cero) de linfocitos T))

o tal vez algo ms eficiente de esta manera:


(Con-open-file (en nombre de archivo) (Let ((cero (make-string 4096))) (Bucle de lectura = (lectura de cero en la secuencia) mientras que (plusp lectura) lectura suma))) 14 MARCA-BROADCAST-STREAM se puede hacer un agujero negro de datos llamndolo sin

argumentos.
15 La mayor pieza que faltaba en el estndar Common Lisp E / S de las instalaciones es una manera

para que los usuarios definir nuevas clases de la corriente. Hay, sin embargo, dos estndares de facto para las corrientes definidas por el usuario. Durante el Common Lisp de normalizacin, David Gray, de Texas Instruments, escribi un borrador de propuesta para una API para permitir a los usuarios definir nuevas clases de la corriente. Por desgracia, no haba tiempo para resolver todas las cuestiones planteadas por su proyecto para incluirlo en el lenguaje estndar. Sin embargo, muchas implementaciones de apoyar a alguna forma de los llamados flujos de Gray, basando su API en el proyecto de propuesta de Gray. Otra API, ms nuevo, llamado corrientes simples, ha sido desarrollado por Franz y se incluyen en Allegro Common Lisp. Fue diseado para mejorar el rendimiento de las corrientes definidas por el usuario en relacin con flujos de gris y ha sido adoptado por algunas de las implementaciones comunes de cdigo abierto Lisp.

15. Prctica: Una biblioteca porttil de nombre de ruta


Como he comentado en el captulo anterior, Common Lisp proporciona una abstraccin, la ruta, que se supone que se aslan de los detalles de cmo diferentes sistemas operativos y sistemas de archivos de nombre. Rutas de acceso proporcionan un API til para la manipulacin de nombres como nombres, pero cuando se trata de las funciones que realmente interactan con el sistema de archivos, las cosas se ponen un poco peludo. La raz del problema, como he mencionado, es que la abstraccin ruta fue diseada para representar los nombres de archivo en una variedad mucho ms amplia de sistemas de archivos que se utilizan ahora. Por desgracia, al hacer rutas lo suficientemente abstracto para dar cuenta de una amplia variedad de sistemas de archivos, los diseadores de Common Lisp dej encargados de la ejecucin con un buen nmero de opciones para hacer acerca de cmo exactamente para asignar la ruta de acceso de abstraccin en cualquier sistema de archivos en particular. En consecuencia, los ejecutores diferentes, cada uno la aplicacin de la abstraccin de ruta para el mismo sistema de archivos, con slo hacer elecciones diferentes en un cruces claves, podra terminar con las implementaciones conformes que, no obstante ofrecer un comportamiento diferente para varios de los principales relacionadas con las funciones de ruta. Sin embargo, de una manera u otra, todas las implementaciones de proporcionar la misma funcionalidad bsica, por lo que no es demasiado difcil escribir una librera que proporciona una interfaz coherente para las operaciones ms comunes a travs de las distintas aplicaciones. Esa es su tarea para este captulo. Adems de darle varias funciones tiles que usted va a usar en futuros captulos, escribiendo esta biblioteca le dar la oportunidad de aprender cmo escribir cdigo que se ocupa de las diferencias entre las implementaciones. La API Las operaciones bsicas de la biblioteca va a apoyar va a obtener una lista de archivos en un directorio y determinar si un archivo o directorio con un nombre dado existe. Tambin voy a escribir una funcin recursiva para caminar una

jerarqua de directorios, llamar a una funcin determinada para cada ruta en el rbol. En teora, estas operaciones de listado de directorios y archivos existencia vienen dados por las funciones estndar DIRECTORIO y SONDA ARCHIVO- . Sin embargo, como se ver, hay maneras bastante diferentes de implementar estas funciones todo dentro de los lmites de las interpretaciones vlidas de la lengua estndar - que te quieren incluir funciones nuevas que proporcionan un comportamiento consistente a travs de las implementaciones. * CARACTERSTICAS * y lectura Tiempo Condicionalizacin Antes de poder implementar esta API en una biblioteca que se ejecutar correctamente en mltiples implementaciones de Lisp comunes, tengo que mostrar el mecanismo de aplicacin para escribir cdigo especfico. Mientras que la mayor parte del cdigo que se escribe puede ser "porttil" en el sentido de que se ejecute la misma en cualquier implementacin de conformidad Common Lisp, de vez en cuando puede ser necesario contar con la aplicacin especfica de la funcionalidad o escribir trozos de cdigo ligeramente diferentes para las distintas aplicaciones . Para que pueda hacerlo sin destruir totalmente la portabilidad de su cdigo, Common Lisp proporciona un mecanismo, llamado tiempo de lectura condicionalizacin , que le permite incluir condicionalmente cdigo basado en distintos aspectos como la aplicacin de lo que est siendo ejecutado in El mecanismo consta de una variable * CARACTERSTICAS * y dos bits adicionales de la sintaxis que entiende el lector de Lisp. * CARACTERSTICAS * es una lista de smbolos, cada smbolo representa una "caracterstica" que est presente en la ejecucin o en la plataforma subyacente. Estos smbolos se utilizan en expresiones de caractersticas que se evalan como verdadero o falso dependiendo de si los smbolos de la expresin estn presentes en * CARACTERSTICAS * Los . La expresin ms simple es caracterstica de un solo smbolo, la expresin es verdadera si el smbolo se encuentra en * CARACTERSTICAS * y false si no lo es. Otras expresiones caractersticas son expresiones booleanas construidas a partir de NO , Y y O operadores. Por ejemplo, si usted quiere condicionar parte del cdigo que debe incluirse slo si la caractersticas foo y bar estaban presentes, se podra escribir la expresin caracterstica (y foo bar) .

El lector utiliza expresiones de funciones en relacin con dos bits de la sintaxis, # + y # - . Cuando el lector ve cualquiera de estos bits de la sintaxis, primero lee una expresin caracterstica y luego lo evala como acabo de describir. Cuando una expresin de funcin despus de un + # es cierto, el lector lee la siguiente expresin normalmente. De lo contrario, se salta la siguiente expresin, tratndolo como espacios en blanco. # - funciona de la misma manera, excepto que lee el formulario si la expresin caracterstica es falsa y se salta si es verdad. El valor inicial de * CARACTERSTICAS * depende de la implementacin y funcionalidad de lo que est implcito en la presencia de smbolos se est tambin definido por la aplicacin. Sin embargo, todas las implementaciones de incluir por lo menos un smbolo que indica qu aplicacin que es. Por ejemplo, Allegro Common Lisp incluye el smbolo: allegro , CLISP incluye : clisp , SBCL incluye : sbcl y CMUCL incluye : CMU . Para evitar las dependencias de paquetes que pueden o no pueden existir en las distintas aplicaciones, los smbolos de * CARACTERSTICAS * son por lo general palabras clave, y el lector se une PAQUETE * * a la PALABRA CLAVE del paquete durante la lectura de las expresiones caractersticas. Por lo tanto, un nombre sin cualificacin paquete se lee como un smbolo de palabras clave. Por lo tanto, se podra escribir una funcin que se comporta de forma diferente en cada una de las implementaciones que acabamos de mencionar de esta manera:
(Defun foo () # + Allegro (do-una-cosa) # + Sbcl (hacer-otra cosa) # + Clisp (algo-otra cosa) # + CMU (an otra versin) # - (O allegro sbcl clisp CMU) (error "No implementado"))

En Allegro que el cdigo se puede leer como si se hubiera escrito as:


(Defun foo () (Hacer-una-cosa))

mientras que en SBCL el lector lea esto:


(Defun foo () (Hacer-otra cosa-))

mientras que en una aplicacin distinta de las expresamente conditionalized, se leer as:
(Defun foo () (El error "No implementado"))

Debido a que el condicionalizacin que sucede en el lector, el compilador no tiene ni siquiera ver las expresiones que se omiten. 1 Esto significa que usted no paga ningn costo de ejecucin para tener versiones diferentes para las distintas aplicaciones. Adems, cuando el lector pasa por alto las expresiones conditionalized, no molesta a los smbolos internar, por lo que las expresiones omitidos de forma segura puede incluir los smbolos de los paquetes que no pueden existir en otras implementaciones.
Embalaje de la Biblioteca
Hablando de paquetes, si se descarga el cdigo completo de esta biblioteca, ver que est definido en un nuevo paquete, com.gigamonkeys.pathnames . Voy a discutir los detalles de la definicin y el uso de paquetes en el captulo 21. Por ahora, debe tener en cuenta que algunas implementaciones de ofrecer sus propios paquetes que contienen funciones con algunos de los mismos nombres que los que te definen en este captulo y hacer que esos nombres disponibles en el paquete de CL-USUARIO. Por lo tanto, si usted trata de definir las funciones de esta biblioteca, mientras que en el paquete de CLusuario, es posible obtener errores o advertencias acerca de vapulear las definiciones existentes. Para evitar esta posibilidad, puede crear un archivo llamado packages.lisp con el siguiente contenido: (En el paquete: CL-usuario) (Defpackage: com.gigamonkeys.pathnames (: Uso: common-lisp) (: Las exportaciones : Lista-directorio : Archivo-existe-p : Directorio-ruta-p : Archivo-ruta-p : Nombre de ruta-como-directorio : Nombre de ruta-como-archivo : A pie de directorio : Directorio-p : Archivo-p)) y CARGA ella. Luego, en el REPL o en la parte superior del archivo donde se escriben las definiciones de este captulo, escriba la siguiente expresin: (En el paquete: com.gigamonkeys.pathnames) Adems de evitar conflictos de nombres con los smbolos que ya estn disponibles en la CL-USUARIO , acondicionamiento de la biblioteca de esta manera tambin hace que sea ms fcil de usar en otro cdigo, como se ver en varios captulos en el futuro.

Listado de un directorio Se puede implementar la funcin de inclusin de un solo directorio, lista de directorio , como una envoltura delgada alrededor de la funcin estndar de DIRECTORIO .DIRECTORIO tiene un tipo especial de ruta, llamada ruta salvaje , que tiene uno o ms componentes que contienen el valor especial : salvaje y devuelve una lista de nombres de caminos que representan los archivos en el sistema de archivos que coinciden con el nombre de ruta salvaje. 2 El algoritmo de

comparacin - como la mayora de las cosas que tienen que ver con la interaccin entre Lisp y un sistema de archivo en particular - no es definido por el estndar del lenguaje , pero la mayora de las implementaciones de Unix y Windows sigue el mismo esquema bsico. El DIRECTORIO funcin tiene dos problemas que usted necesita para hacer frente a la lista de directorio . La principal es que ciertos aspectos de su comportamiento difiere bastante significativamente entre las diferentes implementaciones de Lisp comunes, incluso en el mismo sistema operativo. La otra es que, si bien DIRECTORIO proporciona una potente interfaz para listar los ficheros, usar correctamente es necesario comprender algunos puntos ms sutiles sobre la abstraccin ruta. Entre estas sutilezas y la idiosincrasia de las distintas aplicaciones, en realidad la escritura de cdigo porttil que utiliza DIRECTORIO hacer algo tan simple como una lista de todos los archivos y subdirectorios de un directorio nico puede ser una experiencia frustrante. Usted puede hacer frente a las sutilezas y la idiosincrasia de una vez por todas, al escribir la lista de directorio , y olvidarse de ellos despus de eso. Una sutileza que en el captulo 14 es las dos formas de representar el nombre de un directorio como una ruta: la forma de directorios y forma de archivo. Para obtener DIRECTORIO para devolver una lista de archivos en / home / peter / , que hay que aprobar una ruta salvaje cuyo directorio del componente es el directorio que desee a la lista y cuyo nombre y tipo de componentes son : salvajes . Por lo tanto, para obtener una lista de los archivos en / home / peter / , podra parecer que poda escribir lo siguiente:
(Directorio (make-ruta: nombre: salvaje: Tipo: salvaje: por defecto home-dir))

en home-dir es una ruta que representa / home / peter / . Esto funcionara si homedir fueron en forma de directorio. Pero si fuera en forma de archivos - por ejemplo, si hubiera sido creado por analizar el namecadena "/ home / peter" - entonces la misma expresin que una lista de todos los archivos en / home ya que el componente de nombre "Pedro" sera sustituido por : salvaje . Para evitar tener que preocuparse por la conversin explcita entre las representaciones, se puede definir la lista de directorio para aceptar una ruta nonwild en cualquier forma, que luego se convertir en la ruta natural apropiado.

Para ayudar con esto, se deben definir unos pocos funciones de ayuda. Uno de ellos, los componentes-presente-p , pondr a prueba si un determinado componente de una ruta de acceso est "presente", es decir ni NADA , ni el valor especial : no especfica . 3 Otro, directorio ruta-p , comprueba si un nombre de ruta ya est en forma de directorio , y el tercero, ruta de acceso-como-directorio , convierte cualquier ruta de acceso a un formulario de ruta del directorio.
(Defun componentes presentes-p (valor) (Y el valor (no (valor eql: inespecfica)))) (Defun-directorio de ruta-p (p) (Y (No (componente-presente-p (path-name p))) (No (componente-presente-p (ruta de acceso de tipo p))) p)) (Defun nombre de ruta-como-directorio (nombre) (Let ((ruta de acceso (nombre de ruta))) (Cuando (wild-ruta-p ruta de acceso) (Error de "no es fiable, se puede convertir rutas salvajes".)) (Si no es ((directorio de ruta-p-nombre)) (Make-ruta : El directorio (se aade a (o (ruta-directorio de ruta) (lista: relativa)) (La lista (archivo-namecadena ruta))) : Nombre de nula : Tipo de cero : Por defecto nombre de ruta) ruta)))

Ahora parece que podra generar una ruta natural para pasar al DIRECTORIO llamando MARCA-VA con un nombre de directorio forman devuelto porruta-comodirectorio . Por desgracia, no es tan sencillo, gracias a una peculiaridad en la aplicacin CLISP de DIRECTORIO . En CLISP, DIRECTORIO no devolver los archivos sin extensin a menos que el tipo de componente del comodn es NIL en lugar de : salvaje . As se puede definir una funcin, directorio de comodn- , que tiene una ruta de acceso ya sea en forma de directorio o archivo y devuelve un comodn adecuada para la aplicacin dada usando el tiempo de lectura condicionalizacin para hacer una ruta con un : naturaleza de tipo de componente en todas las implementaciones a excepcin de CLISP y NIL en CLISP.
(Defun directorio comodn (dirname) (Make-ruta : Nombre: salvaje : Tipo de #-clisp: Wild # + clisp nula : Por defecto (ruta-como-el directorio dirname)))

Tenga en cuenta cmo cada una lectura en tiempo condicional opera en el nivel de una sola expresin Despus de #-clisp , la expresin : salvaje o bien se lee o se omiten, del mismo modo, despus de # + clisp , la NIL se lee o se saltan. Ahora usted puede tomar una primera grieta en la lista de directorio de funcin.
(Defun lista-directorio (dirname) (Cuando (wild-ruta-p dirname) (Error de "slo se pueden listar los nombres concretos de directorio.")) (Directorio (directorio de comodn-dirname)))

En la actualidad, esta funcin podra funcionar en SBCL, CMUCL y LispWorks. Por desgracia, un par de diferencias de implementacin an no se han suavizado ms. Uno de ellos es que no todas las implementaciones devolver los subdirectorios del directorio dado. Allegro, SBCL, CMUCL y LispWorks hacer. OpenMCL no por defecto, pero se si se pasaDIRECTORIO un valor real a travs de la implementacin especfica del argumento de palabra clave : directorios . CLISP de DIRECTORIO devuelve subdirectorios slo cuando se pasa una ruta de acceso comodn con : salvaje como el ltimo elemento del componente de directorio y NIL nombre y tipo de componentes. En este caso, devuelvenicamente los subdirectorios, por lo que tendrs que llamar DIRECTORIO dos veces con comodines diferentes y combinar los resultados. Una vez que todas las implementaciones que regresan los directorios, usted descubrir que tambin puede variar si se tiene que devolver los nombres de los directorios en el directorio o en forma de archivo. Usted quiere que la lista de directorio para que siempre devuelva los nombres de directorio en forma de directorios para que pueda diferenciar los subdirectorios de los archivos regulares sobre la base de slo el nombre. A excepcin de Allegro, todas las implementaciones de esta biblioteca apoyar a hacer eso. Allegro, por el contrario, te obliga a pasar DIRECTORIO el argumento de palabra clave especfica de la implementacin : los directorios de los archivos son NIL para conseguir que vuelva directorios en forma de archivo. Una vez que sepa cmo hacer que cada implementacin de hacer lo que quiera, en realidad escribir la lista de directorio es simplemente una cuestin de combinar las diferentes versiones con lectura en tiempo condicional.
(Defun lista-directorio (dirname) (Cuando (wild-ruta-p dirname) (Error de "slo se pueden listar los nombres concretos de directorio.")) (Let ((comodn (wildcard directorio dirname)))

# + (O sbcl lispworks CMU) (Comodn directorio) # + Openmcl (Comodn directorio: los directorios t) # + Allegro (Comodn directorio: los directorios de los archivos son cero) # + Clisp (Nconc (Comodn directorio) (Directorio (subdirectorios clisp-comodn-comodn))) # - (O sbcl CMU lispworks openmcl allegro clisp) (Error de "lista de directorio no est implementada")))

La funcin de los subdirectorios clisp comodn- en realidad no es especfico para CLISP, pero como no la utiliza ninguna aplicacin de otra parte, usted puede proteger su definicin con un condicional lectura del tiempo. En este caso, puesto que la expresin que sigue a la # + est el todo DEFUN , la definicin de funcin entera ser incluido o no, dependiendo de si clisp est presente en * CARACTERSTICAS * Los .
# + Clisp (Defun-clisp subdirectorios-comodn (wildcard) (Make-ruta : El directorio (se aade a (nombre de ruta de directorio de comodn) (lista: las silvestres)) : Nombre de nula : Tipo de cero : Defaults comodn))

Prueba de la existencia de un archivo Para reemplazar SONDA-FILE , puede definir una funcin llamada file-existe-p . Debe aceptar un nombre de ruta y devolver una ruta de acceso equivalente, si el archivo existe y NIL si no lo hace. Debe ser capaz de aceptar el nombre de un directorio, ya sea en forma de directorio o archivo, pero siempre debe devolver un nombre de ruta forma de directorio si el archivo existe y es un directorio. Esto le permitir utilizar archivos existe-p , junto con el directorio de ruta-p- , para comprobar si un nombre arbitrario es el nombre de un archivo o directorio. En teora, el archivo-existe-p es muy similar a la funcin estndar de SONDA-FILE , de hecho, en varias implementaciones - SBCL, LispWorks y OpenMCL -SONDA-FILE ya te da el comportamiento que desea de archivo-existe-p . Sin embargo, no todas las implementaciones de SONDA-FILE se comportan exactamente lo mismo.

Allegro y CMUCL de FILE-SONDA funciones estn cerca de lo que usted necesita que se acepte el nombre de un directorio, ya sea en la forma, pero, en lugar de devolver un nombre de formulario de directorio, simplemente devuelva el nombre de la misma forma que el argumento de que fue aprobada . Por suerte, si se aprueba el nombre de un fichero no directorio en forma de directorio, vuelven NIL . As que con estas implementaciones se puede obtener el comportamiento que usted quiere en primer lugar, pasando el nombre deSONDA-FILE en forma de directorio - si el archivo existe y es un directorio, se le devolver el nombre del formulario de directorio. Si esa llamada devuelve NIL , a continuacin, intenta de nuevo con un nombre de archivo de formulario. CLISP, por otro lado, una vez ms tiene su propia manera de hacer las cosas. Su SONDA-FILE de inmediato seala un error si se pasa un nombre en forma de directorio, independientemente de si un archivo o directorio existe con ese nombre. Tambin seala un error si pasa un nombre en forma de archivo que en realidad es el nombre de un directorio.Para comprobar si existe un directorio, CLISP ofrece su propia funcin: la sonda de directorio (en la extensin del paquete). Esto es casi la imagen especular deSONDA-FILE : se seala un error si se pasa un nombre en forma de archivo o si se pasa un nombre en forma de directorio que le sucede a nombre de un archivo. La nica diferencia es que devuelve T en lugar de un nombre de ruta cuando el directorio llamado existe. Pero incluso en CLISP puede implementar la semntica deseada envolviendo las llamadas a SONDA-FILE y la sonda de directorio en la ignore-ERRORES . 4
(Defun archivo-existe-p (path) # + (O sbcl lispworks openmcl) (Sonda-file pathname) # + (O CMU allegro) (O (sonda de archivo (ruta-como-directorio de ruta)) (La sonda de archivo ruta)) # + Clisp (O (ignore-errores (Sonda de archivo (ruta-como-archivo ruta))) (Ignorar los errores (Let ((directorio de forma (ruta-como-directorio de ruta))) (Cuando (ext: la sonda de directorio de directorio de forma) directorio de forma)))) # - (O sbcl CMU lispworks openmcl allegro clisp) (Error de "archivo no existe-p-prctica"))

La funcin de ruta-como-archivo que usted necesita para la ejecucin CLISP de archivo-existe-p es la inversa de la previamente definida, como ruta de acceso de directorio , que devuelve un nombre de ruta que es la forma de un archivo equivalente de su argumento. Esta funcin, a pesar de ser necesario aqu slo por CLISP, suele ser til, por lo que definir para todas las implementaciones y convertirlo en parte de la biblioteca.
(Defun nombre de ruta-como-archivo (nombre) (Let ((ruta de acceso (nombre de ruta))) (Cuando (wild-ruta-p ruta de acceso) (Error de "no es fiable, se puede convertir rutas salvajes".)) (Si (directorio-ruta-p nombre) (Let * ((directorio (ruta-directorio de ruta)) (Nombre y tipo (ruta de acceso (en primer lugar (ltimo directorio))))) (Make-ruta : El directorio (directorio butlast) : Nombre (nombre de ruta de nombre de nombre y tipo) : Tipo (ruta de acceso del tipo de nombre y tipo) : Ruta por defecto)) ruta)))

Caminando un rbol de directorios Por ltimo, para completar esta biblioteca, se puede implementar una funcin llamada a pie de directorio . A diferencia de las funciones definidas anteriormente, esta funcin no necesita hacer nada para suavizar las diferencias de implementacin, sino que slo tiene que utilizar las funciones que ya ha definido. Sin embargo, es muy til, y lo vas a utilizar varias veces en los captulos siguientes. Se llevar el nombre de un directorio y una funcin y llamar a la funcin de las rutas de acceso de todos los archivos en el directorio, de forma recursiva. Tambin se tendr en dos argumentos clave: : directorios y : prueba . Cuando : directorios es cierto, se llama a la funcin de la ruta de los directorios, as como archivos regulares. El : Prueba de argumento, si se proporciona, especifica otra funcin que se invoca en cada ruta de acceso antes de la funcin principal es, la funcin principal ser llamado slo si la funcin de prueba devuelve el valor true.
(Defun walk-directorio (dirname fn y directorios clave (prueba (constantemente t))) (Etiquetas (A pie ((nombre) (Cond ((Directorio ruta-p nombre) (Cuando (y directorios (nombre funcall prueba)) (Funcall nombre fn)) (Dolist (x (lista-nombre del directorio)) (a pie x))) ((Nombre de funcall el ensayo) (funcall nombre fn))))) (A pie (ruta-como-el directorio dirname))))

Ahora dispone de una biblioteca de funciones tiles para hacer frente a los nombres de ruta. Como ya he dicho, estas funciones son muy tiles en los captulos posteriores, en particular los captulos 23 y 27, donde tendrs que usar piedirectorio de arrastrarse a travs de los rboles de directorios que contienen los mensajes de spam y archivos MP3. Pero antes de llegar a eso, sin embargo, tengo que hablar acerca de la orientacin a objetos, el tema de los dos captulos siguientes.

Original Text: 3Implementations are allowed to return :unspecific instead of NIL as the value of pathname components in certain situations such as when the component isn't used by that implementation.
Mostrar traducciones alternativas 1 Una de las consecuencias un poco molesto de la forma de lectura en tiempo condicionalizacin

funciona es que no hay manera fcil de escribir un caso de cada a travs de. Por ejemplo, si se aade el apoyo a la aplicacin de otra foo mediante la adicin de otra expresin protegido con # + , es necesario recordar que aadir tambin la misma caracterstica en el , o la expresin de funcin despus de la # - o el ERRORforma se evaluar despus de que su nueva se ejecuta el cdigo.
2 Otro valor especial : los salvajes inferiores , puede aparecer como parte del componente de

directorio de una ruta salvaje, pero no lo necesita en este captulo.


3 Las implementaciones se les permite regresar : no especfica en lugar de NIL como el valor de los

componentes de ruta de acceso en determinadas situaciones, como cuando el componente no se utiliza en esa aplicacin.
4 Esta cifra es ligeramente rota en el sentido de que si SONDA-FILE indica un error por alguna otra

razn, este cdigo no lo interpretar bien. Por desgracia, la documentacin CLISP no especifica qu errores podran ser sealado por SONDA-FILE y el directorio de la sonda , y la experimentacin parece indicar que la seal simple archivo de error de s en la mayora de situaciones errneas.

16. Reorientacin del objeto: Funciones Genricas


Debido a la invencin de Lisp es anterior a la subida de la programacin orientada a objetos por un par de dcadas, 1 Lispers nuevos a veces se sorprenden al descubrir lo que es un fondo lenguaje orientado a objetos Common Lisp es. Predecesores inmediatos Common Lisp se desarrollaron en una poca en la orientacin a objetos es una idea nueva y emocionante y hubo muchos experimentos con formas de incorporar las ideas de la orientacin a objetos, sobre todo tal como se manifiesta en Smalltalk, en Lisp. Como parte de la Common Lisp de normalizacin, una sntesis de varios de estos experimentos surgi bajo el nombre comn de objetos del sistema Lisp, o clausura. El estndar ANSI incorporada CLOS en la lengua, por lo que en realidad ya no tiene sentido hablar de la CLOS como una entidad separada. Las caractersticas CLOS contribuido a Common Lisp rango de los que casi no se puede evitar con manifestaciones relativamente esotricas de Lisp como lenguaje-el lenguaje-herramienta de creacin de la filosofa. La cobertura completa de todas estas caractersticas est fuera del mbito de este libro, pero en este captulo y el siguiente voy a describir las caractersticas de pan con mantequilla y dar una visin general del enfoque de Common Lisp a objetos. Debe tener en cuenta desde el principio que el sistema de Common Lisp de objetos ofrece una modalidad bastante diferente de los principios de la orientacin a objetos que muchos otros idiomas. Si usted tiene una comprensin profunda de las ideas fundamentales detrs de la orientacin a objetos, es probable que apreciar la forma particularmente potente y general de Common Lisp manifiesta esas ideas. Por otro lado, si su experiencia con la orientacin a objetos ha sido en gran parte con un solo idioma, es posible que enfoque un tanto extranjera Common Lisp de;. Usted debe tratar de evitar asumir que slo hay un camino para que una lengua para apoyar la orientacin a objetos 2 Si tienen muy poco orientada a objetos experiencia en programacin, que no debera tener problemas para entender las explicaciones aqu, aunque puede ayudar a hacer caso omiso de las comparaciones ocasionales a la manera en otros idiomas hacer las cosas.

Funciones y clases genricas La idea fundamental de la orientacin a objetos es que una poderosa manera de organizar un programa es definir los tipos de datos y las operaciones asociadas con los tipos de datos.En particular, quiero ser capaz de invocar una operacin y tiene el comportamiento exacto determinado por el tipo del objeto o los objetos en el que se invoc la operacin. El ejemplo clsico utilizado, aparentemente por todas las introducciones a la orientacin a objetos, es una operacin de empate que se puede aplicar a los objetos que representan a diversas formas geomtricas. Diferentes implementaciones del sorteo operacin se puede proporcionar para dibujar crculos, tringulos y cuadrados, y una llamada a llamar la realidad se traducir en la elaboracin de un crculo, un tringulo o un cuadrado, dependiendo del tipo de objeto al que el empate se aplica la operacin . Las diferentes implementaciones deempate se definen por separado, y las nuevas versiones se puede definir que dibujan otras formas sin tener que cambiar el cdigo de cualquiera de la persona que llama o de cualquiera de las otras empate implementaciones. Esta caracterstica de la orientacin a objetos se conoce con el nombre griego de lujo polimorfismo , lo que significa "muchas formas", porque una sola operacin conceptual, como dibujar un objeto, puede tomar muchas formas concretas diferentes. Common Lisp, al igual que la mayora de los lenguajes orientados a objetos de hoy en da, es basada en la clase, todos los objetos son instancias de una clase en particular. 3 La clase de un objeto determina su representacin - incorporado en las clases como NMERO y STRING tienen representaciones opacas a las que slo a travs de las funciones estndar para la manipulacin de esos tipos, mientras que las instancias de clases definidas por el usuario, como se ver en el prximo captulo, consisten en elementos con nombre denominados slots . Las clases se organizan en una jerarqua, una taxonoma para todos los objetos. Una clase puede ser definida como una subclase de otras clases, llam a sus superclases . Una clasehereda parte de su definicin de sus superclases y las instancias de una clase tambin se consideran los casos de las superclases. En Common Lisp, la jerarqua de clases tiene una sola raz, de la clase T , que es una superclase directa o indirecta de cualquier otra clase. Por lo tanto, cada dato en Common Lisp es una instancia de T . 4 Common Lisp tambin es compatible con la herencia mltiple - una sola clase puede tener mltiples superclases directas.

Fuera de la familia Lisp, casi todos los lenguajes orientados a objetos siguen el patrn bsico establecido por Simula de tener un comportamiento asociado con las clases a travs demtodos o funciones miembro que pertenecen a una clase en particular. En estos idiomas, se invoca un mtodo en un objeto particular, y la clase de ese objeto determina qu cdigo se ejecuta. Este modelo de invocacin del mtodo que se llama - despus de la terminologa Smalltalk - el paso de mensajes . Conceptualmente, la invocacin del mtodo en un sistema de paso de mensajes se inicia mediante el envo de un mensaje que contiene el nombre del mtodo a ejecutar y los argumentos que el objeto sobre el que se invoca el mtodo. El objeto a continuacin, utiliza su clase para buscar el mtodo asociado con el nombre en el mensaje y lo ejecuta. Debido a que cada clase puede tener su propio mtodo para un determinado nombre, el mismo mensaje, enviado a diferentes objetos, pueden llamar a los mtodos diferentes. Los primeros sistemas de objetos Lisp trabajado en una manera similar, proporcionando una funcin especial ENVIAR que podra ser utilizado para enviar un mensaje a un objeto particular. Sin embargo, esto no fue del todo satisfactoria, ya que hizo llamadas a mtodos diferentes de las llamadas a funciones normales. Sintcticamente llamadas a mtodos se escribe as:
(Enviar foo objeto ')

en lugar de como esto:


(Foo objeto)

Ms importante an, porque los mtodos no eran las funciones, no podan ser pasados como argumentos a funciones de orden superior tales como MAPCAR , si uno quera llamar a un mtodo en todos los elementos de una lista con MAPCAR , uno tena que escribir lo siguiente:
(Mapcar # '(lambda (objeto) (enviar el objeto' foo)) los objetos)

en lugar de esto:
(Nmero de objetos mapcar 'foo)

Con el tiempo la gente que trabaja en el objeto Lisp mtodos de sistemas unificados con las funciones mediante la creacin de un nuevo tipo de funcin llamada funcin genrica .Adems de resolver los problemas que acabamos de describir, las funciones genricas abierto nuevas posibilidades para el sistema de objetos,

incluyendo muchas caractersticas que simplemente no tienen sentido en un sistema de objetos de paso de mensajes. Funciones genricas son el corazn del sistema objeto de Common Lisp y el tema del resto de este captulo. Si bien no puedo hablar acerca de las funciones genricas, sin mencin alguna de las clases, por ahora me centrar en cmo definir y utilizar las funciones genricas. En el siguiente captulo te mostrar cmo definir sus propias clases. Funciones y mtodos genricos Una funcin genrica define una operacin abstracta, especificando su nombre y una lista de parmetros, pero su aplicacin no. Aqu, por ejemplo, es cmo se puede definir una funcin genrica, sorteo , que se utiliza para dibujar diferentes tipos de formas en la pantalla:
(Defgeneric empate (forma) (: Documentacin "Dibuje la forma indicada en la pantalla."))

Voy a hablar sobre la sintaxis de DEFGENERIC en la siguiente seccin, por ahora slo sealar que esta definicin no contiene ningn cdigo real. Una funcin genrica es genrico en el sentido de que puede - al menos en teora aceptar objetos como argumentos. 5 Sin embargo, por s misma una funcin genrica en realidad no puede hacer nada, si usted acaba de definir una funcin genrica, no importa qu argumentos se le llama con, que ser una seal de error. La implementacin real de una funcin genrica es proporcionada por los mtodos . Cada mtodo proporciona una implementacin de la funcin genrica para determinadas clases de argumentos. Tal vez la mayor diferencia entre un genrico basado en las funciones del sistema y un sistema de paso de mensajes es que los mtodos no pertenecen a las clases, sino que pertenecen a la funcin genrica, que es responsable de determinar qu mtodo o mtodos que se ejecutan en respuesta a un determinado invocacin. Mtodos de indicar qu tipo de argumentos que pueden manejar por el especialista de los parmetros requeridos definidos por la funcin genrica. Por ejemplo, en la funcin genricaempate , se puede definir un mtodo que se especializa la forma de parmetro para los objetos que son instancias de la clase crculo , mientras que otro mtodo se especializala forma de los objetos que son instancias de la clase de tringulo . Se vera as, eludiendo el cdigo de dibujo real:

(Defmethod empate ((crculo de la forma)) , ...) (Defmethod empate ((forma de tringulo)) , ...)

Cuando una funcin genrica que se invoca, compara los argumentos reales que se aprob con la specializers de cada uno de sus mtodos para encontrar los correspondientes mtodos, aquellos mtodos cuyo specializers son compatibles con los argumentos reales. Si se invoca el sorteo , pasando a una instancia de crculo , el mtodo que se especializla forma en la clase de crculo es aplicable, mientras que si se le pasa un tringulo , entonces el mtodo que se especializa la forma en la clase de tringulo se aplica. En casos sencillos, slo un mtodo ser de aplicacin, y se encargar de la invocacin. En los casos ms complejos, puede haber varios mtodos que se aplican, sino que est a continuacin, combinar, como lo discutiremos en la seccin "La combinacin de mtodos," en un nico mtodo efectivo que se encarga de la invocacin. Usted puede especializarse un parmetro de dos maneras - por lo general deber especificar una clase que el argumento debe ser una instancia de. Debido a instancias de una clase tambin se consideran los casos de superclases que de clase, un mtodo con un parmetro especializado en una clase en particular puede ser aplicable siempre que el argumento correspondiente es un ejemplo directo de la clase especializada o de cualquiera de sus subclases. El otro tipo de specializer es un as llamado EQL specializer, que especifica un objeto particular al que el mtodo se aplica. Cuando una funcin genrica tiene slo los mtodos especializados en un solo parmetro y todos los specializers son specializers de clase, el resultado de la invocacin de una funcin genrica es muy similar al resultado de la invocacin de un mtodo en un sistema de paso de mensajes - la combinacin del nombre de la operacin y la clase del objeto sobre el que se invoca lo que determina el mtodo a ejecutar. Sin embargo, invirtiendo el orden de bsqueda abre posibilidades que no se encuentran en los sistemas de paso de mensajes. Las funciones genricas de apoyo a los mtodos que se especializan en varios parmetros, proporcionar un marco que permite la herencia mltiple mucho ms manejable, y le permiten utilizar las construcciones declarativas para controlar cmo los mtodos se combinan en un

mtodo eficaz, el apoyo a varios patrones de uso comn, sin una gran cantidad de cdigo repetitivo. Voy a discutir esos temas en un momento.Pero primero que tienes que mirar a los fundamentos de las dos macros se utilizan para definir las funciones genricas DEFGENERIC y DEFMETHOD . DEFGENERIC Para que os hagis una idea de estas macros y las diversas instalaciones que apoyan, te voy a mostrar algo de cdigo que se puede escribir como parte de una aplicacin de banca - o, ms bien, una aplicacin de banca de juguetes, el punto es que un vistazo a algunos las caractersticas del lenguaje, no para aprender a escribir realmente de software bancario. Por ejemplo, este cdigo ni siquiera pretende hacer frente a cuestiones tales como monedas, por no hablar de mltiples pistas de auditora y la integridad transaccional. Porque yo no voy a discutir la forma de definir nuevas clases hasta el prximo captulo, por ahora slo se puede asumir que ciertas clases ya existentes: para empezar, supongamos que hay una clase de una cuenta bancaria y que tiene dos subclases, la comprobacin de la cuenta y la cuenta de ahorros . La jerarqua de clases es el siguiente:

La funcin genrica la primera retirada , lo que disminuye el saldo de la cuenta por un importe especificado. Si el saldo es menor que la cantidad, se debe sealar un error y dejar el saldo sin cambios. Puede comenzar por la definicin de la funcin genrica con DEFGENERIC . La forma bsica de DEFGENERIC es similar a la DEFUN excepto con ningn cuerpo. La lista de parmetros de DEFGENERIC especifica los parmetros que deben ser aceptados por todos los mtodos que se definen en la funcin genrica. En el lugar del cuerpo, una DEFGENERIC puede contener varias opciones. Una opcin que siempre debe incluir es: documentacin , que se utiliza para proporcionar una cadena que describe el propsito de la funcin genrica. Debido a que una funcin genrica es puramente abstracta, es importante tener claro tanto para los usuarios y los ejecutores para qu sirve. Por lo tanto, se puede definir retirar de esta manera:
(Defgeneric retirar (importe de la cuenta) (: Documentacin "Retirar la cantidad especificada de la cuenta. Sealar un error si el saldo actual es menor que la cantidad. "))

DEFMETHOD Ahora ests listo para usar DEFMETHOD para definir los mtodos que implementan retirar . 6 Lista de un mtodo de parmetro debe ser congruente con su funcin genrica de. En este caso, eso significa que todos los mtodos definidos en el retiro debe tener exactamente dos parmetros requeridos. En trminos ms generales, los mtodos deben tener el mismo nmero de parmetros obligatorios y opcionales y debe ser capaz de aceptar los argumentos que corresponden a las y resto o clave y los parmetros especificados por la funcin genrica. 7 Dado que los fundamentos de la retirada son los mismos para todas las cuentas, se puede definir un mtodo que se especializa la cuenta de parmetro en la cuenta bancariade clase. Se puede asumir la funcin de equilibrio devuelve el saldo actual de la cuenta y se puede utilizar con SETF - y por tanto con DECF - para establecer el equilibrio. La funcin de ERROR es una funcin estndar que se utiliza para sealar un error, que voy a discutir con mayor detalle en el captulo 19. Usar estas dos funciones, se puede definir una base retirar mtodo que se parece a esto:
(Defmethod retirar ((cuenta bancaria de la cuenta) la cantidad) (Cuando (<(saldo de la cuenta) la cantidad) (Error de "sobregiro de la cuenta.")) (DECF (saldo de la cuenta) la cantidad))

Como sugiere este cdigo, la forma de DEFMETHOD es an ms como el de DEFUN que DEFGENERIC s '. La nica diferencia es que los parmetros requeridos pueden ser especializada mediante la sustitucin del nombre de parmetro con una lista de dos elementos. El primer elemento es el nombre del parmetro, y el segundo elemento es el specializer, ya sea el nombre de una clase o un EQL specializer, la forma de que hablar en un momento. El nombre del parmetro puede ser cualquier cosa - no tiene que coincidir con el nombre utilizado en la funcin genrica, aunque a menudo lo har. Este mtodo se aplicar siempre que el primer argumento para retirarse es una instancia de una cuenta bancaria . El segundo parmetro, cantidad , est implcitamente especializada en T , y puesto que todos los objetos son instancias de T , que no afecta a la aplicabilidad del mtodo. Ahora supongamos que todas las cuentas de cheques tiene proteccin contra sobregiros. Es decir, cada cuenta de cheques est vinculada a otra cuenta bancaria

que se recurra cuando el saldo de la cuenta de cheques en s mismo no puede cubrir la retirada. Se puede asumir que la funcin de sobregiro de la cuenta tiene unacomprobacin de la cuenta objeto y devuelve una cuenta de banco objeto que representa la cuenta vinculada. Por lo tanto, la retirada de una comprobacin de la cuenta objeto requiere algunos pasos adicionales en comparacin con la retirada de un estndara una cuenta bancaria objeto. En primer lugar, debe comprobar si la cantidad que se retira es mayor que el saldo actual de la cuenta y, si lo es, transferir la diferencia a la cuenta de sobregiro. A continuacin, puede proceder como con un estndar de una cuenta bancaria objeto. Entonces, qu te gustara hacer es definir un mtodo de retiro que se especializa en la comprobacin de la cuenta para manejar la transferencia y luego deja que el mtodo especializado en una cuenta bancaria tomar el control. Este mtodo podra tener este aspecto:
(Defmethod retirar ((cuenta de cheques de la cuenta) la cantidad) (Let ((sobregiros (- cantidad (saldo de la cuenta)))) (Cuando (sobregiros plusp) (Se debe retirar (sobregiro de la cuenta de la cuenta) por sobregiro) (Incf (saldo de la cuenta) por sobregiro))) (Llamada de ltima mtodo))

La funcin CALL-NEXT-MTODO es parte de la maquinaria funcin genrica usada para combinar los mtodos aplicables. Se indica que el control debe pasar de este mtodo con el mtodo especializado en una cuenta bancaria . 8 Cuando se llama sin argumentos, como en este caso, el siguiente mtodo se invoca con todos los argumentos que fueron transmitidas a la funcin genrica. Tambin puede ser llamado con argumentos, que luego se pasan al mtodo siguiente. Usted no est obenlazado a invocar CALL-NEXT-METODO en cada mtodo. Sin embargo, si no lo hace, el nuevo mtodo es el responsable de implementar por completo el comportamiento deseado de la funcin genrica. Por ejemplo, si usted tuviera una subclase de una cuenta bancaria , cuenta de proxy- , que en realidad no perder de vista su propio equilibrio, pero en lugar de retiros delegadas a otra cuenta, puede escribir un mtodo como este (si se asume una funcin, proxy- cuenta , que devuelve la cuenta de proxy):
(Defmethod retirar ((proxy de proxy de la cuenta) la cantidad) (Se debe retirar (proxy de la cuenta de proxy) la cantidad))

Por ltimo, DEFMETHOD tambin le permite crear mtodos especializados en un determinado objeto con un EQL specializer. Por ejemplo, supongamos que la aplicacin de la banca va a ser desplegado en un banco particularmente corrupto. Supongamos que la variable * Cuenta de banco-presidente * contiene una referencia a una cuenta bancaria particular que pertenece - como su nombre indica - que el presidente del banco. Supongamos, adems, la variable * bancaria * representa el banco en su conjunto, y la funcin demalversar roba dinero del banco. El presidente del banco le pida que "arreglar" a retirarse para manejar su cuenta de forma especial.
(Defmethod retirar ((cuenta (EQL * Cuenta de banco-presidente *)) la cantidad) (Let ((sobregiros (- cantidad (saldo de la cuenta)))) (Cuando (sobregiros plusp) (Incf (saldo de la cuenta) (* desfalco bancario de sobregiro *))) (Llamada de ltima mtodo)))

Ntese, sin embargo, que la forma en la EQL specializer que proporciona el objeto de especializarse en - * Cuenta de banco-presidente * en este caso - se evala una vez, cuando el DEFMETHOD se evala. Este mtodo se especializa en el valor de la cuenta *-de-banco-presidente * a la vez se define el mtodo, cambiando la variable ms adelante no va a cambiar el mtodo. Mtodo de Combinacin Fuera del cuerpo de un mtodo, CALL-NEXT-MTODO no tiene sentido. Dentro de un mtodo, que se le da un significado por la maquinaria de la funcin genrica que se construye un mtodo efectivo cada vez que se invoca la funcin genrica utilizando todos los mtodos aplicables a la invocacin particular. Esta nocin de la construccin de un mtodo eficaz mediante la combinacin de mtodos aplicables es el corazn del concepto de funcin genrica y es lo que permite que las funciones genricas de apoyo a las instalaciones no se encuentran en los sistemas de paso de mensajes. Por lo tanto, vale la pena echar un vistazo a lo que realmente est sucediendo. La gente con el modelo de paso de mensajes profundamente arraigado en su conciencia deben prestar especial atencin ya que las funciones genricas gire mtodo de envo de adentro hacia afuera en comparacin con el paso de mensajes, por lo que la funcin genrica, en lugar de la clase, el primer motor. Conceptualmente, el mtodo eficaz se basa en tres pasos: En primer lugar, la funcin genrica crea una lista de los mtodos aplicables en base a los argumentos reales que se aprob. En segundo lugar, la lista de los mtodos aplicables se

ordenan de acuerdo a la especificidad de la specializers sus parmetros. Por ltimo, los mtodos se toman en orden desde la lista ordenada y su cdigo se combinaron para producir el mtodo eficaz. 9 Para encontrar los mtodos aplicables, la funcin genrica se comparan los argumentos reales con los specializers parmetros correspondientes en cada uno de sus mtodos. Un mtodo es aplicable si, y slo si, todos los specializers son compatibles con los argumentos correspondientes. Cuando el specializer es el nombre de una clase, es compatible si se nombra a la clase real del argumento o una de sus superclases. (Recordemos que los parmetros sin specializers explcitas estn implcitamente especializada en la clase T que ser compatible con cualquier argumento.) Un EQL specializer es compatible nicamente cuando el argumento es el mismo objeto que se especific en el specializer. Debido a que todos los argumentos se comparan con los correspondientes specializers, todos ellos influir en que el mtodo es aplicable. Los mtodos que explcitamente se especializan ms de un parmetro se llaman multimethods , voy a hablar de ellos en la seccin "Multimethods". Despus de los mtodos aplicables se han encontrado, la maquinaria funcin genrica tiene que ordenarlas antes de que se puede combinar en un mtodo eficaz. Para pedir dos mtodos aplicables, la funcin genrica compara sus specializers parmetros de izquierda a derecha, 10 y la primera specializer que es diferente entre los dos mtodos determina su orden, con el mtodo con el specializer ms especfica en primer lugar. Debido a que slo los mtodos aplicables estn siendo ordenados, que conoce todos specializers clase ser llamar a las clases que el argumento correspondiente es en realidad una instancia de. En el caso tpico, si dos specializers de clase difieren, uno ser una subclase de la otra. En ese caso, la specializer nombrando a la subclase se considera ms especfico.Esta es la razn por el mtodo que se especializ en cuenta en la comprobacin de la cuenta se consider ms especfico que el mtodo que se especializ en lacuenta de banco . La herencia mltiple complica un poco la nocin de especificidad ya que el argumento real puede ser una instancia de dos clases, ninguno de los cuales es una subclase de la otra. Si estas clases se utilizan como specializers parmetros, la

funcin genrica no puede ordenar usando slo la regla de que las subclases son ms especficos que sus superclases. En el siguiente captulo hablaremos de cmo la nocin de especificidad se extiende para hacer frente a la herencia mltiple. Por ahora, baste decir que hay un algoritmo determinista para ordenar specializers clase. Por ltimo, un EQL specializer siempre es ms especfico que cualquier specializer clase, y porque slo los mtodos aplicables estn siendo consideradas, si ms de un mtodo tiene unEQL specializer de un parmetro en particular, todos ellos deben tener el mismo EQL specializer. La comparacin de estos mtodos por lo tanto se decide en base a otros parmetros. La combinacin Mtodo Estndar Ahora que usted entiende cmo los mtodos aplicables se encuentran y ordenados, ya est listo para echar un vistazo ms de cerca el ltimo paso - como la lista ordenada de los mtodos se combinan en un nico mtodo eficaz. Por defecto, las funciones genricas de usar lo que se llama la combinacin de mtodo estndar . La combinacin de mtodo estndar combina mtodos para que CALL-NEXTMTODO trabaja como ya has visto - el mtodo ms especfico se ejecuta primero, y cada mtodo puede pasar el control al siguiente mtodo ms especfico a travs de CALL-NEXT-MTODO . Sin embargo, hay un poco ms que eso. Los mtodos que hemos estado discutiendo hasta ahora se les llama mtodos principales . Mtodos principales, como su nombre indica, son responsables de proveer a la aplicacin principal de una funcin genrica. La combinacin de mtodo estndar tambin es compatible con tres tipos de auxiliares mtodos: : antes ydespus: y : alrededor de los mtodos. Una definicin de mtodo auxiliar est escrito con DEFMETHOD como un mtodo primario, pero con un calificador de mtodo, que nombra el tipo de mtodo, entre el nombre del mtodo y la lista de parmetros. Por ejemplo, una : antes de mtodo en retiro que se especializa la cuenta de los parmetros de la clase de cuenta de banco empezara as:
(Defmethod retirarse: antes ((cuenta bancaria de la cuenta) la cantidad) ...)

Cada tipo de mtodo auxiliar se combinan en el mtodo efectivo de una manera diferente. Todo el caso : antes de los mtodos - no slo el ms especfico - se ejecutan como parte del mtodo eficaz. Corren, como su nombre indica, antes de que el

principal mtodo ms especfico y se ejecutan en la mayor parte especfica de primer orden. Por lo tanto,: antes de los mtodos se pueden utilizar para hacer una preparacin necesaria para asegurar que el principal mtodo puede funcionar. Por ejemplo, usted podra haber usado una: antes de mtodo especializada en la comprobacin de la cuenta para implementar la proteccin contra sobregiros en cuentas corrientes de esta manera:
(Defmethod retirarse: antes ((cuenta de cheques de la cuenta) la cantidad) (Let ((sobregiros (- cantidad (saldo de la cuenta)))) (Cuando (sobregiros plusp) (Se debe retirar (sobregiro de la cuenta de la cuenta) por sobregiro) (Incf (saldo de la cuenta) por sobregiro))))

Este : antes de mtodo tiene tres ventajas sobre el mtodo principal. Una de ellas es que hace que sea inmediatamente obvio cmo el mtodo cambia el comportamiento general de la retirada de funcin - que no va a interferir en el comportamiento principal o cambiar el resultado obtenido. La siguiente ventaja es que el mtodo principal especializado en una clase ms especfica que la comprobacin de la cuenta no va a interferir con este : antes demtodo, por lo que es ms fcil para un autor de una subclase de la comprobacin de la cuenta para extender el comportamiento de la retirada , manteniendo la parte del comportamiento anterior. Por ltimo, ya que : antes de mtodo no tiene que llamar a CALL-NEXT-METODO para pasar el control al resto de los mtodos, es imposible introducir un error al olvidar. Los mtodos auxiliares otros tambin encajan en el mtodo eficaz en formas sugeridas por sus nombres. Todo el : despus de mtodos de correr tras los mtodos primarios en la mayor parte-especfica-ltima orden, es decir, el reverso de la : antes de mtodos. Por lo tanto, la : antes y : despus de los mtodos se combinan para crear una especie de envoltura anidado en torno a la funcionalidad bsica proporcionada por los principales mtodos - cada una ms especfica : antes de mtodo tiene la oportunidad de arreglar las cosas por lo que la menos especfica : antes de mtodos y mtodos primarios se pueden ejecutar con xito, y cada uno ms especfico : despus de mtodo tendr la oportunidad de limpiar despus de que todos los mtodos primarios y menos especficos : despus de los mtodos. Por ltimo, : en torno a los mtodos se combinan como los mtodos primarios, excepto que estn run "en torno a" todos los otros mtodos. Es decir, el cdigo de la

ms especfica : en torno a mtodo se ejecuta antes que cualquier otra cosa. Dentro del cuerpo de una : en torno a mtodo, CALL-NEXT-MTODO llevar al cdigo de la siguiente ms especfica : en torno a mtodo o, en lo ms mnimo especfico : alrededor de mtodo, para el conjunto de : antes , primaria y : despus de los mtodos de . Casi todos : en torno a los mtodos contendr como una llamada a CALL-NEXT-MTODO porque una : todo mtodo que no por completo apropiarse de la aplicacin de la funcin genrica de todos los mtodos excepto para ms especfica : en torno a los mtodos. De vez en cuando este tipo de secuestros que se pide, pero por lo general : en torno a los mtodos se utilizan para establecer un contexto dinmico en el que el resto de los mtodos se ejecutarn - para enlazar una variable dinmica, por ejemplo, o para establecer un controlador de errores (como yo hablaremos en el captulo 19). El nico momento en que es apropiado para una : en torno a mtodo para no llamar CALL-NEXT-MTODO es cuando se devuelve un resultado almacenado en cach de una llamada previa aCALL-NEXT-MTODO . En cualquier caso, una : todo mtodo que no requiere CALL-NEXT-MTODO es responsable de la correcta aplicacin de la semntica de la funcin genrica para toda clase de argumentos a los que el mtodo puede aplicarse en el futuro, incluidas las subclases. Mtodos auxiliares son una forma conveniente de expresar ciertos patrones comunes de manera ms concisa y concreta. Ellos en realidad no le permiten hacer lo que no poda hacer mediante la combinacin de mtodos primarios con la adhesin diligente a una serie de convenciones de codificacin y un poco de mecanografa extra. Quizs su mayor ventaja es que proporcionan un marco uniforme para extender las funciones genricas. A menudo, una biblioteca se define una funcin genrica y proporcionar un mtodo por defecto primario, permitiendo a los usuarios de la biblioteca para personalizar su comportamiento mediante la definicin de mtodos apropiados de auxiliares. Otras combinaciones de mtodos Adems de la combinacin mtodo estndar, el idioma que especifica nueve otras incorporado en combinaciones mtodo conocido como las simples combinaciones de mtodo incorporado en. Tambin se puede definir combinaciones personalizadas de mtodo, sin embargo, que es una caracterstica bastante esotrico

y ms all del mbito de este libro.Brevemente a cubrir el uso de las simples integrados en las combinaciones para darle una idea de las posibilidades. Todas las combinaciones simples de seguir el mismo patrn: en lugar de invocar el mtodo primario ms especfico y dejar que se invocan menos los mtodos especficos de primaria a travs de CALL-NEXT-FORMA , las combinaciones de mtodos simples de producir un mtodo eficaz que contiene el cdigo de todos los mtodos primarios , uno tras otro, todo envuelto en una llamada a la funcin, macro, u operador especial que le da la combinacin de mtodo de su nombre. Los nueve combinaciones llevan el nombre de los operadores: + , la Y y O , la LISTA y APPEND , los NCONC y MIN , MAX , y progn . Las combinaciones simples tambin admiten slo dos tipos de mtodos, los mtodos primarios, que se combinan como se acaba de describir, y en todo: los mtodos, que funcionan como : en torno a los mtodos de la combinacin de mtodo estndar. Por ejemplo, una funcin genrica que utiliza el + combinacin mtodo devolver la suma de todos los resultados devueltos por sus mtodos primarios. Tenga en cuenta que los Y y Ocombinaciones de mtodos no necesariamente se ejecutarn todos los mtodos primarios, debido a las macros de un cortocircuito comportamiento, una funcin genrica utilizando la Yla combinacin devolver NIL tan pronto como uno de los mtodos hace y le entregar el valor del ltimo mtodo lo contrario. De manera similar, el O combinacin devolver el primer no- NIL valor devuelto por cualquiera de los mtodos. Para definir una funcin genrica que utiliza una combinacin mtodo en particular, de incluir una : el mtodo de combinacin de opcin en el DEFGENERICformulario. El valor se suministra con esta opcin es el nombre de la combinacin de mtodo que desea utilizar. Por ejemplo, para definir una funcin genrica, la prioridad , que devuelve la suma de los valores devueltos por los mtodos individuales utilizando el + combinacin de mtodo, se podra escribir lo siguiente:
(Prioridad defgeneric (trabajo) (: Documentacin "Volver la prioridad en que se debe ejecutar el trabajo.") (: Mtodo-combinacin +))

Por defecto, todas estas combinaciones de mtodos se combinan los mtodos primarios en la mayor parte especfica de primer orden. Sin embargo, usted puede invertir el orden mediante la inclusin de la palabra clave : ms especficos ltima

despus de que el nombre de la combinacin de mtodo en el DEFGENERIC formulario. El orden probablemente, no importa si usted est utilizando el + combinacin a menos que los mtodos tienen efectos secundarios, pero para fines de demostracin se puede cambiarla prioridad para utilizar el orden ms especfica-como esta ltima:
(Prioridad defgeneric (trabajo) (: Documentacin "Volver la prioridad en que se debe ejecutar el trabajo.") (: Mtodo de combinacin +: ms especfica-ltima))

Los mtodos principales de una funcin genrica que utiliza una de estas combinaciones se deben calificar con el nombre de la combinacin de mtodo. Por lo tanto, un mtodo principal definida en la prioridad podra tener este aspecto:
(Prioridad defmethod + ((Trabajo expresa puesto de trabajo)) 10)

Esto hace evidente cuando se ve una definicin de mtodo que es parte de un tipo particular de funcin genrica. Todo el simple integrado en combinaciones de mtodos tambin apoyan : en torno a los mtodos de trabajo que les gusta : en torno a los mtodos de la combinacin de mtodo estndar: la ms especfica : en torno a mtodo se ejecuta antes que cualquier otro mtodo, y CALL-NEXT-MTODO se utiliza para pasar el control a menos -y menos especfica : en torno a los mtodos hasta llegar a los principales mtodos combinados. El : ms especficos ltima opcin no afecta el orden de : en torno alos mtodos. Y, como he mencionado antes, la incorporada en las combinaciones de los mtodos no son compatibles : antes o : despus de los mtodos. Al igual que la combinacin de mtodo estndar, estas combinaciones de mtodo no le permiten hacer lo que no poda hacer "a mano". Por el contrario, que le permiten expresar lo quequieres y deja que el lenguaje nos encargamos de todo el cableado en conjunto para usted, por lo tanto su cdigo ms conciso y expresivo. Dicho esto, probablemente el 99 por ciento del tiempo, la combinacin de mtodo estndar ser exactamente lo que quieres. De los restantes 1 por ciento, probablemente 99 por ciento de ellos ser manejado por una de las combinaciones de mtodo simples incorporado en. Si te encuentras con uno de 1 por ciento del 1 por ciento de los casos en que ninguno de los integrados es suficiente combinaciones, usted puede mirar para arriba -MTODO DE DEFINIR LA COMBINACIN- en su referencia favorita Common Lisp.

Multimethods Los mtodos que explcitamente se especializan ms de uno de los parmetros necesarios de la funcin genrica se denominan multimethods . Multimethods son los que las funciones genricas y el mensaje que pasa realmente separarse. Multimethods no se ajustan a los idiomas de paso de mensajes, porque no pertenecen a una clase particular, sino que cada multimtodo define una parte de las implementaciones de una funcin dada genrico que se aplica cuando la funcin genrica es invocado con argumentos que responden a todos los de la parmetros especializados mtodo.
Multimethods frente a sobrecarga de mtodos
Los programadores utilizan para tipos estticos de paso de mensajes lenguajes como Java y C + + puede pensar multimethods suenan de forma similar a una caracterstica de las lenguas llama sobrecarga de mtodos . Sin embargo, estas dos caractersticas del lenguaje son en realidad muy diferente, ya que los mtodos sobrecargados se eligen en tiempo de compilacin, basada en el tipo en tiempo de compilacin de los argumentos, no en tiempo de ejecucin. Para ver cmo funciona esto, considere las siguientes dos clases de Java: public class A { public void foo (A a) {System.out.println ("A / A");} public void foo (B b) {System.out.println ("A / B");} } pblica de la clase B extends A { public void foo (A a) {System.out.println ("B / A");} public void foo (B b) {System.out.println ("B / B");} } Ahora consideremos lo que sucede cuando se ejecuta el principal mtodo de esta clase. pblica principal de la clase { public static void main (String [] argv) { Un obj = argv [0]. Es igual a ("A")? new A (): new B (); obj.foo (obj); } } Cuando le dices principal para crear instancias de una A , se imprime "A / A", ya que probablemente era de esperar. bash $ java com.gigamonkeys.Main A A/A Sin embargo, si le dices principal crear una instancia de B , entonces el verdadero tipo de obj se tiene en cuenta slo la mitad de la expedicin. bash $ java com.gigamonkeys.Main B B/A Si los mtodos sobrecargados trabaj como multimethods Common Lisp, luego de que se imprima "B / B" en su lugar. Es posible llevar a cabo el envo de mltiples idiomas a mano en paso de mensajes, pero esto va en contra del modelo de paso de mensajes ya que el cdigo en un mtodo de multiplicacin envi no pertenece a ninguna clase.

Multimethods son perfectos para todas aquellas situaciones en las que, en un lenguaje de paso de mensajes, que luchan para decidir a qu clase una determinada conducta debe pertenecer. Es el sonido de un tambor hace que cuando se golpea

con una baqueta en funcin de qu tipo de tambor es o qu tipo de palo que se usa para golpear? Ambos, por supuesto. Para modelar esta situacin en Common Lisp, slo tiene que definir una funcin genrica ritmo que toma dos argumentos.
(Ritmo defgeneric (baqueta) (: Documentacin "Produce un sonido al golpear el tambor da con el palo dado".))

A continuacin, puede definir varias multimethods para implementar ritmo de las combinaciones que le interesan. Por ejemplo:
(Ritmo (Ritmo (Ritmo (Ritmo (Ritmo (Ritmo defmethod defmethod defmethod defmethod defmethod defmethod ((tambor-tambor) (palo de madera-palillo)) ...) ((tambor-tambor) (cepillo de palo)) ...) ((tambor-tambor) (palo suave martillo)) ...) ((tambor tom-tom) (palo de madera-palillo)) ...) ((tambor tom-tom) (cepillo de palo)) ...) ((tambor tom-tom) (palo suave martillo)) ...)

Multimethods no ayudan con la explosin combinatoria - si usted necesita para modelar cinco tipos de tambores y seis tipos de palos, y cada combinacin produce un sonido diferente, no hay manera de evitarlo, que hay treinta diferentes mtodos para poner en prctica todas las combinaciones , con o sin multimethods. Lo multimethods no te salvar de tener que escribir es un montn de cdigo despacho que permite utilizar el mismo incorporado en el envo de polimorfismo que es tan til cuando se trata con mtodos especializados en un solo parmetro. 11 Multimethods tambin le ahorrar el tener que bien par un conjunto de clases con el otro. En el ejemplo de tambor / palo, no requiere de la implementacin de las clases de batera para saber acerca de las diversas clases de palillo, y nada obliga a las clases de palillo de tambor saber nada acerca de las diversas clases de tambor. Los multimethods conectar las clases de otra manera independientes para describir su comportamiento conjunto sin necesidad de la cooperacin de las clases mismas. To Be Continued. . . He cubierto lo bsico, y un poco ms all-de las funciones genricas, los verbos de sistema de objetos de Common Lisp. En el siguiente captulo te mostrar cmo definir sus propias clases.

1 El lenguaje ahora generalmente considerado como el primer lenguaje orientado a objetos, Simula,

fue inventado en la dcada de 1960, slo unos pocos aos despus de la primera Lisp de McCarthy. Sin embargo, la orientacin a objetos realmente no despeg hasta la dcada de 1980 cuando la primera versin ampliamente disponible de Smalltalk fue liberado, seguido por la liberacin de C + +

unos aos ms tarde. Smalltalk tom un poco de inspiracin de Lisp y la combin con las ideas de Simula, para producir una dinmica lenguaje orientado a objetos, mientras que C + + junto Simula con C, otro lenguaje bastante esttico, para dar una esttica lenguaje orientado a objetos. Esta separacin temprana ha llevado a mucha confusin en la definicin de la orientacin a objetos. La gente que viene de la C + + la tradicin tienden a considerar ciertos aspectos de C + +, tales como la encapsulacin de datos estrictos, para ser caractersticas fundamentales de la orientacin a objetos. La gente de la tradicin Smalltalk, sin embargo, tenga en cuenta muchas caractersticas de C + + para ser slo eso, las caractersticas de C + +, y no al ncleo de la orientacin a objetos. De hecho, Alan Kay, el inventor de Smalltalk, se dice que dijo, "me invent el trmino orientado a objetos , y te puedo decir que C + + no era lo que tena en mente. "
2 Hay quienes rechazan la idea de que Common Lisp es orientado a objetos hecho en absoluto. En

particular, la gente que consideran que la encapsulacin de datos estrictos una caracterstica clave de la orientacin a objetos-por lo general los defensores de lenguas relativamente estticos, tales como C + +, Eiffel, o Java - No lo consideramos Common Lisp estar correctamente orientada a objetos. Por supuesto, por esa definicin, Smalltalk, sin duda uno de los originales ms puro y lenguajes orientados a objetos, no es orientado a objetos bien. Por otro lado, la gente que consideran que el paso de mensajes a ser la clave para la orientacin a objetos tambin no estar contento con la afirmacin de que Common Lisp es orientado a objetos desde la orientacin funcin genrica de Common Lisp proporciona grados de libertad que no ofrece el paso de mensajes pura.
3 basados en prototipos idiomas son el otro estilo de lenguaje orientado a objetos. En estas lenguas,

JavaScript siendo quizs el ms famoso ejemplo, los objetos son creados por clonacin de un objeto prototipo. El clon a continuacin, pueden ser modificado y utilizado como un prototipo para otros objetos.
4 T el valor constante y T la clase no tienen una relacin particular, excepto que pasara a tener el

mismo nombre. T el valor es una instancia directa de la clase SMBOLO y slo de manera indirecta una instancia de Tde la clase.
5 Aqu, como en otros lugares, objetos , cualquier dato de Lisp - Common Lisp no distingue, como

algunas lenguas hacen, entre los objetos y "primitivas" los tipos de datos, todos los datos de Common Lisp son objetos, y cada objeto es una instancia de un clase.
6 Tcnicamente se puede omitir la DEFGENERIC por completo - si se define un mtodo con

DEFMETHOD y ninguna funcin genrica como se ha definido, uno se crea automticamente. Pero es una buena forma de definir las funciones genricas de forma explcita, aunque slo sea porque le da un buen lugar para documentar el comportamiento previsto.
7 Un mtodo puede "aceptar" y clave y el resto y argumentos definidos en su funcin genrica por

tener una y resto de parmetros, por tener las mismas claves y parmetros, o mediante la especificaciny permiten a los otros-keys , junto con tecla & . Un mtodo tambin puede especificar y los principales parmetros que no se encuentra en la lista de parmetros de la funcin genrica - cuando la funcin genrica se llama, cualquier tecla & parmetro especificado por la funcin genrica o cualquier otro mtodo de aplicacin, sern aceptados.

8 CALL-NEXT-MTODO es ms o menos anloga a la invocacin de un mtodo de sper en Java o

usar un mtodo explcito de clase completo o nombre de la funcin en Python o C + +.


9 Si bien la construccin del mtodo eficaz suena mucho tiempo, un poco de esfuerzo en el desarrollo

de rpidas implementaciones de Lisp comunes ha entrado en lo que es eficiente. Una estrategia consiste en almacenar en cach el mtodo efectivo para futuras convocatorias con los mismos tipos de argumentos podr proceder directamente.
10 En realidad, el orden en que se comparan specializers es personalizable a travs de la

DEFGENERIC opcin : el argumento de precedencia de orden , aunque esta opcin se utiliza muy poco.
11 En los lenguajes sin multimethods, debe escribir el envo de cdigo por ti mismo para

implementar un comportamiento que depende de la clase de ms de un objeto. El propsito del patrn Visitor diseo popular es estructurar una serie de mtodo solos despach llama as como para proporcionar expedicin mltiple. Sin embargo, requiere un conjunto de clases para saber sobre el otro. El patrn de los visitantes tambin rpidamente se atasca en una explosin combinatoria de la expedicin de los mtodos, si se utiliza para el envo de ms de dos objetos.

17. Reorientacin del objeto: Clases


Si las funciones genricas son los verbos del sistema de objetos, las clases son los sustantivos. Como mencion en el captulo anterior, todos los valores en un programa comn de Lisp son instancias de alguna clase. Adems, todas las clases estn organizadas en una jerarqua nica raz en la clase T . La jerarqua de clases se compone de dos grandes familias de clases, incorporada en-y las clases definidas por el usuario. Las clases que representan los tipos de datos que usted ha estado aprendiendo acerca de hasta ahora, las clases, como INTEGER , STRING , y LISTA , son todos los incorporados. Ellos viven en su propia seccin de la jerarqua de clases, dispuestos en adecuadas relaciones sub-y de la superclase, y son manipulados por las funciones que he estado discutiendo durante gran parte del libro hasta ahora. No se puede subclase estas clases, pero, como se vio en el captulo anterior, puede definir los mtodos que se especializan en ellos, efectivamente extender el comportamiento de las clases. 1 Pero cuando se desea crear nombres nuevos - por ejemplo, las clases que se utilizan en el captulo anterior para la representacin de las cuentas bancarias - que necesita para definir sus propias clases. Ese es el tema de este captulo. DEFCLASS Para crear las clases definidas por el usuario con el DEFCLASS macro. Dado que los comportamientos estn asociados con una clase mediante la definicin de las funciones genricas y mtodos especializados en la clase, DEFCLASS slo es responsable de la definicin de la clase como un tipo de datos. Las tres facetas de la clase como un tipo de datos son su nombre, su relacin con otras clases, y los nombres de los espacios que conforman las instancias de la clase.
2

La forma bsica de un DEFCLASS es bastante simple.


(Defclass nombre ( directa-superclase-name *) ( slot-especificador *))

Qu son las clases definidas por el usuario "?


El trmino definido por el usuario clases no es un trmino del lenguaje estndar tcnicamente lo que estoy hablando cuando digo que las clases definidas por el usuario son clases que subclase NORMAS OBJETO y cuya metaclase es NORMA DE CLASE . Pero como yo no voy a hablar acerca de las maneras que se pueden definir las clases que no subclase NORMAS OBJETO y cuya metaclase no es NORMA DE CLASE , usted

realmente no tiene que preocuparse por eso. definido por el usuario ISN ' t un trmino perfecto para estas clases desde la aplicacin puede definir ciertas clases de la misma manera. Sin embargo, para llamar a los estndares clases sera an ms confuso ya que el incorporado en las clases, tales como INTEGER y STRING , son tan estndar, si no ms, porque estn definidos por el estndar del lenguaje, pero no se extienden NORMAOBJETO . Para complicar ms las cosas, tambin es posible para los usuarios para definir nuevas clases que no lo hacen subclase STANDARD-OBJETO . En particular, la macro defstruct tambin define nuevas clases. Pero eso es en gran parte por compatibilidad con versiones anteriores - defstruct CLOS fue anterior y se modific para definir las clases, cuando CLOS se integr en el lenguaje. Pero las clases que crea son bastante limitadas en comparacin con DEFCLASS clases de educacin. As que en este captulo hablaremos sobre las clases definidas con slo DEFCLASS que utilizan la metaclase por defecto deNORMA DE CLASE , y me referir a ellos como definido por el usuario debido a la falta de un trmino mejor.

Al igual que con las funciones y variables, se puede utilizar cualquier smbolo como el nombre de una nueva clase. 3 Los nombres de clase se encuentran en un espacio de nombres separado de ambas funciones y variables, por lo que puede tener una clase, la funcin y la variable de todos con el mismo nombre. Vamos a usar el nombre de clase como el argumento de MAKE-INSTANCIA , la funcin que crea nuevas instancias de clases definidas por el usuario. Los superclase directa de los nombres de especificar las clases de que la nueva clase es una subclase. Si no hay superclases estn en la lista, la nueva clase, de manera directa subclaseSTANDARD-OBJETO . Todas las clases enumeradas deben ser otras clases definidas por el usuario, lo que garantiza que cada nueva clase est en ltima instancia, descendiente deNORMA-OBJETO . NORMAS OBJETO su vez en subclases T , por lo que todas las clases definidas por el usuario son parte de la jerarqua de clase nica, que tambin contiene toda la las clases incorporadas. Elidir los especificadores de la ranura por un momento, los DEFCLASS las formas de algunas de las clases que se utilizan en el captulo anterior podra tener este aspecto:
(Defclass una cuenta bancaria () ...) (Defclass comprobacin de la cuenta (cuenta bancaria) ...) (Defclass cuenta de ahorros (cuenta bancaria) ...)

Voy a discutir en la seccin de "herencia mltiple" lo que significa una lista de ms de una superclase directa en directo los nombres de superclase .

Ranura para especificadores La mayor parte de un DEFCLASS formulario consta de la lista de especificadores de ranura. Cada especificador de ranura define una ranura que formarn parte de cada instancia de la clase. Cada ranura en una instancia es un lugar que puede contener un valor, que se puede acceder utilizando el SLOT-VALOR funcin. SLOTVALOR toma un objeto y el nombre de una ranura como argumentos y devuelve el valor de la ranura en el llamado dado objeto. Se puede utilizar con SETF para establecer el valor de una ranura en un objeto. Una clase tambin hereda los especificadores de tragamonedas de sus superclases, por lo que el conjunto de espacios realmente presentes en cualquier objeto es la enlace de todos los espacios especificados en una clase de DEFCLASS la forma y las exigidas en todas sus superclases. Como mnimo, una ranura especificador de nombre a la ranura, en cuyo caso el especificador de la ranura puede ser slo un nombre. Por ejemplo, puede definir unacuenta de banco clase con dos franjas horarias, nombre-cliente y el equilibrio , as:
(Defclass una cuenta bancaria () (Nombre-cliente equilibrio))

Cada instancia de esta clase contendr dos ranuras, una para contener el nombre del cliente que la cuenta pertenece y otro para mantener el equilibrio actual. Con esta definicin, se pueden crear nuevas cuentas bancarias- objetos utilizando MARCA-INSTANCIA .
(Make-instance 'a una cuenta bancaria) ==> # @ <BANK-ACCOUNT #x724b93ba>

El argumento de MAKE-INSTANCIA es el nombre de la clase para crear una instancia, y el valor devuelto es el nuevo objeto. 4 La representacin impresa de un objeto est determinado por la funcin genrica PRINT-OBJETO . En este caso, el mtodo aplicable ser proporcionado por la aplicacin, especializada en la NORMAOBJETO . Dado que no todos los objetos que se pueden imprimir de manera que se puede leer de nuevo, la NORMA-OBJETO mtodo de impresin utiliza el # <> sintaxis, lo que har que el lector para sealar un error si trata de leer. El resto de la representacin es definido por la implantacin, pero normalmente ser algo as como la salida se acaba de mostrar, incluyendo el nombre de la clase y un valor distintivo, como la direccin del objeto en la memoria. En el captulo 23 ver un

ejemplo de cmo definir un mtodo en PRINT-OBJETO para hacer objetos de cierta clase se va a imprimir en un formato ms informativo. Utilizando la definicin de una cuenta bancaria que acabamos de dar, los objetos se crearn con sus ranuras Sin consolidar . Cualquier intento de obtener el valor de una ranura sin unir indica un error, por lo que debe establecer una ranura para poder leerlo.
(* Defparameter cuenta * (make-instance 'a una cuenta bancaria)) ==> * CUENTA * (Setf (slot-valor * Cuenta * 'nombre-cliente) "John Doe") ==> "John Doe" (Setf (slot-valor * cuenta * "equilibrio) 1000) ==> 1000

Ahora usted puede acceder al valor de las ranuras.


(Slot-valor * Cuenta * 'nombre-cliente) ==> "John Doe" (Slot-valor * cuenta * "equilibrio) ==> 1000

Objeto de inicializacin Puesto que usted no puede hacer mucho con un objeto con ranuras no consolidados, que sera agradable ser capaz de crear objetos con sus ranuras que ya est iniciado. Common Lisp proporciona tres formas de controlar el valor inicial de las franjas horarias. Los dos primeros implican la adicin de opciones para el especificador de ranura en el DEFCLASS forma: con la : initarg opcin, puede especificar un nombre que puede ser utilizado como un parmetro de palabra clave para MAKE-INSTANCIA , y cuyo argumento se guarda en la ranura. Una segunda opcin, : initForm , le permite especificar una expresin Lisp que se utiliza para calcular el valor de la ranura si no : initarg argumento se pasa aMAKEINSTANCIA . Por ltimo, para un control completo sobre la inicializacin, se puede definir un mtodo de la funcin genrica -INITIALIZE INSTANCIA , el cual es llamado por MARCA-INSTANCIA . 5 Un especificador de ranura que incluye opciones como : initarg o : initForm est escrito como una lista inicial con el nombre de la ranura seguido por las opciones. Por ejemplo, si desea modificar la definicin de una cuenta bancaria para que los llamadores de MAKE-INSTANCIA para pasar el nombre del cliente y del saldo inicial y para proporcionar un valor predeterminado de cero dlares para el equilibrio, que iba a escribir esto:
(Defclass una cuenta bancaria () ((Nombre-cliente : Initarg: nombre-cliente) (Equilibrio : Initarg: el equilibrio

: InitForm 0)))

Ahora usted puede crear una cuenta y especificar los valores de ranura, al mismo tiempo.
(* Defparameter cuenta * (Make-instance 'a una cuenta bancaria: nombre-cliente "John Doe": balance de 1000)) (Slot-valor * Cuenta * 'nombre-cliente) ==> "John Doe" (Slot-valor * cuenta * "equilibrio) ==> 1000

Si no se proporciona un : balance de argumento para MAKE-INSTANCIA , el SLOTVALOR de equilibrio se calcula mediante la evaluacin de la forma especificada con el : initForm opcin. Pero si usted no proporciona un : nombre-cliente argumento, el nombre-cliente franja va a quedar sin consolidar, y un intento de leerlo antes de establecer que ser una seal de error.
(Slot-valor (make-instance 'a una cuenta bancaria)' equilibrio) ==> 0 (Slot-valor (de creacin de instancia "a una cuenta bancaria) 'nombre-cliente) ==> Error

Si desea asegurarse de que el nombre del cliente se suministra cuando se crea la cuenta, puede indicar un error en la initForm ya que slo se evaluar si un initarg no se suministra.Tambin puede utilizar initforms que generan un valor diferente cada vez que estn evaluados - el initForm se evala de nuevo para cada objeto. Para experimentar con estas tcnicas, usted puede modificar el nombre-cliente especificador de ranura y aadir una nueva ranura, nmero-cuenta , que se inicializa con el valor de un contador cada vez mayor.
(Defvar * Cuenta los nmeros * 0) (Defclass una cuenta bancaria () ((Nombre-cliente : Initarg: nombre-cliente : InitForm (error ". Debe introducir un nombre de cliente")) (Equilibrio : Initarg: el equilibrio : InitForm 0) (Nmero-cuenta : InitForm (* incf cuenta los nmeros *))))

La mayora del tiempo la combinacin de : initarg y : initForm opciones ser suficiente para inicializar correctamente un objeto. Sin embargo, mientras que un initForm puede ser cualquier expresin Lisp, no tiene acceso al objeto que est siendo inicializado, lo que no puede inicializar una ranura basado en el valor de otro. Para ello es necesario definir un mtodo de la funcin genrica -INITIALIZE INSTANCIA .

El mtodo principal de INITIALIZE INSTANCIA- especializada en STANDARDOBJETO se encarga de las ranuras de inicializacin en funcin de su : initarg y: initForm opciones. Puesto que usted no quiere molestar a eso, la forma ms comn de agregar cdigo de inicializacin personalizados es definir una : despus . mtodo especializado en la clase 6 Por ejemplo, supongamos que desea agregar una ranura de tipo de cuenta de que es necesario establecer a uno de los valores : oro , la plata:o : bronce sobre la base de saldo inicial de la cuenta. Usted puede cambiar su definicin de clase para esto, aadir la cuenta del tipo de ranura sin opciones:
(Defclass una cuenta bancaria () ((Nombre-cliente : Initarg: nombre-cliente : InitForm (error ". Debe introducir un nombre de cliente")) (Equilibrio : Initarg: el equilibrio : InitForm 0) (Nmero-cuenta : InitForm (incf * Cuenta los nmeros *)) cuenta de-tipo))

A continuacin, puede definir una : despus de mtodo en INITIALIZE INSTANCIAque establece la cuenta del tipo de ranura en funcin del valor que se ha almacenado en el balance de la ranura. 7
(Defmethod de inicializacin de instancia: despus de ((cuenta bancaria de la cuenta) y la llave) (Let ((el equilibrio (balance de ranura de valor de la cuenta '))) (Setf (slot-valor de la cuenta "cuenta tipo) (Cond ((> = Saldo 100000): el oro) ((> = Saldo 50000): la plata) (T: bronce)))))

La clave y la lista de parmetros se requiere para mantener la lista el mtodo de parmetros congruentes con el function's genrico - la lista de parmetros especificados para elINITIALIZE-INSTANCIA funcin genrica incluye y la llave para permitir que los mtodos individuales para abastecer a sus parmetros de palabras clave propias, pero no exigir ningn tipo de seres particulares. Por lo tanto, cada mtodo debe especificar y la llave , incluso si no se especifica ninguna clave y parmetros. Por otro lado, si un INITIALIZE-INSTANCIA mtodo especializado en una clase en particular no especifica una clave y los parmetros, ese parmetro se convierte en un parmetro legal para MAKE-INSTANCIA al crear una instancia de esa clase. Por ejemplo, si el banco a veces paga un porcentaje del saldo inicial como un bono,

cuando se abre una cuenta, usted podra poner en prctica que el uso de un mtodo en INITIALIZE INSTANCIA- que toma un argumento de palabra clave para especificar el porcentaje de la prima de esta manera:
(Defmethod de inicializacin de instancia: despus de ((cuenta bancaria de la cuenta) Y llave de apertura de bonificacin en porcentaje) (Cuando la apertura de bonificacin porcentual (Balance de incf (slot-valor de la cuenta ') (* (Slot-valor de la cuenta "equilibrio) (/ apertura-bono-porcentaje de 100)))))

Al definir este INITIALIZE-INSTANCIA mtodo, se hace : la apertura de bonificacin porcentual de un argumento legal para MAKE-INSTANCIA al crear una cuenta de banco de objetos.
CL-> USER (defparameter * Cuenta * (make-instancia 'A una cuenta bancaria : Nombre-cliente "Sally Sue" : Balance de 1000 : Apertura de bonificacin porcentual 5)) * CUENTA * CL-> USER (slot-valor * Cuenta * "equilibrio) 1050

Funciones de acceso Entre MAQUILLAJE INSTANCIA y SLOT DE VALOR , que tiene todas las herramientas que necesita para crear y manipular las instancias de sus clases. Todo lo dems es posible que desee hacerlo puede ser implementado en trminos de esas dos funciones. Sin embargo, como cualquiera que est familiarizado con los principios de buenas prcticas de programacin orientados a objetos se sabe, acceder directamente a las ranuras (o campos o variables miembro) de un objeto puede llevar al cdigo frgil. El problema es que el acceso directo a las ranuras ata su cdigo con demasiada fuerza a la estructura de hormign de la clase. Por ejemplo, supongamos que usted decide cambiar la definicin deuna cuenta bancaria para que, en lugar de almacenar el equilibrio actual como un nmero, se almacena una lista de las marcas temporales retiros y depsitos. El cdigo que tiene acceso directo al balance de la ranura probable que se rompa si cambia la definicin de clase para eliminar la ranura o para guardar la nueva lista en la ranura de edad. Por otro lado, si se define una funcin, el equilibrio , que accede a la ranura, se puede definir ms adelante para preservar su comportamiento, aunque los cambios internos de representacin. Y el cdigo que utiliza esta funcin continuar trabajando sin ninguna modificacin.

Otra ventaja de utilizar funciones de acceso en vez de acceso directo a las franjas horarias a travs de SLOT-VALOR es que permiten limitar las formas fuera de cdigo se puede modificar una ranura. 8 Se puede estar bien para los usuarios de la cuenta bancaria de clase para obtener el saldo actual, pero usted puede querer todas las modificaciones en el equilibrio para ir a travs de otras funciones que se le proporcionan, tales como depsito y retiro . Si los clientes saben que se supone manipular objetos slo a travs de la API publicada funcional, puede proporcionar un balance de la funcin, pero no hacen setf poder, si desea que el saldo a ser de slo lectura. Por ltimo, con funciones de acceso hace que el cdigo de ms ordenado, ya que le ayuda a evitar un montn de usos del lugar detallado SLOT-VALOR funcin. Es trivial para definir una funcin que lee el valor del saldo ranura.
(Balance de defun (cuenta) (Balance de ranura de valor de la cuenta '))

Sin embargo, si usted sabe que va a definir subclases de una cuenta bancaria , puede ser una buena idea para definir el equilibrio como una funcin genrica. De esta manera, usted puede proporcionar mtodos diferentes en el equilibrio de las subclases o ampliar su definicin de los mtodos auxiliares. As que usted puede escribir esto en su lugar:
(Balance de defgeneric (cuenta)) (Defmethod equilibrio ((cuenta bancaria de la cuenta)) (Balance de ranura de valor de la cuenta '))

Como acabo de hablar, no quiere personas que llaman para poder establecer directamente el equilibrio, pero para otros espacios, tales como nombre-cliente , tambin lo desea, puede proporcionar una funcin para ajustarlos. La manera ms limpia para definir una funcin es como un SETF funcin. Un SETF funcin es una manera de extender SETF , la definicin de un nuevo tipo de lugar que sabe cmo va a establecer. El nombre de un SETF funcin es una lista de dos elemento cuyo primer elemento es el smbolo de setf y cuyo segundo elemento es un smbolo, por lo general el nombre de una funcin que se utiliza para acceder al lugar de laSETF funcin establecer. Un SETF funcin puede tomar cualquier nmero de argumentos, pero el primer argumento es siempre el valor que se asignar al lugar. 9 Usted podra, por ejemplo, definir un SETF funcin para establecer el nombre-cliente ranura en una cuenta de banco como ste :

(Defun (setf nombre-cliente) (nombre de cuenta) (Setf (slot-valor de la cuenta "nombre-cliente) nombre))

Despus de evaluar esta definicin, una expresin como la siguiente:


(Setf (cliente-el nombre de mi cuenta) "Sally Sue")

se compila como una llamada a la SETF la funcin que acaba de definir, con "Sally Sue" como primer argumento y el valor de mi cuenta como el segundo argumento. Por supuesto, como con las funciones de lector, usted probablemente querr a su SETF funcin a ser genrico, por lo que en realidad haba que definir de esta manera:
(Defgeneric (setf nombre-cliente) (cuenta de valor)) (Defmethod (setf nombre-cliente) (valor (cuenta bancaria de la cuenta)) (Setf (slot-valor de la cuenta "nombre-cliente) Valor))

Y, por supuesto, usted tambin desea definir una funcin de lector de nombrecliente .
(Defgeneric nombre-cliente (cuenta)) (Defmethod nombre-cliente ((cuenta bancaria de la cuenta)) (Slot-valor de la cuenta "nombre-cliente))

Esto le permite escribir lo siguiente:


(Setf (nombre-cliente * Cuenta *) "Sally Sue") ==> "Sally Sue" (Nombre-cliente * Cuenta *) ==> "Sally Sue"

No hay nada difcil de escribir estas funciones de acceso, pero no estara de acuerdo con El Camino de Lisp a tener que escribir todo a mano. Por lo tanto, DEFCLASS admite tres opciones de tragamonedas que le permiten crear de forma automtica las funciones de lector y escritor de una ranura especfica. El : lector de opcin especifica un nombre para ser utilizado como el nombre de una funcin genrica que acepta un objeto como su nico argumento. Cuando el DEFCLASSse evala la funcin genrica se crea, si no existe ya. Entonces un mtodo especializado su nico argumento en la nueva clase y devolver el valor de la ranura se agrega a la funcin genrica. El nombre puede ser cualquier cosa, pero es tpico de lo que sea lo mismo que la propia ranura. Por lo tanto, en vez de forma explcita por escrito del balance de la funcin genrica y el mtodo como se indica anteriormente, puede cambiar el especificador de la ranura para el balance de la ranura en la definicin deuna cuenta bancaria a la siguiente:

(Equilibrio : Initarg: el equilibrio : 0 initForm : Lector de equilibrio)

El : escritor opcin se utiliza para crear una funcin genrica y un mtodo para establecer el valor de una ranura. La funcin y el mtodo creado cumplan con los requisitos para un SETF funcin, tomando el valor nuevo como el primer argumento y devolverlo como resultado, por lo que se puede definir un SETF funcin proporcionando un nombre como(setf nombre-cliente) . Por ejemplo, usted podra proporcionar mtodos de lector y escritor de nombre-cliente equivalentes a los que acaba de escribir, cambiando el especificador de la ranura a lo siguiente:
(Nombre-cliente : Initarg: nombre-cliente : InitForm (error "Debe introducir un nombre de cliente.") : Lector de nombre-cliente : Escritor (setf nombre-cliente))

Puesto que es bastante comn a querer el lector y el escritor funciones, DEFCLASS tambin proporciona una opcin, : de acceso , que crea a la vez una funcin de lector y el correspondiente SETF funcin. As que en lugar de la especificacin ranura acaba de mostrar, por lo general iba a escribir esto:
(Nombre-cliente : Initarg: nombre-cliente : InitForm (error "Debe introducir un nombre de cliente.") : Acceso nombre-cliente)

Por ltimo, una opcin de ltima ranura que debe saber sobre el : documentacin de opcin, que puede utilizar para proporcionar una cadena que documenta los efectos de la ranura. Poniendo todo junto y aadir un mtodo de lectura de los nmeros de cuenta y tipo de cuenta- ranuras, el DEFCLASS formulario para lacuenta bancaria de clase se vera as:
(Defclass una cuenta bancaria () ((Nombre-cliente : Initarg: nombre-cliente : InitForm (error "Debe introducir un nombre de cliente.") : Acceso nombre-cliente : "El nombre del Cliente" documentacin) (Equilibrio : Initarg: el equilibrio : 0 initForm : Lector de equilibrio : Documentacin "Balanza por cuenta corriente") (Nmero-cuenta : InitForm (incf * Cuenta los nmeros *) : Lector de nmero-cuenta : Documentacin "Nmero de cuenta, nica dentro de un banco.")

(Cuenta del tipo de : Lector de cuenta del tipo de : Documentacin "Tipo de cuenta, una de: oro,: de plata, o bien:. De bronce")))

CON CON-Slots y Accessors de Durante el uso de funciones de acceso har que su cdigo sea ms fcil de mantener, an pueden ser un poco ms detallado. Y habr veces, al escribir los mtodos que implementan los comportamientos de bajo nivel de una clase, que especficamente lo desea, puede acceder a las ranuras directa para establecer una ranura que no tiene ninguna funcin escritor o para llegar a la ranura de valor sin que se produjeran los mtodos auxiliares definida sobre la funcin de lector para funcionar. Esto es lo que SLOT-VALOR es, sin embargo, todava es bastante detallado. Para empeorar las cosas, una funcin o mtodo que tenga acceso a la ranura de la misma en varias ocasiones se pueden obstruir con llamadas a funciones de acceso y SLOT DE VALOR . Por ejemplo, incluso un mtodo bastante simple como la siguiente, que evala una sancin a una cuenta de banco si su saldo es inferior a un cierto mnimo, est lleno de llamadas al equilibrio y la RANURA DE VALOR :
(Defmethod eva-bajo pena de la balanza ((cuenta bancaria de la cuenta)) (Cuando (<de cuenta (saldo) * saldo mnimo *) (Balance de DECF (slot-valor de la cuenta ') (* (saldo de la cuenta) 0.01))))

Y si usted decide que quiere acceder directamente al valor de la ranura con el fin de evitar la utilizacin de mtodos auxiliares, se vuelve an ms desordenada.
(Defmethod eva-bajo pena de la balanza ((cuenta bancaria de la cuenta)) (Cuando (<equilibrio (con ranura de valor de la cuenta ') * saldo mnimo *) (DECF (slot-valor de la cuenta "equilibrio) (* (slot-valor de la cuenta" equilibrio) .01))))

Dos macros estndar, CON-Slots y los CON Accessors , puede ayudar a poner en orden este desorden. Ambos macros crear un bloque de cdigo en el que simples nombres de las variables puede ser usado para referirse a las franjas horarias en un objeto en particular. CON-Slots proporciona acceso directo a las franjas horarias, como por SLOT-VALOR, mientras que los descriptores de acceso CON proporciona una forma rpida para acceso mtodos. La forma bsica de CON-Slots es como sigue:
(Con ranuras ( slot *) ejemplo-la forma del cuerpo de forma *)

Cada elemento de ranuras puede ser o bien el nombre de una ranura, que tambin se utiliza como un nombre de variable, o una lista de dos tem donde el primer elemento es un nombre que se utilizar como una variable y el segundo es el nombre de la ranura. La instancia forma se evala una vez para producir el objeto cuyas ranuras se tendr acceso. Dentro del cuerpo, cada aparicin de uno de los nombres de las variables se traduce en una llamada a SLOT-VALOR con el objeto y el nombre de la ranura apropiada como argumentos. 10Por lo tanto, usted puede escribir evaluacin bajo pena de la balanza de esta manera:
(Defmethod eva-bajo pena de la balanza ((cuenta bancaria de la cuenta)) (Con ranuras (balance) de cuenta (Cuando (<* El balance de saldo mnimo *) (Balance de DECF (* balance de 0,01)))))

o, utilizando el formulario de lista de dos punto, de esta manera:


(Defmethod eva-bajo pena de la balanza ((cuenta bancaria de la cuenta)) (Con ranuras ((balance de equilibrio)) cuenta (Cuando (<* bal de saldo mnimo *) (DECF bal (* bal 0.01)))))

Si usted ha definido el equilibrio con un : de acceso en lugar de un : Lector de , entonces usted podra tambin utilizarlos descriptores de acceso CON . La forma de los descriptores de acceso CON es el mismo que CON-Slots , excepto que cada elemento de la lista de la ranura es una lista de dos elemento que contenga un nombre de variable y el nombre de una funcin de acceso. Dentro del cuerpo decon participacin en los descriptores de acceso , una referencia a una de las variables es equivalente a una llamada a la funcin de descriptor de acceso correspondiente. Si la funcin de acceso es SETF poder, entonces tambin lo es la variable.
(Defmethod eva-bajo pena de la balanza ((cuenta bancaria de la cuenta)) (Con los de acceso ((Saldo)) cuenta (Cuando (<* El balance de saldo mnimo *) (Balance de DECF (* balance de 0,01)))))

La primera equilibrio es el nombre de la variable, y el segundo es el nombre de la funcin de descriptor de acceso; ellos no tienen que ser el mismo. Usted podra, por ejemplo, escribir un mtodo para combinar dos cuentas con dos llamadas a los descriptores de acceso CON , uno para cada cuenta.
(Defmethod combinacin de cuentas ((cuenta1 una cuenta bancaria) (cuenta2 cuenta de banco)) (Con los de acceso ((balance1 equilibrio)) cuenta1 (Con los de acceso ((balance2 equilibrio)) cuenta2 (Incf balance1 balance2) (Setf balance2 0))))

La eleccin de si se debe utilizar CON-Slots en comparacin con los descriptores de acceso CON es la misma que la eleccin entre SLOT-VALOR y una funcin de acceso: cdigo de bajo nivel que proporciona la funcionalidad bsica de una clase puede utilizar SLOT-VALOR o con ranuras de la manipular directamente las ranuras de una manera no compatibles con funciones de acceso o para evitar de forma explcita los efectos de los mtodos auxiliares que pueden haber sido definidos en las funciones de acceso. Pero en general usted debe utilizar las funciones de acceso o de los CON Accessors a menos que tenga una razn especfica para no hacerlo. Asignados por la clase-Slots La opcin de ranura ltimo que usted necesita saber es : la asignacin . El valor de : la asignacin puede ser : ejemplo, o : la clase y los valores de: ejemplo, si no se especifica. Cuando una ranura tiene : clase de la asignacin, la ranura tiene slo un valor nico, que se almacena en la clase y compartida por todas las instancias. Sin embargo, : la clase ranuras se accede a la misma : ejemplo, las ranuras - they're acceder con SLOT-VALOR o una funcin de acceso, lo que significa que usted puede acceder al valor de la ranura slo a travs de una instancia de la clase a pesar de que en realidad no es almacenan en la instancia. El : initForm y : initarg opciones tienen esencialmente el mismo efecto, excepto el initForm se evala una vez cuando la clase se define en lugar de cada vez que se crea una instancia. Por otro lado, el paso de una initarg deMAKE-INSTANCIA fijar el valor, que afecta a todas las instancias de la clase. Porque no se puede conseguir en una ranura de clase asignado sin una instancia de la clase, de clase asignados por franjas horarias que no son realmente equivalentes a estticas o declase de los campos en lenguajes como Java, C + + y Python. 11 Por el contrario, la clase asignada ranuras se utilizan sobre todo para ahorrar espacio, y si usted va a crear varias instancias de una clase y todas las instancias van a tener una referencia al mismo objeto - por ejemplo, un conjunto de recursos compartidos usted puede ahorrar el costo de cada instancia tenga su propia referencia, haciendo que el slot clase-asignado.

Las ranuras y Herencia Como he comentado en el captulo anterior, las clases heredan el comportamiento de sus superclases, gracias a la maquinaria de la funcin genrica - un mtodo especializado en la clase A es aplicable no slo a los casos directos de una , sino tambin a los casos de una subclase de los s. Las clases tambin se heredan las ranuras de sus superclases, pero el mecanismo es ligeramente diferente. En Common Lisp un objeto dado, slo puede tener una ranura con un nombre en particular. Sin embargo, es posible que ms de una clase en la jerarqua de la herencia de una clase dada se especifica una ranura con un nombre en particular. Esto puede ocurrir ya sea porque una subclase incluye un especificador de ranura con el mismo nombre que se especifica en una ranura de una superclase o porque superclases mltiples ranuras especifica con el mismo nombre. Common Lisp resuelve estas situaciones mediante la fusin de todos los prescriptores con el mismo nombre de la nueva clase y sus superclases para crear una especificacin nica para cada nombre de ranura nica. Al combinar los prescriptores, diferentes opciones de ranura son tratados de manera diferente. Por ejemplo, desde una ranura slo puede tener un valor por defecto nico, en caso de mltiples clases especificar una : initForm , la nueva clase utiliza el de la clase ms especfica. Esto permite a una subclase de especificar un valor predeterminado diferente a la que de lo contrario iba a heredar. Por otro lado, : initarg s no necesita ser exclusivo - cada uno : initarg opcin en una ranura especificador crea un parmetro de palabra clave que se puede utilizar para inicializar la ranura; mltiples parmetros no crear un conflicto, por lo que la nueva ranura especificador contiene toda la : initarg s. Las personas que llaman deMAKE-INSTANCIA puede utilizar cualquiera de los : initarg s para inicializar la ranura. Si una persona pasa varios argumentos de palabras clave que inicializan la misma ranura, entonces el argumento ms a la izquierda en la llamada a MAKEINSTANCIA se utiliza. Heredado : lector , : escritor , y : de acceso opciones no se incluyen en el especificador de la ranura resultante de la fusin ya que los mtodos creados por la superclase DEFCLASS ya se aplican a la nueva clase. La nueva clase puede, sin embargo, crear sus propias funciones de acceso mediante el suministro de su propia : lector ,: escritor , o : de acceso opciones.

Por ltimo, el : asignacin opcin es, como : initForm , determinado por la clase ms especfica que especifica la ranura. Por lo tanto, es posible que todas las instancias de una clase a compartir una : Clase de ranura, mientras que las instancias de una subclase pueden tener cada uno su propia : ejemplo, la ranura del mismo nombre. Y una subclase sub-entonces puede volver a definir : la clase ranura, por lo que todas las instancias de que la clase otra vez a compartir una sola ranura. En este ltimo caso, la ranura compartida por las instancias de la subclase sub-es diferente que la ranura compartida por la superclase original. Por ejemplo, suponga que tiene las siguientes clases:
(Defclass foo () ((A: initarg: a: initForm "A": acceso a) (B: initarg: b: initForm "B": de acceso b))) (Bar defclass (foo) ((A: initForm (error "Debe proporcionar un valor para un")) (B: initarg: el-b: el acceso-b: la asignacin: la clase)))

Cuando una instancia de la clase de barra , se puede utilizar el initarg heredado : una , para especificar un valor para la ranura de uno y, de hecho, debe hacerlo para evitar un error, ya que el : initForm suministrada por barra reemplaza a la heredada de foo . Para iniciar la b ranura, usted puede utilizar el initarg heredada: b o el initarg nueva: la opcin-b . Sin embargo, debido a la : asignacin opcin en la b ranura en barra , el valor especificado se almacenarn en la ranura compartida por todas las instancias de barra . Esa misma ranura se puede acceder ya sea con el mtodo de la funcin genrica b que se especializa en la foo , o con el nuevo mtodo en la funcin genricade la b- que se especializa directamente en barra . Para acceder a la una ranura a cada uno foo o bar , usted continuar utilizando la funcin genrica uno . Por lo general, la fusin de las definiciones de ranura funciona bastante bien. Sin embargo, es importante tener en cuenta cuando se usa herencia mltiple que dos ranuras de relacin que parecen tener el mismo nombre se pueden combinar en una sola ranura en la nueva clase. Por lo tanto, los mtodos especializados en las diferentes clases podra terminar la manipulacin de la misma ranura cuando se aplica a una clase que extiende las clases. Esto no es un gran problema en la prctica, ya que, como se ver en el captulo 21, puede utilizar el sistema de paquetes para evitar colisiones entre los nombres de las piezas desarrolladas independientemente del cdigo.

Herencia Mltiple Todas las clases que hemos visto hasta ahora slo han tenido una sola superclase directa. Common Lisp tambin es compatible con la herencia mltiple - una clase puede tener mltiples superclases directas, heredando los mtodos aplicables y especificadores de franjas horarias de todos ellos. La herencia mltiple no cambiar drsticamente cualquiera de los mecanismos de la herencia que he discutido hasta el momento - cada clase definida por el usuario ya dispone de mltiples superclases ya que todos ellos se extienden NORMAS OBJETO , que se extiende T , y as tener al menos dos superclases. La arruga que la herencia mltiple, aade, es que una clase puede tener ms de una directa superclase. Esto complica la nocin de la especificidad de clase que se utiliza tanto en la construccin de los mtodos efectivos para una funcin genrica, y cuando la fusin hered especificadores de ranura. Es decir, si las clases slo poda tener una sola superclase directa, ordenando las clases por la especificidad sera trivial-una clase y todas sus superclases se puede ordenar en lnea recta a partir de la propia clase, seguido de su superclase directa individual, seguido por su superclase directa, todo el camino hasta T . Sin embargo, cuando una clase tiene mltiples superclases directas, las superclases son por lo general no relacionados entre s - de hecho, si uno era una subclase de otra, no tendra que subclase tanto de forma directa. En ese caso, la regla de que las subclases son ms especficas que las superclases no es suficiente para ordenar todas las superclases. As Common Lisp utiliza una segunda regla que ordena superclases no relacionados de acuerdo con el orden en que aparecen en la DEFCLASS directa 's superclase lista - clases anteriores en la lista se consideran ms especficas que las clases ms adelante en la lista. Esta regla es reconocidamente un tanto arbitraria pero le permite a cada clase para tener un lineal clase lista de prioridad , que se puede utilizar para determinar qu superclases debe ser considerado ms especfico que otros. Ntese, sin embargo, no hay orden global de las clases - cada clase tiene su propia clase lista de prioridad, y las mismas clases que pueden aparecer en diferente orden en las diferentes clases de listas de clase de precedencia. Para ver cmo funciona esto, vamos a agregar una clase a la aplicacin de la banca: el mercado monetario de la cuenta . Una cuenta de mercado de dinero combina las caractersticas de una cuenta de cheques y una cuenta de ahorros: un cliente puede

girar cheques en contra de ella, sino que tambin gana intereses. Es posible que lo definen as:
(Defclass del mercado de dinero de la cuenta (de cheques, cuenta de ahorros de la cuenta) ())

La lista de prioridad para la clase de mercado de dinero de la cuenta sern los siguientes:
(Mercado de dinero de la cuenta la comprobacin de la cuenta cuenta de ahorros una cuenta bancaria norma-objeto t)

Tenga en cuenta cmo esta lista satisfaga a ambas normas: todas las clases aparece antes de todas sus superclases, y la comprobacin de la cuenta y lacuenta de ahorro- aparecen en el orden especificado en DEFCLASS . Esta clase no define las ranuras de s mismo, pero van a heredar las ranuras de ambos de sus superclases directas, incluidas las ranuras que heredan de sus superclases. Del mismo modo, cualquier mtodo que es aplicable a cualquier clase en la lista de la clase preferencia ser aplicable a un mercado de dinero de la cuenta objeto. Debido a que todos los especificadores de ranura para la misma ranura se agrupen, no importa que el mercado monetario de la cuenta hereda los especificadores misma ranura de lacuenta de banco dos veces. 12 La herencia mltiple es ms fcil de entender cuando las superclases diferentes que proporcionan ranuras completamente independientes y comportamientos. Por ejemplo, elmercado de dinero de la cuenta van a heredar las ranuras y comportamientos para hacer frente a los controles de verificacin de la cuenta y las franjas horarias y los comportamientos para calcular el inters de la cuenta de ahorros . Usted no tiene que preocuparse por la lista de la clase precedente para los mtodos y las ranuras heredadas de una sola superclase o de otra. Sin embargo, tambin es posible heredar mtodos diferentes para la misma funcin genrica de las superclases diferentes. En ese caso, la lista de clase preferencia entra en juego. Por ejemplo, supongamos que la aplicacin de banca se define una funcin genrica de impresin comunicado utiliza para generar estados de cuenta mensuales. Es de suponer que no sera ya los mtodos de impresin de la declaracin especializada tanto en la comprobacin de la cuenta y la cuenta de

ahorro- . Ambos mtodos se aplicarn a los casos de mercados de dinero de la cuenta , pero la especializada en una comprobacin de la cuenta se considerar ms especfico que el de cuenta de ahorros debido a la comprobacin de la cuenta anterior a cuenta de ahorros en elmercado de dinero de la cuenta 's de clase prevalece lista. Suponiendo que los mtodos heredados son todos los mtodos primarios y no se ha definido ningn otro mtodo, el mtodo especializada enla comprobacin de la cuenta se utilizar si se invoca la declaracin de impresin en mercados de dinero de la cuenta . Sin embargo, eso no necesariamente le dar la conducta que desea, ya que probablemente desea una declaracin de cuenta de mercado monetario para contener elementos tanto de una cuenta corriente y un estado de cuenta de ahorros. Puede modificar el comportamiento de impresin-la declaracin de mercado de dinero de la cuenta s de un par de maneras. Una forma sencilla es definir un nuevo mtodo principal especializado en mercado de dinero de la cuenta . Esto le da ms control sobre el comportamiento nuevo, pero probablemente requerir un cdigo ms nueva que otras opciones voy a discutir en un momento. El problema es que mientras que usted puede utilizar CALL-NEXT-METODO para llamar a "arriba" para el siguiente mtodo ms especfico, a saber, los organismos especializados en una comprobacin de la cuenta , no hay manera de invocar a un particular menos un mtodo especfico, como el especializado en una cuenta de ahorros . Por lo tanto, si usted quiere ser capaz de reutilizar el cdigo que imprime el ahorro y la cuenta departe de la instruccin, tendr que romper ese cdigo en una funcin separada, que luego se puede llamar directamente tanto desde el mercado de dinero de la cuenta ycuenta de ahorros- la declaracin de impresin mtodos. Otra posibilidad es escribir los mtodos primarios de las tres clases para llamar a CALL-NEXT-MTODO . A continuacin, el mtodo especializado enmercado de dinero de la cuenta a utilizar CALL-NEXT-METODO para invocar el mtodo especializada en la comprobacin de la cuenta . Cuando ese mtodo llama CALLNEXT-MTODO , que se traducir en la gestin de la cuenta de ahorros , ya que el mtodo ser el siguiente mtodo ms especfico en funcin demercado de dinero de la cuenta @ s de clase lista de prioridad. Por supuesto, si usted va a depender de una convencin de programacin - que cada mtodo llama CALL-NEXT-METODO - para asegurar que todos los mtodos

aplicables se ejecutan en algn momento, usted debe pensar acerca del uso de mtodos auxiliares en su lugar. En este caso, en lugar de definir los mtodos primarios deimpresin-la declaracin de la comprobacin de la cuenta y la cuenta de ahorro- , puede definir los mtodos como : despus de los mtodos, la definicin de un mtodo primario nico en una cuenta bancaria . Entonces, la impresin comunicado , pidi a unmercado de dinero de la cuenta , se imprimir una declaracin bsica de la cuenta, la produccin por el mtodo principal especializado enuna cuenta bancaria , seguido por la salida de los detalles del : despus de mtodos especializados en la cuenta de ahorros y lacomprobacin de cuenta . Y si desea aadir detalles especficos del mercado de dinero de la cuenta s, se puede definir una : despus de mtodo especializado en mercado de dinero de la cuenta , que se desarrollar el ltimo de todos. La ventaja de utilizar mtodos auxiliares es que deja bastante claro qu mtodos son los principales responsables de la aplicacin de la funcin genrica y cules slo estn contribuyendo bits adicionales de funcionalidad. La desventaja es que usted no consigue un control preciso sobre el orden en que los mtodos auxiliares de correr - si quera que lacomprobacin de la cuenta de parte de la instruccin de imprimir antes de la cuenta de ahorros una parte, usted tendra que cambiar el orden en que lascuentas de dinero en el mercado subclases las clases. Pero eso es un cambio bastante drstico que podra afectar a otros mtodos y las ranuras heredadas. En general, si usted se encuentra haciendo girar la orden de la lista de superclase directa como una forma de afinar el comportamiento de los mtodos especficos, es probable que necesite dar un paso atrs y reconsiderar su enfoque. Por otro lado, si no te importa exactamente lo que el orden no es ms que quiero que sea consistente a travs de varias funciones genricas, a continuacin, utilizando los mtodos auxiliares puede ser justo la cosa. Por ejemplo, si adems de la impresin de la declaracin tiene una impresin detallada-declaracin de funcin genrica, se puede implementar utilizando las dos funciones : despus de los mtodos de las diferentes subclases de una cuenta bancaria , y el orden de las partes tanto de forma regular y una declaracin detallada ser la misma. Buen Diseo Orientado a Objetos Eso es todo por las caractersticas principales del sistema de objetos de Common Lisp. Si usted tiene un montn de experiencia en programacin orientada a objetos,

es probable que pueda ver cmo Comn caractersticas de Lisp se puede utilizar para la implementacin de buenas diseos orientados a objetos. Sin embargo, si usted tiene menos experiencia con la orientacin a objetos, es posible que tenga que pasar algn tiempo la absorcin de la forma orientada a objetos del pensamiento. Por desgracia, eso es un tema bastante grande y ms all del mbito de este libro. O, como la pgina de manual de sistema de objetos de Perl dice, "Ahora lo que necesita slo para ir y comprar un libro sobre metodologa de diseo orientado a objetos y golpear su frente con l durante los prximos seis meses o menos." O bien, puede esperar a que algunos de los captulos prcticos, ms adelante en este libro, donde puedes encontrar varios ejemplos de cmo estas caractersticas se utilizan en la prctica. Por ahora, sin embargo, ya est listo para tomar un descanso de toda esta teora de la orientacin a objetos y girar con el tema bastante diferente de cmo hacer buen uso de los poderosos, pero a veces crptica de Common Lisp, FORMATO funcin.

1 Definicin de nuevos mtodos de una clase existente puede parecer extrao que la gente se utilizan

para las lenguas tipos estticos, tales como C + + y Java en el que todos los mtodos de una clase debe ser definida como parte de la definicin de clase. Pero los programadores con experiencia en el tipado dinmico lenguajes orientados a objetos como Smalltalk y Objective C se encuentra nada extrao en la adicin de nuevos comportamientos a las clases existentes.
2 En otros lenguajes orientados a objetos, las franjas horarias que se podra llamar los campos , las

variables miembro , o atributos .


3 Como al nombrar a las funciones y variables, no es del todo cierto que se puede utilizar cualquier

smbolo como un nombre de clase - no puede utilizar nombres definidos por el estndar del lenguaje. Usted ver en el captulo 21 la forma de evitar este tipo de conflictos de nombres.
4 El argumento de MAKE-INSTANCIA en realidad puede ser el nombre de la clase o una clase de

objeto devuelto por la funcin de clase de o FIND-CLASE .


5 Otra forma de afectar a los valores de las tragamonedas es la : default-initargs opcin de

DEFCLASS . Esta opcin se utiliza para especificar las formas que sern evaluados para proporcionar argumentos a favor de parmetros de inicializacin especficos que no se les da un valor de una llamada particular a MAKE-INSTANCIA . Usted no necesita preocuparse : defaultinitargs por ahora.
6 Adicin de una : despus de mtodo para inicializar-INSTANCIA es el comn analgico Lisp a la

definicin de un constructor en Java o C + + o un __init__ mtodo en Python.


7 Uno de los errores que usted puede hacer hasta que se acostumbre a la utilizacin de mtodos

auxiliares es definir un mtodo en INITIALIZE INSTANCIA- pero sin el : despus calificador. Si lo

hace, obtendr un nuevo mtodo principal que las sombras de la una por defecto. Puede eliminar el mtodo deseado utilizando las funciones de principal Remove-MTODO y FIND-MTODO . Algunos entornos de desarrollo pueden proporcionar una interfaz grfica de usuario para hacer lo mismo.
(Quitar-el mtodo # "inicializacin de instancia (Encontrar-el mtodo # "inicializacin de instancia () (lista de (encontrar la clase 'a una cuenta bancaria)))) 8 Por supuesto, ofreciendo una funcin de acceso en realidad no limita nada, ya otro tipo de cdigo

puede todava utilizar SLOT-VALOR para llegar a las ranuras directamente. Common Lisp no proporciona encapsulacin estricta de las ranuras de la forma en que algunos lenguajes como C + + y Java hacerlo, sin embargo, si el autor de una clase proporciona funciones de acceso y los ignoras, utilizando SLOT-VALOR en cambio, es mejor que sepa lo que usted ' est haciendo. Tambin es posible utilizar el sistema de paquetes, lo que voy a discutir en el captulo 21, para que sea an ms evidente que determinadas franjas horarias no se van a acceder directamente, dejando de exportar los nombres de las ranuras.
9 Una consecuencia de la definicin de un SETF funcin - por ejemplo, (setf foo) - es que si tambin

es necesario definir la funcin de acceso correspondiente, foo , en este caso, puede utilizar todas las macros modifican construidas sobre SETF , como INCF , DECF , PUSH y POP , en el nuevo tipo de lugar.
10 la "variable" nombres proporcionados por los CON RANURAS y con participacin en los

descriptores de acceso no son verdaderas variables, sino que est implementada usando un tipo especial de macro, llamado smbolo de macro , que permite que un simple nombre para expandirse en ejecucin de cdigo arbitrario. Macros de smbolos se introdujeron en la lengua para apoyar CON-Slots ylos CON Accessors , pero tambin se pueden utilizar para sus propios fines. Voy a discutir en detalle un poco ms en el captulo 20.
11 La Meta Object Protocolo (MOP), que no es parte de la lengua estndar, pero con el apoyo de la

mayora de las implementaciones de Common Lisp, ofrece una funcin, la clase prototipo , que devuelve una instancia de una clase que se puede utilizar para acceder a las ranuras de la clase . Si est utilizando una aplicacin compatible con el MOP y pasar a ser la traduccin de un cdigo de otro idioma que hace un uso intensivo de los campos estticos o de clase, esto le puede dar una forma de facilitar la traduccin. Pero no es todo lo que idiomtica.
12 En otras palabras, Common Lisp no sufre de la herencia de diamante problema de la manera,

digamos, C + + lo hace. En C + +, cuando uno las subclases de la clase dos clases que heredan tanto una variable miembro de una superclase comn, la clase inferior hereda la variable de miembro en dos ocasiones, dando lugar a un sin fin de confusin.

18. A pocos Recetas FORMATO


Common Lisp de FORMATO funcin es - junto con la extensa LOOP macro - una de las dos caractersticas comunes de Lisp que inspira una profunda respuesta emocional en una gran cantidad de usuarios comunes de Lisp. Algunos lo aman, otros lo odian. 1 FORMATO aficionados @ s les encanta por su gran potencia y concisin, mientras que sus detractores lo odian por el potencial de mal uso y su opacidad. Complejo FORMATO cadenas de control a veces tienen un parecido sospechoso a ruido en la lnea, pero FORMATO sigue siendo popular entre Lispers comunes que les gusta ser capaz de generar pequeos trozos de legible de salida sin tener que saturar su cdigo con una gran cantidad de cdigo de salida de generacin. Mientras FORMATO cadenas 's de control puede ser crptico, por lo menos una sola FORMATO expresin no el desorden de las cosas tan mal. Por ejemplo, supongamos que desea imprimir los valores de una lista delimitada por comas. Usted podra escribir lo siguiente:
(Bucle de contras en la lista hacer (formato t "~ a" (contras de coches)) cuando (los contras cdr) hacer (formato de t ","))

Eso no es tan malo, pero cualquiera que lea este cdigo tiene que analizar mentalmente slo para darse cuenta de que todo lo que est haciendo es imprimir el contenido de la listaa la salida estndar. Por otro lado, se puede decir a simple vista que la expresin siguiente tiene la impresin de la lista , de alguna forma, a la salida estndar:
(Formato t "~ {~ a ~ ^, ~}" lista)

Si te importa exactamente qu forma tomar la salida, entonces usted tendr que examinar la cadena de control, pero si lo que quieres es una aproximacin de primer orden de lo que esta lnea de cdigo que est haciendo, que es disponible de inmediato. En cualquier caso, usted debe tener al menos un conocimiento de la lectura FORMATO , y vale la pena tener una idea de lo que puede hacer antes de que usted afiliado con el pro-o anti- FORMATO campamento. Tambin es importante comprender al menos los conceptos bsicos de FORMATO debido a otras funciones estndar, tales como las funciones de sealizacin condicin discutidos en el

captulo siguiente, utilice FORMATO de estilo-las cadenas de control para generar la salida. Para complicar ms las cosas, FORMATO compatible con tres tipos muy diferentes de formato: mesas de impresin de datos, impresin bonita- las expresiones, y la generacin de mensajes legibles con los valores interpolados. Tablas de impresin de datos como texto es un poco passe en estos das, es uno de esos recordatorios que Lisp es casi tan antigua como FORTRAN. De hecho, varias de las directivas que se pueden utilizar para imprimir valores de punto flotante en campos de ancho fijo se basa muy directamente en FORTRANdescriptores de edicin , que se utilizan en FORTRAN para leer e imprimir las columnas de datos dispuestos en campos de ancho fijo. Sin embargo, usando Common Lisp como un reempbucle de FORTRAN est fuera del mbito de este libro, as que no voy a discutir los aspectos de FORMATO . Bastante de impresin es tambin ms all del mbito de este libro - no porque sea cosa del pasado, pero slo porque es demasiado grande de un tema. Brevemente, el Comn impresora Lisp bonita es un sistema personalizable para imprimir estructurados en bloques de datos tales como - pero no se limitan a - s-expresiones mientras que variando la indentacin y dinmicamente agregando lnea rompe segn sea necesario. Es una gran cosa cuando lo necesite, pero no es a menudo necesaria en la programacin del da a da. 2 En su lugar, me centrar en las partes de FORMATO se pueden utilizar para generar legibles cadenas con valores interpolados. Incluso limitar el mbito de esa manera, todava hay un poco justo para cubrir. Usted no debe sentirse obenlazado a recordar cada detalle se describe en este captulo. Puede llegar a ser muy lejos con slo unos pocos FORMATO idiomas. Voy a describir las caractersticas ms importantes de FORMATO primero, le toca a usted la cantidad de un FORMATO asistente que desea llegar a ser. La funcin FORMATO Como hemos visto en captulos anteriores, el FORMATO funcin toma dos argumentos necesarios: un destino para su produccin y una cadena de control que contiene el texto literal y embebidos directivas . Todos los argumentos adicionales proporcionan los valores utilizados por las directivas de la cadena de control que

interpolar valores en la salida. Me referir a estos argumentos como argumentos de formato . El primer argumento FORMATO , el destino de la salida, puede ser T , NIL , un arroyo, o una cadena con un puntero de relleno. T es la abreviatura de la corriente* NORMAS DE SALIDA * , mientras que NIL causas FORMATO para generar su salida a una cadena, que luego regresa. 3 Si el destino es una corriente, la salida se escribe en la secuencia. Y si el destino es una cadena con un puntero de relleno, la salida formateada se agrega a la final de la cadena y el puntero de relleno est adecuadamente ajustada. Excepto cuando el destino es NIL y se devuelve una cadena, FORMATO devuelve NIL . El segundo argumento, la cadena de control, es, en esencia, un programa en el FORMATO idioma. El FORMATO lenguaje no es en absoluto lispy - su sintaxis bsica se basa en los personajes, no s-expresiones, y est optimizada para la compactacin en lugar de fcil comprensin. Esta es la razn por un complejo FORMATO cadena de control puede terminar parecindose a ruido en la lnea. La mayora de FORMATO directivas 's simplemente interpolar una discusin en la salida de una forma u otra. Algunas directivas, como por ejemplo ~% , lo que provoca FORMATOpara emitir una nueva lnea, no consumen ningn argumento. Y otros, como se ver, puede consumir ms de un argumento. Una directiva incluso le permite saltar alrededor de la lista de argumentos con el fin de procesar el mismo argumento ms de una vez, o para omitir algunos de los argumentos en ciertas situaciones. Pero antes de discutir las directivas especficas, echemos un vistazo a la sintaxis general de una directiva. Directivas FORMATO Todas las directivas comienzan con una tilde ( ~ ) y terminan con un carcter nico que identifica a la Directiva. Puede escribir el personaje, ya sea en maysculas o en minsculas.Algunas directivas de tener parmetros de prefijo , que se escriben inmediatamente despus de la tilde, separados por comas, y se utiliza para controlar cosas tales como el nmero de dgitos para imprimir despus del punto decimal cuando se imprime un nmero de punto flotante. Por ejemplo, el ~ $ Directiva, una de las directrices utilizadas para imprimir valores de punto flotante, por defecto imprime dos dgitos despus del punto decimal.
CL-USER> (formato t "~ $" pi)

3,14 NIL

Sin embargo, con un parmetro de prefijo, se puede especificar que se debe imprimir su argumento, digamos, a cinco decimales como esta:
CL-USER> (formato t "~ 5 $" pi) 3,14159 NIL

Los valores de los parmetros de prefijo son o bien los nmeros, escritos en decimal, o caracteres, escrito como una comilla simple seguida por el carcter deseado. El valor de un parmetro prefijo tambin se pueden derivar de los argumentos en formato de dos maneras: Un parmetro prefijo de v hace FORMATO para consumir un argumento format y utilice su valor para el parmetro prefijo. Y un parmetro prefijo de # ser evaluado como el nmero de argumentos de formato restantes. Por ejemplo:
CL-USER> (formato t "~ v $" 3 pi) 3,142 NIL CL-USER> (formato t "~ # $" pi) 3.1 NIL

Voy a dar algunos ejemplos ms realistas acerca de cmo puede utilizar el # argumento en la seccin "Formato condicional". Tambin se pueden omitir parmetros prefijo completo. Sin embargo, si desea especificar un parmetro, pero no los tiene ante s, usted debe incluir una coma para cada parmetro no especificado. Por ejemplo, el ~ F directiva, otra directiva para la impresin de valores de punto flotante, tambin toma un parmetro para controlar el nmero de decimales que se van a imprimir, pero es el segundo parmetro en lugar de la primera. Si desea utilizar F ~ para imprimir un nmero de cinco cifras decimales, se puede escribir lo siguiente:
CL-USER> (formato t "~, 5f" pi) 3,14159 NIL

Tambin puede modificar el comportamiento de algunas directivas con los dos puntos y en el inicio de sesin modificadores , que se colocan despus de que los parmetros de prefijo y antes de caracteres que identifica de la Directiva. Estos modificadores de cambiar el comportamiento de la directiva en cosas pequeas. Por ejemplo, con un modificador de colon, el~ D directiva usada a enteros de salida en decimal emite el nmero con comas que separan cada tres dgitos, mientras que las

causas modificadoras en-signo ~ D para incluir un signo ms cuando el nmero es positivo.


CL-USER> (formato t "~ d" 1000000) 1000000 NIL CL-USER> (formato t "~: d" 1000000) 1000000 NIL CL-USER> (formato t "~ @ d", 1000000) +1000000 NIL

Cuando tiene sentido, es posible combinar los dos puntos y el signo-modificadores para conseguir ambas modificaciones.
CL-USER> (formato t "~: @ d", 1000000) 1000000 NIL

En las directivas en las dos conductas no pueden ser modificados de manera significativa combinados, usando modificadores no est definido o determinado un tercer significado. Formato bsico Ahora ya ests listo para ver las directivas especficas. Voy a empezar con varias de las directivas ms utilizadas, incluyendo algunos que hemos visto en captulos anteriores. La directiva de la mayor parte de propsito general se ~ A , que consume un argumento de formato de cualquier tipo y lo devuelve en esttica forma (legible). Por ejemplo, las cadenas son de salida sin comillas o caracteres de escape, y los nmeros salen de una manera natural para el tipo de nmero. Si lo que desea para emitir un valor para el consumo humano, esta directiva es su mejor apuesta.
(Cero formato "El valor es: ~ un" 10) ==> "El valor es: 10" (Cero formato "El valor es: ~ un" "foo") ==> "El valor es: foo" (Cero formato "El valor es: ~ A" (lista 1 2 3)) ==> "El valor es: (1 2 3)"

Una directiva estrechamente relacionados, ~ S , tambin consume un argumento de formato de cualquier tipo y la emite. Sin embargo, ~ S trata de generar una salida que puede ser ledo de nuevo con READ . Por lo tanto, las cadenas se encierran entre comillas, los smbolos sern paquete cualificado cuando sea necesario, y as sucesivamente. Los objetos que no tienen un READ representacin de poder se imprimen con la sintaxis de objetos ilegible, # <> . Con un modificador de colon, tanto el ~ Una y ~ S directivas emiten NIL como ()en lugar de NIL . Tanto el ~ A y ~ S

directivas tambin tomar hasta cuatro parmetros prefijo, que pueden ser utilizados para controlar si el relleno se aade despus (o antes con el modificador arroba) el valor, pero los parmetros son realmente tiles para la generacin de tablas datos. Las otras dos directivas ms usadas son ~% , el cual emite una nueva lnea, y ~ & , que emite una nueva lnea . La diferencia entre ambos es que el % ~ siempre emite un salto de lnea, mientras que ~ y emite un slo si no est ya en el comienzo de una lnea. Esto es til cuando la escritura de funciones dbilmente acoplados que cada generan un pedazo de la produccin y que deben ser combinados de diferentes maneras. Por ejemplo, si una funcin genera una salida que termina con un salto de lnea ( % ~ ) y otra funcin genera una salida que se inicia con una nueva lnea ( ~ & ), usted no tiene que preocuparse de conseguir una lnea en blanco si se llama a ellos uno despus del otro. Ambas directivas se puede tomar un parmetro nico prefijo que especifica el nmero de lneas nuevas a emitir. El % ~ directiva simplemente se emiten que los caracteres de nueva lnea muchos, mientras que el ~ &directiva emitir ya sea n - 1 o n saltos de lnea, dependiendo de si se inicia en el comienzo de una lnea. Menos frecuente es la relacionada ~ ~ directiva, lo que provoca FORMATO para emitir una tilde literal. Al igual que el % ~ y ~ y directivas, que se puede parametrizar con un nmero que controla la forma en que muchos tildes a emitir. El carcter y las Directivas enteros Adems de las directivas de propsito general, ~ Una y ~ S , FORMATO soporta varias directivas que pueden ser utilizados para emitir valores de tipos especficos de maneras particulares. Uno de los ms sencillos de stos es el ~ C directiva, que se utiliza para emitir caracteres. No toma ningn argumento prefijo, pero puede ser modificado con el colon y los modificadores en el signo-. Sin modificaciones, su comportamiento no es diferente de un ~ ., excepto que slo funciona con caracteres Las versiones modificadas son ms tiles. Con un modificador de colon, del ~: C salidas no imprimibles personajes tales como el espacio, tabulador, nueva lnea, y por su nombre. Esto es til si desea emitir un mensaje al usuario acerca de algn personaje. Por ejemplo, el texto siguiente:
(Formato t "Error de sintaxis carcter inesperado:. ~: C" char)

puede emitir mensajes como este:

Error de sintaxis. Inesperado personaje: un

sino tambin como el siguiente:


Error de sintaxis. Carcter inesperado: el espacio

Con el modificador de arroba, @ ~ C emitir el carcter en la sintaxis de carcter literal de Lisp.


CL-USER> (formato t "~ @ ~% c" # \ a) # \ A NIL

Con modificadores por el colon y en el inicio de sesin, el ~ C directiva puede imprimir informacin adicional acerca de cmo entrar en el carcter en el teclado si se requiere combinaciones de teclas especiales. Por ejemplo, en Macintosh, en ciertas aplicaciones se puede introducir un carcter nulo (cdigo de carcter 0 en ASCII o en cualquier superconjunto ASCII, tales como ISO-8859-1 o Unicode) pulsando la tecla Control y escribiendo @. En OpenMCL, si imprime el carcter nulo con la ~: C directiva, que le dice lo siguiente:
(Formato nulo "~: @ c" (cdigo-char 0)) ==> "^ @ (@ Control)"

Sin embargo, no todos los Lisps poner en prctica este aspecto de la C ~ directiva. E incluso si lo hacen, pueden o no ser exactos - por ejemplo, si usted est funcionando OpenMCL en el limo, la C-@ acordes clave es interceptada por Emacs, invocando el set-mark-comando . 4 Directivas formato dedicado a los nmeros que emiten son otra categora importante. Mientras que usted puede utilizar los ~ A y ~ S directivas para emitir un nmero, si usted desea tener un control preciso sobre la forma en que estn impresos, es necesario utilizar una de las directivas especficas de nmeros. Las directrices numricas se pueden dividir en dos subcategoras: las directivas para dar formato a valores enteros y las directivas para dar formato a valores de punto flotante. Cinco estrechamente relacionadas con las directivas valores enteros de formato: D ~ , ~ X , ~ S , ~ B y ~ R . El ms utilizado es el D ~ directiva, que genera nmeros enteros en base 10.
(Cero formato "d ~" 1000000) ==> "1000000"

Como he mencionado anteriormente, con un modificador de colon se agrega una coma.

(Formato nulo "~: d" 1000000) ==> "1.000.000"

Y con un modificador de la arroba, que siempre imprime una seal.


(Formato nulo "~ @ d" 1000000) ==> "1000000"

Y los dos modificadores se pueden combinar.


(Formato nulo "~: @ d" 1000000) ==> "1000000"

El parmetro primer prefijo puede especificar un ancho mnimo para la salida, y el segundo parmetro se puede especificar un carcter de relleno a utilizar. El carcter de relleno por defecto es el espacio, y el relleno se inserta siempre antes de que el nmero mismo.
(Cero formato "~ 12d" 1000000) ==> "1000000" (Cero formato "~ 12, '0 d" 1000000) ==> "000001000000"

Estos parmetros son tiles para dar formato a las cosas como las fechas en un formato de ancho fijo.
(Cero formato "~ 4, 0 D-2 ~, ~ 0 d-2, 0 d" 2005 6 10) ==> "10/06/2005"

Los parmetros tercero y cuarto se utilizan en conjuncin con el modificador de colon: el tercer parmetro especifica el carcter que se utiliza como separador entre los grupos y dgitos, y el cuarto parmetro especifica el nmero de dgitos por grupo. Estos parmetros por defecto de una coma y el nmero 3. Por lo tanto, puede utilizar la directiva ~: D sin parmetros para la salida de nmeros enteros grandes en el formato estndar para los Estados Unidos, pero puede cambiar la coma de un perodo y la agrupacin de la 3 a 4 con ~,, ', 4D. .
(Formato nulo "~: d" 100 millones) ==> "100000000" (Nula en formato ". ~,, ', 4: d" 100 millones) ==> "1.0000.0000"

Tenga en cuenta que debe utilizar comas para mantener los lugares de la anchura no especificada y los parmetros de relleno de caracteres, lo que les permite mantener sus valores predeterminados. El X ~ , ~ O y B ~ directivas funcionan igual que la D ~ Directiva, salvo que emiten los nmeros en formato hexadecimal (base 16), octal (base 8), y el binario (base 2).
(Cero formato "~ x" 1000000) ==> "f4240" (Cero formato "~ o" 1000000) ==> "3641100" (Cero formato "~ b" 1000000) ==> "11110100001001000000"

Por ltimo, el ~ I Directiva es el general radix Directiva. Su primer parmetro es un nmero entre 2 y 36 (ambos inclusive) que indica lo que la base de su uso. Los

parmetros restantes son los mismos que los cuatro parmetros aceptados por los del ~ D , los ~ X , los de O ~ y ~ B directivas, y el colon y el en-seas modificadores de modificar su comportamiento en la misma manera. El R ~ directiva tambin tiene un comportamiento especial si se utiliza sin parmetros prefijo, que voy a discutir en la seccin "Directivas de Lenguaje en Ingls." Directivas de punto flotante Cuatro directivas de formato de punto flotante de los valores: ~ F , E ~ , ~ G y ~ $ . Los tres primeros de ellos son las directivas basadas en descriptores de editar FORTRAN. Voy a saltar la mayor parte de los detalles de las Directivas, puesto que en su mayora tienen que ver con el formato de punto flotante de los valores para su uso en forma de tabla. Sin embargo, usted puede utilizar el F ~ , ~ E , y ~ $ directivas para interpolar los valores de punto flotante en texto. El G ~ , o en general, la Directiva de punto flotante, por el contrario, combina aspectos de los ~ F y E del ~ directivas de una forma que realmente slo tiene sentido para la generacin de la produccin de tabla. El ~ F Directiva emite su argumento, que debe ser un nmero, 5 en formato decimal, posiblemente, controlar el nmero de dgitos despus del punto decimal. El ~ F directiva est, sin embargo, permitido el uso de la notacin cientfica informatizada si el nmero es lo suficientemente grande o pequeo. El E ~ Directiva, por el contrario, siempre emite nmeros en notacin cientfica computarizada. Ambas Directivas tienen una serie de parmetros de prefijo, pero hay que preocuparse slo de la segunda, que controla el nmero de dgitos a imprimir despus de la coma decimal.
(Nula en formato "f ~" pi) ==> "3.141592653589793d0" (Cero formato "~, 4f" pi) ==> "3,1416" (Formato nulo "e ~" pi) ==> "3.141592653589793d 0" (Cero formato "~, 4e" pi) ==> "3.1416d 0"

El ~ de $ , o una directiva monetaria, es similar a la F ~ , pero un poco ms simple. Como su nombre indica, est diseado para emitir unidades monetarias. Si no hay parmetros, es bsicamente equivalente a ~, 2F . Para modificar el nmero de dgitos impresos despus del punto decimal, se utiliza el primero parmetro, mientras que el segundo parmetro controla el nmero mnimo de dgitos a imprimir antes de la coma decimal.
(Cero formato "~ $" pi) ==> "3,14" (Cero formato "~ $ 2,4" pi) ==> "0.003,14"

Todas las tres directivas, ~ F , ~ E , y ~ de $ , se pueden hacer para imprimir siempre un signo, ms o menos, con el modificador en-signo. 6 Directivas del Lenguaje en Ingls Algunas de las ms prcticas FORMATO directivas para generar legibles los mensajes son los que emite para el texto de Ingls. Estas directivas permiten emitir los nmeros como las palabras en ingls, para emitir los marcadores plurales basadas en el valor de un argumento de formato, y la aplicacin de las conversiones de casos a las secciones de FORMATO de salida 's. El R ~ directiva, que he comentado en "El carcter y las Directivas enteros", cuando se utiliza con ningn tipo de base se especifica, imprime los nmeros como las palabras en ingls o nmeros romanos. Cuando se utiliza con ningn parmetro prefijo y modificadores no se emite el nmero de palabras, como un nmero cardinal.
(Cero formato "~ r" 1234) ==> "un mil doscientos treinta y cuatro"

Con el modificador de colon, que emite el nmero como ordinal.


(Formato nulo "~: r" 1234) ==> "mil doscientos treinta y cuarto"

Y con un modificador de la arroba, que emite el nmero como un nmero romano, tanto con una arroba y dos puntos, emite "viejo estilo" nmeros romanos en la que cuatros y nueves estn escritos como IIII y VIIII en lugar de IV y IX.
(Formato nulo "~ @ r" 1234) ==> "MCCXXXIV" (Formato nulo "~: @ r" 1234) ==> "MCCXXXIIII"

Para los nmeros demasiado grandes para ser representados en la forma dada, ~ R se comporta como D ~ . Para ayudarle a generar mensajes con las palabras correctamente en plural, FORMATO proporciona la ~ P Directiva, que slo emite un s menos que el argumento correspondiente esun .
(Formato nulo "archivo ~ p" 1) ==> "archivo" (Formato nulo "archivo ~ p" 10) ==> "archivos" (Formato nulo "archivo ~ p" 0) ==> "archivos"

Tpicamente, sin embargo, va a utilizar -P con el modificador de colon, lo que hace que volver a procesar el argumento de formato anterior.
(Formato nulo "~ ~ r archivo: p" 1) ==> "un archivo" (Formato nulos ~ ~ r archivo: P "10) ==>" diez archivos " (Formato de las negativas "~ r archivo ~: P" 0) ==> "archivos de cero"

Con el modificador en-signo, que puede ser combinado con el modificador de colon, ~ P emite ya sea y o IES .
(Formato nulo "~ ~ r Famil: @ p" 1) ==> "una familia" (Formato de las negativas "del ~ ~ r famil: @ p" 10) ==> "diez familias" (Formato de las negativas "del ~ ~ r famil: @ p") ==> 0 "cero las familias"

Obviamente, ~ P no puede resolver todos los problemas de pluralizacin y no es ninguna ayuda para la generacin de mensajes en otros idiomas, pero es muy til para los casos que se ocupa. Y el ~ [ Directiva, que voy a discutir en un momento, le da una forma ms flexible para condicionar las partes del FORMATO de salida 's. La ltima directiva para tratar el texto Ingls emisor es ~ ( , que le permite controlar el caso de texto en la salida. Cada ~ ( se empareja con un ~) , y toda la produccin generada por la parte de la cadena de mando entre el dos marcadores se convertir a todos en minsculas.
(Cero formato "~ (~ a ~)", "foo") ==> "foo" (Cero formato "~ (~ @ ~ r)" 124) ==> "CXXIV"

Puede modificar ~ ( con un signo para que sea en mayscula la primera palabra en una seccin de texto, con los dos puntos para llegar a aprovechar todas las palabras, y con los dos modificadores para convertir todo el texto en maysculas. (Una palabra con el propsito de esta directiva es una secuencia de caracteres alfanumricos delimitados por caracteres alfanumricos o los extremos del texto.)
(Cero formato (Cero formato (Formato nulo (Formato nulo "~ (~ a ~)", "The quick brown fox") ==> "The quick brown fox" "@ ~ (~ a ~)", "The quick brown fox") ==> "The quick brown fox" "~: (~ a ~)", "The quick brown fox") ==> "The Quick Brown Fox" "~: @ (~ a ~)", "The quick brown fox") ==> "The quick brown fox"

Formato condicional Adems de las directivas que los argumentos de interpolar y modificar la salida de otro, FORMATO ofrece varias directivas que implementan las construcciones simples de control dentro de la cadena de control. Uno de ellos, que se utiliza en el captulo 9, es la condicin Directiva ~ [. Esta directiva se cierra con el correspondiente ~] , y en medio de una serie de clusulas separadas por ~; . El trabajo del ~ [ Directiva es escoger una de las clusulas, que es procesada por FORMATO . Sin modificadores o parmetros, la clusula es seleccionado por ndice numrico, el ~ [ Directiva consume un argumento de formato, que debe ser un nmero, y toma el ensimo (base cero) en la clusula N es el valor del argumento.
(Cero formato "~ [~ cero; UNO ~; dos ~]" 0) ==> "cero" (Formato nulo "~ [~ cero; UNO ~; dos ~]" 1) ==> "uno"

(Formato nulo "~ [~ cero; UNO ~; dos ~]" 2) ==> "dos"

Si el valor del argumento es mayor que el nmero de clusulas, no se imprime nada.


(Cero formato "~ [~ cero; UNO ~; dos ~]" 3) ==> ""

Sin embargo, si el separador de ltima clusula es ~ de:, en lugar de ~; , a continuacin, la ltima clusula sirve como una clusula de incumplimiento.
(Formato nulo "~ [~ cero; UNO ~, ~ dos:; Mucho ~]" 3) ==> "mucho" (Formato nulo "~ [~ cero; UNO ~, ~ dos:; Mucho ~]" 100) ==> "mucho"

Tambin es posible especificar la clusula de que se selecciona mediante un parmetro prefijo. A pesar de que sera una tontera usar un valor literal en la cadena de control, recordemos que # utiliza como parmetro el prefijo significa que el nmero de argumentos que quedan para ser procesado. Por lo tanto, se puede definir una cadena de formato como el siguiente:
(Defparameter lista *-etc * "# ~ [NINGUNO ~, ~ a ~, ~ A y ~ a ~:; ~ a, ~ a ~] ~ # [~ y ~ a ~:;, ~ a, etc ~].")

y luego utilizar de esta manera:


(Formato (Formato (Formato (Formato (Formato (Formato nula-lista nula-lista nula-lista nula-lista nula-lista nula-lista * * * * * * etc etc etc etc etc etc *) ==> "NINGUNO". * 'a) ==> "A" * 'a' b) ==> "A y B." * 'a' b 'c) ==> "A, B y C." * 'a' b 'c' d) ==> "A, B, C, etc" * 'a' b 'c' d 'e) ==> "A, B, C, etc"

Tenga en cuenta que la cadena de control contiene en realidad dos del ~ del ~] [ directivas, tanto de los que utilizan # para seleccionar la clusula de usar. El primero consume entre cero y dos argumentos, mientras que el segundo consume uno ms, si est disponible. FORMATO ignorar cualquier argumento que no se consumen durante el procesamiento de la cadena de control. Con un modificador de colon, el ~ [ slo puede contener dos clusulas, la directiva consume un solo argumento y procesa la primera opcin si el argumento es NIL y la segunda clusula es otra cosa. Usted utiliza esta variante de ~ [ en el captulo 9 para generar pasa / no pasa mensajes, de esta manera:
(Formato t "~: [~ FAIL; pase ~]" resultado de ensayo)

Tenga en cuenta que cualquier clusula puede estar vaco, pero la directiva debe contener un ~, .

Por ltimo, con un modificador de la arroba, el ~ [ Directiva slo puede tener una clusula. La directiva consume un argumento y, si es no NIL , procesa la clusula despus de realizar copias de seguridad para hacer el argumento disponible para ser consumida de nuevo.
(Cero (Cero (Cero (Cero formato formato formato formato "~ "~ "~ "~ @ @ @ @ [x [x [x [x = = = = ~ ~ ~ ~ a a a a ~] ~] ~] ~] ~ ~ ~ ~ @ @ @ @ [y [y [y [y = = = = ~ ~ ~ ~ a a a a ~]" ~]" ~]" ~]" 10 20) ==> "x = 10 y = 20" 10 nil) ==> "x = 10" nula 20) ==> "y = 20" ninguna ninguna) ==> ""

Iteracin Otro FORMATO directiva que has visto ya, de paso, es la Directiva de la iteracin ~ { . Esta directiva indica FORMATO para iterar sobre los elementos de una lista o implcita sobre la lista de los argumentos de formato. Sin modificadores, ~ { consume un argumento de formato, que debe ser una lista. Al igual que el [~ directiva, que siempre se empareja con un ~] Directiva, el ~ { Directiva siempre se empareja con un clausura ~ }. El texto entre los dos marcadores se procesa como una cadena de control, que se basa sus argumentos en la lista consumida por el ~ {Directiva. FORMATO repetidamente procesar esta cadena de control durante el tiempo que la lista que se reiter en cuenta elementos de la izquierda. En el ejemplo siguiente, el ~ {consume el argumento de formato nico, la lista (1 2 3) y, a continuacin procesa la cadena de control de "~ uno," , repitiendo hasta que todos los elementos de la lista han sido consumidos.
(Formato nulo "~ {~ A, ~}" (lista 1 2 3)) ==> "1, 2, 3,"

Sin embargo, es molesto que en la salida el ltimo elemento de la lista es seguida por una coma y un espacio. Usted puede arreglar eso con la ~ ^ Directiva, dentro del cuerpo de un~ { Directiva, el ~ ^ causa de la iteracin para detener de inmediato, sin procesar el resto de la cadena de control, cuando no hay elementos que permanecen en la lista. Por lo tanto, para evitar la impresin de la coma y un espacio despus del ltimo elemento de una lista, puede ir precedido de una ~ ^ .
(Formato nulo "~ {~ a ~ ^, ~}" (lista 1 2 3)) ==> "1, 2, 3"

Las primeras dos veces a travs de la iteracin, todava hay elementos sin procesar en la lista cuando el ~ ^ se procesa. La tercera vez a travs, sin embargo, despus de la ~ unaDirectiva consume el 3 , el ~ ^ causar FORMATO para salir de la iteracin sin necesidad de imprimir la coma y el espacio.

Con un modificador de signo-, ~ { procesa los argumentos de formato restantes en forma de lista.
(Formato nulo "~ {@ ~ a ~ ^, ~}" 1 2 3) ==> "1, 2, 3"

Dentro del cuerpo de un ~ {... ~ }, el parmetro prefijo especial # se refiere al nmero de puntos restantes para ser procesado en la lista en lugar de el nmero de argumentos de formato restantes. Usted puede usar eso, junto con el ~ [ Directiva, para imprimir una lista separada por comas con una "y" antes de que el ltimo elemento de esta manera:
(Formato nulo "~ {~ a ~ # [~, y ~:;, ~] ~}" (lista 1 2 3)) ==> "1, 2 y 3"

Sin embargo, eso no funciona realmente bien si la lista es larga dos puntos, ya que aade una coma de ms.
(Formato nulo "~ {~ a ~ # [~, y ~:;, ~] ~}" (lista 1 2)) ==> "1 y 2"

Usted podra arreglar eso en un montn de maneras. A continuacin se aprovecha el comportamiento de ~ {@ cuando anidado dentro de otro ~ { o ~ @ { directiva - que se repite en todo lo que los elementos permanecen en la lista se reiter por el exterior ~ { . Puedes combinarlo con un ~ # [ Directiva para hacer la cadena de control siguiente para el formato de listas de acuerdo a la gramtica Ingls:
(Defparameter * Ingls-lista * "~ ~ {# [~, ~ A ~, ~ A y ~ a ~:; ~ @ ~ a ~ {# [~, y ~:;, ~] ~ ~}] ~}") (Formato (Formato (Formato (Formato (Formato nula nula nula nula nula * * * * * Ingls-lista Ingls-lista Ingls-lista Ingls-lista Ingls-lista * * * * * '()) ==> "" '(1)) ==> "1" '(1 2)) ==> "1 y 2" '(1 2 3)) ==> "1, 2 y 3" '(1 2 3 4)) ==> "1, 2, 3 y 4"

Mientras que raya en la cadena de control de ser "slo escritura" de cdigo, no es demasiado difcil de entender si se toma un poco a la vez. El exterior ~ ~ {... } se consumen y iterar sobre una lista. El cuerpo entero de la iteracin a continuacin, consta de un ~ # [... ~] ; la salida generada cada vez que a travs de la iteracin por lo tanto depender del nmero de artculos dejados para ser procesado de la lista. Separados del ~ # [... ~] Directiva relativa a la ~, separadores de la clusula, se puede ver que est formado por cuatro clusulas, la ltima de las cuales es una clusula de incumplimiento, ya que est precedida por una ~:; en lugar de una llanura ~; . La primera clusula, ya que cuando hay cero los elementos para ser procesados, est vaco, lo cual tiene sentido - si no hay ms elementos para ser procesados, la iteracin se ha dejado ya. La segunda clusula maneja el caso de un elemento con

un simple ~ una directiva. Dos elementos se manejan con "~ A y ~ a" . Y la clusula de incumplimiento, que se ocupa de tres o ms elementos, se compone de otra directiva iteracin, esta vez usando ~ {@ para iterar sobre los elementos restantes de la lista est procesado por el exterior ~ { . Y el cuerpo de esa iteracin es la cadena de control que puede manejar una lista de tres o ms elementos correctamente, lo cual est bien en este contexto. Debido a que el ~ @ { loop consume todos los restantes elementos de la lista, el bucle externo repite slo una vez. Si quieres imprimir algo especial, como "<empty>" cuando la lista estaba vaca, tiene un par de maneras de hacerlo. Tal vez la ms fcil es poner el texto que desee en la primera (cero) de la clusula de exterior ~ # [ a continuacin, agregue un modificador de dos puntos para el clausura ~ } de la iteracin externa - de los dos puntos obliga a la repeticin que se ejecute, al menos vez, incluso si la lista est vaca, momento en el que FORMATO procesa la clusula de orden cero de la directiva condicional.
(Defparameter * Ingls-lista * "~ ~ {# [<empty> ~, ~ A ~, ~ A y ~ a ~:; ~ @ ~ a ~ {# [~, y ~:;, ~] ~ ~}] ~:}") (Formato nula * Ingls-lista * '()) ==> "<empty>"

Sorprendentemente, el ~ { Directiva prev an ms variaciones con diferentes combinaciones de parmetros de prefijo y modificadores. No voy a hablar de ellos, aparte de decir que usted puede utilizar un parmetro prefijo de nmero entero para limitar el nmero mximo de iteraciones y que, con un modificador de colon, cada elemento de la lista (ya sea real o una lista de la lista construida por el @ ~ { directiva) debe ser una lista cuyos elementos sern utilizados como argumentos a la cadena de control en el ~: {... ~ } Directiva. Hop, Skip, Jump Una directiva mucho ms simple es la ~ * Directiva, que le permite saltar alrededor en la lista de argumentos de formato. En su forma bsica, sin modificadores, sino que simplemente se salta el siguiente argumento, consumirla sin emitir nada. Ms a menudo, sin embargo, se utiliza con un modificador de colon, lo que hace que se mueva hacia atrs, permitiendo que el mismo argumento para ser utilizado una segunda vez. Por ejemplo, puede utilizar ~: * para imprimir un argumento numrico, una vez que una palabra y una vez en nmeros como ste:

(Cero formato "~ ~ r: * (~ d)" 1) ==> "un (1)"

O bien, podra aplicar una directiva similar a la ~: P para un plural irregular por el peinado ~: * con ~ [ .
(Nula en formato "Vi ~ r ~ El: * ~ [VES ~ f ~:; VES ~]". 0) ==> ". Vi cero elfos" (Nula en formato "Vi ~ r ~ El: * ~ del ~ [VES; f ~:; VES ~]". 1) ==> "Vi a un elfo." (Nula en formato "Vi ~ r ~ El: * ~ [VES ~ f ~:; VES ~]". 2) ==> ". Vi a dos elfos"

En esta cadena de control, el R ~ imprime el argumento de formato como un nmero cardinal. A continuacin, el ~: * Directiva respalda lo que el nmero tambin se utiliza como el argumento de la ~ [ . Directiva, la seleccin de entre las clusulas para cuando el nmero es cero, uno, o cualquier otra cosa 7 Dentro de una del ~ { directiva, del ~ * Los saltos o retrocede a travs de los elementos de la lista. Por ejemplo, usted podra imprimir slo las teclas de un plist como esta:
(Cero formato "~ {~ s ~ * ~ ^ ~}" '(: un 10: b 20)) ==> ": A: B"

El ~ * directiva tambin se puede dar un parmetro prefijo. Sin modificadores o con el modificador de colon, este parmetro especifica el nmero de argumentos para seguir adelante o hacia atrs y los valores predeterminados a uno. Con un modificador de la arroba, el prefijo de parmetro especifica un absoluto, ndice de base cero de la discusin para ir a, por defecto cero. La variante en el signo de * ~ puede ser til si desea utilizar cuerdas de control diferentes para generar mensajes diferentes para los mismos argumentos y si los diferentes mensajes que utilizar los argumentos en diferentes rdenes. 8 Y mucho ms. . . Y an hay ms - que no he mencionado el ~? directiva, que puede tomar fragmentos de cadenas de control de los argumentos de formato o la del ~ / Directiva, que le permite llamar a una funcin arbitraria de manejar el argumento siguiente formato. Y luego estn todas las directivas para la generacin de salidas tabulares y muy impreso. Sin embargo, las directivas tratan en este captulo debe ser suficiente para el momento. En el siguiente captulo, usted se mover en el sistema del estado de Common Lisp, el Common Lisp analgica a excepcin de otros idiomas y sistemas de control de errores.

1 Por supuesto, la mayora de la gente darse cuenta de que no vale la pena conseguir que trabaj a lo

largo de nada en un lenguaje de programacin y usarla o no, sin mucha angustia. Por otro lado, es interesante que estas dos caractersticas son las dos caractersticas de Common Lisp que implementan lo que esencialmente son lenguajes especficos de dominio utilizando una sintaxis que no se basa en el S-expresiones. La sintaxis de FORMAT 's cadenas de control est basada en caracteres, mientras que el prolongado LOOP macro puede ser entendido slo en trminos de la gramtica de las LOOP palabras clave. Que uno de los golpes comunes tanto en FORMATO y LOOP es que "no son lispy suficiente" evidencia de que Lispers realmente les gusta la sintaxis s-expresin.
2 Los lectores interesados en la impresora bonita lo desea, puede leer el peridico "XP: un sistema

comn de Lisp impresin bastante" por Richard Waters. Es una descripcin de la impresora bonita que fue incorporada eventual en Common Lisp. Puede descargarlo desde ftp://publications.ai.mit.edu/ai-publications/pdf/AIM-1102a.pdf .
3 Para confundir un poco las cosas, la mayora de las otras funciones I / O tambin aceptan T y NIL ,

como designadores de la corriente , pero con un significado diferente: como un indicador de flujo, T designa el flujo bidireccional * TERMINAL-IO * , mientras que NIL designa * NORMAS DE SALIDA * como una secuencia de salida y * standard-input * como un flujo de entrada.
4 Esta variante de la C ~ directiva tiene ms sentido en plataformas como las mquinas Lisp, donde

los principales acontecimientos de prensa estuvieron representados por caracteres de Lisp.


5 Tcnicamente, si el argumento no es un nmero real, ~ F se supone que el formato como por la ~ D

directiva, que a su vez se comporta como el ~ Una directiva si el argumento no es un nmero, pero no todas las implementaciones obtener este derecho.
6 Pues bien, eso es lo que dice el estndar del lenguaje. Por alguna razn, tal vez sus races en una

base de cdigo ancestral comn, varios comunes implementaciones de Lisp no implementan este aspecto de la ~ FDirectiva correctamente.
7 Si usted encuentra "Vi a cero elfos" a ser un poco torpe, se puede utilizar una cadena de formato un

poco ms elaborada que hace otro uso de ~: * como esto:


(Nula en formato "Vi ~ [no ~:; ~: * ~ ~ r] el ~: * ~ [VES ~ f ~:; VES ~]". 0) ==> ". No vi elfos" (Nula en formato "Vi ~ [no ~:; ~: * ~ ~ r] el ~: * ~ [~ VES; f ~:; VES ~]". 1) ==> "Vi a un elfo." (Nula en formato "Vi ~ [no ~:; ~: * ~ ~ r] el ~: * ~ [~ VES; f ~:; VES ~]". 2) ==> "Vi a dos elfos." 8 Este tipo de problema puede surgir cuando se trata de localizar una aplicacin y traducir legibles

los mensajes en diferentes idiomas. FORMATO puede ayudar con algunos de estos problemas, pero de ninguna manera es un sistema de localizacin en toda regla.

19. Ms all de control de excepciones: Condiciones y se reinicia


Una de las grandes caractersticas de Lisp es su condicin de sistema. Se sirve un propsito similar a la excepcin de los sistemas de manejo en Java, Python y C + +, pero es ms flexible. De hecho, su flexibilidad se extiende ms all del manejo de errores - las condiciones son ms generales que las excepciones en que una condicin puede representar cualquier incidencia durante la ejecucin de un programa que pueda ser de inters para el cdigo a diferentes niveles en la pila de llamadas. Por ejemplo, en la seccin "Otros usos para las condiciones", vers que las condiciones se puede utilizar para emitir advertencias sin interrumpir la ejecucin del cdigo que emite la advertencia al tiempo que permite el cdigo ms arriba en la pila de llamadas para controlar si el mensaje de advertencia es impreso. Por el momento, sin embargo, me centrar en el tratamiento de errores. La condicin de sistema es ms flexible que los sistemas de excepcin, porque en lugar de proporcionar una divisin en dos partes entre el cdigo que indica un error 1 y el cdigo que maneja, dos condiciones que el sistema divide las responsabilidades en tres partes - la sealizacin de una condicin, el manejo de , y reiniciar . En este captulo, voy a describir cmo se puede utilizar en condiciones de parte de una hipottica aplicacin para el anlisis de los archivos de registro. Usted ver cmo se puede utilizar el sistema de condiciones para permitir una funcin de bajo nivel para la deteccin de un problema al analizar un archivo de registro y sealar un error, que les permitan a nivel de cdigo para proporcionar varias formas posibles de la recuperacin de este tipo de error, y para permitir que el cdigo al ms alto nivel de la aplicacin para definir una poltica para elegir qu estrategia de recuperacin para su uso. Para empezar, voy a introducir algo de terminologa: los errores , ya que voy a utilizar el trmino, son las consecuencias de la ley de Murphy. Si algo puede salir mal, saldr mal: un archivo que el programa tiene que leer no podr contar, un disco que tiene que escribir a la voluntad de estar lleno, el servidor que est hablando se estrellar, o la red va a bajar. Si alguna de estas cosas sucedan, puede dejar un pedazo de cdigo que haga lo que desee. Pero no hay error, no hay lugar en el cdigo que se puede fijar para que el archivo existe o no existe el disco no est

lleno. Sin embargo, si el resto del programa est en funcin de las acciones que se van a tomar, entonces ser mejor que tratar con el error de alguna manera o si se han introducido un error. Por lo tanto, los errores no son causados por errores, pero dejar de lado para manejar un error es casi seguro que un error. As que, qu significa para gestionar un error? En un programa bien escrito, cada funcin es un cuadro negro que oculta su funcionamiento interno. Los programas se construyen a partir de entonces las capas de funciones: funciones de alto nivel se construyen en la parte superior de las funciones de nivel inferior, y as sucesivamente. Esta jerarqua de la funcionalidad en tiempo de ejecucin se manifiesta en la forma de la pila de llamadas: si alta llamadas medianas , que llama baja , cuando el flujo de control est en baja , es tambin an en medio y alto , es decir, que todava estn en la la pila de llamadas. Debido a que cada funcin es un cuadro negro, los lmites de la funcin son un lugar excelente para lidiar con los errores. Cada funcin - de baja , por ejemplo tiene un trabajo que hacer. Su llamada directa - a medio , en este caso - se cuenta con ella para hacer su trabajo. Sin embargo, un error que le impide hacer su trabajo pone todas sus llamadas en situacin de riesgo: medio de llamados de baja porque necesita el trabajo realizado que baja lo hace, y si el trabajo no se hacen, a medio est en problemas. Pero esto significa quea medio llamada 's, alta , tambin est en problemas - y as sucesivamente hasta la pila de llamadas a la parte superior del programa. Por otro lado, debido a que cada funcin es un cuadro negro, si cualquiera de las funciones en la pila de llamadas de alguna manera pueden hacer su trabajo a pesar de los errores fundamentales, ninguna de las funciones anteriores se tiene que saber que haba un problema - todas las funciones importa es que la funcin se llama de alguna manera hizo el trabajo que se espera de l. En la mayora de los idiomas, se controlan los errores mediante la devolucin de una funcin en su defecto la persona que llama y dar la opcin de recuperacin o en su defecto en s.Algunos idiomas usan el mecanismo de devolucin de la funcin normal, mientras que las lenguas con las excepciones devolver el control al tirar o levantar una excepcin. Las excepciones son una gran mejora sobre el uso de la funcin vuelve a la normalidad, pero ambos sistemas sufren de un defecto comn: mientras que la bsqueda de una funcin que puede recuperarse, la pila se desenvuelve, lo que significa que el cdigo que podra recuperarse tiene que

hacerlo sin el contexto de lo que el menor cdigo de nivel estaba tratando de hacer cuando el error ocurri realmente. Considere la cadena de llamadas hipottico de alta , media , baja . Si baja un error y medio no se puede recuperar, la pelota est en el alto tribunal "s. Para alta para controlar el error, o bien debe hacer su trabajo sin la ayuda de medio o de alguna manera cambiar las cosas para llamar a medio va a funcionar y llame de nuevo. La primera opcin es tericamente limpia, sino que implica una gran cantidad de cdigo adicional - una implementacin de conjunto extra de lo que fuera medio se supone que debe hacer. Y se desenrolla ms de la pila, el trabajo ms que necesita ser hecho de nuevo. La segunda opcin - hacer las paces y volver a intentarlo - es difcil, por alta que sea capaz de cambiar el estado del mundo, por lo que una segunda llamada en medio de no terminar causando un error en baja , se necesitara un indecoroso conocimiento de los mecanismos internos de ambas medianas y bajas , contrariamente a la idea de que cada funcin es un cuadro negro. El Camino de Lisp Sistema de Common Lisp de errores de manejo le da una salida a este dilema, ya que permite separar el cdigo que realmente se recupera de un error en el cdigo que decide la forma de recuperar. Por lo tanto, usted puede colocar el cdigo de recuperacin de funciones de bajo nivel sin comprometerse a utilizar realmente cualquier estrategia de recuperacin particular, dejando que la decisin de cdigo en funciones de alto nivel. Para tener una idea de cmo funciona esto, supongamos que usted est escribiendo una aplicacin que lee algn tipo de archivo de registro de texto, tales como registro de un servidor Web. En algn lugar de su aplicacin tendr una funcin para analizar las entradas del registro individuales. Supongamos que usted va a escribir una funcin,de anlisis, registro de entrada , que se pasa una cadena que contiene el texto de una entrada de registro nico y que se supone que debe devolver unregistro de entrada de objeto que representa la entrada. Esta funcin se llama desde una funcin, de anlisis, de archivo de registro , que lee un archivo de registro completo y devuelve una lista de objetos que representan a todas las entradas en el archivo.

Para simplificar las cosas, el anlisis sintctico-registro de entrada de la funcin no ser necesario para analizar las entradas de formato incorrecto. Ser, sin embargo, ser capaz de detectar cuando su entrada est mal formado. Pero qu debe hacer cuando se detecta una mala entrada? En C que te devuelven un valor especial para indicar que haba un problema. En Java o Python que tirara o lanzar una excepcin. En Common Lisp, que indican una condicin. Condiciones Una condicin es un objeto cuya clase indica la naturaleza general de la condicin y cuyos datos de instancia, lleva la informacin sobre los detalles de las circunstancias particulares que llevan a la condicin de ser sealado. 3 En este programa de registro de anlisis hipottico, podra definir una clase de condicin,malformaciones-registro de entrada de error , que de anlisis, registro de entrada indicar si se ha dado datos que no se puede analizar. Clases de condicin se definen con la DEFINIR-CONDICIN macro, que trabaja esencialmente el mismo que DEFCLASS la excepcin de que la superclase de las clases definidas por defecto con DEFINE-ESTADO es CONDICIN en lugar de objetos estndar . Las ranuras se especifican de la misma manera, y las clases de condicin se puede multiplicar por separado y heredar de otras clases que descienden de ESTADO . Sin embargo, por razones histricas, las clases de condicin no estn obenlazados a darse casos deNORMA-OBJETO , por lo que algunas de las funciones que se utilizan con DEFCLASS clases de educacin no estn obenlazados a trabajar en las condiciones. En particular, las franjas horarias de una condicin que no se puede acceder mediante SLOT-VALOR , debe especificar una : Lector de opcin o un : acceso opcin para cualquier ranura cuyo valor se va a utilizar. Del mismo modo, los nuevos objetos de condicin se crean con MAKE-ESTADO en lugar de HACER INSTANCIA- . MAKE-CONDICIN inicializa las ranuras de la condicin de nuevo basado en la : initarg s que ha pasado, pero no hay manera de personalizar an ms la inicializacin de una condicin, equivalente aINICIALIZAR -INSTANCIA . 4 Cuando se utiliza la condicin de sistema de gestin de errores, debe definir las condiciones de las subclases de ERROR , una subclase de ESTADO . Por lo tanto, se puede definircon formato incorrecto, registro de entrada de error , con una ranura

para sostener el argumento de que se pas alanlisis sintctico-registro de entrada , de esta manera:
(Definir condicin incorrecto-registro de entrada de error (error) ((Texto: initarg: texto: el texto lector)))

Manejadores de condiciones En anlisis sintctico-registro de entrada tendr una seal de malformacionesregistro de entrada de error si no puede analizar la entrada de registro. Usted hace una sea con la funcin de los errores ERROR , que llama a la funcin de bajo nivel SEAL y las gotas en el depurador si la condicin no se maneja.Usted puede llamar ERROR dos maneras: se puede pasar un objeto ya condicin de instancia, o se puede pasar el nombre de la clase y cualquier otra condicin initargs necesarios para construir una nueva condicin, y se crear una instancia de la condicin para usted. El primero es en ocasiones tiles para resignaling un objeto condicin existente, pero el segundo es ms conciso. De este modo, se podra escribir de anlisis-registro de entrada como esta, eludiendo los detalles de la realidad, el anlisis de una entrada de registro:
(Texto de anlisis defun-registro de entrada de () (Si (bien formado registro de entrada de texto-p) (Make-instance 'registro de entrada ...) (Error de "formato incorrecto, registro de entrada de error: texto texto)))

Qu sucede cuando el error se seala depende del cdigo anterior de anlisis, registro de entrada en la pila de llamadas. Para evitar el aterrizaje en el depurador, se debe establecer un manejador de condiciones en una de las funciones que llevan a la llamada a parse-registro de entrada . Cuando una condicin es sealada, la maquinaria de sealizacin se ve a travs de una lista de controladores de estado activo, en busca de un controlador que puede manejar la condicin de ser sealado sobre la base de la condicin de clase. Cada manejador de condiciones consta de un especificador de tipo que indica qu tipos de condiciones que puede manejar y una funcin que toma un solo argumento, la condicin. En cualquier momento dado puede haber muchos manejadores de condiciones activos establecidos en distintos niveles de la pila de llamadas. Cuando una condicin es sealada, la maquinaria de sealizacin encuentra el controlador ms recientemente establecido cuyo especificador de tipo es compatible con la condicin que se est sealizado y llama a su funcin, pasndole el objeto de condicin.

La funcin de controlador a continuacin, puede elegir si desea manejar la condicin. La funcin puede negarse a tratar la condicin de simplemente regresar con normalidad, en cuyo caso se devuelve el control a la SEAL funcin, que buscar el siguiente manejador ms reciente creacin con un especificador de tipo compatible. Para manejar la enfermedad, la funcin debe transferir el control fuera de la SEAL a travs de una salida no local . En la siguiente seccin, veremos cmo un controlador puede elegir dnde transferir el control. Sin embargo, muchos manejadores de condiciones simplemente quiere descansar la pila en el lugar donde se establecieron y luego ejecutar algn cdigo. La macro -MANEJO DE CASOestablece este tipo de manejador de condiciones. La forma bsica de un MANIPULADOR-CASE es la siguiente:
(Manejador de los casos la expresin de la clusula de errores *)

donde cada error de la clusula es de la forma siguiente:


( condiciones de tipo ([ var ]) el cdigo )

Si la expresin devuelve normalmente, entonces su valor es devuelto por el MANIPULADOR-CASE . El cuerpo de un MANIPULADOR-CASE debe ser una expresin nica, puede utilizar progn de combinar varias expresiones en una sola forma. Si, sin embargo, la expresin indica una condicin que es una instancia de cualquiera de las condiciones de tipo s se especifica en ninguna clusula de errores , el cdigo en la clusula de error apropiado es ejecutado y su valor devuelto por la MANIPULADOR-CASE . La var , si se incluye, es el nombre de la variable que contendr el objeto de condicin, cuando el cdigo del controlador se ejecuta. Si el cdigo no es necesario para acceder al objeto de condicin, se puede omitir el nombre de la variable. Por ejemplo, una manera de manejar la malformacin-registro de entrada, el error sealado por parse-registro de entrada en su persona que llama, anlisis sintctico de archivo de registro , sera ignorar la entrada con formato incorrecto. En la siguiente funcin, el MANEJO DE CASO- ya sea la expresin devolver el valor devuelto por parse-registro de entrada o el retorno NIL si una malformacinregistro de entrada de error se indica.(El que en el LOOP clusula recoger lo es otro LOOP palabra clave, lo que se refiere al valor de la prueba ms recientemente evaluado condicional, en este caso el valor deentrada .)
(Defun-parse de archivo de registro (archivo)

(Con-de archivos abiertos (en el archivo: direccin: de entrada) (bucle para el texto = (lase lnea en ninguna ninguna), mientras que el texto para la entrada = (manejador de los casos (anlisis sintctico-registro de entrada de texto) (Malformaciones-registro de entrada-error () nil)) cuando la entrada se recogen)))

Cuando parse-registro de entrada devuelve normalmente, su valor ser asignado a la entrada y recogida por el LOOP . Pero side anlisis-registro de entrada de seales de una malformacin-registro de entrada de error , entonces la clusula de error devolver NIL , que no sern recogidos.
JAVA-STYLE EXCEPTON MANEJO
MANIPULADOR-CASE es el anlogo ms cercano en Common Lisp que en Java o Python manejo de excepciones de estilo. Dnde podra escribir esto en Java: try { doStuff (); doMoreStuff (); Captura} (SomeException se) { recuperar (se); } o esta en Python: Proveedores: doStuff () doMoreStuff () excepto SomeException, s: recuperar (se) en Common Lisp que iba a escribir esto: (Manejador de caso (Progn (Do-cosas) (Lo-ms-cosas)) (Alguna excepcin-(se) (Recuperacin SE)))

Esta versin del anlisis sintctico de archivo de registro tiene una deficiencia grave: se est haciendo demasiado. Como su nombre indica, el trabajo deanlisis sintctico del archivo de registro es para analizar el archivo y producir una lista de registro de entrada- objetos, si no puede, no es su lugar para decidir qu hacer en su lugar. Y si desea utilizar anlisis sintctico-archivo de registro en una aplicacin que quiere decirle al usuario que el archivo de registro est daado o es que quiere recuperarse de las entradas mal formadas mediante la fijacin de ellos y volver a analizar? O tal vez una aplicacin est muy bien con los saltos, pero slo hasta un cierto nmero de entradas corruptas se han visto. Usted podra tratar de solucionar este problema moviendo el MANIPULADORCASE a una funcin de nivel superior. Sin embargo, entonces no tendra ninguna manera de implementar la poltica actual de saltarse las entradas individuales cuando el error fue sealado, la pila se desenrolla todo el camino a la funcin de ms alto nivel, abandonando el anlisis del archivo de registro completo. Lo que

queremos es una manera de proporcionar la estrategia actual de recuperacin sin necesidad de que siempre se utiliza. Reinicia La condicin de sistema le permite hacer esto mediante la divisin del control de errores de cdigo en dos partes. Usted coloca el cdigo que en realidad se recupera de errores enreinicios , y manejadores de condiciones puede manejar una condicin mediante la invocacin de un reinicio apropiadas. Usted puede colocar el cdigo en reiniciar las funciones a medio o bajo nivel, como el anlisis sintctico-archivo de registro o de anlisis, registro de entrada , mientras se mueven los manejadores de condiciones en los niveles superiores de la aplicacin. Para cambiar de anlisis de archivo de registro, por lo que se establece un reinicio en lugar de un manejador de condiciones, se puede cambiar elMANIPULADORCASE con un CASE-RESTART . La forma de RESTART-CASE es muy similar a un MANIPULADOR-CASE , excepto los nombres de los reinicios son slo nombres, no necesariamente los nombres de las clases de condicin. En general, un nombre de reinicio deber describir la accin de la reanudacin toma. Enanlisis sintctico de archivo de registro , puede llamar a la reanudacin de saltos de registro de entrada ya que eso es lo que hace. La nueva versin se ver as:
(Defun-parse de archivo de registro (archivo) (Con-de archivos abiertos (en el archivo: direccin: de entrada) (bucle para el texto = (lase lnea en ninguna ninguna), mientras que el texto para la entrada = (reinicio de los casos (anlisis sintctico-registro de entrada de texto) (Skip-registro de entrada () nil)) cuando la entrada se recogen)))

Si se llama a esta versin del anlisis sintctico de archivo de registro en un archivo de registro que contiene entradas daadas, no va a manejar el error directamente, que terminar en el depurador. Sin embargo, entre los que se reinicie varias presentadas por el depurador ser una llamadade saltos de registro de entrada , que, si lo desea, har anlisis sintctico de archivo de registro para continuar en su camino como antes.Para evitar terminar en el depurador, se puede establecer un manejador de condiciones que invoca el skip-registro de entrada se reinicie automticamente. La ventaja de establecer un reinicio en lugar de tener -parse de archivo de registro manejar el error directamente es que haceanlisis sintctico de archivo de registro

que puedan utilizarse en ms situaciones. El cdigo de alto nivel que llama a parsearchivo de registrono tiene que invocar a los salto-de entrada-registro de reinicio. Se puede elegir para controlar el error en un nivel superior. O, como voy a mostrar en la siguiente seccin, puede agregar reinicia al anlisis sintctico-registro de entrada para proporcionar otras estrategias de recuperacin, a continuacin, manejadores de condiciones puede elegir qu tipo de estrategia que desea utilizar. Pero antes de que se puede hablar de eso, usted necesita para ver cmo configurar un manejador de condiciones que va a invocar lasde salto de entrada de inicio de sesin de reinicio. Usted puede configurar el controlador de cualquier parte de la cadena de llamadas que conducen aanlisis sintctico de archivo de registro . Esto puede ser muy alto en su solicitud, no necesariamente en elanlisis sintcticoarchivo de registro de llamadas 's directa. Por ejemplo, supongamos que el principal punto de entrada a su solicitud es una funcin,analizador de registro , que se encuentra un montn de troncos y las analiza con la funcin de analizar-registro , que finalmente conduce a una llamada alanlisis sintctico de archivo de registro . Sin ningn tipo de control de errores, que podra tener este aspecto:
(Defun Log-Analyzer () (Dolist (log (encontrar-all-logs)) (Anlisis log-log)))

El trabajo de anlisis-log es llamar, directa o indirectamente, de anlisis, registro de archivos y luego hacer algo con la lista de entradas de registro devueltos. Una versin muy simple podra tener este aspecto:
(Defun analiza-log (log) (Dolist (entrada (anlisis sintctico-log-archivo de registro)) (Anlisis de entrada de la entrada)))

donde la funcin de analizar la entrada es de suponer que cualquiera que sea responsable de la extraccin de informacin que te interesa de cada entrada de registro y escondindolo en algn lugar. As, la ruta de acceso desde la funcin de nivel superior, registro analizador- , a anlisis sintctico-log-entrada , que en realidad seala un error, es como sigue:

Suponiendo que uno siempre quiere saltar las entradas de registro con formato incorrecto, puede cambiar esta funcin para crear un manejador de condiciones que invoca elskip-registro de entrada de reiniciar para usted. Sin embargo, no se

puede utilizar MANIPULADOR-CASE para establecer el manejador de condiciones, porque entonces la pila se desenrolla a la funcin donde el MANIPULADOR-CASE aparece. En su lugar, usted necesita utilizar la macro de nivel inferior MANIPULADOR-BIND . La forma bsica de MANIPULADOR-BIND es el siguiente:
(Manejador-bind ( vinculante *) formulario *)

donde cada enlace es una lista de un tipo de estado y una funcin de controlador de un argumento. Despus de los enlaces de controlador, el cuerpo de la MANIPULADOR-BINDpuede contener cualquier nmero de formas. A diferencia del cdigo del controlador de MANIPULADOR-CASE , el cdigo del controlador debe ser un objeto de la funcin, y se debe aceptar un solo argumento. Una diferencia ms importante entre MANIPULADOR-BIND y gestiona el caso- es que la funcin de controlador obenlazado porMANIPULADOR-BIND se llevar a cabo sin desenrollar la pila - el flujo de control seguir siendo en la llamada a parseregistro de entrada cuando esta funcin se llama. La llamada a Invoke-RESTART se encuentra e invocar la reanudacin ms recientemente vinculado con el nombre dado. As que usted puede agregar un controlador deLog-Analyzer que invocar el skip-registro de entrada de reiniciar establecido en el anlisis sintctico-archivo de registro de esta manera:5
(Defun Log-Analyzer () (Manejador-bind ((malformaciones-registro de entrada de error # '(Lambda (c) (Invoke-restart 'skip-registro de entrada)))) (Dolist (log (encontrar-all-logs)) (Anlisis log-log))))

En este MANIPULADOR-BIND , la funcin de controlador es una funcin annima que llama a la reanudacin de saltos de registro de entrada . Tambin puede definir una funcin con nombre que hace lo mismo y se unen en su lugar. De hecho, una prctica comn en la definicin de un reinicio es definir una funcin, con el mismo nombre, y teniendo un solo argumento, la condicin, que invoca a la reanudacin del mismo nombre. Tales funciones se llaman funciones de reiniciar . Se puede definir una funcin de reinicio para saltar-registro de entrada de esta manera:
(Defun saltos de registro de entrada (c) (Invoke-restart 'skip-registro de entrada))

A continuacin, puede cambiar la definicin de analizador de registro a lo siguiente:


(Defun Log-Analyzer ()

(Manejador-bind ((malformaciones-registro de entrada de error # 'skip-registro de entrada)) (Dolist (log (encontrar-all-logs)) (Anlisis log-log))))

Como se ha dicho, el salto-registro de entrada de la funcin de reinicio se supone que un salto de entrada de inicio de sesin de reinicio se ha establecido. Si un formato incorrecto, registro de entrada de error est siempre indicado por el cdigo llamado de Log-Analyzer sinskip-registro de entrada despus de haber sido establecida, la llamada a Invoke-RESTART ser una seal de CONTROL-ERROR cuando no puede encontrar elskip- registro de entrada de reiniciar. Si desea permitir la posibilidad de que una malformacin-registro de entrada de error puede ser sealado de cdigo que no tiene un salto-registro de entrada de reiniciar establecido, puede cambiar el skip-registro de entrada a esta funcin:
(Defun saltos de registro de entrada (c) (Let ((reiniciar (encontrar-restart 'skip-registro de entrada))) (Cuando se reinicie (invoke-reiniciar reiniciar))))

FIND-RESTART busca de un reinicio con un nombre y devuelve un objeto que representa la reanudacin, si el reinicio se encuentra y NIL en caso contrario. Puede invocar el reinicio pasando el objeto de reinicio para INVOKE-RESTART . As, cuando skip-registro de entrada es atado con MANIPULADOR-BIND , que se encargar de la condicin mediante la invocacin del skip-registro de entrada de reiniciar, si est disponible y de lo contrario volver con normalidad, dando a los controladores de otras condiciones, unidos ms alto en la pila, una oportunidad para manejar la condicin. Proporcionar varios reinicios Desde que se reinicia debe ser expresamente invocado para tener algn efecto, se pueden definir varios reinicios, cada uno ofreciendo una estrategia de recuperacin diferente. Como mencion anteriormente, no todas las aplicaciones de anlisis de log-necesariamente quiere saltarse las entradas mal formadas. Algunas aplicaciones podran querer-parse de archivo de registro para incluir un tipo especial de objeto que representa las entradas mal formadas en la lista de registro de entrada- objetos, otras aplicaciones pueden tener alguna forma de reparar una entrada mal y tal vez quieran una manera de pasar la entrada fija de nuevo deanlisis sintctico-registro de entrada .

Para permitir que los protocolos de recuperacin ms complejas, se reinicia puede tomar argumentos arbitrarios, que se pasan en la llamada a Invoke-RESTART . Puede proporcionar apoyo tanto a las estrategias de recuperacin de los que acabo de mencionar la adicin de dos reinicios del equipo paraanlisis sintctico-registro de entrada , cada uno de ellos toma un solo argumento. Uno simplemente devuelve el valor que se pasa como el valor de retorno deanlisis sintctico-registro de entrada , mientras que el otro intenta analizar su argumento en el lugar de la entrada en el registro original.
(Texto de anlisis defun-registro de entrada de () (Si (bien formado registro de entrada de texto-p) (Make-instance 'registro de entrada ...) (Reinicio de los casos (con formato incorrecto, registro de entrada de error de error ": texto texto) (Valor de uso (valor) de valor) (Reanlisis de entrada (fijo-texto) (anlisis sintctico-log-fijo de entrada de texto)))))

El nombre de El valor de uso es un nombre estndar para este tipo de reinicio. Common Lisp define una funcin de reinicio para El valor de uso similar a lade saltos de registro de entrada de la funcin que acaba de definir. As pues, si usted quiere cambiar la poltica en las entradas mal formadas a uno que crea una instancia de malformaciones-registro de entrada , puede cambiar de registro analizador a este (suponiendo que la existencia de unamalformacin-registro de entrada de clase con un : texto initarg ):
(Defun Log-Analyzer () (Manejador-bind ((malformaciones-registro de entrada de error # '(Lambda (c) (Valor de uso (Make-instance 'incorrecto-registro de entrada: texto (c)))))) (Dolist (log (encontrar-all-logs)) (Anlisis log-log))))

Tambin podra haber puesto estas nuevas reinicia en anlisis sintctico de archivo de registro en vez deanlisis sintctico-registro de entrada . Sin embargo, por lo general, quiere poner reinicia en el cdigo de ms bajo nivel posible. No sera, sin embargo, ser apropiado para mover el salto de entrada de inicio de sesin de reinicio en el anlisis sintctico-registro de entrada ya que podra causaranlisis sintctico-registro de entrada para volver a veces, normalmente con NIL , lo mismo que se inici tratando de evitar. Y sera una idea igual de mal para eliminar los saltos de entrada de inicio de sesin de reinicio de la teora de que el manejador de condiciones podra conseguir el mismo efecto mediante la invocacin de los valores

de uso de reinicio con NIL como el argumento, que requerira que el manejador de condiciones de tienen un conocimiento ntimo de cmo losanlisis sintcticoarchivo de registro con las obras. En su forma actual, el salto-registro de entrada es una parte bien abstracto, desenlazado de la API de inicio de sesin de anlisis. Otros usos de las Condiciones Mientras que las condiciones se utilizan principalmente para el tratamiento de errores, que pueden ser utilizados para otros fines - usted puede utilizar las condiciones, los controladores de condicin, y se reinicia la construccin de una gran variedad de protocolos entre el cdigo de bajo y de alto nivel. La clave para entender el potencial de las condiciones es entender que meramente sealizacin una condicin no tiene ningn efecto sobre el flujo de control. La sealizacin de la funcin primitiva SEAL implementa el mecanismo de bsqueda de un manejador de condiciones de aplicacin y la invocacin de su funcin de controlador. La razn de un controlador puede negarse a manejar una condicin normal mediante la devolucin se debe a que la llamada a la funcin de controlador es slo una llamada a la funcin normal - cuando los rendimientos del controlador, el control pasa de nuevo a LA SEAL , que luego mira para otro, menos vinculado recientemente controlador que puede manejar la condicin. Si SEAL se queda sin manejadores de condiciones antes de que la condicin se maneja, sino que tambin vuelve con normalidad. El ERROR funcin que ha estado usando las llamadas SEAL . Si el error es manejada por un manejador de condiciones que transfiere el control a travs de MANIPULADOR-CASEo invocando un reinicio, entonces la llamada a la SEAL nunca regresa. Pero si SEAL devoluciones, ERROR invoca el depurador llamando a la funcin almacena en* debugger-hook * . Por lo tanto, una llamada a ERROR no puede volver con normalidad; la condicin debe ser manejadas por un manejador de condiciones o en el depurador. Otra de las funciones de sealizacin de estado, WARN , proporciona un ejemplo de un tipo diferente de protocolo basado en el sistema de condicin. Al igual que ERROR , WARNllamadas SEAL para indicar una condicin. Pero si SEAL devoluciones, WARN no invoca el depurador - se imprime la condicin de * ERRORES DE SALIDA * y devuelveNIL ., lo que permite a quien lo llama para proceder WARN tambin establece un reinicio, ADVERTENCIA MUFLA- , en torno

a la llamada a la SEAL de que puede ser utilizado por un manejador de condiciones para hacer WARN retorno sin imprimir nada. La funcin de reinicio MUFLA-ADVERTENCIA encuentra y llama a sus epnimos reinicio, lo que indica un ERROR DE CONTROL- si no hay reinicio de esas caractersticas. Por supuesto, una condicin sealizado con WARN tambin podra ser manejado de alguna otra manera - un manejador de condiciones podra "promover" una advertencia a un error por el manejo de ella como si se tratara de un error. Por ejemplo, en la solicitud de inicio de sesin de anlisis, si hay maneras en que una entrada del registro podra estar ligeramente incorrecto, pero sigue siendo apta para su procesamiento, se podra escribir de anlisis, registro de entrada para seguir adelante y analizar las entradas un poco defectuosos, pero para indicar una condicin con la WARN , cuando lo hizo. A continuacin, la aplicacin ms grande podra optar por dejar la impresin de advertencia, para amortiguar la advertencia, o para tratar la advertencia como un error, la recuperacin de la misma manera que lo hara a partir de un formato incorrecto, registro de entrada de error . Un tercio de error-sealizacin funcin, CERROR , proporciona todava otro protocolo. Al igual que ERROR , CERROR le dejar en el depurador si la condicin que seala que no se maneja. Pero al igual que WARN , se establece un reinicio antes de que se seala la condicin. El reinicio, CONTINUE , hace CERROR para volver normal - si el reinicio es invocado por un manejador de condiciones, que le mantendr fuera del depurador completo. De lo contrario, puede utilizar el reinicio una vez que ests en el depurador para reanudar el cmputo inmediatamente despus de la llamada a CERROR . La funcin CONTINUAR encuentra y llama a la CONTINUE reinicia si est disponible y devuelve NIL lo contrario. Tambin puede crear sus propios protocolos de SEAL - cada vez que baja a nivel de cdigo debe comunicar dicha informacin a la pila de llamadas a un mayor nivel de cdigo, el mecanismo de condicin es un mecanismo razonable para su uso. Pero para la mayora de los propsitos, uno de los errores estndar o protocolos de alerta debera ser suficiente. Vamos a usar el sistema de condiciones en los futuros captulos prcticos, tanto para el manejo de errores regular y, en el captulo 25, para ayudar en el manejo de un caso extremo complicado de analizar los archivos ID3. Por desgracia, es el destino de control de errores para obtener siempre muy poca atencin en los textos

de programacin - un correcto manejo, o falta de ella, es a menudo la mayor diferencia entre el cdigo y el cdigo ilustrativa templado, con calidad de produccin. El truco para escribir este ltimo tiene ms que ver con la adopcin de una forma particularmente rigurosa de pensar sobre el software que con los detalles de las construcciones del lenguaje de programacin particular. Dicho esto, si su meta es escribir ese tipo de software, se encuentra el sistema de Common Lisp condicin es una excelente herramienta para la escritura de cdigo slido y uno que se adapte muy bien en el estilo de desarrollo incremental de Common Lisp.
Escribir software robusto
Para informacin sobre cmo escribir software robusto, que podra hacer peor que empezar por encontrar una copia de la fiabilidad del software (John Wiley & Sons, 1976) de Glenford J. Meyers. Bertrand Meyer escritos sobre Diseo por Contrato tambin proporcionan una manera til de pensar acerca de la exactitud del software. Por ejemplo, vanse los captulos 11 y 12 de su construccin de software orientada a objetos (Prentice Hall, 1997). Tenga en cuenta, sin embargo, que Meyer Bertrand es el inventor de Eiffel, una esclavitud de forma esttica y el lenguaje escrito disciplina en la escuela Algol / Ada. Mientras que l tiene un montn de cosas inteligentes que decir acerca de la orientacin a objetos y la fiabilidad del software, hay una brecha bastante amplia entre su punto de vista de la programacin y El Camino de Lisp. Por ltimo, para un excelente resumen de los temas ms importantes que rodean construccin sistemas de alta disponibilidad, consulte el Captulo 3 del clsico de procesamiento de transacciones: Conceptos y Tcnicas (Morgan Kaufmann, 1993) de Jim Gray y Andreas Reuter.

En el prximo captulo voy a dar una visin general de algunos de los 25 operadores especiales que no han tenido la oportunidad de utilizar con todo, al menos no directamente.

1 Tira o plantea una excepcin en Java / Python trminos 2 capturas la excepcin en Java / Python trminos 3 En este sentido, una condicin que se parece mucho a una excepcin en Java o Python, excepto que

no todas las condiciones de representar a un error o una excepcional situacin.


4 En algunas implementaciones Common Lisp, las condiciones se definen como subclases de

NORMA-OBJETO , en cuyo caso SLOT-VALOR , MAQUILLAJE INSTANCIA , y INITIALIZE INSTANCIA- va a funcionar, pero no es porttil para confiar en ella.
5 El compilador puede quejarse si el parmetro no se utiliza nunca. Usted puede silenciar que la

alerta mediante la adicin de una declaracin (declare (ignorar c)) como la primera expresin de laLAMBDA cuerpo.

20. Los Operadores Especiales


En cierto modo, el aspecto ms impresionante del sistema de condicin cubierta en el captulo anterior es que si no fuera ya parte de la lengua, podra ser escrito enteramente como una biblioteca de nivel de usuario. Esto es posible porque los operadores especiales Common Lisp -, mientras que no toca directamente en la sealizacin o las condiciones de manejo - proporcionar suficiente acceso a los mecanismos subyacentes de la lengua para ser capaz de hacer cosas como el control de la anulacin de la pila. En los captulos anteriores he hablado de los operadores ms utilizados especiales, pero vale la pena estar familiarizado con los dems por dos razones. En primer lugar, algunos de los operadores de uso poco frecuente especiales se utilizan con poca frecuencia, simplemente porque todo lo que necesita se dirigen no se plantea a menudo. Es bueno estar familiarizado con estos operadores especiales por lo que cuando uno de ellos se pide, usted por lo menos saben que existe. En segundo lugar, debido a que los 25 operadores especiales, junto con la regla bsica para la evaluacin de las llamadas a funciones y el incorporado en los tipos de datos proporcionan la base para el resto del lenguaje, una cierta familiaridad con ellos le ayudar a entender cmo funciona el lenguaje . En este captulo, hablaremos de todos los operadores especiales, brevemente, algunos y algunas de longitud, para que pueda ver cmo encajan entre s. Voy a sealar cules se puede esperar para utilizar directamente en su propio cdigo, cules sirven como base para otras construcciones que se utilizan todo el tiempo, y que los que rara vez vamos a usar directamente, sino que puede ser til en macro cdigo generado. Control de Evaluacin La primera categora de operadores especiales contiene los tres operadores que proporcionan un control bsico de la evaluacin de las formas. Son PRESUPUESTO , SI , y progn , y he hablado a todos ya. Sin embargo, vale la pena notar cmo cada uno de estos operadores especiales ofrece un tipo fundamental de control sobre la evaluacin de una o ms formas.CITA evita la evaluacin en conjunto y le permite obtener en la S-expresiones como la de datos. SI proporciona la operacin fundamental de la eleccin booleano de que todos los otros constructos

condicionales de ejecucin puede ser construido. 1 Y progn proporciona la capacidad de secuencia de un nmero de formas. Manipular el entorno de lxico La clase ms grande de operadores especiales contiene los operadores que manipulan y acceder al entorno lxico . LET y LET * , que ya hemos discutido, son ejemplos de operadores especiales que manipulan el entorno lxico, ya que pueden introducir nuevos enlaces lxicos de las variables. Cualquier construccin, tales como una DO o DOTIMES , que se une a las variables lxicas tendr que ampliarse en un LET o LET * . 2 El setq operador especial es la que tiene acceso al entorno lxico, ya que puede ser utilizado para definir las variables cuyos enlaces fueron creados por LET y LET * . Las variables, sin embargo, no son la nica cosa que puede ser nombrado dentro de un mbito lxico. Aunque la mayora de funciones estn definidas a nivel global con DEFUN , tambin es posible crear funciones locales con los operadores especiales FLET y ETIQUETAS , macros locales con MACROLET , y un tipo especial de macro, llamados un smbolo de macro , con SMBOLO-MACROLET . Al igual que LET le permite introducir una variable lxica cuyo mbito de aplicacin es el cuerpo de la LET , FLET y ETIQUETAS le permiten definir una funcin que se puede denominar nicamente dentro del mbito de la FLET o ETIQUETAS formulario. Estos operadores especiales son muy tiles cuando se necesita una funcin local que es un poco demasiado complejo para definir en lnea como un Lambda expresin o de que es necesario utilizar ms de una vez. Ambos tienen la misma forma bsica, que se parece a esto:
(FLET ( la funcin de definicin *) forma corporal *)

y de esta manera:
(Etiquetas que la funcin de definicin *) forma corporal *)

donde cada funcin de definicin tiene la siguiente forma:


( nombre ( parmetro *) formulario de *)

La diferencia entre FLET y ETIQUETAS es que los nombres de las funciones definidas con FLET slo se puede utilizar en el cuerpo de la FLET , mientras que los nombres introducidos por ETIQUETAS se puede utilizar inmediatamente, incluso

en los cuerpos de las funciones definidas por las ETIQUETAS . Por lo tanto, ETIQUETAS pueden definir funciones recursivas, mientras FLET no se puede. Podra parecer que la limitacin de FLET no puede ser utilizado para definir las funciones recursivas, pero Common Lisp proporciona FLET y ETIQUETAS porque a veces es til ser capaz de escribir las funciones locales que pueden llamar a otra funcin del mismo nombre, ya sea una funcin definida globalmente o un local de la funcin de un mbito de inclusin. Dentro del cuerpo de un FLET o ETIQUETAS , puede utilizar los nombres de las funciones definidas como cualquier otra funcin, incluso con la FUNCIN operador especial.Puesto que usted puede utilizar FUNCIN para obtener el objeto de la funcin que representa una funcin definida con FLET o ETIQUETAS , y desde un FLET o ETIQUETASpuede ser en el mbito de aplicacin de otras formas de enlace, como LET s, estas funciones pueden ser clausuras. Debido a que las funciones locales pueden referirse a las variables del mbito de inclusin, que a menudo pueden ser escritas para tomar un menor nmero de parmetros que las funciones de ayuda equivalentes. Esto es particularmente til cuando se necesita para pasar una funcin que toma un solo argumento como parmetro funcional. Por ejemplo, en la siguiente funcin, que se volver a ver en el captulo 25, el FLET funcin de edicin, nmero de versin , toma un nico argumento, como lo requiere lacita del directorio , sino que tambin puede utilizar la variable versiones , introducido por el adjuntando LET :
(Defun contar las versiones (dir) (Let (((versiones mapcar # '(lambda (x) (x contras 0))' (2 3 4)))) (FLET ((nmero de versin (archivo) (Incf (cdr (assoc (versin principal (lectura ID3 del archivo)) las versiones))))) (Walk-dir # 'recuento de la versin: test #' mp3-p)) versiones))

Esta funcin tambin podra ser escrito usando una funcin annima en el lugar de la FLET ed cuenta la versin , pero la funcin de dar un nombre significativo lo hace un poco ms fcil de leer. Y cuando una funcin auxiliar debe recurse, una funcin annima simplemente no va a hacer. 3 Si no desea definir una funcin auxiliar recursiva como una funcin global, puede utilizar ETIQUETAS . Por ejemplo, la siguiente funcin, recoger las hojas , se utiliza el ayudante de funcin recursiva paseo a pie de un rbol y se

renen todos los tomos en el rbol en una lista, que recogen las hojas, luego regresa (despus de darle la vuelta):
(Defun recoger las hojas (de rbol) (Let ((hojas ())) (Etiquetas ((pie (rbol) (Cond ((rbol de null)) ((rbol de tomo) (push hojas de los rboles)) (T (a pie (rbol de coche)) (A pie (cdr tree)))))) (rbol de pie)) (Hojas nreverse)))

Ntese de nuevo cmo, en el paseo de funcin, puede hacer referencia a la variable, las hojas , introducido por el adjuntando LET . FLET y ETIQUETAS tambin operaciones tiles para utilizar en expansiones de macros - una macro se puede ampliar en el cdigo que contiene un FLET o ETIQUETAS para crear funciones que se pueden utilizar dentro del cuerpo de la macro. Esta tcnica se puede utilizar ya sea para introducir funciones que el usuario de la macro se llaman o simplemente como una forma de organizar el cdigo generado por la macro. Esto, por ejemplo, es como una funcin como CALLNEXT-MTODO , que slo se puede utilizar en una definicin de mtodo, se podra definir. Un pariente cercano de FLET y ETIQUETAS es el operador especial MACROLET , que se puede utilizar para definir macros locales. Las macros locales que funcionan igual que las macros globales definidos con defmacro excepcin sin abarrotar el espacio de nombres global. Cuando un MACROLET forma se evala, las formas del cuerpo se evalan con las definiciones de macro locales en vigor y, posiblemente, la funcin global y sombras definiciones de las macros o las definiciones locales de las formas que encierra. Al igual que FLETy ETIQUETAS , MACROLET se puede utilizar directamente, pero tambin es un objetivo til para macro-cdigo generado - envolviendo un cdigo proporcionado por el usuario en unMACROLET , una macro puede proporcionar construcciones que se pueden utilizar slo dentro de ese cdigo o puede sombra de una macro definido globalmente. Vas a ver un ejemplo de este ltimo uso de la MACROLET en el captulo 31. Finalmente, una ltima macro-definicin de operador especial es SMBOLOMACROLET , que define un tipo especial de macro llamada, muy apropiadamente, un smbolo de macro .Macros de smbolos son como las macros regulares excepto

que no puede tener argumentos y se conocen con el smbolo de plano en lugar de un formulario de lista. En otras palabras, despus de haber definido una macro smbolo con un nombre en particular, el uso de ese smbolo en una posicin de valor se ampliar y la forma resultante evaluado en su lugar. As es como las macros, como CON-Slots y los CON Accessors son capaces de definir "variables" que acceden al estado de un objeto en particular bajo las sbanas. Por ejemplo, el siguiente CON-Slots forma:
(Con ranuras (xyz) foo (lista xyz)))

podra expandirse hacia el cdigo que utiliza SMBOLO-MACROLET :


(Let ((#: g149 foo)) (Smbolo-macrolet ((X (slot-valor #: g149 'x)) (Y (slot-valor #: g149-y)) (Z (slot-valor #: g149 'z))) (Lista de xyz)))

Cuando la expresin (lista xyz) se evala, los smbolos x , y y z ser reemplazado con sus expansiones, tales como (slot-valor #: g149 'x) . 4 Macros de smbolos son a menudo locales, que se define con el SMBOLOMACROLET , pero Common Lisp tambin proporciona una macro DEFINESYMBOL-MACRO que define una macro smbolo global. Una macro smbolo definido con MACROLET SMBOLO- sombras macros otros smbolos del mismo nombre definido conDEFINE-SYMBOL-MACRO o encierran MACROLET SMBOLOformas. Flujo local de Control de Los prximos cuatro operadores especiales que abordar tambin crear y usar nombres en el entorno lxico, sino a los efectos de alterar el flujo de control en lugar de definir nuevas funciones y macros. He mencionado estos cuatro operadores especiales, de paso, ya que proporcionan los mecanismos subyacentes utilizados por otras caractersticas del lenguaje. SonBLOQUE , return-from , TAGBODY , y GO . Los dos primeros, BLOQUE y return-from , se utilizan juntos para escribir el cdigo que devuelve de inmediato de una seccin de cdigo - habl de return-from en el captulo 5, como una manera de regresar de inmediato a partir de una funcin, pero es ms general que el . Los otros dos, TAGBODY y GO , proporcionan un muy bajo nivel de construccin goto esa es la base para todas las construcciones de bucles de alto nivel que ya has visto.

El esqueleto bsico de un BLOQUE formulario es este:


(Bloque de nombre de la forma *)

El nombre es un smbolo, y las formas son las formas de Lisp. Los formularios son evaluados en orden, y el valor de la ltima forma se devuelve como el valor de la BLOQUE menos que un RETORNO DE- se utiliza para devolver a partir del bloque temprano. Un RETORNO DE- forma, como se vio en el captulo 5, consiste en el nombre del bloque de regresar de y, opcionalmente, un formulario que proporciona un valor para volver. Cuando un RETORNO DE- se evala, hace que el llamado BLOQUE regresar de inmediato. SiRETORNO DE- se llama con un formulario de valor de retorno, el BLOQUE devolver el valor resultante, de lo contrario, el BLOQUE evala a NIL . A BLOQUE nombre puede ser cualquier smbolo, que incluye NIL . Muchas de las macros de constructo de control estndar, tales como DO , DOTIMES y dolist , generar una expansin que consiste en un BLOQUE llamado NIL . Esto le permite utilizar el RETORNO macro, que es un poco de azcar sintctica (ida y vuelta-desde cero ...) , para salir de tales bucles. As, el bucle siguiente imprimir ms menos diez nmeros al azar, parando tan pronto como se pone un nmero mayor que 50:
(Dotimes (i 10) (Let ((respuesta (al azar 100))) (Respuesta de impresin) (If (respuesta> 50) (vuelta))))

Funcin de definicin de macros como DEFUN , FLET , y ETIQUETAS , por su parte, envuelven sus cuerpos en un BLOCK con el mismo nombre que la funcin. Es por eso que usted puede utilizar return-from regresar de una funcin. TAGBODY y el IR tiene una relacin similar entre s como BLOQUE y REGRESO DE: un TAGBODY forma define un contexto en el que se definen los nombres que pueden ser utilizados por IR . El esqueleto de un TAGBODY es como sigue:
(Tagbody etiqueta-o-compuesto de forma *)

donde cada etiqueta-o-compuesto-forma es o bien un smbolo, llamado una etiqueta , o una forma no vaco lista. Los formularios de lista se evalan en el orden y las etiquetas ignoradas, excepto como lo veremos en un momento. Despus de la ltima forma de la TAGBODY se evala, el TAGBODY devuelve NIL . En cualquier lugar dentro del mbito lxico de la TAGBODY puede utilizar el GO operador especial

para saltar inmediatamente a cualquiera de las etiquetas, y la evaluacin se reanudar con la forma sigue a la etiqueta. Por ejemplo, puede escribir un bucle infinito con triviales TAGBODY y GO de esta manera:
(Tagbody superior (Print 'hola) (Ir arriba))

Tenga en cuenta que mientras que los nombres de las etiquetas debe aparecer en el nivel superior de la TAGBODY , no anidadas en otras formas, el GO operador especial puede aparecer en cualquier lugar dentro del mbito de la TAGBODY . Esto significa que usted puede escribir un bucle que se repite un nmero aleatorio de momentos como este:
(Tagbody superior (Print 'hola) (Cuando (plusp (random 10)) (ir arriba)))

Un ejemplo an ms tonto de la TAGBODY , lo que demuestra que puede tener varias etiquetas en una sola TAGBODY , se parece a esto:
(Tagbody un (print 'a) (if (zerop (al azar 2)) (ir c)) b (print 'b) (if (zerop (al azar 2)) (ir a)) c (letra 'c) (if (zerop (al azar 2)) (vaya b)))

Esta forma se saltan al azar imprimir una s, b s, y c s hasta que finalmente los ltimos RANDOM expresin devuelve 1 y el control llega al final de la TAGBODY . TAGBODY rara vez se utiliza directamente, ya que casi siempre es ms fcil escribir iterativa construcciones en trminos de las macros de bucle ya existentes. Es til, sin embargo, para la traduccin de algoritmos escritos en otros idiomas en Common Lisp, ya sea automtica o manualmente. Un ejemplo de una herramienta de traduccin automtica es el traductor de FORTRAN a Common Lisp, f2cl, que traduce el cdigo fuente en FORTRAN Common Lisp con el fin de hacer varias bibliotecas FORTRAN a disposicin de los programadores de Lisp comunes. Dado que muchas bibliotecas FORTRAN fueron escritos antes de la revolucin de la programacin estructurada, que estn llenos de gotos. El compilador f2cl simplemente traducir esos gotos de GO s en su caso TAGBODY s. 5 Del mismo modo, TAGBODY y GO puede ser til cuando se traduce algoritmos descritos en prosa o diagramas de flujo - por ejemplo, en la serie clsica de Donald Knuth El Arte de la Programacin de Computadoras , describe los algoritmos que

utilizan una "receta" formato: paso 1, haga lo siguiente; paso dos, hacer eso, el paso 3, vuelva al paso 2, y as sucesivamente. Por ejemplo, en la pgina 142 de The Art of Computer Programming, volumen 2: Algoritmos Seminumerical , tercera edicin (Addison-Wesley, 1998), describe el algoritmo S, que va a utilizar en el captulo 27, en esta forma:
El algoritmo S (tcnica de seleccin de la muestra). Para seleccionar n registros al azar de un conjunto de N, donde 0 <n <= N. S1. [Inicializar.] Set t <- 0, m <- 0. (Durante este algoritmo, m representa el nmero de registros seleccionados hasta ahora, y t es el nmero total de registros de entrada que nos hemos ocupado con.) S2. [Generar U.] Generar un nmero aleatorio U, distribuido uniformemente entre cero y uno. S3. [Test.] Si (N - t) U> = n - m, vaya al paso S5. S4. [Seleccionar.] Seleccione el siguiente registro de la muestra, y aumentar la M & T en 1. Si m <n, vaya al paso S2, de lo contrario la muestra se completa y termina el algoritmo. S5. [Ir]. Ir al siguiente registro (no se incluyen en la muestra), t aumento de 1, y vuelve al paso S2.

Esta descripcin puede ser fcilmente traducido a una funcin Lisp Comn, despus de cambiar el nombre de unas pocas variables, de la siguiente manera:
(Defun algoritmo-s (n max); mximo es N en el algoritmo de Knuth (Let (visto; t en el algoritmo de Knuth seleccionado; m en algoritmo de Knuth u, U en el algoritmo de Knuth (Registros ())); la lista en la que guardar los registros seleccionados (Tagbody s1 (Setf visto 0) (Setf selecciona 0) s2 (Setf u (al azar 1.0)) s3 (Cuando (> = (* (- max visto) u) (- n seleccionado)) (go S5)) s4 (Pulsar los registros observados) (Incf seleccionado) (Incf visto) (Si (<seleccionado n) (Ir s2) (Ida y vuelta-del algoritmo-s (registros nreverse))) s5 (Incf visto) (Ir s2))))

No es la ms bonita de cdigo, pero es fcil comprobar que se trata de una traduccin fiel del algoritmo de Knuth. Sin embargo, este cdigo, a diferencia de descripcin en prosa de Knuth, se puede ejecutar y probado. Entonces usted puede comenzar refactorizacin, comprobando despus de cada cambio de que la funcin sigue funcionando. 6 Despus de empujar las piezas de todo un poco, podra terminar con algo como esto:
(Defun algoritmo-s (n mx) (Circular para visto desde 0 cuando (<(* (- max visto) (aleatoria 1.0)) n) recopilar y hacer ver (DECF n) hasta que (zerop n)))

Si bien puede no ser inmediatamente obvio que este cdigo implementa correctamente el algoritmo S, si ha llegado hasta aqu a travs de una serie de funciones que se comportan de forma idntica a la original de la traduccin literal de la receta de Knuth, tendra buenas razones para creer que es correcta. Desenredar la pila Otro aspecto de la lengua que los operadores especiales le dan el control sobre el comportamiento de la pila de llamadas. Por ejemplo, mientras que normalmente usa BLOQUE yTAGBODY para gestionar el flujo de control dentro de una nica funcin, tambin se pueden utilizar, junto con los clausuras, para forzar un retorno inmediato no local de una funcin de ms abajo en la pila. Eso es porque BLOQUE nombres y TAGBODY etiquetas se puede cerrar en cualquier cdigo dentro del mbito lxico de la BLOQUE o TAGBODY . Por ejemplo, considere esta funcin:
(Defun foo () (Formato t "Introduccin foo% ~") (Bloquear un (Formato t "~ BLOQUE Entrando%") (Bar # '(lambda () (retorno de una))) (Formato t "BLOQUE Dejando ~%")) (Formato t "Leaving foo% ~"))

La funcin annima pas a impedir que utiliza return-from para regresar desde el BLOQUE . Pero eso RETORNO DE- no se evalan hasta que la funcin annima se invoca con funcall o APLICABLE . Ahora bien, supongamos bar se parece a esto:
(Barra de defun (fn) (Formato t "barra de Introduccin ~%") (Baz fn) (Formato t "barra Dejando ~%"))

Sin embargo, la funcin annima no se invoca. Ahora mira Baz .

(Defun baz (Formato (Funcall (Formato

(fn) t "Introduccin de Baz ~%") fn) t "Leaving baz ~%"))

Finalmente se invoca la funcin. Pero, qu significa RETORNO DE- un bloque que es de varias capas para arriba en la pila de llamadas? Resulta que funciona bien - de la pila se desenrolla de nuevo al marco donde el BLOQUE se estableci y se devuelve el control del BLOQUE . Los FORMATO expresiones en foo , bar y baz indicar lo siguiente:
CL-USER> (foo) Introduccin de foo Entrando BLOQUE Introduccin de barras Introduccin de baz Dejando foo NIL

Tenga en cuenta que el nico "Saliendo ..." mensaje que se imprime es la que aparece despus de que el BLOQUE de foo . Debido a que los nombres de los bloques son un mbito lxico, un RETORNO DEsiempre regresa desde el ms pequeo que encierra BLOCK en el entorno lxico en que elreturn-from formulario aparece incluso si el return-from se ejecuta en un contexto dinmico diferente. Por ejemplo, la barra de tambin podra contener un BLOQUEnombrado uno , de esta manera:
(Barra de defun (fn) (Formato t "barra de Introduccin ~%") (Un bloque (baz fn)) (Formato t "barra Dejando ~%"))

Este extra BLOQUE no va a cambiar el comportamiento de los foo en absoluto - el nombre de un lxico que se resuelva, en tiempo de compilacin, no de forma dinmica, por lo que el bloque de la intervencin no tiene ningn efecto sobre el return-from . Por el contrario, el nombre de un BLOQUE puede ser utilizado slo por return-from s que aparecen en el mbito lxico del BLOQUE , no hay manera de que el cdigo fuera del bloque para regresar desde el bloque, excepto mediante la invocacin de un clausura que se cierra sobre unreturn-from desde el mbito lxico de la BLOQUE . TAGBODY y GO funcionan del mismo modo, en este sentido, como BLOQUE y RETURN FROM- . Cuando se invoca un clausura que contiene un GO forma, si el GO

se evala, la pila va a descansar de nuevo a la adecuada TAGBODY y despus salta a la etiqueta especificada. BLOQUE nombres y TAGBODY etiquetas, sin embargo, difieren de los lxicos asignaciones de variables de una manera importante. Como he discutido en el captulo 6, los enlaces lxicos tienen extensin indefinida, es decir, los enlaces pueden quedarse an despus de la forma de enlace ha regresado. BLOQUE s y TAGBODY s, por el contrario, tienen una extensin dinmica - puede return-from un BLOQUE o IR a un TAGBODY nica etiqueta, mientras que el BLOQUE o TAGBODY est en la pila de llamadas. En otras palabras, un clausura que captura un nombre de bloque o TAGBODY etiqueta puede ser pasado hacia abajo la pila para ser invocada ms tarde, pero no pueden ser devueltos hasta la pila. Si se llama a un clausura que trata de return-from un BLOQUE , despus de que el BLOQUE s ha regresado, obtendr un error. Del mismo modo, tratando de IR a un TAGBODY que ya no existe se producir un error. 7 Es poco probable que usted tendr que utilizar BLOQUE y TAGBODY a ti mismo para este tipo de desenredo de pila. Pero lo ms probable es que usando indirectamente siempre que utilice el sistema de condicin, por lo que entender cmo funcionan le ayudar a entender mejor qu es exactamente, por ejemplo, la invocacin de un reinicio que est haciendo. 8 CATCH y THROW son otro par de operadores especiales que pueden obligar a la pila para relajarse. Vamos a usar estos operadores, incluso con menos frecuencia que los otros mencionados hasta el momento - they're remanentes de anteriores dialectos de Lisp que no tenan sistema de Common Lisp de condicin. Definitivamente no se debe confundir contratar / atrapar y tratar / , excepto las construcciones de lenguajes como Java y Python. CATCH y THROW son las contrapartes dinmicas del BLOQUE y RETURN FROM- . Es decir, que se coloca CATCH en torno a un cuerpo de cdigo y luego usar THROW para hacer que el CATCH formulario para volver inmediatamente con un valor especificado. La diferencia es que la enlace entre un CATCH y THROW se establece de forma dinmica - en lugar de un nombre de mbito lxico, la etiqueta de un CATCH es un objeto, llamado etiqueta de captura , y cualquier Tiro evaluado dentro de la extensin dinmica de la CATCHque arroja ese objeto deshacer la pila de nuevo a la CATCH forma y hacer que regrese de inmediato. Por lo tanto, usted

puede escribir una versin de la foo , bar y baz funciones desde antes de usar CATCH y THROW lugar de BLOQUE y return-from as:
(Defparameter * obj * (ninguna ninguna contras)), es decir, un objeto arbitrario (Defun foo () (Formato t "Introduccin foo% ~") (Captura * obj * (Formato t "Introduccin CATCH% ~") (Bar) (Formato t "Leaving LA CAPTURA% ~")) (Formato t "Leaving foo% ~")) (Barra de defun () (Formato t "barra de Introduccin ~%") (BAZ) (Formato t "barra Dejando ~%")) (Baz defun () (Formato t "Introduccin de Baz ~%") (Lanzar * obj nula *) (Formato t "Leaving baz ~%"))

Ntese cmo no es necesario pasar un clausura abajo de la pila - baz puede llamar TIRO directamente. El resultado es bastante similar a la versin anterior.
CL-USER> (foo) Introduccin de foo Entrando CATCH Introduccin de barras Introduccin de baz Dejando foo NIL

Sin embargo, CATCH y THROW son casi demasiado dinmico. Tanto en el CATCH y THROW , la forma de la etiqueta se evala, lo que significa que sus valores estn determinados en tiempo de ejecucin. Por lo tanto, si algo de cdigo en barra de reasignar o rebote * obj * , el LANZAMIENTO de Baz no tirar a la misma CATCH . Esto hace que CATCH yTHROW mucho ms difcil de razonar acerca de BLOQUE y RETURN FROM- . La nica ventaja, que la versin de foo , bar y baz que el uso CATCH y THROW demuestra, es que no hay necesidad de pasar por un clausura de fin de cdigo de bajo nivel para volver a partir de una CATCH - cualquier cdigo que se ejecuta dentro del medida dinmica de un CATCHpuede hacer que se vuelva al lanzar el objeto correcto. En los antiguos dialectos de Lisp que no tenan nada por el estilo de condicin de sistema Common Lisp, la CATCH y THROW fueron utilizados para el tratamiento de errores. Sin embargo, para mantenerlas viables, las etiquetas de las capturas fueron por lo general los smbolos que acabamos de citar, por lo que podra decir mirando

a un CATCH y THROW si se conectar en tiempo de ejecucin. En Common Lisp que rara vez tendr que cualquier llamada a utilizar CATCH y THROW ya que la condicin de sistema es mucho ms flexible. El ltimo operador especial relacionada con el control de la pila es otro que he mencionado de pasada antes - UNWIND-PROTECT . UNWIND-PROTECT le permite controlar lo que sucede a medida que se desenrolla de la pila - para asegurarse de que cierto cdigo siempre se ejecuta independientemente de la forma el control abandona el mbito de laUNWIND-PROTECT , ya sea por un rendimiento normal, por un reinicio que se invocan, o por cualquiera de las formas descritas en esta seccin. 9 El esqueleto bsico deUNWIND-PROTECT es el siguiente:
(Desconectar de proteccin protegidos de forma de limpieza de forma *)

La nica forma de proteccin se evala y, a continuacin, independientemente de cmo se devuelve, las formas de limpieza son evaluados. Si la forma protegidavuelve normalmente, entonces todo lo que se devuelve se devuelve desde el UNWIND-PROTECT despus de la limpieza de forma ejecutar. Las formas de limpieza se evalan en el entorno de la misma dinmica que el UNWINDPROTECT , por lo que los mismos enlaces dinmicos de variables, se reinicia y manejadores de condiciones ser visible para el cdigo de limpieza en las formas como se vean justo antes de la UNWIND-PROTECT . De vez en cuando vamos a usar UNWIND-PROTECT directamente. Ms a menudo que lo utilizar como base para la CON- macros de estilo, similar a la CON-OPENFILE , los que evalan cualquier nmero de formas corporales en un contexto en el que tienen acceso a algn recurso que debe ser limpiado despus de que los re hecho, independientemente de si regresan normalmente o libertad bajo fianza a travs de una salida no local reinicio o de otra ndole. Por ejemplo, si estuviera escribiendo una biblioteca de base de datos que se definen las funciones de conexin abierta y cercana a la conexin , puede escribir una macro como esta: 10
(Defmacro con la base de datos de conexin ((var y resto abierto args) y el cuerpo del cuerpo) `(Let ((var (abierta la conexin, abra-@ args))) (Desconectar de proteccin (progn, el cuerpo de @) (Primer sentido, var))))

que le permite escribir cdigo como este:


(Con base de datos de conexin (conn: host "foo": el usuario "scott": contrasea "tigre")

(Hacer-cosas de conexion) (Lo-ms-cosas de conexion))

y no tiene que preocuparse de cerrar la conexin de base de datos, ya que el UNWIND-PROTECT se asegurar de que se cierra, no importa lo que sucede en el cuerpo de labase de datos con conexin formulario. Varios valores Otra de las caractersticas de Common Lisp que he mencionado de pasada - en el captulo 11, cuando discut GetHash - es la capacidad de una sola forma de devolver varios valores.Voy a hablar de ello con ms detalle ahora. Es, sin embargo, un poco fuera de lugar en un captulo sobre los operadores especiales, ya que la capacidad de devolver mltiples valores no est previsto por slo uno o dos operadores especiales, pero est profundamente integrado en el lenguaje. Los operadores que usted utiliza con ms frecuencia cuando se trata de varios valores son macros y funciones pero no los operadores especiales. Pero es el caso de que la capacidad bsica para llegar a los valores de retorno es provista por un operador especial, de varios valores CALL- , en la que el ms comnmente utilizado de varios valores, BIND se construye la macro. La clave para entender acerca de los valores mltiples es que la devolucin de varios valores es muy diferente de devolver una lista - si un formulario devuelve varios valores, a menos que haga algo especfico para captar los mltiples valores, todos, pero el valor principal que ser descartados silenciosamente. Para ver la diferencia, considere la funcin GetHash , que devuelve dos valores: el valor se encuentra en la tabla hash y un valor booleano que es NIL , cuando el valor no se encuentra. Si se lo devolvi esos dos valores en una lista, cada vez que llama GetHash tendra que desarmar la lista para obtener el valor real, independientemente de si se preocupaba por el valor de retorno segundo. Suponga que tiene una tabla hash, * H * , que contiene valores numricos. Si GetHash regres una lista, no podra escribir algo como esto:
(+ (GetHash 'a * h *) (GetHash' b * h *))

por + espera que sus argumentos sean nmeros, no las listas. Sin embargo, debido a que el mecanismo de mltiple valor silenciosamente descarta el valor de retorno secundario cuando no ha querido, esta forma funciona bien.

Hay dos aspectos en el uso de valores mltiples - la devolucin de valores mltiples y que apunta a los valores devueltos por las formas no primarios que devuelven varios valores. Los puntos de partida para la devolucin de valores mltiples son las funciones de VALORES y los valores de listas- . Estas son funciones regulares, los operadores que no son especiales, por lo que sus argumentos se pasan de la forma habitual. VALORES tiene un nmero variable de argumentos y los devuelve como varios valores; VALORES LISTAtoma una sola lista y devuelve sus elementos como mltiples valores. En otras palabras:
(Los valores de la lista x) === (se aplican los valores # 'x)

El mecanismo por el cual se devuelven varios valores depende de la implementacin al igual que el mecanismo para pasar argumentos en funciones es. Casi todas las construcciones del lenguaje que devuelven el valor de algunas subformulario "pasar a travs de" mltiples valores, devolver todos los valores devueltos por el subformulario. Por lo tanto, una funcin que devuelve el resultado de llamar VALORES o los valores de listas- que en s devolver varios valores - y tambin lo har otra funcin cuyo resultado viene de llamar a la primera funcin. Y as sucesivamente. 11 Sin embargo, cuando una forma se evala en una posicin de valor, slo el valor principal se utilizar, por lo que adems de la forma anterior, funciona de la manera que usted esperara. El operador especial de varios valores-CALL proporciona el mecanismo para conseguir sus manos en los mltiples valores devueltos por un formulario.multiple-value-CALL es similar a la funcall excepto que mientras funcall es una funcin regular y, por tanto, puede ver y transmitir slo los valores primarios que se le pasan, las MLTIPLES LLAMADAS DE VALOR pases, a la funcin que devuelve el primer subformulario, todos los valores devueltos por los subformularios restantes.
(+ Funcall # '(valores 1 2) (valores 3 4)) ==> 4 (Multiple-value-llamada # '+ (valores 1 2) (valores 3 4)) ==> 10

Sin embargo, es bastante raro que usted simplemente quiere pasar todos los valores devueltos por una funcin a otra funcin. Lo ms probable es que usted desea para guardar los mltiples valores de diferentes variables y luego hacer algo con ellos. El multiple-value-BIND macro, que se vio en el captulo 11, es el operador ms utilizado para la aceptacin de mltiples valores de retorno. Su esqueleto se parece a esto:

(Multiple-value-bind ( variables *) Los valores de forma del cuerpo de forma *)

La forma de los valores es evaluado, y los mltiples valores que sus declaraciones se unen a las variables de . A continuacin, las formas del cuerpo se evalan con los enlaces en vigor. As:
(Multiple-value-bind (xy) (valores 1 2) (+ Xy)) ==> 3

Otra macro, multiple-value-LISTA , es an ms simple - toma una forma nica, la evala, y recoge los mltiples valores resultantes en una lista. En otras palabras, es la inversa de VALORES-LISTA .
CL-> USER (multiple-value-list (valores 1 2)) (1 2) USUARIO CL-> (los valores de la lista (multiple-value-list (valores 1 2))) 1 2

Sin embargo, si usted se encuentra con varios valores-LISTA mucho, puede ser una seal de que alguna funcin debe devolver una lista para empezar en lugar de varios valores. Por ltimo, si desea asignar mltiples valores devueltos por una forma de variables existentes, puede utilizar VALORES como SETF lugar capaz. Por ejemplo:
CL-> USER (defparameter * x * nil) * X * CL-> USER (defparameter * y * nil) * Y * USUARIO CL-> (setf (valor * x ** y *) (en el piso (57/34))) 1 23/34 CL-USUARIO> * x * 1 CL-USUARIO> * y * 23/34

EVAL-AL Un operador especial que usted tendr que entender para escribir ciertos tipos de macros es EVAL-AL . Por alguna razn, los libros de Lisp a menudo tratan EVAL-AL como un tema de slo magos. Sin embargo, el nico requisito previo para la comprensin de EVAL-AL es una comprensin de cmo las dos funciones de CARGA y COMPILE ARCHIVO-interactan. Y la comprensin de EVAL-CUANDO ser importante a medida que comience a escribir ciertos tipos de macros ms sofisticados, como los que usted va a escribir en los captulos 24 y 31.

Ya me he referido brevemente a la relacin entre CARGA y COMPILE ARCHIVO- en los captulos anteriores, pero vale la pena revisar de nuevo aqu. El trabajo de CARGA es cargar un archivo y evaluar todas las formas de nivel superior que contiene. El trabajo de compile-file es compilar un archivo fuente en un archivo de FASL, que puede ser cargado conLOAD de tal manera que (load "foo.lisp") y (load "foo.fasl") son esencialmente equivalentes. Debido CARGA evala cada formulario antes de leer el siguiente, los efectos secundarios de la evaluacin de las formas anteriormente en el archivo puede afectar a cmo las formas ms adelante en la forma son ledos y evaluados. Por ejemplo, la evaluacin de una in-Package forma cambia el valor de * PAQUETE * , lo que afectar la forma en formas posteriores se leen. 12 Del mismo modo, un defmacro forma temprana en un archivo se puede definir una macro que puede ser utilizado por el cdigo ms adelante en el archivo. 13 Compile-file , por el contrario, por lo general no evala las formas que est recopilando, es cuando el FASL se carga de que las formas-o sus equivalentes compilados - ser evaluado. Sin embargo, compile-file debe evaluar algunas formas, como en paquete y defmacro formas, con el fin de mantener el comportamiento de(load "foo.lisp") y (load "foo.fasl") consistente. Entonces, cmo las macros como en paquete y defmacro trabajo al ser procesada por compile-file ? En algunas versiones de pre-Common Lisp de Lisp, el compilador de archivo, simplemente saba que deben evaluar ciertas macros, adems de su redaccin. Common Lisp evitar la necesidad de tales kludges tomando prestada la EVAL-CUANDOoperador especial de Maclisp. Este operador, como su nombre indica, le permite controlar cuando los bits de cdigo especficas que se evalan. El esqueleto de un EVAL-CUANDOforma se parece a esto:
(Eval-cuando ( situacin *) forma corporal *)

Hay tres posibles situaciones - : compilacin nivel superior y nivel superior: carga- , y ejecutar: - y que los que se especifiquen los controles cuando las formas del cuerpo ser evaluado. El IC-AL , con mltiples situaciones es equivalente a varios EVALCUANDO formas, uno por cada situacin, cada uno con el cdigo de un mismo cuerpo. Para explicar el significado de las tres situaciones, voy a tener que explicar un poco acerca de cmo compile-file , que tambin se conoce como el compilador de archivos , va sobre la compilacin de un archivo.

Para explicar cmo se compile-file compila EVAL-CUANDO formas, tengo que introducir una distincin entre la compilacin de alto nivel y la compilacin de las formas no-formularios de nivel superior. Un formulario de nivel superior es, en trminos generales, que se compila en cdigo que se ejecutar cuando el FASL se carga. Por lo tanto, todas las formas que aparecen directamente en el nivel superior de un archivo de cdigo fuente se compilan como formularios de nivel superior. Igualmente, las formas que aparecen directamente en un nivel superior progn se compilan como formularios de nivel superior ya que el progn s mismo no hacer nada - slo agrupa a sus subformularios, que se ejecutan cuando el FASL se carga. 14 Del mismo modo, las formas que aparecen directamente en un MACROLET o SMBOLO MACROLET- se compilan como formularios de nivel superior, porque despus de que el compilador ha ampliado las macros locales o macros de smbolos, no habr remanente de la MACROLET o SMBOLO MACROLET- en el cdigo compilado. Por ltimo, la expansin de una forma macro de nivel superior se compila como un formulario de nivel superior. Por lo tanto, un DEFUN que aparece en el nivel superior de un archivo de cdigo fuente es un formulario de nivel superior - el cdigo que define la funcin y la asocia con su nombre se ejecutar cuando el FASL se carga - pero las formas en el cuerpo de la funcin, que no se ejecutar hasta que la funcin se llama, no son formularios de nivel superior. La mayora de las formas se compilan de la misma cuando se compila como formas de nivel superior y no de nivel superior, pero la semntica de un EVAL-CUANDO depender de si se est compilado como un formulario de nivel superior, compilado como una forma no de nivel superior , o simplemente evaluado, combinado con qu situaciones se enumeran en su lista situacin. Las situaciones de compilacin: nivel superior , y : la carga del nivel superior de control el significado de una EVAL-CUANDO compilado como un formulario de nivel superior. Cuando : compilacin nivel superior est presente, el compilador de archivos evaluar los subformularios en tiempo de compilacin.Cuando : nivel superior de carga est presente, se compilar los subformularios como formularios de nivel superior. Si ninguna de estas situaciones est presente en un nivel superior EVAL-AL , el compilador lo ignora. Cuando un EVAL-CUANDO se compila como una forma no de nivel superior, que est bien compilado como un progn , si el : ejecucin de situacin se especifica, o

ignoradas. Del mismo modo, una evaluacin EVAL-AL - que incluye ms alto nivel EVAL-CUANDO s en un archivo fuente procesada por CARGA y EVAL-CUANDO s evala en tiempo de compilacin, ya que aparecen como subformularios de un alto nivel EVAL-AL con la : compilacin nivel superior la situacin - es tratado como un prognsi : ejecucin est presente e hizo caso omiso de lo contrario. Por lo tanto, una macro como en paquete puede tener el efecto necesario, tanto en tiempo de compilacin y cuando se carga desde la fuente mediante la expansin a un EVAL-ALcomo el siguiente:
(Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf * paquete * (encontrar-paquete "nombre-paquete")))

* PAQUETE * se puede establecer en tiempo de compilacin, debido a la : compilacin nivel superior la situacin, establece cuando el FASL se carga a causa de: carga de nivel superior , y establecer si la fuente se carga a causa de la : llevar a cabo . Hay dos maneras de que usted es ms probable que utilice EVAL-AL . Una de ellas es si usted quiere escribir macros que tienen que ahorrar un poco de informacin en tiempo de compilacin que se utiliza cuando se genera la expansin de las formas macro otros en el mismo archivo. Esto normalmente se presenta con las macros de definicin en una definicin temprana en un archivo pueden afectar el cdigo generado para una definicin ms adelante en el mismo archivo. Vas a escribir este tipo de macro en el Captulo 24. El otro momento es posible que tenga EVAL-AL es si usted quiere poner la definicin de una macro y funciones de ayuda que utiliza en el mismo archivo que el cdigo que utiliza la macro. defmacro ya incluye un EVAL-AL en su expansin por lo que la definicin de la macro est inmediatamente disponible para ser utilizado ms adelante en el archivo. PeroDEFUN normalmente no tiene la definicin de funciones disponibles en tiempo de compilacin. Sin embargo, si utiliza una macro en el mismo archivo que est definido en lo que necesita la macro y las funciones que utiliza para definirse. Si se coloca la DEFUN s de cualquier otra funcin auxiliar utilizado por el macro en un EVAL-AL con: compilacin nivel superior , las definiciones que estar disponible cuando la funcin de la expansin de la macro se ejecuta. Usted probablemente querr incluir: nivel superior de carga y de : ejecutar , as desde los macros, se necesita la definicin de funciones despus de que el archivo se compila y se carga o si carga la fuente en lugar de compilar.

Otros Operadores Especiales Los cuatro operadores especiales restantes, LOCAL , LA , en tiempo de cargaVALOR , y PROGV , todo le permiten llegar a partes de la lengua subyacente que no se puede acceder de otra manera. LOCAL y LA son parte del sistema de declaracin de Common Lisp , que se utiliza para comunicar cosas que el compilador que no afectan el significado de su cdigo, pero que puede ayudar a que el compilador genera un cdigo mejor - ms rpido, ms claros mensajes de error, y as sucesivamente. 15 Voy a discutir brevemente en el captulo declaraciones 32. Los otros dos, en tiempo de carga-VALOR y PROGV , se utilizan con poca frecuencia, y explicar la razn por la que jams podra quieren usarlos tomara ms tiempo de explicar lo que hacen. As que slo voy a decir lo que ellos hacen lo que saben que est all. Algn da va a golpear en una de esas raras ocasiones en que es justo la cosa, y luego estar listo. En tiempo de carga-VALOR se utiliza, como su nombre indica, para crear un valor que se determina en tiempo de carga. Cuando el compilador compila el cdigo del archivo que contiene un tiempo de carga-VALOR forma, se encarga de evaluar el primer subformulario una vez, cuando el FASL se carga, y para el cdigo que contiene eltiempo de carga-VALOR forma para referirse a ese valor. En otras palabras, en lugar de escribir esto:
(* Defvar cargado-a * (get-universal-tiempo)) (Defun al-cargado () * carga-en *)

usted puede escribir lo siguiente:


(Defun al-cargado () (tiempo de carga de valor (get-universal-tiempo)))

En el cdigo no se procesa por compile-file , en tiempo de carga-VALOR se evala una vez, cuando se compila el cdigo, que puede ser cuando se compila de forma explcita una funcin con COMPILE o ms temprano, debido a la compilacin implcita realizada por la puesta en prctica en el curso de la evaluacin de la cdigo. En el cdigo sin compilar, en tiempo de carga-VALOR evala su forma cada vez que se evala. Por ltimo, PROGV crea nuevos enlaces dinmicos de las variables cuyos nombres se determinan en tiempo de ejecucin. Esto es principalmente til para la aplicacin de los intrpretes para lenguajes integrados con las variables de forma dinmica con mbito. El esqueleto bsico es el siguiente:

(Progv smbolos lista de forma corporal *)

los valores de lista de

donde los smbolos-la lista es una forma que da como resultado una lista de smbolos y valores de la lista- es una forma que da como resultado una lista de valores. Cada smbolo est dinmicamente enlazado al valor correspondiente y, a continuacin los corporales-formas son evaluados. La diferencia entre PROGV y la LET es que debido a que los smbolos-la listase evala en tiempo de ejecucin, los nombres de las variables que se unen se puede determinar de forma dinmica. Como digo, esto no es algo que tiene que hacer a menudo. Y eso es todo para los operadores especiales. En el prximo captulo, voy a volver a tercos temas prcticos y le mostrar cmo utilizar el sistema de Common Lisp del paquete para tomar el control de los espacios de nombres para que pueda escribir bibliotecas y aplicaciones que pueden coexistir sin pisotear los de sus compaeros.

1 Por supuesto, si SI no era un operador especial, pero alguna otra forma condicional, como COND ,

se, usted podra construir SI como una macro. De hecho, en muchos dialectos de Lisp, a partir de Lisp original de McCarthy, COND era el operador primitiva evaluacin condicional.
2 Bueno, tcnicamente estas construcciones tambin se podra ampliar en un Lambda de expresin,

ya que, como mencion en el captulo 6, LET podra definirse - y fue en algunos Lisps anteriores como una macro que se expande en una invocacin de una funcin annima.
3 Por sorprendente que pueda parecer, lo que realmente es posible hacer recurse funciones

annimas. Sin embargo, debe utilizar un mecanismo bastante esotrica conocida como Y Combinator . Pero el Y Combinator es un resultado interesante terica, no una herramienta prctica de programacin, por lo que est bien fuera del mbito de este libro.
4 No es necesario que los CON RANURAS llevarse a cabo con SMBOLO-MACROLET - en algunas

aplicaciones, CON-Slots puede recorrer el cdigo provisto y generar una expansin con x , y y z ya se sustituyen por los correspondientes valor SLOT- formas. Usted puede ver cmo su implementacin lo hace mediante la evaluacin de esta forma:
(Macroexpand-1 '(con ranuras (xyz) obj (lista xyz)))

Sin embargo, caminando por el cuerpo es mucho ms fcil para la implementacin de Lisp que hacer que para el cdigo de usuario, para sustituir x , y y z cuando aparecen en las posiciones de valor requiere de un andador cdigo que comprende la sintaxis de todos los operadores especiales y que se expande de forma recursiva todos los macronutrientes forma con el fin de determinar si sus expansiones son los smbolos en las posiciones de valor. La implementacin de Lisp, obviamente, tiene un cdigo de Walker a su disposicin, pero es una de las pocas partes de Lisp que no est expuesto a los usuarios de la lengua.

5 Una versin de f2cl est disponible como parte de la coleccin de Cdigo Abierto de Common Lisp

(CLOCC): http://clocc.sourceforge.net/ . Por el contrario, tenga en cuenta los trucos de los autores de f2j, un traductor de FORTRAN a Java, tienen que jugar. Aunque la Java Virtual Machine (JVM) tiene una instruccin goto, que no est expuesto directamente en Java. As que para compilar gotos FORTRAN, primero compilar el cdigo FORTRAN en legal fuente de Java con las llamadas a una clase ficticia para representar las etiquetas y los gotos. Luego compilar el cdigo fuente con un regular compilador de Java y post-procesar los cdigos de bytes para traducir las llamadas ficticias en cdigos de bytes JVM nivel. Inteligente, pero lo que es un dolor.
6 Dado que este algoritmo depende de los valores devueltos por RANDOM , es posible que desee

probarlo con una semilla aleatoria compatible, que se puede obtener mediante la enlace * RANDOMESTADO * el valor de (hacer-al azar-estado cero) alrededor de cada llamada a algoritmo-s . Por ejemplo, usted puede hacer una comprobacin de validez bsica de algoritmo-s mediante la evaluacin de lo siguiente:
(Let ((* aleatoria de estado * (cero make-aleatoria-estado))) (algoritmo-s 10 200))

Si sus refactorizaciones son todas vlidas, esta expresin se debe evaluar a la misma lista cada vez.
7 Esta es una restriccin bastante razonable - no est del todo claro lo que me refiero a volver de una

forma que ya ha regresado - a menos que, por supuesto, usted es un programador de sistema. Plan de apoyocontinuaciones , una construccin del lenguaje que hace que sea posible volver a la llamada de funcin ms de una vez. Sin embargo, para una variedad de razones, pocas, o ninguna, idiomas distintos del programa de apoyo a este tipo de continuacin.
8 Si usted es el tipo de persona que le gusta saber cmo funcionan las cosas hasta el final de los bits,

puede ser instructivo para pensar acerca de cmo se podra implementar el sistema de macros condiciones de uso de BLOQUE , TAGBODY , clausuras, y las variables dinmicas.
9 UNWIND-PROTECT es esencialmente equivalente a try / finally construye en Java y Python. 10 Y, en efecto, CLSQL, el multi-Lisp, Multi Base de SQL biblioteca de interfaz, proporciona una

macro similar llamado con bases de datos . Pgina CLSQL la casa se encuentra enhttp://clsql.b9.com .
11 Un pequeo puado de macros no pasan por los valores de retorno adicionales de los formularios

que evalan. En particular, el PROG1 macro, que evala un nmero de formas como una progn antes de devolver el valor de la primera forma, devuelve valor primario que forma la nica. Del mismo modo, PROG2 , que devuelve el valor de la segunda de sus subformularios, slo devuelve el valor primario. El operador especialde varios valores-PROG1 es una variante de PROG1 que devuelve todos los valores devueltos por la primera forma. Es una verruga menor que PROG1 an no se comportan comode varios valores-PROG1 , pero no se utiliza con bastante frecuencia es que importe mucho. Los O y COND macros tampoco son siempre transparentes para varios valores, devuelve slo el valor principal de subformularios ciertas.
12 La razn por la carga de un archivo con una in-Package forma en la que no tiene ningn efecto

sobre el valor de * PAQUETE * despus de la CARGA vuelve es porque CARGA une PAQUETE * * a

su valor actual antes de hacer cualquier otra cosa. En otras palabras, algo equivalente a lo siguiente LET se envuelve alrededor del resto del cdigo en CARGA :
(Let ((* paquete ** paquete *)) ...)

Cualquier cesin a * PAQUETE * ser el nuevo enlace, y la enlace de edad ser restaurado cuando CARGA devoluciones. Tambin se une la variable * * READTABLE , que no he hablado, de la misma manera.
13 En algunas implementaciones, puede ser capaz de salirse con la evaluacin de defun s que

utilizan las macros indefinidas en el cuerpo de la funcin, siempre y cuando las macros se definen antes de la funcin se llama en realidad. Pero eso funciona, en todo caso, slo cuando CARGA Ing las definiciones de la fuente, no al compilar con compile-file , por lo que en las definiciones generales de macro deben ser evaluados antes de ser utilizado.
14 Por el contrario, los subformularios en un nivel superior LET no se compilan como formularios de

nivel superior, ya que no est ejecutado directamente cuando el FASL se carga. Las tareas se ejecutarn, pero es en el contexto de ejecucin de los enlaces establecidos por la LET . En teora, un LET que se une a ninguna variable puede ser tratada como una progn , pero no lo es - los formularios que aparecen en un LET nunca se tratan como formularios de nivel superior.
15 La declaracin de uno que tiene un efecto sobre la semntica de un programa es el ESPECIAL

declaracin mencionada en el captulo 6.

21. La programacin en el Gran: Paquetes y smbolos


En el captulo 4 discute cmo el lector Lisp traduce los nombres de texto en los objetos que se pasa al evaluador, en representacin de ellos con un tipo de objeto llamado smbolo .Resulta que el tener un tipo de datos incorporado especficamente para representar los nombres es muy til para una gran cantidad de tipos de programacin. 1 Sin embargo, eso no es el tema de este captulo. En este captulo hablaremos de uno de los aspectos ms inmediatos y prcticos de la trata de nombres: cmo evitar conflictos de nombres entre las piezas desarrolladas independientemente del cdigo. Supongamos, por ejemplo, ests escribiendo un programa y decide utilizar una biblioteca de terceros. No quiero tener que saber el nombre de cada funcin, una variable de clase, o la macro utiliza en los interiores de la biblioteca con el fin de evitar conflictos entre los nombres y los nombres que utiliza en su programa. Le gustara a la mayora de los nombres en la biblioteca y los nombres en el programa que se consideran distintos, incluso si llegaran a tener la misma representacin textual. Al mismo tiempo, que le gustara algunos nombres definidos en la biblioteca para ser de fcil acceso - los nombres que componen su API pblica, que usted desea utilizar en su programa. En Common Lisp, esto se reduce problemas de espacio de nombres reduce a una cuestin de controlar la forma en que el lector traduce los nombres de texto en smbolos: si quieres dos apariciones del mismo nombre que se considera la misma por el evaluador, es necesario asegurarse de que el lector utiliza la mismo smbolo para representar a cada nombre. Por el contrario, si quieres dos nombres que se consideran distintos, incluso si, por casualidad, el nombre textual misma, que necesita el lector para crear smbolos diferentes para representar a cada nombre. Cmo el lector utiliza paquetes En el captulo 4 analiza brevemente cmo el lector traduce los nombres de Lisp en smbolos, pero pasa por alto la mayor parte de los detalles - ahora es el momento para echar un vistazo ms de cerca lo que realmente sucede.

Voy a empezar con la descripcin de la sintaxis de los nombres entendido por el lector y la forma en que la sintaxis se refiere a los paquetes. Por el momento, se puede pensar en un paquete como una tabla que asigna las cadenas a los smbolos. Como se ver en la siguiente seccin, la asignacin real es ligeramente ms flexible que una tabla de bsqueda simple, pero no de manera que importa mucho para el lector. Cada paquete tiene un nombre, que puede ser usado para encontrar el paquete mediante la funcin FIND-PAQUETE . Las dos funciones principales que el lector utiliza para acceder a las asignaciones de nombre a smbolos en un paquete son FIND-SYMBOL y INTERN . Ambas estas funciones toman una cadena y, opcionalmente, un paquete. Si no se proporciona, el argumento por defecto los paquetes en el valor de la variable global * PAQUETE * , tambin llamado el paquete actual . FIND-SYMBOL se ve en el paquete de un smbolo con la cadena dada un nombre y lo devuelve, o NIL si no hay smbolo se encuentra. INTERN tambin devolver un smbolo existente, de lo contrario se crea un nuevo smbolo con la cadena como el nombre y lo agrega al paquete. La mayora de los nombres que utilizamos son no cualificados , los nombres que no contienen dos puntos. Cuando el lector lee como un nombre, que se traduce en un smbolo mediante la conversin de las letras en maysculas y sin escape que pasa la cadena resultante de INTERN . Por lo tanto, cada vez que el lector lee el mismo nombre en el mismo paquete, que va a obtener el objeto mismo smbolo. Esto es importante porque el evaluador utiliza la identidad del objeto de smbolos para determinar qu funcin, una variable o elemento de otro programa un smbolo dado se refiere a. Por lo tanto, la razn de una expresin como (hola-mundo) da lugar a llamar a un particular, hola-mundo es la funcin porque el lector devuelve el mismo smbolo, cuando se lee la llamada de funcin, como lo hizo cuando ley el DEFUN forma que define la funcin. Un nombre que contiene una sola coma o dos puntos dobles es un nombre de paquete-calificado. Cuando el lector lee un nombre de paquete-cualificada, se divide el nombre en el colon (s) y utiliza la primera parte como el nombre de un paquete y la segunda parte como el nombre del smbolo. El lector busca el paquete apropiado y lo utiliza para traducir el nombre del smbolo a un objeto smbolo.

Un nombre que contiene slo dos puntos solo debe referirse a una externa smbolo uno de los paquetes de las exportaciones para el uso pblico. Si el paquete no contiene el nombre de un smbolo con un nombre determinado, o si lo hace, pero no se ha exportado, el lector seala un error. Un nombre de dos puntos se puede referir a cualquier smbolo en el envase el nombre, aunque por lo general es una mala idea - el conjunto de smbolos exportados define la interfaz pblica de un paquete, y si no se respeta la decisin del autor del paquete sobre lo que ha decidido apostar pblico y cules mantener en privado, usted est pidiendo problemas en el camino. Por otro lado, a veces un autor del paquete se descuidan para exportar un smbolo que realmente debera ser pblico. En ese caso, el nombre de dos puntos le permite realizar su trabajo sin tener que esperar para la prxima versin del paquete para ser liberados. Dos otros fragmentos de la sintaxis de smbolo de que el lector entiende son los smbolos de palabras clave y smbolos uninterned. Smbolos de palabras clave se escriben con nombres que comienzan con dos puntos. Estos smbolos estn internados en el paquete llamado KEYWORD y exportados de forma automtica. Adems, cuando el lector de los internos en un smbolo de la PALABRA CLAVE , tambin define una variable constante por el smbolo que su nombre y valor. Es por eso que puede utilizar palabras clave en las listas de argumentos, sin citarlos cuando aparecen en una posicin de valor, se evalan a s mismos. As:
(EQL ': foo: foo) ==> T

Los nombres de los smbolos de palabras clave, como todos los smbolos, se convierten en maysculas por el lector antes de que sean internados. El nombre no incluye los dos puntos principales.
(Smbolo de nombre: foo) ==> "FOO"

Uninterned smbolos se escriben con una de las principales #: . Estos nombres (menos el #: ) se convierten en maysculas como algo normal y luego se traducen en smbolos, pero los smbolos no son internados en cualquier paquete, cada vez que el lector lee un #: nombre, se crea un nuevo smbolo. As:
('#: Foo' eql #: foo) ==> NIL

Usted raramente, si alguna vez, escriba la siguiente sintaxis s mismo, sino que a veces se ve cuando se imprime una s-expresin que contiene smbolos devueltos por la funcinGENSYM .

(Gensym) ==> #: G3128

Un poco de paquete y el vocabulario de smbolos Como ya he mencionado anteriormente, la asignacin de nombres a los smbolos que utiliza un paquete es un poco ms flexible que una tabla de bsqueda simple. En esencia, cada paquete contiene una tabla de bsqueda de nombre a smbolo, pero un smbolo puede ser accesible a travs de un nombre no calificado de un paquete determinado de otras maneras.Para hablar con sensatez acerca de estos otros mecanismos, usted necesitar un poco de vocabulario. Para empezar, todos los smbolos que se pueden encontrar en un paquete determinado, utilizando FIND-SYMBOL se dice que son accesibles en ese paquete. En otras palabras, los smbolos accesibles en un paquete son aquellos que pueden ser a que se refiere con nombres no cualificados cuando el paquete es actual. Un smbolo puede ser accesible de dos maneras. El primero es para el paquete de nombre-a-smbolo de tabla para contener una entrada para el smbolo, en cuyo caso se dice que el smbolo a ser presente en el paquete. Cuando los internos lector un nuevo smbolo en un paquete, ste se agrega al nombre a la tabla de smbolos del paquete. El paquete en el que por primera vez un smbolo de internado se llama el smbolo del paquete de su casa . La otra manera un smbolo puede ser accesible en un paquete es si el paquete hereda ella. Un paquete hereda smbolos de otros paquetes por el uso de los otros paquetes. Slo externossmbolos en los envases utilizados son heredados. Un smbolo es algo externo en un paquete de exportacin de la misma. Adems de causar al ser heredado por el uso de paquetes, se exporta un smbolo tambin como se vio en la seccin anterior - que hace posible hacer referencia al smbolo con un solo nombre y coma calificado. Para mantener las asignaciones de nombres a los smbolos determinista, el sistema de paquetes permite que slo un smbolo para ser accesible en un paquete determinado para cada nombre. Es decir, un paquete no se puede tener un smbolo presente y un smbolo heredado con el mismo nombre o heredar dos smbolos diferentes, de diferentes paquetes, con el mismo nombre. Sin embargo, puede resolver los conflictos, haciendo uno de los smbolos accesibles una sombra smbolo, lo que hace que los otros smbolos del mismo nombre inaccesible. Adems de su

nombre a la tabla de smbolos, cada paquete tiene una lista de smbolos de sombreado. Un smbolo existente puede ser importada en otro paquete, aadiendo que de nombre a la tabla de smbolos del paquete. As, el mismo smbolo puede estar presente en varios paquetes.A veces se va a importar smbolos simplemente porque desea que sean accesibles en el paquete de importacin sin necesidad de utilizar su paquete en casa. Otras veces usted va a importar un smbolo, ya que slo los smbolos actuales se pueden exportar ni ser sombra smbolos. Por ejemplo, si un paquete tiene que utilizar dos paquetes que tienen smbolos externos del mismo nombre, uno de los smbolos debern ser importados en el paquete utilizando con el fin de ser agregado a su lista remedo y hacer que el otro smbolo inaccesible. Por ltimo, un smbolo presente se puede uninterned desde un paquete, lo cual ocasiona que ser removido de la tabla nombre-a-smbolo y, si se trata de un smbolo remedo, de la lista remedo. Es posible que unintern un smbolo de un paquete para resolver un conflicto entre el smbolo y un smbolo externo de un paquete que desea utilizar. Un smbolo que no est presente en cualquier paquete se llama un uninterned smbolo, ya no puede ser ledo por el lector, y se imprimir usando el #: loquesea sintaxis. Tres paquetes estndar En la siguiente seccin te muestra cmo definir sus propios paquetes, incluyendo cmo hacer un paquete de utilizar otro y cmo exportar, la sombra, y los smbolos de importacin.Pero primero echemos un vistazo a unos cuantos paquetes que usted ha estado utilizando ya. Cuando empiece a Lisp, el valor de * PAQUETE * es tpicamente elcommon-lisp-USUARIO paquete, tambin conocido como CL-USUARIO . 2 CL-USUARIO utiliza el paquete common-lisp , que exporta a todos los nombres definidos por el estndar del lenguaje . Por lo tanto, cuando se escribe una expresin en el REPL, todos los nombres de las funciones estndar, macros, variables, y as sucesivamente, se traducirn a los smbolos exportados por common-lisp , y todos los dems nombres se intern en el common-lisp EL USUARIO paquete. Por ejemplo, el nombre del paquete * *se exporta de common-lisp - si usted quiere ver el valor de * PAQUETE * , usted puede escribir lo siguiente:
CL-USUARIO> paquete * * # <El COMMON-LISP-USER package>

por common-lisp-USUARIO utiliza common-lisp . O usted puede utilizar un nombre de paquete-calificado.


CL-USUARIO> common-lisp: * paquete * # <El COMMON-LISP-USER package>

Usted puede incluso utilizar common-lisp apodo de 's, CL .


CL-USUARIO> cl: * paquete * # <El COMMON-LISP-USER package>

Pero * X * no es un smbolo en el common-lisp , por lo que, si escribe esto:


CL-> USER (defvar * x * 10) * X *

el lector lee defvar como el smbolo de la common-lisp paquete y * X * como un smbolo en el common-lisp-USUARIO . El REPL no se puede iniciar en el common-lisp paquete porque no se le permite a los smbolos de nuevo interno en el mismo; common-lisp-USUARIO sirve como un "cero" paquete donde usted puede crear sus propios nombres, sin dejar de tener fcil acceso a todos los smbolos de common-lisp . 3 Normalmente, todos los paquetes que va a definir tambin utilizar common-lisp , por lo que no tiene que escribir cosas como esta:
(CL: defun (x) (cl: + x 2))

El paquete estndar tercera es la PALABRA CLAVE paquete, el paquete del lector Lisp utiliza para los nombres de internos que comienzan con dos puntos. Por lo tanto, tambin puede referirse a cualquier smbolo de la palabra clave con una calificacin paquete explcito de la palabra clave de esta manera:
CL-> User: una : Un CL-USUARIO> palabra clave: un : Un CL-> USER (EQL: una palabra clave: a) T

La definicin de sus propios paquetes Trabajo en el common-lisp-USUARIO est muy bien para experimentos en el REPL, pero una vez que comience a escribir programas reales que usted desea definir nuevos paquetes para diferentes programas cargados en el mismo entorno Lisp no pisotear los de sus compaeros. Y cuando se escribe bibliotecas que va a utilizar en

diferentes contextos, tendr que definir paquetes por separado y luego exportar los smbolos que componen la API de las bibliotecas pblicas. Sin embargo, antes de comenzar a definir los paquetes, es importante entender una cosa acerca de lo que los paquetes no , no lo hacen. Los paquetes no proporcionan un control directo sobre lo que puede llamar a la funcin o acceder a lo variable. Ellos le proporcionan un control bsico de los espacios de nombres mediante el control de cmo el lector traduce los nombres de texto en objetos de smbolos, pero no es hasta ms tarde, en el evaluador, que se interpreta el smbolo como el nombre de una funcin o una variable o cualquier otra cosa.Por lo tanto, no tiene sentido hablar acerca de cmo exportar una funcin o una variable de un paquete. Puede exportar los smbolos utilizados para ciertos nombres ms fciles de consultar, pero el sistema de paquetes no permite restringir cmo esos nombres se utilizan. 4 Con esto en mente, usted puede comenzar a buscar la manera de definir los paquetes y atarlos juntos. Para definir nuevos paquetes con la macro DEFPACKAGE , que le permite no slo crear el paquete, pero para especificar los paquetes que utiliza, lo que los smbolos que exporta, y lo que los smbolos que importa de otros paquetes y resolver los conflictos mediante la creacin de smbolos sombras. 5 Voy a describir las diferentes opciones en trminos de cmo puede utilizar los paquetes al escribir un programa que organiza mensajes de correo electrnico en una base de datos. El programa es puramente hipottico, como lo son las bibliotecas que me referir - el punto es ver cmo los paquetes utilizados en dicho programa podra estructurarse. El primer paquete que haba necesidad es el fin de brindar un espacio de nombres para la aplicacin - usted quiere ser capaz de nombrar a sus funciones, variables, y as sucesivamente, sin tener que preocuparse por conflictos de nombres con cdigo no relacionado. As que te definen un nuevo paquete con DEFPACKAGE . Si la aplicacin es lo suficientemente simple para ser escrito sin bibliotecas ms all de las facilidades que ofrece el lenguaje en s mismo, se podra definir un paquete sencillo como esto:
(Defpackage: com.gigamonkeys.email-db (: Uso: common-lisp))

Esto define un paquete, llamado COM.GIGAMONKEYS.EMAIL-DB , que hereda todos los smbolos exportados por el common-lisp paquete. 6

En realidad tienes varias opciones de cmo representar los nombres de los paquetes y, como se ver, los nombres de los smbolos en un DEFPACKAGE . Paquetes y smbolos se nombran con cadenas. Sin embargo, en un DEFPACKAGE formulario, puede especificar los nombres de los paquetes y los smbolos con los designadores de cuerda . Un designador de cadena es una cadena, la cual se designa, un smbolo, que designa a su nombre, o un personaje, que designa a una cadena de un carcter que contiene slo el personaje. El uso de smbolos de palabras clave, como en el anterior DEFPACKAGE , es un estilo comn que le permite escribir los nombres en minscula - el lector podr convertir los nombres en maysculas para ti. Tambin puede escribir el DEFPACKAGE con cadenas, pero luego tienes que escribir en maysculas, porque los verdaderos nombres de la mayora de los smbolos y los paquetes estn en maysculas hecho, debido a la conversin de caso realizado por el lector. 7
(Defpackage "COM.GIGAMONKEYS.EMAIL-PP" (: Uso de "common-lisp"))

Tambin puede utilizar los smbolos nonkeyword - los nombres en DEFPACKAGE no son evaluados - pero luego el mismo acto de la lectura de la DEFPACKAGE forma hara que esos smbolos para ser internados en el paquete actual, que por lo menos va a contaminar ese espacio de nombres y Tambin puede causar problemas ms adelante si se intenta utilizar el paquete. 8 Para leer el cdigo de este paquete, usted tiene que hacer el paquete actual con la in-Package macro:
(En el paquete: com.gigamonkeys.email-db)

Si escribe esta expresin en el REPL, que va a cambiar el valor de * PAQUETE * , que afecta a cmo el REPL lee las expresiones posteriores, hasta que se cambia con otra llamada a la in-package . Del mismo modo, si usted incluye un in-package en un archivo que se carga con LOAD o compilado con compile-file , que va a cambiar el paquete, que afecta a la forma en expresiones posteriores en el archivo son ledas. 9 Con el paquete actual establece en el COM.GIGAMONKEYS.EMAIL-DB bulto que no sea los nombres heredados de la common-lisp paquete, se puede utilizar cualquier nombre que desee para cualquier fin que desee. De este modo, se podra definir un nuevo hola-mundo la funcin que podran coexistir con la hola-mundo la funcin

definida anteriormente en el common-lisp-USUARIO . Aqu est el comportamiento de la funcin existente:


CL-> USER (hola-mundo) hola, mundo NIL

Ahora usted puede cambiar al nuevo paquete utilizando in-package . 10 Observe cmo cambia el sistema - la forma exacta est determinada por el entorno de desarrollo, pero en el limo del sistema por defecto consiste en una versin abreviada del nombre del paquete.
CL-> USER (en el paquete: com.gigamonkeys.email-db) # <El COM.GIGAMONKEYS.EMAIL-DB package> CORREO ELECTRNICO-DB>

Se puede definir una nueva hola-mundo en este paquete:


CORREO ELECTRNICO-DB> (defun hola-mundo () (en formato t "hola de paquete de correo electrnico-DB ~%")) Hola-mundo

Y lo prueba, de esta manera:


CORREO ELECTRNICO-DB> (hola-mundo) hola de paquete de correo electrnico-DB NIL

Ahora volver a la CL-USUARIO .


CORREO ELECTRNICO-DB> (en el paquete: CL-usuario) # <El COMMON-LISP-USER package> CL-USUARIO>

Y la funcin de edad no se altera.


CL-> USER (hola-mundo) hola, mundo NIL

Embalaje bibliotecas reutilizables Mientras trabajaba en la base de datos de correo electrnico, puede escribir varias funciones relacionadas con el almacenamiento y recuperacin de texto que no tiene nada en particular que hacer con el correo electrnico. Usted puede darse cuenta de que esas funciones podran ser tiles para otros programas y deciden volver a empaquetar como una biblioteca. Debe definir un nuevo paquete, pero esta vez va a exportar ciertos nombres para ponerlos a disposicin de otros paquetes.
(Defpackage: com.gigamonkeys.text-db (: Uso: common-lisp) (: Exportacin: abierto-db

: Guardar : Tienda))

Una vez ms, se utiliza el common-lisp paquete, porque vas a necesitar el acceso a las funciones estndar en COM.GIGAMONKEYS.TEXT-DB . El : exportacin clusula especifica nombres que estarn en el exterior COM.GIGAMONKEYS.TEXT-DB y accesible tanto en paquetes que : usan la misma. Por lo tanto, despus de haber definido este paquete, usted puede cambiar la definicin del paquete principal de la aplicacin a lo siguiente:
(Defpackage: com.gigamonkeys.email-db (: Uso: common-lisp: com.gigamonkeys.text-db))

Ahora el cdigo escrito en COM.GIGAMONKEYS.EMAIL-DB puede utilizar los nombres no calificados para referirse a los smbolos exportados, tanto de commonlisp yCOM.GIGAMONKEYS.TEXT DB- . Todos los dems nombres continuar a ser internados directamente en la COM.GIGAMONKEYS.EMAIL-DB paquete. Importacin de nombres individuales Ahora supongamos que usted encuentra una biblioteca de terceros de las funciones para la manipulacin de mensajes de correo electrnico. Los nombres utilizados en la API de la biblioteca se exportan desde el paquete COM.ACME.EMAIL , por lo que poda : el uso que el paquete para obtener un fcil acceso a esos nombres. Pero supongamos que usted necesita utilizar una nica funcin de esta biblioteca, y otros conflictos exporta smbolos con los nombres que ya utiliza (o piensa utilizar) en nuestro propio cdigo. 11 En este caso, puede importar el smbolo que necesita con un : de importacin y de la clusula en el DEFPACKAGE . Por ejemplo, si el nombre de la funcin que desea utilizar esde anlisis de correo electrnico, direccin , puede cambiar el DEFPACKAGE a esto:
(Defpackage: com.gigamonkeys.email-db (: Uso: common-lisp: com.gigamonkeys.text-db) (: Importacin-a partir de: com.acme.email: anlisis sintctico-direccin de correo electrnico))

Ahora en cualquier lugar el nombre de anlisis de correo electrnico, direccin aparece en la lectura del cdigo en el COM.GIGAMONKEYS.EMAIL-DBpaquete, que ser ledo como el smbolo de la COM.ACME.EMAIL . Si necesita importar ms de un smbolo a partir de un solo paquete, usted puede incluir varios nombres despus de que el nombre del paquete en una sola : las importaciones de la clusula. Un

DEFPACKAGE tambin puede incluir mltiples : las importaciones de las clusulas con el fin de importar smbolos de diferentes paquetes. De vez en cuando te encontrars con la situacin opuesta - un paquete puede exportar un montn de nombres que desea utilizar y unos pocos no lo hacen. En lugar de enumerar todos los smbolos que se desea utilizar en una : las importaciones de la clusula, en su lugar puede : usar el paquete y, a continuacin una lista de los nombres que ustedno desea heredar en una : La sombra de la clusula. Por ejemplo, supongamos que el COM.ACME.TEXT paquete exporta un montn de nombres de funciones y clases utilizadas en el procesamiento de texto. Supongamos, adems, que la mayora de estas funciones y las clases son las que usted querr utilizar en el cdigo, pero uno de los nombres, losde construccinndice , los conflictos con un nombre que ya ha utilizado. Puede hacer que el ndice de construccin de COM.ACME.TEXT inaccesibles al sombrear la misma.
(Defpackage: com.gigamonkeys.email-db (: Utilizar : Common-lisp : Com.gigamonkeys.text-db : Com.acme.text) (: Importacin-a partir de: com.acme.email: anlisis sintctico-direccin de correo electrnico) (: La sombra: build-index))

El : La sombra de la clusula provoca un nuevo smbolo llamado BUILD-INDEX que se crea y se agrega directamente a COM.GIGAMONKEYS.EMAIL-DB 's nombresmbolo en el mapa. Ahora bien, si el lector lee el nombre BUILD-INDEX , se lo traducir al smbolo en COM.GIGAMONKEYS.EMAIL-DB 's mapa, en lugar de uno que de otro modo pueden heredar de COM.ACME.TEXT . El nuevo smbolo se aade tambin a una lista de smbolos de sombra que es parte de la COM.GIGAMONKEYS.EMAIL-DB paquete, as que si luego usa otro paquete que tambin exporta una BUILD-INDEX smbolo, el sistema de paquetes se sabe que no hay conflicto - que desea que el smbolo deCOM.GIGAMONKEYS.EMAIL-DB que se utiliza en lugar de cualquier otro smbolo que con el mismo nombre heredado de otros paquetes. Una situacin similar puede presentarse si usted desea utilizar dos paquetes que exportan el mismo nombre. En este caso el lector no sabr lo que hered el nombre a utilizar cuando se lee el nombre textual. En tales situaciones se debe resolver la ambigedad al sombrear los nombres en conflicto. Si no necesita utilizar el nombre de cada paquete, podra ensombrecer el nombre con un : La sombra de la clusula,

la creacin de un nuevo smbolo con el mismo nombre en el paquete. Pero si usted realmente desea utilizar uno de los smbolos heredados, entonces usted necesita para resolver la ambigedad con un : sombra-de importacin y de la clusula. Al igual que uno : importacin-de la clusula, una : sombra-de importacin y de la clusula consiste en el nombre del paquete seguido de los nombres de importar de ese paquete. Por ejemplo, siCOM.ACME.TEXT las exportaciones de un nombre de SAVE que entra en conflicto con el nombre exportado de COM.GIGAMONKEYS.TEXT-DB , se puede resolver la ambigedad con la siguiente DEFPACKAGE :
(Defpackage: com.gigamonkeys.email-db (: Utilizar : Common-lisp : Com.gigamonkeys.text-db : Com.acme.text) (: Importacin-a partir de: com.acme.email: anlisis sintctico-direccin de correo electrnico) (: La sombra: build-index) (: Sombra-import-a partir de: com.gigamonkeys.text-db: save))

Mecnica de embalaje Que cubre los conceptos bsicos de cmo utilizar los paquetes para administrar espacios de nombres en varias situaciones comunes. Sin embargo, otro nivel de la forma de utilizar los paquetes vale la pena discutir - la mecnica de primas de la forma de organizar el cdigo que utiliza diferentes paquetes. En esta seccin voy a discutir algunas reglas del pulgar sobre la forma de organizar el cdigo - donde poner su DEFPACKAGE forma en relacin con el cdigo que utiliza los paquetes a travs de in-package . Dado que los paquetes son utilizados por el lector, un paquete debe ser definido antes de que pueda LOAD o COMPILAR PRESENTAR- un archivo que contiene un in-Packageexpresin de cambiar a ese paquete. Los paquetes tambin se debe definir antes de que otros DEFPACKAGE formas se puede referir a ellos. Por ejemplo, si usted va a : el usoCOM.GIGAMONKEYS.TEXT-DB en COM.GIGAMONKEYS.EMAIL-DB , entonces COM.GIGAMONKEYS.TEXT-DB 's DEFPACKAGE deben ser evaluados antes de laDEFPACKAGE de COM.GIGAMONKEYS.EMAIL -DB . El mejor primer paso hacia la fabricacin de paquetes de seguros existe cuando es necesario es poner todo tu DEFPACKAGE s en archivos separados del cdigo que se necesita para ser ledo en esos paquetes. Alguna gente tiene gusto de crear un foo-

package.lisp archivo para cada paquete individual, y otros crear un nico packages.lisp que contiene todos los DEFPACKAGE formas para un grupo de paquetes relacionados. Cualquiera de estos enfoques es razonable, aunque el enfoque de un solo archivo por paquete tambin requiere que usted disponga para cargar los archivos individuales en el orden correcto de acuerdo a las dependencias interpackage. De cualquier manera, una vez que todos los DEFPACKAGE formas se han separado del cdigo que se lee en los paquetes que definen, puede hacer arreglos para CARGAR los archivos que contienen el DEFPACKAGE s antes de compilar o cargar cualquiera de los otros archivos. Para los programas simples que usted puede hacer esto a mano: simplemente CARGAR el archivo o archivos que contienen los DEFPACKAGE formas, posiblemente a compilar con compile-file en primer lugar. Luego CARGA los archivos que utilizan los paquetes, una vez ms, opcionalmente, en primer lugar con la compilacin compile-file . Ntese, sin embargo, que los paquetes no existen hasta que LOAD las definiciones de paquetes, ya sea la fuente o los archivos generados por compile-file . Por lo tanto, si ests compilando todo, usted todava debe CARGA todas las definiciones de paquetes antes de que puedacompile-file los archivos que se leen en los paquetes. Haciendo estos pasos con la mano conseguir aburrido despus de un tiempo. Para los programas simples que usted puede automatizar los pasos al escribir un archivo, load.lisp , que contiene los correspondientes CARGA y COMPILE ARCHIVOllamadas en el orden correcto. A continuacin, slo puede CARGA ese archivo. Para los programas ms complejos que usted desea utilizar una definicin del sistema para gestionar las instalaciones de carga y recopilacin de archivos en el orden correcto. 12 La otra regla de oro es fundamental que cada archivo debe contener exactamente un in-Package forma, y debe ser la primera forma en el archivo que no sea un comentario. Los archivos que contienen DEFPACKAGE formas debe comenzar con (in-package "common-lisp-USUARIO") , y todos los dems archivos deben contener unin-Package de uno de sus paquetes. Si usted viola esta regla y los paquetes de conmutacin en el centro de un archivo, usted confundir a los lectores humanos que no se dan cuenta de la segunda inpackage .Adems, muchos entornos de desarrollo, en particular, Emacs Lisp a base

de seres como el limo, busque una in-Package para determinar el paquete que debe utilizar cuando se comunica con Common Lisp. Mltiples en paquete formas por archivo puede confundir estas herramientas tambin. Por otro lado, es bueno tener varios archivos de lectura en el mismo paquete, cada uno con una idntica in-Package formulario. Es slo una cuestin de cmo le gusta organizar el cdigo. La otra parte de la mecnica de embalaje tiene que ver con la forma de nombrar los paquetes. Los nombres de paquetes vivir en un espacio de nombres plano - los nombres de paquetes son slo cadenas, y los diferentes paquetes deben tener nombres distintos textualmente. Por lo tanto, usted tiene que considerar la posibilidad de conflictos entre nombres de paquetes. Si ests usando solo los paquetes que te desarrollados, entonces usted probablemente puede conseguir lejos con usar nombres cortos para los paquetes. Pero si usted est planeando utilizar las bibliotecas de terceros o para publicar su cdigo para su uso por otros programadores, a continuacin, es necesario seguir una convencin de nomenclatura que reduzca al mnimo la posibilidad de conflictos de nombres entre los diferentes paquetes. Lispers Muchos hoy en da estn adoptando Java estilo de los nombres, como los utilizados en este captulo, que consta de un nombre de dominio de Internet invertida seguida por un punto y una cadena descriptiva. Problemas comunes del paquete Una vez que usted est familiarizado con los paquetes, usted no va a gastar un montn de tiempo pensando en ellos. No es slo que mucho ms a ellos. Sin embargo, un par de problemas que muerden la mayora de los nuevos programadores de Lisp que el sistema de paquetes parece ms complicado y hostil de lo que realmente es. El gotcha nmero uno se presenta con mayor frecuencia cuando se juega en torno a la REPL. Se le busca en alguna biblioteca que define ciertas funciones interesantes. Vas a tratar de llamar a una de las funciones de esta manera:
CL-USER> (foo)

y conseguir que el depurador se dej caer en este error:


intentar llamar `FOO ', que es una funcin no definida. [Estado de tipo indefinido-FUNCTION] Reinicia:

0: 1: 2: 3: 4: 5:

[PRUEBA DE NUEVO] Trate de llamar a FOO nuevo. [RETURN-VALUE] Devolver un valor en lugar de llamar FOO. [El valor de uso] Trate de llamar a una funcin distinta de FOO. [STORE-VALUE] setf el smbolo de funcin de FOO y llame de nuevo. [abortar] manejo Abortar solicitud BABA. [abortar] Abortar por completo de este proceso (Lisp).

Ah, por supuesto - se olvid de utilizar el paquete de la biblioteca. As que salir del depurador y tratar de USO-PAQUETE paquete de la biblioteca con el fin de tener acceso al nombre de FOO as que usted puede llamar a la funcin.
CL-> USER (uso de paquete: foolib)

Pero eso te deja de nuevo en el depurador con este mensaje de error:


Utilizando el paquete `FOOLIB los resultados en los conflictos de nombres de estos smbolos: FOO [Estado del tipo de paquete-ERROR] Reinicia: 0: [CONTINUAR] Unintern los smbolos en conflicto de la 'common-lisp-USUARIO paquete. 1: [abortar] manejo Abortar solicitud BABA. 2: [abortar] Abortar por completo de este proceso (Lisp).

Eh? El problema es la primera vez que se llama foo , el lector lee el nombre foo e internado en CL-USUARIO antes de que el evaluador se apoder de ella y descubri que este nuevo smbolo de internado no es el nombre de una funcin. Este nuevo smbolo a continuacin, entra en conflicto con el smbolo del mismo nombre exportado fuera de la FOOLIBpaquete. Si se haba acordado de USO-PAQUETE FOOLIB antes de intentar llamar a foo , el lector ha ledo foo como el smbolo hereditario y no se internaron un foo smboloCL-USUARIO . Sin embargo, no todo est perdido, porque el primer reinicio ofrecido por el depurador arreglar las cosas de la manera correcta: se unintern la foo smbolo decommon-lisp-USUARIO , poniendo la CL-USUARIO paquete de vuelta al estado en que se encontraba antes de que llama foo , permitiendo que la USE-PAQUETE de proceder y teniendo en cuenta la hered foo que est disponible en CL-USUARIO . Este tipo de problema tambin puede producirse durante la carga y la compilacin de los archivos. Por ejemplo, si se ha definido un paquete, MI-APP , para el cdigo que se va a utilizar las funciones con nombres de la FOOLIB paquete, pero se olvidaron de : utilizar FOOLIB , al compilar los archivos con una (in-package: myapp ) en ellos, el lector smbolos internos nuevos MI-APP para los nombres que se supona iban a ser ledos como smbolos de FOOLIB . Cuando intenta ejecutar el cdigo compilado, obtendr errores de undefined de funcin. Si a continuacin,

tratar de redefinir el MI-APP paquete : utilizar FOOLIB , obtendr el error de smbolos contradictorios. La solucin es el mismo: seleccione el reinicio de unintern los smbolos contradictorios de MI-APP . A continuacin, tendrs que volver a compilar el cdigo en el MI-APP paquete de lo que se refiere a los nombres heredados. El gotcha prximo es esencialmente el reverso de la Gotcha primero. En este caso, usted se ha definido un paquete - una vez ms, vamos a decir que es MI-APP - que utiliza otro paquete, por ejemplo, FOOLIB . Ahora empezar a escribir cdigo en el MI-APP paquete. A pesar de que utiliz FOOLIB con el fin de poder remitir a la foo funcin, FOOLIBpuede exportar otros smbolos tambin. Si usted usa uno de esos smbolos exportados - por ejemplo, barras - como el nombre de una funcin en su propio cdigo, Lisp no se quejar. En cambio, el nombre de la funcin ser el smbolo exportado por FOOLIB , que darle una paliza a la definicin de barra de FOOLIB . Este gotcha es ms insidioso, ya que no causa un error - desde el punto de vista del evaluador es slo les pide que asociar una nueva funcin con un nombre antiguo, algo que es perfectamente legal. Se sospecha que slo porque el cdigo de hacer la redefinicin fue ledo con un valor diferente para PAQUETE * * que el paquete del nombre. Sin embargo, el evaluador no tiene por qu saberlo. Sin embargo, en la mayora de Lisps obtendr una advertencia sobre "la redefinicin de BAR, originalmente definido en ? ". Se debera prestar atencin a esas advertencias. Si una paliza de una definicin de una biblioteca, puede restaurarlo a cargar el cdigo de la biblioteca con la CARGA . 13 El ltimo paquete relacionado con gotcha es, en comparacin, bastante trivial, pero muerde mayora de los programadores de Lisp por lo menos un par de veces: se define un paquete que utiliza common-lisp y tal vez algunas bibliotecas. Luego, en el REPL se cambia a ese paquete para jugar. Entonces usted decide dejar de fumar por completo y Lisp intenta llamar a (dejar de fumar) . Sin embargo, dejar de fumar no es un nombre de la common-lisp paquete - es definido por la aplicacin de algn paquete de aplicacin especfica que le sucede a ser utilizado por common-lispUSUARIO . La solucin es simple - los paquetes de cambio de nuevo a CL-USER para salir. O bien, utilice el acceso directo BABA REPL dejar de fumar , que tambin le ahorrar de tener que recordar que en ciertas implementaciones comunes de Lisp la funcin de dejar de fumar esla salida , no dejar de fumar .

Ya casi ha terminado con su gira de Common Lisp. En el siguiente captulo hablaremos de los detalles de la extensa LOOP macro. Despus de eso, el resto del libro est dedicado a "prcticas": un filtro de correo no deseado, una biblioteca para analizar los archivos binarios, y varias partes de un servidor de streaming de MP3 con una interfaz Web.

1 El tipo de programacin que se basa en un tipo de datos smbolo se llama, muy apropiadamente,

simblica computacin. Es normalmente en contraste con numrica de programacin. Un ejemplo de un programa principalmente simblico que todos los programadores deben estar familiarizados con un compilador - que trata el texto de un programa como datos simblicos y la traduce en una nueva forma.
2 Cada paquete tiene un nombre oficial y cero o ms apodos que se pueden utilizar en cualquier

lugar que usted necesita para usar el nombre del paquete, como en el paquete de nombres calificados o para referirse al paquete en una DEFPACKAGE o EN PAQUETE- forma.
3 common-lisp-USUARIO tambin se le permite dar acceso a los smbolos exportados por otros

definidos por la implementacin de paquetes. Si bien esto est pensado como una conveniencia para el usuario - que hace que la aplicacin especfica de la funcionalidad de fcil acceso - tambin puede causar confusin entre los nuevos Lispers: Lisp se quejan de un intento de redefinir un nombre que no aparece en el lenguaje estndar . Para ver qu paquetes common-lisp-USUARIO hereda los smbolos de una aplicacin en particular, evaluar esta expresin en el REPL:
(Mapcar # 'nombre-paquete (paquete-de uso de la lista: CL-usuario))

Y para averiguar qu paquete un smbolo de vino de origen, evaluar lo siguiente:


(Nombre del paquete (smbolo-package 'un smbolo-))

con algn smbolo- sustituye por el smbolo en cuestin. Por ejemplo:


(Nombre del paquete (coche-smbolo de paquete ")) ==>" common-lisp " (Nombre del paquete (foo smbolo-package ')) ==> "common-lisp-USUARIO"

Los smbolos heredados de la aplicacin definidos por paquetes volver algn otro valor.
4 Esto es diferente de el sistema de paquetes de Java, que proporciona un espacio de nombres para

las clases, pero tambin participa en el mecanismo de control de acceso de Java. El lenguaje no-Lisp con un sistema de paquetes como la mayora de los paquetes de Common Lisp es Perl.
5 Todas las manipulaciones realizadas por DEFPACKAGE tambin se puede realizar con las

funciones que los ipulate hombre-objetos de paquete. Sin embargo, desde un paquete generalmente necesita ser completamente definido antes de que pueda ser utilizado, esas funciones se utilizan raramente. Adems, DEFPACKAGE se encarga de realizar todas las manipulaciones de paquetes en el orden correcto - por ejemplo,DEFPACKAGE aade a la lista de smbolos de sombra antes de que se trata de utilizar los envases utilizados.

6 En muchas implementaciones de Lisp del : uso de la clusula es opcional si slo desea : utilizar

common-lisp - si se omite, el paquete heredar automticamente los nombres de una lista definida por la implementacin de paquetes que generalmente se incluyen common-lisp . Sin embargo, el cdigo ser ms porttil si siempre se especifica explcitamente los paquetes que desea : el uso . Los que se oponen a la tipificacin puede utilizar el apodo del paquete y escribir (: uso: cl) .
7 Uso de palabras clave en lugar de cuerdas tiene otra ventaja - Allegro proporciona un "modo

moderno" Lisp en el que el lector no hace caso de conversin de nombres y en el que, en lugar de un common-lisppaquete con los nombres en maysculas, proporciona un common-lisp paquete de con los nombres en minscula. Estrictamente hablando, esto no es un Lisp Lisp conforme comn, ya que todos los nombres en la norma se definen para ser mayscula. Pero si usted escribe sus DEFPACKAGE formas que utilizan los smbolos de palabras clave, que van a trabajar tanto en Common Lisp y, en este pariente cercano.
8 Algunas personas, en lugar de palabras clave, el uso uninterned smbolos, usando el #: sintaxis. (Defpackage #: com.gigamonkeys.email-db (: Utilice #: common-lisp))

Esto ahorra un poco de memoria, al no internar cualquier smbolo en el envase la palabra clave - el smbolo pueden convertirse en basura despus de DEFPACKAGE (o el cdigo que se expande a) se hace con ella.Sin embargo, la diferencia es tan pequea que realmente se reduce a una cuestin de esttica.
9 La razn para utilizar en paquete en vez de SETF Ing. * PAQUETE * es que en paquete se expande

en el cdigo que se ejecutar cuando el archivo es compilado por compile-file , as como cuando se carga el archivo, cambiando la forma en que el lector lee el resto del archivo durante la compilacin.
10 En el buffer de REPL de lodo tambin puede cambiar los paquetes con un acceso directo REPL.

Escriba una coma y, a continuacin, introduzca el cambio de paquete en el Comando del sistema.
11 Durante el desarrollo, si se intenta : utilizar un paquete que exporta un smbolo con el mismo

nombre como un smbolo que ya internados en el paquete utilizando, Lisp sealar un error y por lo general ofrecen un reinicio que unintern el smbolo ofensivo del uso de paquete. Para ms informacin sobre esto, vea la seccin "problemas comunes del paquete".
12 El cdigo de los captulos de "prctica", disponible desde el sitio Web de este libro, utiliza el

sistema de ASDF definicin de la biblioteca. ASDF es sinnimo de otro centro de definicin del sistema.
13 Algunos comunes implementaciones de Lisp, como Allegro y SBCL, proporcionar una funcin de

"bloqueo" de los smbolos en un paquete en particular por lo que se puede utilizar en la definicin de formas tales como DEFUN , defvar y DEFCLASS slo cuando su paquete de hogar es el paquete actual .

22. LOOP para Cinturones Negros


En el captulo 7 discute brevemente la extensa LOOP macro. Como ya he mencionado a continuacin, LOOP ofrece lo que es esencialmente un lenguaje de propsito especial slo para escribir las construcciones de iteracin. Esto podra parecer como un montn de molestias - la invencin de un lenguaje entero slo para escribir bucles. Pero si usted piensa acerca de las formas de bucles se utilizan en los programas, lo que realmente hace un poco de sentido. Cualquier programa de cualquier tamao en absoluto va a contener un buen nmero de bucles. Y aunque no todo ser lo mismo, no todos van a ser nico o bien, los patrones surgirn, sobre todo si se incluye el cdigo inmediatamente antes y despus de los bucles - los patrones de cmo las cosas estn preparadas para el circuito, los patrones de en lo que se hace en el circuito adecuado, y los patrones en lo que se hace despus del bucle. El LOOP lenguaje de captura de estos patrones por lo que puede expresar directamente. El LOOP macro tiene un montn de partes - una de las principales quejas de LOOP detractores 's es que es demasiado complejo. En este captulo, voy a hacer frente a LOOP cabeza, que le da un recorrido sistemtico de las distintas partes y cmo encajan juntos. Las partes de un LOOP Usted puede hacer lo siguiente en un bucle : Paso numricamente las variables y ms de varias estructuras de datos Recoger, contar, sumar, minimizar y maximizar los valores observados, mientras que un bucle Ejecutar arbitrarias expresiones Lisp Decidir cundo terminar el bucle Condicionalmente hacer cualquiera de estas Adems, LOOP se proporciona la sintaxis para lo siguiente: Creacin de variables locales para el uso dentro del bucle Especificar arbitrarias expresiones Lisp para ejecutar antes y despus del bucle adecuado La estructura bsica de un bucle es un conjunto de clusulas, cada una de ellas comienza con una palabra de bucle . 1 Cmo se analiza cada clusula por la LOOP macro depende de la palabra clave. Algunas de las principales palabras clave que

has visto en el captulo 7, son de , la recogida , resumen , contar , hacer , y por ltimo . Iteracin de control La mayora de las clusulas de control llamados iteracin comenzar con la palabra clave bucle para , o su sinnimo como , 2 seguido por el nombre de una variable. Lo que sigue despus de que el nombre de la variable depende del tipo de por clusula. Los incisos de una de clusula puede iterar a travs de lo siguiente: Los rangos de nmeros, arriba o abajo, por intervalos especificados Los elementos individuales de una lista Las clulas de los contras que componen una lista Los elementos de un vector, incluidos los subtipos tales como cadenas de bits y vectores Los pares de una tabla hash Los smbolos en un paquete Los resultados de la evaluacin repetidamente una forma dada Un solo bucle puede tener varias de las clusulas con cada clusula de nombrar su propia variable. Cuando un bucle tiene varias de las clusulas, el ciclo termina tan pronto como cualquiera de la clusula llegue a su condicin final. Por ejemplo, el bucle siguiente:
(Circular para cada elemento en la lista para i desde 1 hasta 10 hacer (algo))

se repetir en la mayora de diez veces, pero puede dejar de antes, si la lista contiene menos de diez artculos. Los bucles de conteo Clusulas aritmticos iteracin controlar el nmero de veces que el cuerpo del bucle ser ejecutado por pisar una variable sobre un rango de nmeros, ejecutando el cuerpo una vez por paso. Estas clusulas consisten de uno a tres de las siguientes frases preposicionales despus de la para (o como ): el desde donde frase, el a donde frase, y el por cunto frase. El desde donde frase especifica el valor inicial de la variable de la clusula. Se trata de una de las preposiciones de , downfrom o upfrom seguido de un formulario, el cual proporciona el valor inicial (un nmero).

El a donde frase especifica un punto de parada para el bucle y se compone de una de las preposiciones a , descuento de hasta , a continuacin , downto , opor encima . seguido por un formulario, que suministra el punto de parada Con hasta y downto , el cuerpo del ciclo se dar por terminado (sin ejecutar de nuevo el cuerpo) cuando la variable pasa por el punto de parada, con el siguiente y anterior , se detiene un earlier.The iteracin por la cantidad de la frase se compone de las preposicionespor y forma un , que se debe evaluar como un nmero positivo. La variable se dio un paso (hacia arriba o abajo, segn lo determinado por las frases de otros) por esta cantidad en cada iteracin o por uno si se omite. Debe especificar al menos una de estas frases preposicionales. Los valores predeterminados son para empezar en cero, incrementar la variable por uno en cada iteracin, y vaya para siempre o, ms probablemente, hasta que alguna otra clusula termina el bucle. Puede modificar cualquiera o todos estos valores predeterminados mediante la adicin de las locuciones prepositivas adecuadas. La arruga slo es que si quieres paso a paso de decrecimiento, no hay ningn valor predeterminado desde donde el valor, por lo que debe especificar, ya sea con una de la o downfrom . As, el texto siguiente:
(Loop for i hasta el 10 i por cobrar)

recoge los once primeros nmeros enteros (de cero a diez), pero el comportamiento de este:
(Loop for i downto -10 i cobrar); incorrecta

no est definido. En su lugar, tiene que escribir lo siguiente:


(bucle para i desde 0 downto -10 i por cobrar)

Tambin tenga en cuenta que debido a que LOOP es una macro, que se ejecuta en tiempo de compilacin, que tiene que ser capaz de determinar la direccin al paso la variable basada nicamente en las preposiciones - no los valores de las formas, que no podr ser conocido hasta tiempo de ejecucin. As, el texto siguiente:
(Circular para i 10 a 20 ...)

funciona bien ya que por defecto es paso a paso incremental. Pero esto:
(Loop for i 20 a 10 ...)

no sabr la cuenta atrs desde veinte hasta diez. Peor an, no le dar un error - no se acaba de ejecutar el bucle ya que ya es mayor de diez. En su lugar, debe escribir lo siguiente:
(bucle para i desde el 20 downto 10 ...)

o este:
(Loop for i downfrom 20 a 10 ...)

Por ltimo, si lo que desea es un bucle que se repite un nmero determinado de veces, puede reemplazar una clusula de la siguiente forma:
para i desde 1 hasta el nmero de forma

con una repeticin de la clusula de esta manera:


repita el nmero de forma

Estas clusulas son idnticos en efecto, salvo la repeticin de la clusula no crea una variable de bucle explcito. Recorrer las colecciones y paquetes El de las clusulas para iterar sobre listas son mucho ms simples que las clusulas de la aritmtica. Apoyan a slo dos frases preposicionales, en y en . Una frase de esta forma:
de var en forma de lista de

pasos var sobre todos los elementos de la lista producida mediante la evaluacin de lista-forma .
(Loop for i in (lista 10 20 30 40) i por cobrar) ==> (10 20 30 40)

De vez en cuando esta clusula se complementa con una de frase, que especifica una funcin a utilizar para desplazarse por la lista. El valor predeterminado es CDR , pero puede ser cualquier funcin que toma una lista y devuelve una lista secundaria. Por ejemplo, usted podra reunir todos los dems elementos de una lista con un bucle de esta manera:
(Loop for i in (lista 10 20 30 40) de # 'cddr i cobrar) ==> (10 30)

Una de sintagma preposicional se utiliza para el paso var contras sobre las clulas que componen una lista.
(Bucle de x en (lista 10 20 30) recopilar x) ==> ((10 20 30) (20 30) (30))

Esta frase tambin puede tomar una por la preposicin:


(Bucle de x en (lista 10 20 30 40) de # 'cddr recolectar x) ==> ((10 20 30 40) (30 40))

Looping sobre los elementos de un vector (que incluye cadenas y vectores de bits) es similar a recorrer los elementos de una lista, excepto la preposicin a travs se utiliza en lugar de en . 3 Por ejemplo:
(Bucle para todo x "abcd" recolectar x) ==> (# \ a # \ b # \ c # \ d)

Iterar sobre una tabla hash o el paquete es un poco ms complicado porque las tablas hash y los paquetes tienen diferentes conjuntos de valores que puede ser que desee para repetir: las claves o valores en una tabla hash, y los diferentes tipos de smbolos en un paquete. Ambos tipos de repeticin siguen el mismo patrn. El patrn bsico es el siguiente:
(Bucle para var siendo las cosas en hash o paquete de ...)

Para las tablas de hash, los posibles valores de las cosas son hash de las claves y los valores hash , que causan var estar enlazado a los valores sucesivos de cualquiera de las teclas o los valores de la tabla hash. El hash-o paquete de forma que se evala una vez para producir un valor, que debe ser una tabla hash. Para iterar sobre un paquete, las cosas pueden ser smbolos , los smbolos presentes los y smbolos external- , que causan var a estar obenlazado a cada uno de los smbolos accesibles en un paquete, cada uno de los smbolos presentes en un paquete (en otras palabras, internados o importados en el mismo paquete), o cada uno de los smbolos que han sido exportadas desde el paquete. El hash-o paquete de forma se evala para producir el nombre de un paquete, que se levant como por FIND-PAQUETE o un objeto de paquete. Sinnimos tambin estn disponibles para las partes de la de la clusula. En lugar de la , puede utilizar cada uno , se puede utilizar de vez de en el , y usted puede escribir las cosas en el singular (por ejemplo, de hash de clave o smbolo ). Por ltimo, ya que a menudo se desea de las claves y los valores cuando se repite ms de una tabla hash, las clusulas de la tabla hash apoyar un uso inciso al final de la clusula de la tabla hash.
(Bucle para k son la clave hash en h utilizando (hash-valor v) ...) (Bucle de v son los valores hash de h utilizando (hash de clave k) ...)

Ambos bucles se unen k a cada tecla en la tabla hash y v el valor correspondiente. Tenga en cuenta que el primer elemento de la usando subclusula debe estar en la forma singular.4 Igual-Entonces iteracin Si ninguno de los otros de las clusulas apoya exactamente la forma de una mejora de la variable que necesita, usted puede tomar el control completo sobre el paso a paso con un igual, entonces la clusula. Esta clusula es similar a las clusulas vinculantes en un Do bucle, pero fundido en una sintaxis ms Algolish. La plantilla es la siguiente:
(Bucle para var = valor inicial de forma [a continuacin paso a la forma ] ...)

Como de costumbre, var es el nombre de la variable que se intensifique. Su valor inicial se obtiene mediante la evaluacin inicial de-forma-valor una vez antes de la primera iteracin.En cada iteracin posterior, paso-forma se evala, y su valor se convierte en el nuevo valor de var . Sin continuacin, parte de la clusula, el valor inicial de forma se vuelve a evaluar en cada iteracin para ofrecer el nuevo valor. Tenga en cuenta que esto es diferente de una DO clusula vinculante sin ningn tipo paso. El paso-forma puede hacer referencia a variables del bucle otros, incluidas las variables creadas por otros para clusulas ms adelante en el bucle. Por ejemplo:
(Repeticin de bucle 5 para x = 0 entonces y para y = 1 entonces (+ xy) recopilar y) ==> (1 2 4 8 16)

Sin embargo, tenga en cuenta que cada uno de la clusula se evala por separado en el orden en que aparece. As que en el bucle anterior, en la segunda iteracin x se establece en el valor de y antes de Y. cambios (en otras palabras, 1 ). Pero y entonces se establece en la suma de su valor antiguo (todava 1 ) y el nuevo valor de x . Si el orden de la paraclusulas se invierte, el cambio de resultados.
(Repeticin de bucle 5 para y = 1 entonces (+ xy) para x = 0 entonces y recopilar y) ==> (1 1 2 4 8)

A menudo, sin embargo, usted querr las formas a paso para mltiples variables para ser evaluados antes de cualquiera de las variables se le da su nuevo valor (similar a como DO los pasos de sus variables). En ese caso, usted puede unirse a

varios de clusulas mediante la sustitucin de todos menos al primero para con el y el . Ya has visto esta formulacin ya est en el LOOP versin de la computacin de Fibonacci en el captulo 7. Aqu hay otra variante, con base en los dos ejemplos anteriores:
(Repeticin de bucle 5 para x = 0 entonces y e y = 1 entonces (+ xy) recoger y) ==> (1 1 2 3 5)

Variables locales Mientras que las principales variables necesarias dentro de un bucle se declar implcitamente en general de las clusulas, algunas veces tendrs que las variables auxiliares, que se puede declarar con con las clusulas.
con var [= forma-valor ]

El nombre de var se convierte en el nombre de una variable local que dejar de existir cuando el bucle termina. Si el con clusula contiene una = forma-valor una parte, la variable se inicializa, antes de la primera iteracin del bucle, el valor de la forma-valor . Mltiple , con clusulas pueden aparecer en un bucle, cada clusula es evaluada de forma independiente en el orden en que aparece y se asigna el valor antes de proceder a la clusula siguiente, permitiendo las variables ms tarde a depender del valor de las variables ya declaradas. Mutuamente variables independientes se pueden declarar en una de las clusulas con una , y entre cada declaracin. Variables desestructurantes Una caracterstica til de LOOP no he mencionado an es la posibilidad de una lista de valores desestructurar asignados a las variables de bucle. Esto le permite desmontar el valor de las listas que de lo contrario pueden asignar a una variable de bucle, de forma similar a BIND desestructurada- obras, pero un poco menos elaborado. Bsicamente, puede reemplazar cualquier variable en un bucle de o con la clusula con un rbol de smbolos, y el valor de la lista que se habra asignado a la variable simple en lugar ser desestructurado en las variables mencionadas por los smbolos en el rbol. Un ejemplo sencillo es el siguiente:
CL-USUARIO> (circular para (ab) en '((1 2) (3 4) (5 6)) hacer (en formato t "a: ~ a, b: ~ a ~%" ab)) uno: 1; b: 2 uno: 3; b: 4

uno: 5; b: 6 NIL

El rbol tambin puede incluir listas de puntos, en cuyo caso el nombre despus de los actos de puntos como un descanso y los parmetros, que se enlaza a una lista que contiene todos los elementos restantes de la lista. Esto es especialmente til con los de / en bucles ya que el valor es siempre una lista. Por ejemplo, este LOOP (que he usado en el Captulo 18 para emitir una lista separada por comas):
(Bucle de contras en la lista hacer (formato t "~ a" (contras de coches)) cuando (los contras cdr) hacer (formato de t ","))

tambin puede escribirse as:


(Bucle for (elemento. Descanso) en la lista hacer (en formato t "~ un" artculo) cuando se hacen los dems (formato t ","))

Si desea ignorar un valor en la lista desestructurado, puede utilizar NIL en lugar de un nombre de variable.
(Circular para (un cero) en '((1 2) (3 4) (5 6)) recoger una) ==> (1 3 5)

Si la lista de desestructuracin contiene ms variables que hay valores en la lista, las variables adicionales se establecen para NIL , por lo que todas las variables esencialmente comoopcionales y parmetros. No hay, sin embargo, cualquier equivalente a y clave parmetros. Valor de acumulacin Las clusulas de acumulacin de valor son quizs la parte ms poderosa de LOOP . Mientras que las clusulas de control de iteracin proporcionar una sintaxis concisa para expresar la mecnica bsica del bucle, que no son drsticamente diferentes de los mecanismos equivalentes proporcionadas por las DO , dolist y DOTIMES . Las clusulas de acumulacin de valor, por el contrario, proporcionan una notacin concisa para un puado de idiomas bucle comn que tiene que ver con la acumulacin de valores, mientras que un bucle. Cada clusula de acumulacin comienza con un verbo y sigue este patrn:
verbo forma [en var ]

Cada vez que a travs del bucle, una clusula de acumulacin de evaluar la forma y guarda el valor de una manera determinada por el verbo . Con una en el inciso, el valor se guarda en la variable que se llama var . La variable es local al bucle, como

si hubiera sido declarado en una con la clusula. Sin en el inciso, la clusula de acumulacin de lugar se acumula un valor predeterminado para la expresin bucle. Los verbos son posibles recolectar , agregar , nconc , conteo , suma , mximo , y reducir al mnimo . Tambin disponible como sinnimos son las formas de participio de presente: la recogida , aadiendo , nconcing , contar , sumar , maximizar y minimizar . A recoger la clusula se acumula una lista que contiene todos los valores de la forma en el orden en que se ve. Se trata de una construccin especialmente til debido a que el cdigo que tendra que escribir para recopilar una lista con el fin de la manera ms eficiente LOOP hace es ms dolorosa que la que normalmente escribe a mano. 5 En cuanto arecolectar son los verbos aadir y nconc . Estos verbos tambin se acumulan valores en una lista, pero se unen a los valores, que deben estar listas, en una sola lista, como si por las funciones APPEND o NCONC . 6 Las clusulas de acumulacin restantes se utilizan para acumular valores numricos. El verbo cuenta cuenta el nmero de veces que la forma es verdad, la suma se acumula un total acumulado de los valores de la forma , maximizar la recoge el valor ms grande visto en forma , y reducir al mnimo recoge los ms pequeos. Por ejemplo, suponga que define una variable aleatoria * * que contiene una lista de nmeros aleatorios.
(Defparameter * random * (repeticin de bucle 100 recoger (al azar 10000)))

A continuacin, el siguiente bucle devolver una lista que contiene informacin resumida varios de los nmeros:
(Loop for i in * random * contar (evenp i) en iguala contar (oddp i) a las cuotas suma total de i en maximizar i en max minimizando i en min finalmente, (ida y vuelta (min lista total mxima iguala las probabilidades)))

Ejecucin incondicional Tan til como las construcciones de acumulacin de valor son, LOOP no sera un muy buen centro de propsito general iteracin si no haba una manera de ejecutar cdigo arbitrario de Lisp en el cuerpo del bucle. La forma ms sencilla de ejecutar cdigo arbitrario dentro de un cuerpo del ciclo es con un do clusula. En comparacin con las clusulas que he descrito hasta ahora,

con sus preposiciones y apartados, lo es un modelo de simplicidad Yodaesque. 7 A do clusula consiste en la palabra no (o hacer ), seguido de una o ms formas de Lisp que estn evaluadas cuando el do clusula. El hacer la clusula termina en el parntesis de clausura del bucle o la palabra clave del siguiente ciclo. Por ejemplo, para imprimir los nmeros del uno al diez, podra escribir lo siguiente:
(bucle para i desde 1 hasta 10 hacer (print i))

Otro, ms dramtica, la forma de ejecucin inmediata es un retorno clusula. Esta clusula consiste en la palabra retorno seguido por una sola forma de Lisp, que se evala, con el valor resultante inmediatamente devuelve como el valor del bucle. Tambin puede salir de un bucle en un do clusula de uso de cualquiera de los operadores normales Lisp de control de flujo, tales como RETURN y RETURN FROM- . Tenga en cuenta que un cambio siempre devuelve la clusula de la inmediata adjuntando LOOP expresin, mientras que un RETURN o return-from en un hacer clusula puede volver de cualquier expresin que encierra. Por ejemplo, comparar el texto siguiente:
(Bloque exterior (Bucle de retorno de 0 i 100), 100 de regresar de LOOP (Print "Esto va a imprimir") 200) ==> 200

a esto:
(Bloque exterior (bucle para i desde 0 hacer (retorno de la externa 100)); 100 de regresar de BLOQUE (Print "Esto no va a imprimir") 200) ==> 100

El hacer y el retorno de las clusulas son llamados colectivamente los incondicionales de ejecucin de las clusulas. Ejecucin condicional Debido a que un do clusula puede contener formas arbitrarias de Lisp, puede utilizar las expresiones Lisp que desee, incluyendo las construcciones de control, tales como SI yCUANDO . Por lo tanto, la siguiente es una manera de escribir un bucle que imprime slo los nmeros pares entre uno y diez:
(bucle para i desde 1 hasta 10 hacer (cuando (evenp i) (impresin i)))

Sin embargo, a veces querrs control condicional en el mbito de las clusulas de bucle. Por ejemplo, suponga que desea sumar slo los nmeros pares entre uno y

diez con unresumen clusula. No se podra escribir un bucle con una do la clusula, porque no habra manera de "recuperar" la suma que en el medio de forma regular la expresin Lisp. En casos como este, es necesario utilizar uno de LOOP 's propias expresiones condicionales como esta:
(Bucle para i de 1 a 10, cuando (evenp i) la suma i) ==> 30

LOOP cuenta con tres construcciones condicionales, y todos siguen este patrn bsico:
condicional de prueba de forma circular-la clusula

La condicin puede ser , si , si , o menos . La prueba de forma regular de alguna forma de Lisp, y bucle de la clusula puede ser una clusula de acumulacin de valor (contar , recoger , y as sucesivamente), una ejecucin incondicional de la clusula, o de otra clusula de ejecucin condicional. Clusulas Mltiples de bucle se puede conectar a una sola condicional por unirse a ellos con y . Como un poco ms de azcar sintctico, en la clusula primera vuelta, despus de que el formulario de prueba, puede utilizar la variable de que para referirse al valor devuelto por la forma de prueba. Por ejemplo, el bucle siguiente recoge los noNIL los valores que se encuentran en un hash de la hora de buscar las llaves en alguna lista- :
(bucle para la clave en alguna lista, cuando (GetHash clave de un hash) que cobro revertido)

Una clusula condicional se ejecuta cada vez que a travs del bucle. Un caso , o cuando se ejecuta la clusula de su bucle de la clusula si se prueba de forma se evala como verdadera. Una menos que se invierte la prueba, la ejecucin de la clusula de bucle slo cuando la prueba de forma es NIL . A diferencia de sus homnimos comunes Lisp,LOOP 's , si y cuando no son ms que sinnimos - no hay ninguna diferencia en su comportamiento. Las tres clusulas condicionales tambin pueden tomar una cosa rama, la cual es seguida por otra clusula bucle o varias clusulas unidas por y . Cuando las clusulas condicionales estn anidados, el conjunto de clusulas conectadas a una clusula condicional interior se puede cerrar con la palabra final . El final es opcional cuando no se necesita para eliminar la ambigedad un condicional anidado - al final de una oracin condicional se desprende del final del ciclo o el inicio de otra clusula no se unieron y .

El siguiente bucle ms bien tonto demuestra las diversas formas de LOOP condicionales. La actualizacin de anlisis de la funcin se llamar cada vez que a travs del bucle con los ltimos valores de las distintas variables acumuladas por las clusulas dentro de los condicionales.
(Circular para i desde 1 hasta 100 si (evenp i) reducir al mnimo i en min-incluso y maximizar la i en un mximo de equilibrio y a menos que (zerop (i mod 4)) En resumen, incluso en i-no-gatas-total final y suma en total de pares ms reducir al mnimo i en min-impar y maximizar la i en max-impar y cuando (zerop (i mod 5)) suma que en cinco aos-total final y suma en total de imparhacer (actualizacin de anlisis de minutos, incluso max-aun minutos y pico max-impar incluso totalimpar total de cinco aos-total aunque-no-Fours-en total))

Configuracin y derribar Una de las ideas clave que los diseadores de la LOOP lenguaje tena acerca de los bucles reales "in the wild" es que el bucle adecuado es a menudo precedido por un bit de cdigo para establecer las cosas y luego seguido por algo ms de cdigo que hace algo con los valores computado por el bucle. Un ejemplo trivial, en Perl, 8 podra tener este aspecto:
my $ evens_sum = 0; my $ odds_sum = 0; foreach my $ i (@ list_of_numbers) { if ($ i% 2) { $ Odds_sum + = $ i; Else {} $ Evens_sum + = $ i; } } if ($ evens_sum> $ odds_sum) { print "La suma de iguala mayor \ n"; Else {} print "La suma de probabilidades mayor \ n"; }

El bucle adecuado en este cdigo es el foreach comunicado. Sin embargo, el foreach bucle no valerse por s misma:. el cdigo en el cuerpo del bucle se refiere a las

variables declaradas en las dos lneas antes del bucle 9 Y el trabajo del bucle no es todo en vano sin el caso de la declaracin despus de que el bucle que en realidad informa de los resultados.En Common Lisp, por supuesto, el LOOP construccin es una expresin que devuelve un valor, por lo que es incluso ms a menudo una necesidad de hacer algo despus de que el bucle adecuado, es decir, generar el valor de retorno. Por lo tanto, dijo que las LOOP diseadores, vamos a darle una forma de incluir el cdigo que es realmente parte del bucle en el mismo bucle. Por lo tanto, LOOP ofrece dos palabras clave, inicialmente y , finalmente , los que introducen el cdigo que se ejecute fuera del cuerpo principal del bucle. Despus de la inicialmente o finalmente , estas clusulas consistir en toda la Lisp forma hasta el inicio de la clusula de bucle siguiente o el final del bucle. Todos losinicialmente formas se combinan en una sola prlogo , que se ejecuta una vez, inmediatamente despus de todas las variables de bucle local se inicializan y antes de que el cuerpo del bucle. El ltimo, las formas estn combinadas de forma similar en un eplogo que se ejecuta despus de la ltima iteracin del cuerpo del bucle. Tanto el prlogo y el eplogo, el cdigo se puede referir a las variables de bucle local. El prlogo se ejecuta siempre, incluso si el cuerpo del bucle se repite cero veces. El bucle puede regresar sin correr el eplogo si alguna de las siguientes situaciones: Un retorno de la clusula se ejecuta. VOLVER , VOLVER-DE , u otra transferencia de control de construccin se llama desde dentro de una forma de Lisp en el cuerpo. 10 El bucle se termina por una siempre y nunca , o thereis clusula, como voy a discutir en la prxima seccin. Dentro del cdigo de eplogo, DEVUELVA o return-from puede ser utilizado para proporcionar explcitamente un valor de retorno para el bucle. Este tipo de valor devuelto explcito tendr prioridad sobre cualquier valor que de otro modo podran ser proporcionados por una acumulacin o una clusula de rescisin de pruebas. Para permitir el RETORNO DE- que se utiliza para volver de un bucle especfico (til cuando se anidan LOOP expresiones), usted puede nombrar a un bucle con la palabra clave de bucle llamado . Si una llamada clusula aparece en un bucle, debe ser la clusula primera. Para un ejemplo sencillo, supongamos que las listas es una lista de listas y desea encontrar un artculo que coincide con algunos criterios en

una de esas listas anidadas. Usted puede encontrar con un par de bucles anidados como este:
(Bucle externo nombrado para la lista en las listas de hacerlo (Bucle para cada elemento en la lista de tareas (Si (lo-que-yo-soy-buscando-a-punto p) (Ida y vuelta, desde el punto exterior))))

Las pruebas de terminacin Mientras que la de y la repeticin de las clusulas de proporcionar la infraestructura bsica para el control del nmero de iteraciones, a veces tendr que salir de un bucle de tiempo. Ya hemos visto cmo un regreso o una clusula RETURN o RETORNO DE- dentro de un do clusula de inmediato puede terminar el bucle, pero as como hay patrones comunes para la acumulacin de valores, tambin hay patrones comunes para decidir cundo es el momento de rescatar el un bucle. Estos modelos son compatibles con LOOP por las clusulas de rescisin, al mismo tiempo , hasta que , la siempre y nunca , y thereis . Todos ellos siguen el mismo patrn.
bucle palabra prueba de forma

Los cinco evaluar la prueba de forma cada vez que a travs de la iteracin y decidir, basndose en el valor resultante, ya sea para terminar el bucle. Se diferencian en lo que sucede despus de terminar el bucle - si lo hacen - y cmo deciden. Las palabras clave de bucle , mientras que y hasta que presentar a los "leves" clusulas de rescisin. Cuando deciden dar por terminado el ciclo, el control pasa al eplogo, omitiendo el resto del cuerpo del bucle. El eplogo a continuacin, puede devolver un valor o hacer lo que quiera hasta el final del bucle. A , mientras que la clusula termina el bucle por primera vez la forma de prueba es falsa, hasta que , por el contrario, se detiene la primera vez que el formulario de prueba es verdadera. Otra forma de terminacin leve es proporcionada por el LOOP-ACABADO macro. Se trata de una forma regular de Lisp, no de una clusula de bucle, para que pueda ser utilizado en cualquier lugar dentro de las formas de un Lisp do clusula. Tambin causa un salto inmediato a la eplogo bucle. Puede ser til cuando la decisin de romper el bucle no puede ser fcilmente condensada en un solo formulario que se puede utilizar con un mismo tiempo o hasta que la clusula.

Los otros tres clusulas - siempre y nunca , y thereis - terminar el bucle con extremo prejuicio, sino que respondi de inmediato desde el bucle, saltndose no slo las clusulas posteriores de bucle, sino tambin el eplogo. Tambin proporcionan un valor predeterminado para el circuito, incluso cuando no son la causa del bucle termine. Sin embargo, si el bucleno se denuncie por uno de estos exmenes de terminacin, el eplogo se ejecuta y puede devolver un valor distinto del predeterminado proporcionado por las clusulas de rescisin. Debido a que estas clusulas de proporcionar a sus propios valores de retorno, no se puede combinar con las clusulas de acumulacin a menos que la clusula de acumulacin tiene una en el inciso. El compilador (o intrprete) debe indicar un error en tiempo de compilacin si are.The siempre y nunca las clusulas de devolver slo valores booleanos, por lo que son ms tiles cuando se necesita utilizar una expresin de bucle, como parte de un predicado. Usted puede utilizar siempre para comprobar que el formulario de prueba es verdadero en cada iteracin del bucle. A la inversa, nunca pruebas de que el formulario de prueba se evala como NIL en cada iteracin. Si el formulario de prueba falla (devuelveNIL en un siempre clusula o no- NIL en un nunca clusula), el bucle termina inmediatamente, regresando NIL . Si el bucle se ejecuta hasta su finalizacin, el valor predeterminado de T se proporciona. Por ejemplo, si desea probar que todos los nmeros en una lista, los nmeros , son an, usted puede escribir lo siguiente:
(Si (bucle para n en nmeros siempre (evenp n)) (Print "todos los nmeros pares."))

De manera equivalente se podra escribir lo siguiente:


(Si (bucle de n en nmeros nunca (oddp n)) (Print "todos los nmeros pares."))

Un thereis clusula se utiliza para comprobar si el formulario de prueba alguna verdad. Tan pronto como el formulario de prueba devuelve un no- NIL valor, el bucle se termina, devolviendo ese valor. Si el bucle se ejecuta hasta su finalizacin, el thereis clusula prev un valor de retorno de NIL .
(Bucle de caracteres a travs de "abc123" thereis (dgito-char-p char)) ==> 1 (Bucle de caracteres a travs de "abcdef" thereis (dgito-char-p char)) ==> NIL

Poniendo todo junto Ahora que has visto todas las caractersticas principales de la LOOP instalacin. Puede combinar cualquiera de las clusulas que he tratado, siempre y cuando usted cumpla con las siguientes reglas: La llamada clusula, en su caso, debe ser la primera clusula. Despus de la llamada clusula de hecho todo el principio , con , para y repetir las clusulas. Luego viene las clusulas del cuerpo: Prueba de ejecucin condicional e incondicional, la acumulacin, y la terminacin. 11 Terminar con cualquier finalmente clusulas. El LOOP macro se expandir en el cdigo que realiza las siguientes acciones: Inicializa todas las variables del bucle local, como declaran con con o de las clusulas, as como los crea de forma implcita por las clusulas de acumulacin. Las formas de valor iniciales se evalan en el orden de las clusulas aparecen en el bucle. Ejecutar los formularios proporcionados por cualquier inicialmente clusulas - el prlogo - en el orden en que aparecen en el bucle. Iterar, ejecutando el cuerpo del bucle como se describe en el prrafo siguiente. Ejecutar los formularios proporcionados por cualquier finalmente clusulas el eplogo - en el orden en que aparecen en el bucle. Mientras que el bucle se iteracin, el cuerpo se ejecuta en primer lugar, paso a paso las variables de control de iteracin y luego ejecutar cualquier ejecucin condicional o incondicional, la acumulacin, o clusulas de rescisin de prueba en el orden en que aparecen en el cdigo del bucle. Si alguna de las clusulas contenidas en el cuerpo del bucle terminar el bucle, el resto del cuerpo se omiten y las declaraciones de bucle, posiblemente despus de ejecutar el eplogo. Y eso es casi todo lo que hay que hacer. 12 Vamos a usar LOOP con bastante frecuencia en el cdigo ms adelante en este libro, as que vale la pena tener un poco de conocimiento de la misma. Ms all de eso, depende de lo mucho que lo utilice. Y con eso, ya est listo para sumergirse en los captulos prcticos que conforman el resto del libro - primero, escribiendo un filtro de spam.

1 El trmino clave de bucle es un poco desafortunado, como palabras clave de bucle no son palabras

clave en el sentido normal de ser smbolos de la PALABRA CLAVE paquete. De hecho, cualquier smbolo, de cualquier paquete que, con el nombre apropiado a hacer, el LOOP macro slo se

preocupa por sus nombres. Por lo general, sin embargo, estn escritas sin calificador paquete y se lo lea (e internados si es necesario) en el paquete actual.
2 Porque uno de los objetivos de la LOOP es permitir que las expresiones de bucle para ser escrito

con una sintaxis cuasi-Ingls, muchas de las palabras tienen sinnimos que se tratan de la misma LOOP , pero permiten cierta libertad para expresar las cosas en Ingls un poco ms idiomtica para los diferentes contextos.
3 Usted puede preguntarse por qu LOOP no se puede averiguar si se trata de recorrer una lista o un

vector sin necesidad de preposiciones diferentes. Esta es otra consecuencia de LOOP es una macro: el valor de la lista o vector no se conocer hasta que en tiempo de ejecucin, pero LOOP , como una macro, tiene que generar cdigo en tiempo de compilacin. Y LOOP diseadores 's quera que generar cdigo extremadamente eficiente. Para ser capaces de generar cdigo eficiente para recorrer a travs de, por ejemplo, un vector, lo que necesita saber en tiempo de compilacin que el valor va a ser un vector en tiempo de ejecucin - por lo tanto, las preposiciones se necesitan diferentes.
4 No me preguntes por qu LOOP autores 's se acobardaron en el estilo de no-parntesis para el uso

de la subclusula.
5 El truco est en mantener el ahold de la cola de la lista y aadir nuevas clulas de los contras por

SETF cin del CDR de la cola. Un equivalente autgrafa del cdigo generado por(i bucle para recolectar hasta el 10 i) se vera as:
(Ver ((lista de cero) (cola nula) (i 0 (1 + i))) ((> I 10) Lista) (Let (((nuevas cons i nil))) (If (null lista) (Setf nueva lista) (Setf (cdr cola) nueva)) (Setf cola nueva)))

Por supuesto que rara vez va, si acaso, escribir el cdigo de esa manera. Vamos a usar cualquiera de LOOP o (si, por alguna razn, usted no desea utilizar LOOP ) la norma PUSH / NREVERSE idioma para recoger los valores.
6 Recurdese que NCONC es la versin destructiva de APPEND - es seguro de usar un nconc clusula

slo si los valores que se estn recogiendo son nuevas listas que no comparten una estructura con otras listas. Por ejemplo, esto es seguro:
(Loop for i hasta 3 nconc (Lista II)) ==> (0 0 1 1 2 2 3 3)

Pero esto te metes en problemas:


(Loop for i en (lista 1 2 3) nconc i) ==> indefinido

Cuanto ms tarde lo ms probable es entrar en un bucle infinito como las diversas partes de la lista producida por (lista 1 2 3) estn destructivamente modificados para apuntar el uno al otro. Pero incluso eso no est garantizado - el comportamiento no es ms que definido.
7 "No! Trate de no. hacer ... o no. No hay intentos." - Yoda, El Imperio Contraataca

8 No estoy metiendo con Perl aqu - este ejemplo se vera ms o menos el mismo en cualquier idioma

que basa su sintaxis en C.


9 Perl le permiten salirse con la suya no declarar las variables si el programa no use strict . Sin

embargo, usted debe siempre use strict en Perl. El cdigo equivalente en Python, Java o C siempre requerirn las variables que se declar.
10 Puede provocar un bucle para terminar con normalidad, ejecuta el eplogo, a partir del cdigo

Lisp ejecutan como parte del cuerpo del bucle con la macro local de LOOP-META .
11 Algunas implementaciones de Lisp comunes le permitir salirse con las clusulas del cuerpo de

mezcla y de las clusulas, sino que es estrictamente definido, y algunas implementaciones se rechazan tales bucles.
12 El aspecto de LOOP no he tocado en absoluto es la sintaxis para declarar los tipos de variables de

bucle. Por supuesto, no he hablado de las declaraciones de tipos fuera de LOOP tampoco. Voy a cubrir el tema en general un poco en el captulo 32. Para obtener informacin sobre cmo trabajar con LOOP , consulte a su referencia favorita Common Lisp.

23. Prctico: un filtro de spam


En 2002, Paul Graham, que tiene algn tiempo en sus manos despus de la venta de Viaweb a Yahoo, escribi el ensayo "Un Plan para el Spam" 1 que puso en marcha una pequea revolucin en la tecnologa de filtrado de spam. Antes de que el artculo de Graham, los filtros de la mayora del spam se escribe en trminos de reglas hechas a mano: si un mensaje tiene XXX en el tema, es probable que sea un spam, y si un mensaje tiene un ms de tres o ms palabras en una fila de letras maysculas, que es probablemente un correo no deseado.Graham pas varios meses tratando de escribir como un filtro basado en normas antes de darse cuenta que era fundamentalmente una tarea del alma de succin.
Reconocer las caractersticas individuales de spam que usted tiene que tratar de entrar en la mente del spammer, y, francamente, quiero pasar el menor tiempo en la mente de los spammers como sea posible.

Para evitar tener que pensar como un spammer, Graham decidi tratar el spam distinguir entre mensajes legtimos, tambin conocido como el jamn , con base en las estadsticas recogidas sobre el que las palabras se producen en qu tipos de mensajes de correo electrnico. El filtro no perdera de vista la frecuencia con la aparicin de determinadas palabras, tanto en el spam y los mensajes de jamn y luego usar las frecuencias asociadas a las palabras en un mensaje nuevo para calcular la probabilidad de que era spam o jamn. Llam a su mtodo bayesiano de filtrado despus de la tcnica estadstica que se utiliza para combinar las frecuencias de palabras individuales en una probabilidad global. 2 El corazn de un filtro de spam En este captulo, implementar el ncleo de un motor de filtrado de spam. Usted no va a escribir una sopa de frutos secos-a-aplicacin de filtrado de spam, sino que nos centraremos en las funciones de clasificacin de mensajes nuevos y entrenando el filtro. Esta aplicacin va a ser lo suficientemente grande que vale la pena definir un nuevo paquete para evitar conflictos de nombres. Por ejemplo, en el cdigo fuente se puede descargar desde el sitio Web de este libro, yo uso el nombre del paquete COM.GIGAMONKEYS.SPAM , la definicin de un paquete que utiliza tanto la norma

de common-lisp paquete y elCOM.GIGAMONKEYS.PATHNAMES paquete desde el captulo 15 , as:


(Defpackage: com.gigamonkeys.spam (: Uso: common-lisp: com.gigamonkeys.pathnames))

Cualquier archivo que contiene cdigo para esta aplicacin debe comenzar con esta lnea:
(En el paquete: com.gigamonkeys.spam)

Usted puede utilizar el mismo nombre de paquete o reemplazar com.gigamonkeys con un poco de dominio que controlar. 3 Tambin puede escribir la misma forma en el REPL para cambiar a este paquete para probar las funciones que usted escribe. En BABA esto va a cambiar el mensaje de laCL-USUARIO> de > SPAM de esta manera:
CL-> USER (en el paquete: com.gigamonkeys.spam) # <El COM.GIGAMONKEYS.SPAM package> SPAM>

Una vez que tenga definido un paquete, usted puede comenzar en el cdigo actual. La funcin principal que necesita para poner en prctica tiene un trabajo simple tome el texto de un mensaje como un argumento y clasificar el mensaje como spam, jamn, o no est seguro. Usted puede aplicar esta funcin bsica, definiendo en trminos de otras funciones que usted va a escribir en un momento.
(Texto defun clasificar () (Clasificacin (puntaje (extracto de las caractersticas de texto))))

Lectura de adentro hacia fuera, el primer paso en la clasificacin de un mensaje es para extraer caractersticas para pasar a la puntuacin de la funcin. En resultados que usted calcular un valor que puede ser traducido a una de las tres clasificaciones - spam, jamn, o no est seguro - por la funcin de la clasificacin . De las tres funciones,clasificacin es la ms simple. Se puede asumir puntaje devolver un valor cercano a 1 si el mensaje es un correo no deseado, cerca de 0 si se trata de un jamn, y cerca de 0,5, si no est claro. Por lo tanto, se puede implementar la clasificacin de esta manera:
(* Defparameter max-jamn de puntuacin de 0,4 *) (Defparameter * min-spam-score * 0.6) (Defun clasificacin (puntaje) (Cond ((<= Resultado * max-jamn-score *) 'jamn) ((> = Puntuacin * min-spam-score *) 'spam)

(T 'no est seguro)))

El extracto de las caractersticas de la funcin es casi tan sencillo, aunque requiere un poco ms de cdigo. Por el momento, las caractersticas que va a extraer sern las palabras que aparecen en el texto. Para cada palabra, es necesario no perder de vista el nmero de veces que se ha visto en un correo no deseado y el nmero de veces que se ha visto en un jamn. Una forma conveniente de mantener a esos pedazos de datos junto con la palabra en s es definir una clase, la palabra-funcin , con tres franjas horarias.
(Defclass palabra-funcin () ((La palabra : Initarg: la palabra : La palabra de acceso : InitForm (error "tendr que presentar: la palabra") : La documentacin "La palabra esta caracterstica representa".) (Spam recuento : Initarg: spam recuento : Acceso spam recuento : 0 initForm : Documentacin "Nmero de mensajes de spam que hemos visto esta caracterstica in") (Jamn de conteo : Initarg: jamn de conteo : Acceso jamn recuento : 0 initForm : Documentacin "El nmero de jamones que hemos visto esta caracterstica in")))

Usted mantendr la base de datos de caractersticas en una tabla hash de esta manera puede encontrar el objeto que representa una determinada caracterstica. Se puede definir una variable especial, * La funcin de base de datos * , para mantener una referencia a la tabla hash.
(Defvar * La funcin de base de datos * (make-hash-table: test # 'igual))

Usted debe usar defvar en lugar de DEFPARAMETER porque no quiere * La funcin de base de datos * que se restablezca si le toca volver a cargar el archivo que contiene esta definicin durante el desarrollo - es posible que tenga los datos almacenados en la base de datos * funciones * que usted don ' t quiero perder. Por supuesto, eso significa que si usted no desea borrar la base de datos de funcin, no puede volver a evaluar el defvar formulario. As que usted debe definir una funcinclara base de datos .
(Defun clara base de datos () (Setf * La funcin de base de datos * (make-hash-table: test # 'igual)))

Para encontrar las caractersticas presentes en un determinado mensaje, el cdigo tendr que extraer las palabras individuales y luego buscar la

correspondientepalabra-funcin de objeto en la base de datos * funciones * . Si * La funcin de base de datos * no contiene ninguna de estas caractersticas, se tendr que crear una nueva palabra-funcin para representar la palabra. Usted puede encapsular ese poco de lgica en una funcin, pasante-funcin , que tiene una palabra y devuelve la funcin apropiada, creando si es necesario.
(Defun interna-funcin (la palabra) (O (GetHash palabra * La funcin de base de datos *) (Setf (GetHash palabra * La funcin de base de datos *) (Make-instance 'palabra de programas: Word))))

Puede extraer las palabras individuales del texto del mensaje con una expresin regular. Por ejemplo, utilizando el porttil de Common Lisp Perl Compatible Regular Expression (CL-PPCRE) Biblioteca escrito por Edi Weitz, puede escribir extraer de las palabras de esta manera: 4
(Defun extracto de las palabras (texto) (Borrar duplicados(CL-ppcre: todos los partidos-como-cadenas "[a-zA-Z] {3,}" de texto) : Test # 'cadena =))

Ahora todo lo que queda para poner en prctica extracto de las caractersticas es poner extraer las caractersticas y funcin interna-juntos. Como extracto de las palabras- devuelve una lista de cadenas y desea una lista con cada cadena traducida a la correspondientefuncin de la palabra- , este es un momento perfecto para usar MAPCAR .
(Defun extraer las caractersticas (de texto) (Mapcar # 'interno-funcin (extracto de las palabras de texto)))

Puede probar estas funciones en el REPL de esta manera:


SPAM> (extracto de las palabras "foo bar baz") ("Foo" "bar" "baz")

Y usted puede asegurarse de que el DELETE-DUPLICADOS est trabajando de esta manera:


SPAM> (extracto de las palabras "foo bar baz foo bar") ("Baz" "foo" "bar")

Tambin puede probar las caractersticas del extracto .


Spam> (extracto de las caractersticas "foo bar baz foo bar") (# # #x71ef28da> <WORD-FEATURE @ @ <WORD-FEATURE #x71e3809a> # @ <WORD-FEATURE #x71ef28aa>)

Sin embargo, como se puede ver, el mtodo por defecto para la impresin de objetos arbitrarios no es muy informativo. Mientras se trabaja en este programa, que ser

de utilidad para poder imprimir caractersticas de textos objetos de una manera menos opaco. Por suerte, como he mencionado en el captulo 17, la impresin de todos los objetos se lleva a cabo en trminos de una funcin genrica PRINTOBJETO , por lo que para cambiar la forma caracterstica de la palabra- los objetos se imprimen, slo tiene que definir un mtodo de PRINT-OBJETO que se especializa en la palabra funcin . Para hacer ms fcil la aplicacin de tales mtodos, Common Lisp ofrece la macroPRINT que no son legibles-OBJETO . 5 La forma bsica de PRINT no son legibles por-objeto es el siguiente:
(Impresin que no son legibles-objeto ( objeto de identidad ) forma corporal *) flujo variable clave y el tipo de

El objeto de argumento es una expresin que se evala como el objeto que desea imprimir. Dentro del cuerpo de PRINT no son legibles-OBJETO , flujo variable est enlazada a una secuencia a la que usted puede imprimir lo que quieras. Lo que se imprime en esa corriente se emitir por la IMPRESIN que no son legiblesOBJETO y cerrado en la sintaxis estndar para los objetos ilegibles, # <> . 6 PRINT que no son legibles-OBJETO tambin le permite incluir el tipo de objeto y una indicacin de la identidad del objeto a travs de la palabra los parmetros de tipo y de la identidad . Si son no- NIL , la salida se iniciar con el nombre de la clase del objeto y fin, con indicacin de la identidad del objeto similar a lo que est impreso por el incumplimiento PRINT-OBJETO mtodo para NORMAS OBJETO s. Por la palabra-funcin , es probable que desee definir un PRINT-OBJETO mtodo que incluye el tipo, pero no la identidad, junto con los valores de la palabra , el jamn de conteo y recuento de spam ranuras. Este mtodo sera el siguiente:
(Defmethod impresin-objeto ((palabra objeto-funcin) la corriente) (Impresin que no son legibles-objeto (flujo de objeto: tipo de t) (Con ranuras (palabra jamn recuento de spam cuenta) objeto (Formato de secuencia "~ s: jamones ~ ~ d: d spams" palabra de jamn recuento de spam cuenta))))

Ahora, al probar las caractersticas del extracto en el REPL, puede ver ms claramente qu caractersticas se extraen.
Spam> (extracto de las (# <WORD-FEATURE "baz" # <WORD-FEATURE "foo" # <WORD-FEATURE "bar" caractersticas "foo bar baz foo bar") :hams 0 :spams 0> :hams 0 :spams 0> :hams 0 :spams 0>)

Entrenando el filtro Ahora que usted tiene una manera de no perder de vista las caractersticas individuales, que est casi listo para poner en prctica puntaje . Pero primero es necesario escribir el cdigo que vamos a usar para entrenar el filtro spam para resultados se tienen algunos datos para su uso. Vas a definir una funcin, de tren , que tiene un poco de texto y un smbolo que indica qu tipo de mensaje que es - el jamn o el correo no deseado - los incrementos y que sea el recuento de jamn o el recuento de correo no deseado de todas las caractersticas presentes en el texto como as como un conteo global de los jamones o spams procesados. Una vez ms, usted puede tomar un enfoque de arriba hacia abajo y poner en prctica en trminos de otras funciones que an no existen.
(Defun de tren (tipo de texto) (Dolist (funcin (extracto de las caractersticas de texto)) (Incremento de conteo tipo de caracterstica)) (Incremento-total-del tipo de cuenta))

Usted ya ha escrito extracto de caractersticas , por lo que al lado est el incremento de recuento , que tiene una funcin de la palabra- y un tipo de mensaje y de incrementos de la ranura correspondiente de la entidad. Dado que no hay razn para pensar que la lgica de incrementar estos aspectos va a cambiar para diferentes tipos de objetos, se puede escribir como una funcin regular. 7 Debido a que ha definido tanto el jamn de recuento y el spam cuenta- con un : accesoopcin, puede utilizar INCF y las funciones de acceso creadas por DEFCLASS para incrementar la ranura correspondiente.
(Defun incremento de cuentas (el tipo de accidente) (Tipo ecase (Jamn (incf (jamn de recuento de funcin))) (Correo no deseado (incf (spam recuento de funcin)))))

El ECASE constructo es una variante del CASO , ambos de los cuales son similares a de caso declaraciones en Algol-derivados de lenguas (rebautizado interruptor en C y su progenie). Ambos evaluar su primer argumento - la forma clave - y luego encontrar la clusula cuyo primer elemento - la clave - es el mismo valor de acuerdo a EQL . En este caso, eso significa que la variable de tipo se evala, dando cualquier valor que se pasa como segundo argumento de incremento de recuento . Las claves no son evaluados. En otras palabras, el valor de tipo sern comparados con los objetos literales ledos por el lector de Lisp como parte de la ECASE formulario. En esta funcin, eso significa que las claves son los smbolos de jamn y

el correo no deseado , no los valores de las variables con nombre de jamn y elcorreo no deseado . As pues, si el incremento de conteo se llama as:
(Incremento de contar con jamn-alguna caracterstica-')

el valor de tipo ser el smbolo de jamn , y la primera rama de la ECASE sern evaluados y se incrementa la funcin de conteo de jamn. Por otro lado, si se llama as:
(Incremento de spam, contar con algn rasgo-')

a continuacin, la segunda rama se ejecutar, incrementa el nmero de spam. Tenga en cuenta que los smbolos de jamn y el spam se citan cuando se llamaincremento de conteo pues de lo contrario estaran evaluados como los nombres de las variables. Sin embargo, no est citado, cuando aparecen en ECASE ya ECASE no evala las teclas. 8 El Correo de ECASE significa "exhaustiva" o "error", que significa ECASE debe sealar un error si el valor de la clave es otra cosa que una de las claves en la lista. El ordinario deCASE es ms flexible, regresando NIL si no hay clusula de equivalencia se encuentra. Para llevar a cabo el incremento total-recuento , es necesario decidir dnde almacenar los recuentos, por el momento, dos variables ms especiales,total * de spam * y * Los totales de jamones * , lo har bien.
(* Los defvar total de los spams * 0) (* Los defvar total de los jamones * 0) (Defun tipo de incremento-total-count () (Tipo ecase (Jamn (incf * Los totales de jamones *)) (Correo no deseado (incf * Los totales de los spams *))))

Usted debe usar defvar para definir estas dos variables por la misma razn que lo utiliza con funciones de base de datos * * - Los datos they'll bodegas construidas mientras se ejecuta el programa que no necesariamente quiere tirar porque le toca para recargar su cdigo durante el desarrollo. Sin embargo, usted desea restablecer las variables si alguna vez reiniciar * La funcin de base de datos * , por lo que habra que aadir unas pocas lneas de clara base de datos como se muestra aqu:
(Defun clara base de datos () (Setf * La funcin de base de datos * (make-hash-table: test # 'igual) * Los totales de los spams * 0 * Los totales de jamones * 0))

Palabra por-Estadsticas El corazn de una estadstica filtro de spam es, por supuesto, las funciones que calculan las estadsticas-basadas en probabilidades. Los matices matemticos 9 de exactamente por qu funcionan estos clculos estn fuera del mbito de este libro los lectores interesados lo desea, puede hacer referencia a varios artculos por Gary Robinson. 10 me centrar ms bien en cmo se ponen en ejecucin. El punto de partida para los clculos estadsticos es el conjunto de valores medidos las frecuencias almacenadas en la base de datos * funciones * ,total * de spam * , y * el total de jamones * . Suponiendo que el conjunto de mensajes formados en es estadsticamente representativa, se puede tratar a las frecuencias observadas como las probabilidades de las mismas caractersticas que muestran en jamones y spams en los futuros mensajes. El plan bsico es clasificar un mensaje mediante la extraccin de las caractersticas que contiene, el clculo de la probabilidad individual de que un determinado mensaje que contiene la funcin es un correo no deseado, y luego la combinacin de todas las probabilidades individuales en una puntuacin total para el mensaje. Los mensajes con muchas caractersticas de "spam" y pocas funciones "Hammy", recibir una puntuacin de cerca de 1, y los mensajes con caractersticas Hammy muchos y pocos rasgos spam marcar cerca de 0. La primera funcin estadstica que se necesita es uno que calcula la probabilidad bsica de que un mensaje que contiene una determinada caracterstica es un spam. Mediante un punto de vista, la probabilidad de que un mensaje dado que contiene la caracterstica es un spam es la relacin de los mensajes de spam que contienen la caracterstica a todos los mensajes que contienen la caracterstica. De este modo, se podra calcular de esta manera:
(Funcin defun-spam de probabilidad () (Con ranuras (spam recuento de jamn-count) funcin (/ Spam-count (+ spam recuento de jamn-cuenta))))

El problema con el valor calculado por esta funcin es que est fuertemente afectada por la probabilidad global de que cualquier mensaje ser un spam o el ham uno. Por ejemplo, supongamos que usted consigue nueve veces tanto como el jamn como spam en general. Una caracterstica completamente neutral aparecer en un correo no deseado por cada nueve jamones, que le da una probabilidad de spam de 1/10 de acuerdo con esta funcin.

Pero usted est ms interesado en la probabilidad de que una determinada caracterstica aparecer en un mensaje de spam, independiente de la probabilidad de conseguir un spam o el ham. Por lo tanto, es necesario dividir el nmero de spam por el nmero total de mensajes de spam y capacitados en el recuento de jamn por el nmero total de los jamones. Para evitar la divisin por cero errores, si alguno de los spams total * * o * Total-jamones * es cero, se debe tratar la frecuencia correspondiente a cero. (Obviamente, si el nmero total de cualquiera de los spams o jamones es cero, entonces el correspondiente per-caracterstica de recuento tambin ser cero, as que usted puede tratar a la frecuencia resultante igual a cero, sin efectos negativos.)
(Funcin defun-spam de probabilidad () (Con ranuras (spam recuento de jamn-count) funcin (Let ((spam frecuencia (/ spam-count (mximo 1 * total de los spams *))) (HAM-frecuencia (/ jamn-count (mximo 1 * total de los jamones *)))) (/ Spam-frecuencia (+ spam frecuencia de jamn de frecuencia)))))

Esta versin adolece de otro problema - que no toma en cuenta el nmero de mensajes analizados para llegar a las probabilidades por-palabra. Supongamos que usted ha entrenado en 2.000 mensajes, spam y medio de jamn mitad. Consideremos ahora dos caractersticas que han aparecido slo en los spams. Uno ha aparecido en todos los 1.000 los spams, mientras que el otro apareci slo una vez. De acuerdo con la definicin actual de correo no deseado de probabilidad , la aparicin de cualquier funcin predice que un mensaje es spam, con igual probabilidad, a saber, 1. Sin embargo, todava es muy posible que la caracterstica que ha aparecido una sola vez es en realidad una caracterstica neutra - es, obviamente, poco frecuente, ya sea en los spams y piernas, que aparecen slo una vez en 2.000 mensajes. Si entrenados en otros 2.000 mensajes, que bien podra aparecer una vez ms, esta vez en un jamn, por lo que de repente una caracterstica neutra, con una probabilidad de spam de 0.5. As que parece que le gustara calcular una probabilidad de que de alguna manera los factores en el nmero de puntos de datos que van en la probabilidad de cada funcin. En sus papeles, Robinson sugiri una funcin basada en la nocin bayesiano de la incorporacin de los datos observados en los conocimientos previos o supuestos. Bsicamente, se calcula una nueva probabilidad, comenzando con una

probabilidad de asumir antes y un peso para dar asume que la probabilidad antes de aadir nueva informacin. La funcin de Robinson es la siguiente:
(Defun-spam bayesiano de probabilidad (funcin opcional y (Que se supone-la probabilidad 1/2) (Peso 1)) (Let ((bsica-probabilidad (spam probabilidad de funcin)) (Puntos de datos (+ (spam recuento de funcin) (jamn recuento funcin)))) (/ (+ (* Peso supuesto de probabilidad) (* Datos de los puntos bsicos de probabilidad)) (+ Peso de puntos de datos))))

Robinson sugiere que los valores de 1/2 para el supuesto de probabilidad y 1 para el peso . A partir de estos valores, una caracterstica que ha aparecido en un correo no deseado y los jamones no tiene un bayesiano-spam de probabilidad de 0,75, una caracterstica que ha aparecido en 10 spams y los jamones no tiene unbayesianospam de probabilidad de aproximadamente 0.955, y que una ha coincidido en 1.000 mensajes de spam y los jamones no tiene una probabilidad de spam de aproximadamente 0,9995. La combinacin de Probabilidades Ahora que usted puede calcular el bayesiano-spam de probabilidad de cada caracterstica individual que se encuentra en un mensaje, el ltimo paso en la aplicacin de lapuntuacin de la funcin es encontrar una manera de combinar un montn de probabilidades individuales en un nico valor entre 0 y 1. Si las probabilidades de caractersticas individuales son independientes, entonces sera matemticamente sonido para multiplicarlos juntos para conseguir una probabilidad combinada.Pero es poco probable que en realidad son independientes - ciertas caractersticas es probable que aparezcan juntos, mientras que otros nunca lo hacen. 11 Robinson propuso la utilizacin de un mtodo para combinar probabilidades inventado por el estadstico RA Fisher. Sin entrar en los detalles de exactamente por qu su tcnica funciona, es la siguiente: En primer lugar, se combinan las probabilidades de multiplicar entre s. Esto le da un nmero cercano a 0 las probabilidades ms bajas que haba en el conjunto original. Luego tomar el registro de ese nmero y se multiplica por -2. Fisher mostr en 1950 que si las probabilidades individuales son independientes y extrae de una distribucin uniforme entre 0 y 1, entonces el valor resultante sera en una distribucin de chicuadrado. Este valor y el doble del nmero de probabilidades se puede alimentar en

una inversa de chi-cuadrado funcin, y va a devolver la probabilidad de que refleja la probabilidad de obtener un valor que grande o mayor al combinar el mismo nmero de probabilidades seleccionados al azar. Cuando la inversa de chi-cuadrado funcin devuelve una probabilidad baja, significa que hubo un nmero desproporcionado de bajas probabilidades (ya sea una gran cantidad de probabilidades relativamente bajas o unas pocas probabilidades muy bajas) en las probabilidades individuales. Para utilizar esta probabilidad para determinar si un determinado mensaje es un spam, usted comienza con una hiptesis nula , un hombre de paja que usted espera de derribar. La hiptesis nula es que el mensaje que se clasifican en realidad es slo una coleccin aleatoria de caractersticas. Si lo fuera, entonces las probabilidades individuales, la probabilidad de que cada funcin que aparece en un correo no deseado - tambin sera al azar. Es decir, una seleccin aleatoria de las caractersticas generalmente contienen algunas de las caractersticas con una alta probabilidad de aparecer en el spam y otras caractersticas con una baja probabilidad de aparecer en el correo no deseado. Si se va a combinar estas probabilidades seleccionados al azar segn el mtodo de Fisher, usted debe obtener un valor de mediana combinada, que a la inversa de chi-cuadrado funcin le dir que es muy probable que se produzcan por casualidad, ya que, de hecho, habra. Pero si la inversa de chi-cuadrado funcin devuelve una probabilidad muy baja, significa que es poco probable que las probabilidades de que entraron en el valor combinado fueron seleccionados al azar, haba pocas probabilidades demasiados para que eso sea probable. As que usted puede rechazar la hiptesis nula y en lugar de adoptar la hiptesis alternativa de que las funciones relacionadas fueron extrados de una muestra sesgada - uno con pocas caractersticas de alta probabilidad de spam y muchas caractersticas de baja probabilidad de spam. En otras palabras, debe ser un mensaje de jamn. Sin embargo, el mtodo de Fisher no es simtrica ya que la inversa de chi-cuadrado funcin devuelve la probabilidad de que un nmero dado de probabilidades seleccionadas al azar se combinaran para un valor tan grande o ms grande que el uno usted consigui mediante la combinacin de las probabilidades reales. Esta asimetra trabaja a su ventaja, porque cuando se rechaza la hiptesis nula, ya sabes lo que la hiptesis ms probable es. Cuando se combinan las probabilidades individuales de spam a travs del mtodo de Fisher, y le dice que hay una alta

probabilidad de que la hiptesis nula es malo - que el mensaje no es una coleccin aleatoria de palabras -, entonces significa que es probable que el mensaje es un jamn. El nmero devuelto es, si no literalmente la probabilidad de que el mensaje es un jamn, por lo menos una buena medida de su "hamminess." A la inversa, la combinacin de Fisher de las probabilidades individuales de jamn te ofrece una medida del mensaje de "spam se." Para obtener un resultado final, es necesario combinar estas dos medidas en un solo nmero que le da una combinacin de hamminess-spam se puntaje que va de 0 a 1. El mtodo recomendado por Robinson es para agregar la mitad de la diferencia entre el hamminess y las puntuaciones de spam se a 1/2, en otras palabras, para promediar el spam se y 1 menos la hamminess. Esto tiene el efecto agradable que cuando concuerden las dos planillas (alta y spam se hamminess baja, o viceversa) que va a terminar con un fuerte indicador de cerca de 0 o 1. Pero cuando los resultados y spam se hamminess son altos o bajos tanto, entonces usted va a terminar con un valor final de cerca de 1/2, que se puede tratar como un "seguro" de clasificacin. La puntuacin de la funcin que implementa este esquema es el siguiente:
(Puntuacin de defun (caractersticas) (Let ((spam probs ()) (jamn probs ()) (nmero-de-probs 0)) (Dolist (caractersticas de funciones) (A menos que (sin entrenamiento-p caracterstica) (Let ((spam problemas (float (bayesiano-spam probabilidad de funcin) 0.0d0))) (Empujar proble-spam spam probs) (Push (- 1.0d0 problemas de spam) de jamn probs) (Incf nmero-de-probs)))) (Let ((h (- 1 (Fisher-spam probs nmero-de-probs))) (S (- 1 (Fisher jamn probs nmero-de-probs)))) (/ (+ (- 1 h) s) 2.0d0))))

Se toma una lista de caractersticas y de bucle por encima de ellos, la creacin de dos listas de probabilidades, un listado de las probabilidades de que un mensaje que contiene cada funcin es un correo no deseado y el otro de que un mensaje que contiene cada funcin es un jamn. Como una optimizacin, tambin puede contar el nmero de probabilidades, mientras que un bucle sobre ellos y pasar la cuenta a los pescadores para no tener que contarlas de nuevo en los pescadores s mismo. El valor devuelto porlos pescadores ser baja si las probabilidades individuales contenidas bajas probabilidades demasiados han venido de texto al azar. Por lo tanto, una baja de Fisherpuntuacin de las probabilidades de spam quiere decir que

hubo muchas caractersticas Hammy, restando que el puntaje de 1 le da una probabilidad de que el mensaje es un jamn. Por el contrario, restando el pescador puntuacin de las probabilidades de jamn le da la probabilidad de que el mensaje era un spam. La combinacin de esas dos probabilidades le da una puntuacin de spam se general entre 0 y 1. Dentro del bucle, puede usar la funcin sin entrenamiento-p para saltar las caractersticas extradas de el mensaje de que nunca fueron vistos durante el entrenamiento. Estas caractersticas tienen un recuento de spam y los recuentos de jamn de cero. El inexperto-p la funcin es trivial.
(Defun sin entrenamiento-p (funcin) (Con ranuras (spam recuento de jamn-count) funcin (Y (zerop spam recuento) (zerop jamn cuenta))))

La nica funcin nueva es otra de pescadores en s. Suponiendo que usted ya tena una inversa-chi-cuadrado funcin, los pescadores es conceptualmente simple.
(Defun Fisher (probs nmero-de-probs) "El cmputo de Fisher descrito por Robinson." (Inverso-chi-cuadrado (* 2 (log (reducir el # '* probs))) (* 2 El nmero-de-probs)))

Por desgracia, hay un pequeo problema con esta sencilla aplicacin. Si bien la utilizacin REDUCIR es una manera concisa e idiomtica de la multiplicacin de una lista de nmeros, en esta aplicacin particular, existe el peligro de que el producto ser un nmero demasiado pequeo para ser representado como un nmero de punto flotante. En ese caso, el resultado ser subdesbordamiento a cero. Y si el producto de las probabilidades underflow, todas las apuestas estn apagadas, porque tomar el LOG de cero o bien sealar un error o, en algunos la aplicacin, el resultado negativo en una cuenta especial hasta el infinito valor, lo que har que todos los clculos posteriores, esencialmente sin sentido. Esto es especialmente lamentable en esta funcin por el mtodo de Fisher es ms sensible cuando las probabilidades de entrada son bajas - casi cero - y por lo tanto en mayor peligro de causar la multiplicacin de desbordamiento. Afortunadamente, usted puede usar un poco de matemticas de secundaria para evitar este problema. Recordemos que el registro de un producto es el mismo como la suma de los registros de los factores. As que en lugar de multiplicar todas las probabilidades y luego tomar el registro, puede sumar los registros de cada

probabilidad. Y como REDUCE toma unatecla: parmetro de palabra clave, se puede utilizar para realizar el clculo conjunto. En lugar de esto:
(Log (reducir el # '* probs))

escribir lo siguiente:
(Reducir # '+ probs: tecla' # 'log)

Chi Cuadrado Inverso La implementacin de la inversa del chi-cuadrado en esta seccin es una traduccin bastante directa de una versin escrita en Python por Robinson. El significado exacto de esta funcin matemtica est fuera del mbito de este libro, pero usted puede conseguir un sentido intuitivo de lo que lo hace pensando en cmo los valores se pasan alos pescadores que afectan el resultado: las probabilidades ms bajas se pasa a los pescadores , el ms pequeo es el producto de las probabilidades ser. El registro de un producto pequeo ser un nmero negativo con un valor grande absoluta, que luego se multiplica por -2, lo que lo convierte un nmero an ms grande positivo. Por lo tanto, las probabilidades ms bajas se pasa a los pescadores , mayor ser el valor que va a pasar a la inversa del chi-cuadrado . Por supuesto, el nmero de probabilidades involucradas tambin afecta el valor pasado a la inversa-chi-cuadrado . Dado que las probabilidades son, por definicin, inferior o igual a 1, las probabilidades ms que van en un producto, menor va a ser, y cuanto mayor sea el valor que se pasa a la inversa-chi-cuadrado . Por lo tanto, la inversa del chi-cuadrado debe devolver una baja probabilidad de que el valor combinado de Fisher es anormalmente grande para el nmero de probabilidades de que entraron en ella. La siguiente funcin hace exactamente eso:
(Defun inversa-chi-cuadrado (valor de grados de libertad) (Assert (evenp grados de libertad)) (Min (Bucle con m = (/ valor de 2) para i abajo (/ grados de libertad 2) para el prob = (exp (- m)) entonces (pro * (/ km)) sumando problemas) 1.0))

Recuerde del Captulo 10 que EXP plantea electrnico con el argumento dado. As, el ms grande valor es, menor es el valor inicial de proble ser. Pero que valor inicial luego sern ajustados hacia arriba ligeramente para cada grado de libertad, siempre y cuando m es mayor que el nmero de grados de libertad. Como el valor devuelto porla inversa del chi-cuadrado se supone que es otra probabilidad, es

importante para fijar el valor devuelto con el MIN ya que los errores de redondeo en la multiplicacin y la exponenciacin puede hacer que el bucle para volver una suma slo una sombra ms de 1. Entrenando el filtro Puesto que usted escribi clasificar y entrenar para tener un argumento de cadena, puede probar fcilmente en el REPL. Si usted no tiene, sin embargo, debe cambiar al paquete en el que he estado escribiendo este cdigo mediante la evaluacin de un in-Package formulario en el REPL o usando el atajo de BABA cambio de paquete . Para utilizar el acceso directo BABA, escriba una coma en el REPL a continuacin, escriba el nombre en el indicador. Al presionar Tab mientras se escribe el nombre del paquete se basa en funcin de autocompletar los paquetes a su Lisp conoce. Ahora se puede invocar cualquiera de las funciones que forman parte de la aplicacin de spam. En primer lugar, debe asegurarse de que la base de datos est vaca.
SPAM> (clear-base de datos)

Ahora usted puede entrenar el filtro con un poco de texto.


SPAM> (tren de "hacer dinero rpido", "correo no deseado)

Y luego ver lo que piensa el clasificador.


SPAM> (clasificacin de "hacer dinero rpido") SPAM SPAM> (clasificar "Quieres ir al cine?") NO EST SEGURO

Mientras que en ltima instancia, todo lo que importa es la clasificacin, sera bueno poder ver el puntaje bruto tambin. La forma ms fcil de obtener ambos valores sin molestar a ningn otro tipo de cdigo es cambiar la clasificacin para devolver varios valores.
(Defun clasificacin (puntaje) (Valores (Cond ((<= Resultado * max-jamn-score *) 'jamn) ((> = Puntuacin * min-spam-score *) 'spam) (T 'no est seguro)) puntaje))

Usted puede hacer este cambio y luego volver a compilar esta funcin slo uno. Debido a clasificar devuelva lo que la clasificacin vuelve, sino que tambin va a volver ahora dos valores. Sin embargo, dado que el valor de retorno primario es el

mismo, las llamadas de cualquiera de las funciones que esperan un solo valor no se ver afectada.Ahora, cuando usted probar clasificar , usted puede ver exactamente lo que pas en la puntuacin de la clasificacin.
SPAM> (clasificacin de "hacer dinero rpido") SPAM 0.863677101854273D0 SPAM> (clasificar "Quieres ir al cine?") NO EST SEGURO 0.5D0

Y ahora usted puede ver qu pasa si usted entrenar el filtro con el texto de jamn un poco ms.
SPAM> (tren "Tiene usted algn dinero para el cine?" 'Jamn) 1 SPAM> (clasificacin de "hacer dinero rpido") SPAM 0.7685351219857626D0

Todava es correo no deseado, pero un poco menos seguro desde el dinero fue visto en el texto de jamn.
SPAM> (clasificar "Quieres ir al cine?") HAM 0.17482223132078922D0

Y ahora esto es claramente reconocible gracias jamn a la presencia de la palabra pelculas , ahora una caracterstica toques extravagantes. Sin embargo, usted realmente no quiere entrenar el filtro con la mano. Lo que realmente me gustara es una manera fcil de apuntar a un montn de archivos y entrenarlo en ellos. Y si quieres poner a prueba lo bien que el filtro funciona realmente, que le gustara a continuacin, utilizarlo para clasificar a otro conjunto de archivos de tipos conocidos y ver cmo lo hace. As que la ltima parte del cdigo que usted escribe en este captulo ser un instrumento de prueba que pone a prueba el filtro en un corpus de mensajes de los tipos conocidos, con una cierta fraccin de la formacin y la medicin de la precisin de la hora de clasificar filtro es el resto. Probando el filtro Para probar el filtro, se necesita un corpus de mensajes de los tipos conocidos. Puede utilizar mensajes por ah en tu bandeja de entrada, o usted puede tomar uno de los cuerpos cavernosos en la Web. Por ejemplo, el corpus SpamAssassin 12 contiene varios miles de mensajes de la mano clasificado como spam, jamn fcil, y el jamn duro. Para que sea ms fcil de usar los archivos que tienen, se puede

definir un banco de pruebas que ha expulsado un conjunto de pares de archivo / tipo. Se puede definir una funcin que toma un nombre de archivo y un tipo y se agrega al cuerpo de esta manera:
(Defun agrega-archivo-a-cuerpo (tipo de nombre de archivo de corpus) (Vector de empuje se extienden (tipo de lista de nombre de archivo) corpus))

El valor de corpus debe ser un vector de ajustable con un puntero de relleno. Por ejemplo, usted puede hacer un nuevo corpus de esta manera:
(* Defparameter corpus * (make-array 1000: regulable t: relleno triple 0))

Si usted tiene los jamones y los spams ya segregados en directorios separados, es posible que desee agregar todos los archivos en un directorio como el mismo tipo. Esta funcin, que utiliza la lista de directorio de la funcin del captulo 15, har el truco:
(Defun agrega-directorio-a-cuerpo (corpus escriba dir) (Dolist (nombre de archivo (lista-directorio dir)) (Add-archivo-de-corpus corpus tipo de nombre de archivo)))

Por ejemplo, suponga que tiene un directorio electrnico que contiene dos subdirectorios, spam y ham , cada uno de los mensajes que contienen el tipo indicado, se puede aadir todos los archivos en los directorios de dos corpus * * de esta manera:
SPAM> (aadir directorio-a-corpus "mail / spam /" 'spam * corpus *) NIL SPAM> (aadir directorio-a-corpus "correo / jamn /" 'ham * corpus *) NIL

Ahora se necesita una funcin para probar el clasificador. La estrategia bsica ser la de seleccionar un trozo aleatorio de la corpus para entrenar en y luego probar el corpus por clasificar el resto del corpus, comparando la clasificacin devuelto por el clasificar funcin a la clasificacin conocida. Lo principal que quiero saber es Qu tan exacto es el clasificador - Qu porcentaje de los mensajes se clasifican correctamente? Pero es probable que tambin estar interesado en lo que los mensajes fueron mal clasificados y en qu direccin - no eran ms falsos positivos o falsos negativos ms? Para que sea ms fcil de realizar diferentes anlisis de la conducta del clasificador, se deben definir las funciones de prueba para crear una lista de resultados en bruto, que luego se puede analizar como quiera. La funcin principal de la prueba podra tener este aspecto:
(Defun prueba-clasificador (corpus de pruebas de fraccin) (Claro, base de datos)

(Let * ((arrastrando los pies (seleccin aleatoria del vector corpus)) (Tamao (longitud corpus)) (Tren-en (piso (* tamao (- 1 prueba de fraccin))))) (Tren-de-corpus barajan: empezar a 0: en el extremo del tren-) (Test-de-corpus barajan: inicio de tren-el)))

Esta funcin comienza con la limpieza de la base de datos de caractersticas. 13 A continuacin, se baraja el cuerpo, usando una funcin que va a poner en prctica en un momento, y las cifras a cabo, basndose en la prueba de fraccin parmetro, la cantidad de mensajes que va a entrenar en la forma y muchas que va a reservar para la prueba. Las dos funciones de ayuda de tren-de-corpus y la prueba del corpus va a tener tanto : inicio y : finales parmetros de palabra clave, lo que les permite operar en una subsecuencia del corpus dado. El tren-de-corpus funcin es bastante simple - loop simplemente sobre la parte correspondiente del corpus, el uso desestructurada-BIND para extraer el nombre del archivo y el tipo de la lista se encuentra en cada elemento, y luego pasar el texto del archivo con el nombre y la el tipo de formacin . Debido a que algunos mensajes de correo, tales como aquellos con archivos adjuntos, son bastante grandes, se debe limitar el nmero de caracteres que va a tomar a partir del mensaje. Va a obtener el texto con una funcinde inicio de archivo , que podr aplicar en un momento, que tiene un nombre y un nmero mximo de caracteres para volver. de tren-de-cuerpo se parece a esto:
(Defparameter * max-chars * (* 10 1024)) (Defun de tren-de-corpus (corpus y la llave (inicio 0) final) (Bucle para idx desde el comienzo a continuacin (o al final (cuerpo de longitud)) hacer (Desestructuracin-bind (tipo de archivo) (Aref corpus idx) (Tren (de inicio de archivo del archivo * max-chars *) Tipo))))

La prueba-de-corpus funcin es similar, excepto que desea devolver una lista que contiene los resultados de cada clasificacin para que pueda analizarlos despus del hecho.Por lo tanto, debe captar tanto la clasificacin y la puntuacin devuelta por clasificar y seleccionar una lista de nombre de archivo, el tipo real, el tipo devuelto porclasificar , y la puntuacin. Para hacer que los resultados sean ms legibles, se pueden incluir palabras clave en la lista para indicar que los valores son lo que.
(Defun prueba-de-corpus (corpus y la llave (inicio 0) final) (Bucle para idx desde el comienzo a continuacin (o al final (longitud corpus)) recogen (Desestructuracin-bind (tipo de archivo) (Aref corpus idx) (Multiple-value-bind (puntuacin de clasificacin) (Clasificar (de inicio de archivo del archivo * max-chars *)) (Lista de

: : : :

Archivo Tipo Tipo Clasificacin de la clasificacin Puntaje de puntuacin)))))

Un par de funciones de utilidad Para terminar la ejecucin de la prueba-clasificador , tiene que escribir las dos funciones de utilidad que en realidad no tienen nada especial que ver con el filtrado de spam,reproduccin aleatoria de vectores y de inicio de archivos . Una manera fcil y eficiente para poner en prctica aleatoria-vector se utiliza el algoritmo de Fisher-Yates. 14 Usted puede comenzar mediante la implementacin de una funcin, nshuffle-vector , que baraja un vector en el lugar. Este nombre sigue la misma convencin de otras funciones destructivas, tales como NCONC y NREVERSE . Se parece a esto:
(Defun nshuffle-vector (vector) (Circular para idx downfrom (1 - (vector de longitud)) a 1 para la otra = (random (1 + idx)) hacerlo (a menos que (= idx otros) (Rotatef (Aref vector idx) (Aref otro vector)))) vectorial)

La versin no destructiva simplemente hace una copia del vector original y lo pasa a la versin destructiva.
(Defun shuffle-vector (vector) (Nshuffle-vector (copy-ss vector)))

La funcin de utilidad otra parte, de inicio de archivo , es casi tan sencillo con una sola arruga. La forma ms eficaz de leer el contenido de un archivo en la memoria es la de crear una matriz del tamao apropiado y el uso LECTURA SECUENCIA para llenar pulg Por lo tanto, podra parecer que usted podra hacer una matriz de caracteres que es ya sea el tamao del archivo o el nmero mximo de caracteres que desea leer, lo que sea menor. Por desgracia, como ya he dicho en el captulo 14, la funcin ARCHIVO DE LONGITUDno est del todo bien definido cuando se trata de secuencias de caracteres que el nmero de caracteres codificados en un archivo puede depender tanto de la codificacin de caracteres utilizada y el texto en particular en el archivo. En el peor de los casos, la nica manera de conseguir una medida exacta de la cantidad de caracteres en un archivo es para leer en realidad todo el archivo. Por lo tanto, es ambigua lo ARCHIVO DE LONGITUD debe hacer cuando se pasa un flujo de caracteres, en la mayora de las implementaciones,ARCHIVO DE LONGITUD siempre devuelve el nmero de

octetos en el archivo, que puede ser mayor que el nmero de caracteres que se pueden leer desde el archivo. Sin embargo, READ-SECUENCIA devuelve el nmero de caracteres realmente leer. As, usted puede intentar leer el nmero de caracteres indicado porEXPEDIENTE DE LONGITUD y devolver una cadena si el nmero real de caracteres ledos era ms pequeo.
(Defun de inicio de archivo (file-max caracteres) (Con-open-file (en archivo) (Let * ((longitud (min (archivo de larga duracin en) max-chars)) (Texto (de creacin de cadena de longitud)) (Leer (read-secuencia de texto en))) (If (<longitud de lectura) (Texto de lectura subseq 0) texto))))

Anlisis de los resultados Ahora ests listo para escribir algo de cdigo para analizar los resultados generados por la prueba-clasificador . Recordemos que la prueba-clasificadordevuelve la lista devuelta por la prueba-de-corpus en el que cada elemento es un plist que representa el resultado de la clasificacin de un archivo. Este plist contiene el nombre del archivo, el tipo real del archivo, la clasificacin y la puntuacin devuelta por clasificar . El primer bit de cdigo de anlisis se debe escribir es una funcin que devuelve un smbolo que indica si un determinado resultado era correcto, un falso positivo, un falso negativo, perdi un jamn o un spam perdido. Usted puede utilizardesestructurada-BIND para sacar el : Tipo y : clasificacin de los elementos de una lista de resultados individuales (cony permitir que los otroskeys para contar desestructurada-BIND para ignorar los otros pares clave / valor que ve) y luego usar anidado ECASE de traducir las diferentes parejas en un solo smbolo.
(Defun resultado del tipo de resultado () (Desestructuracin-bind (y clasificacin de tipo de clave y permitir que-otraclaves) resultado (Tipo ecase (Jamn (Clasificacin ecase (Correcta ham ') (Spam 'falsos positivos) (Seguro 'desperdiciado jamn))) (Correo no deseado (Clasificacin ecase (Jamn 'falso negativo) (Spam "correcta) (Seguro 'perdido-spam))))))

Puede probar esta funcin en el REPL.


SPAM> (resultado de tipo '(: ARCHIVO Puntuacin 0)) CORREGIR SPAM> (resultado de tipo '(: ARCHIVO clasificacin: Puntuacin 0)) CORREGIR SPAM> (resultado de tipo '(: ARCHIVO Puntuacin 0)) FALSO POSITIVO SPAM> (resultado de tipo '(: ARCHIVO clasificacin: Puntuacin 0)) Los falsos negativos de SPAM> (resultado de tipo '(: ARCHIVO Puntuacin 0)) PERDIDA-HAM SPAM> (resultado de tipo '(: ARCHIVO seguro: Puntuacin 0)) PERDIDA-SPAM # p "foo": jamn tipo: jamn de clasificacin: # p "foo": el spam de tipo: el spam de # p "foo": jamn tipo: el spam de clasificacin: # p "foo": el spam de tipo: jamn de # p "foo": jamn tipo: clasificacin de seguro: # p "foo": el spam de tipo: clasificacin de

Contar con esta funcin hace que sea fcil de analizar y clasificar los resultados de la prueba-clasificador en una variedad de maneras. Por ejemplo, usted puede comenzar por la definicin de funciones de predicado para cada tipo de resultado.
(Defun falsos positivos-p (el resultado) (EQL (resultado del tipo de resultado) 'falsos positivos)) (Defun falso-negativo-p (el resultado) (EQL (resultado del tipo de resultado) 'falso negativo)) (Defun perdido-ham-p (el resultado) (EQL (resultado del tipo de resultado) 'desperdiciado jamn)) (Defun perdido-spam-p (el resultado) (EQL (resultado del tipo de resultado) 'perdido-spam)) (Defun correcta-p (el resultado) (EQL (resultado del tipo de resultado) 'correcta))

Con estas funciones, puede utilizar la lista y las funciones de manipulacin de secuencia que vimos en el captulo 11 para extraer y contar con determinados tipos de resultados.
SPAM> (cuenta-si es falso-positivo-p # '* Resultados *) 6 SPAM> (quitar-si-no # 'falsos positivos-p * Resultados *) ((: FILE # p "ham/5349": HAM TIPO: Spam CLASIFICACIN: SCORE 0.9999983107355541d0) (: FILE # p "ham/2746": HAM TIPO: Spam CLASIFICACIN: SCORE 0.6286468956619795d0) (: FILE # p "ham/3427": HAM TIPO: Spam CLASIFICACIN: SCORE 0.9833753501352983d0) (: FILE # p "ham/7785": HAM TIPO: Spam CLASIFICACIN: SCORE 0.9542788587998488d0) (: FILE # p "ham/1728": HAM TIPO: Spam CLASIFICACIN: SCORE 0.684339162891261d0) (: FILE # p "ham/10581": HAM TIPO: Spam CLASIFICACIN: SCORE 0.9999924537959615d0))

Tambin puede utilizar los smbolos devueltos por resultado de tipo como claves en una tabla hash o un alist. Por ejemplo, usted puede escribir una funcin para

imprimir un resumen de las frecuencias y los porcentajes de cada tipo de resultado con un alist que asigna cada tipo, ms el smbolo extra total de un recuento.
(Defun analizar los resultados (resultados) (Let * ((teclas '(total correcto de falsos positivos falso negativo se perdi, se perdi el jamn-spam)) (Recuento de bucle (la x en las teclas de recopilar (cons x 0)))) (Dolist (elemento resultados) (Incf (cdr (assoc recuento total '))) (Incf (cdr (assoc (resultado del tipo de elemento) cuenta)))) (Bucle con total = (cdr (assoc recuento total ')) para el (la etiqueta. cuenta) en el recuento hacer (en formato t "~ & ~ @ (~ a ~): ~ 20 t ~ ~ 5d, 5t: ~ 6,2% ~% f" cantidad de las etiquetas (* 100 (/ recuento total))))))

Esta funcin permite obtener una salida como sta cuando se pasa una lista de resultados generados por la prueba-clasificador :
> (SPAM analizar los resultados * resultados *) Total: 3761: 100,00% Correcto: 3689: 98,09% Falso positivo: 4: 0,11% Falso negativo: 9: 0,24% Perdidas de jamn: 19: 0,51% Encuentros-spam: 40: 1,06% NIL

Y como ltima parte del anlisis es posible que desee ver por qu un mensaje individual se clasific de la manera que era. Las siguientes funciones le mostrar:
(Defun explicar la clasificacin (archivo) (Let * (el texto ((de inicio de archivo del archivo * max-chars *)) (Caractersticas (extracto de las caractersticas de texto)) (Score (puntuacin de caractersticas)) (Clasificacin (puntaje de clasificacin))) (Show-resumen de archivo de texto puntaje de clasificacin) (Dolist (funcin (ordenados-interesantes caractersticas)) (Show-caracterstica de funcin)))) (Defun show-resumen (archivo de texto puntaje de clasificacin) (Formato t "~ & ~" archivo) (Formato t "~ 2% ~ de ~ 2%" del texto) (Formato t "Clasificado como un ~ con la puntuacin de ~, ~ 5f%" puntuacin de la clasificacin)) (Defun show-funcin (funcin) (Con ranuras (palabra jamn recuento de spam cuenta) funcin (Formato t "del ~ del ~ y del ~ 2t ~ a 30thams: ~ 5d; spams: 5d ~, ~, 10tprob: ~, ~ f%" la palabra jamn recuento de spam cuenta (bayesiano-spam probabilidad de funcin)))) (Caractersticas de defun-ordenados de inters () (Ms o menos (quitar-si # "sin entrenamiento-p caractersticas) # '<: key #' bayesiano-spam de probabilidad))

Qu sigue? Obviamente, usted podra hacer mucho ms el de este cdigo. Para convertirlo en un verdadero filtro de spam, la aplicacin, usted necesita encontrar una manera de integrar en su correo electrnico normal de la infraestructura. Uno de los enfoques que hacen que sea fcil de integrar con cualquier cliente de correo electrnico es para escribir un poco de cdigo para actuar como un proxy POP3 - que es el protocolo de la mayora de los clientes de correo electrnico utiliza para descargar el correo desde servidores de correo. Esta representacin se vendera por correo desde su servidor POP3 real, y servir a su cliente de correo despus de que cualquiera de spam marcado con un cabezazo que se filtra el correo electrnico del cliente puede reconocer fcilmente o, simplemente, dejando a un lado. Por supuesto, tambin se necesitara una manera de comunicarse con el filtro de errores de clasificacin - el tiempo que usted la est poniendo como un servidor, tambin puede proporcionar una interfaz Web. Voy a hablar acerca de cmo escribir interfaces web en el captulo 26, y usted construir uno, para una aplicacin diferente, en el captulo 29. O es posible que desee trabajar en la mejora de la clasificacin bsica - un lugar probable para comenzar es hacer extracto de las caractersticas ms sofisticadas.En particular, puede hacer que el sealizador ms inteligente acerca de la estructura interna de correo electrnico - se puede extraer diferentes tipos de caractersticas de las palabras que aparecen en el cuerpo frente a los encabezados de los mensajes. Y usted podra decodificar varios tipos de codificacin de mensajes, como base 64 y se puede imprimir desde el citado spammers a menudo tratan de ocultar su mensaje con las codificaciones. Pero voy a dejar esas mejoras para usted. Ahora usted est listo para dirigirse por el camino de la construccin de un servidor de streaming de MP3, empezando por escrito una biblioteca de propsito general para analizar los archivos binarios.

1 Disponible en http://www.paulgraham.com/spam.html y tambin en Los hackers y pintores:

grandes ideas de la era del ordenador (O'Reilly, 2004)


2 Desde entonces ha habido un cierto desacuerdo sobre si la tcnica de Graham describe en realidad

era "bayesiano". Sin embargo, el nombre se ha pegado y est en camino de convertirse en un sinnimo de "estadstica" cuando se habla de los filtros de spam.

3 Sera, sin embargo, la falta de forma de distribuir una versin de esta aplicacin mediante un

paquete a partir de com.gigamonkeys ya que no tenemos control sobre ese dominio.


4 Una versin de la CL-PPCRE se incluye con el cdigo fuente disponible en el sitio Web del libro del

libro. O bien, puede descargarlo desde el sitio de Weitz en http://www.weitz.de/cl-ppcre/ .


5 La razn principal para usar PRINT que no son legibles-OBJETO es que se encarga de sealar el

error apropiado si alguien trata de imprimir el objeto legible, como con la S ~ FORMATO Directiva.
6 PRINT que no son legibles-OBJETO tambin seala un error si se utiliza cuando la variable de

control de la impresora * IMPRESIN * legible es cierto. Por lo tanto, un PRINT-OBJETO mtodo que consiste nicamente en un PRINT que no son legibles-OBJETO formulario correctamente pondr en prctica el PRINT-OBJETO contrato con respecto a * IMPRESIN * legible .
7 Si ms adelante decide que es necesario tener diferentes versiones de incremento-funcin de las

distintas clases, puede redefinir el incremento de conteo como funcin genrica y esta funcin como un mtodo especializado en funcin de la palabra- .
8 Tcnicamente, la clave en cada clusula de un CASE o ECASE se interpreta como un designador de

la lista , un objeto que designa a una lista de objetos. Un objeto nonlist sola, entendido como un indicador de la lista, designa a una lista que contiene slo que un objeto, mientras que la lista se designa. Por lo tanto, cada clusula puede tener varias claves; CASE y ECASE seleccionar la clusula de cuya lista de claves contiene el valor de la forma de llave. Por ejemplo, si usted quiere hacer un buen sinnimo de jamn y la mala sinnimo de correo no deseado , podra escribir el incremento de conteo de esta manera:
(Defun incremento de cuentas (el tipo de accidente) (Tipo ecase ((Buen jamn) (incf (jamn de recuento de funcin))) ((Correo no deseado mal) (incf (spam recuento de funcin))))) 9 Hablando de matices matemticos, estadsticos ncleo duro puede ser ofendido por el uso a veces

suelto de la palabra probabilidad en este captulo. Sin embargo, ya que incluso los profesionales, que estn divididos entre el bayesianos y los frequentists, no pueden ponerse de acuerdo sobre lo que es una probabilidad, no me voy a preocupar por l. Este es un libro acerca de la programacin, no las estadsticas.
10 Robinson artculos que directamente informados a este captulo son "una aproximacin

estadstica al problema del spam" (publicado en el Diario de Linux y est disponible enarticle.php http://www.linuxjournal.com/? sid = 6467 y en una forma ms corta en blog de Robinson en0101454/stories/2002/09/16/spamDetection.html http://radio.weblogs.com/ ) y "Por qu Chi? Motivaciones para el uso de Inverse Fisher Chi-Cuadrado Procedimiento de Clasificacin spam" (disponible en http://garyrob.blogs.com/ whychi93.pdf ). Otro artculo que puede ser til es "Manejo de la redundancia en el correo electrnico probabilidades Token" (disponible enhttp://garyrob.blogs.com//handlingtokenredundancy94.pdf ). Las listas de correo archivados del proyecto SpamBayes ( http://spambayes.sourceforge.net/ ) tambin contienen una gran cantidad de informacin til acerca de los algoritmos y enfoques diferentes para probar los filtros de spam.

11 Las tcnicas que se combinan las probabilidades no independientes como si fueran, de hecho,

independiente, se llaman bayesiano ingenuo . Propuesta original de Graham era esencialmente un clasificador bayesiano ingenuo con algunas "derivadas empricamente" factores constantes lanzadas adentro
12 Varios corpus de spam incluyendo el corpus SpamAssassin estn vinculados a partir de

http://nexp.cs.pdx.edu/ ~ PSAM / cgi-bin / view / o PSAM CorpusSets .


13 Si desea realizar una prueba sin alterar la base de datos existente, que podra obligar * La funcin

de base de datos * , total * de spam * , y * Los totales de jamones * con un LET, pero entonces no tendra ninguna manera de mirar la base de datos despus de los hechos - a menos que regres a los valores que se utilizan dentro de la funcin.
14 Este algoritmo se nombra para el mismo Fisher, que invent el mtodo utilizado para combinar

las probabilidades y para Frank Yates, su co-autor del libro cuadros estadsticos para la investigacin biolgica, mdica y agrcola (Oliver y Boyd, 1938) en el que, segn Knuth, ellos siempre que la primera descripcin publicada del algoritmo.

24. Prcticas: los archivos binarios de anlisis sintctico


En este captulo le mostraremos cmo crear una biblioteca que se puede utilizar para escribir el cdigo para leer y escribir archivos binarios. Vamos a usar esta biblioteca en el captulo 25 para escribir un analizador para las etiquetas ID3, el mecanismo utilizado para almacenar metadatos como nombres de artistas y el lbum en archivos MP3. Esta biblioteca es tambin un ejemplo de cmo utilizar macros para extender el lenguaje con construcciones nuevas, convirtindolo en un lenguaje de propsito especial para resolver un problema particular, en este caso, la lectura y escritura de datos binarios. Debido a que va a desarrollar la biblioteca un poco a la vez, incluyendo varias versiones parciales, puede parecer que ests escribiendo un montn de cdigo. Pero cuando todo est dicho y hecho, toda la biblioteca es menos de 150 lneas de cdigo, y el ms largo macro est a slo 20 lneas de largo. Archivos binarios En un nivel suficientemente bajo de abstraccin, todos los archivos son "binario" en el sentido de que slo contienen un montn de nmeros codificados en forma binaria. Sin embargo, es habitual distinguir entre archivos de texto , donde todos los nmeros pueden ser interpretados como personajes que representan a un texto legible y archivos binarios , que contienen datos que, si se interpreta como los personajes, los rendimientos de los caracteres no imprimibles. 1 Formatos de archivos binarios generalmente estn diseados para ser a la vez compacto y eficiente para analizar - que es su principal ventaja sobre los formatos basados en texto. Para hacer frente a esos dos criterios, por lo general estn compuestos por estructuras en disco que pueden ser fcilmente asignados a las estructuras de datos que un programa puede utilizar para representar los mismos datos en la memoria. 2 La biblioteca le dar una forma sencilla de definir la asignacin entre las estructuras en disco definido por un formato de archivo binario y objetos en memoria de Lisp. Uso de la biblioteca, debe ser fcil de escribir un programa que puede leer un archivo binario que se traduzca en objetos de Lisp que se pueden

manipular, a continuacin, escriba de nuevo a otro archivo binario con formato correcto. Fundamentos de formato binario El punto de partida para leer y escribir archivos binarios es abrir el archivo para leer o escribir bytes individuales. Como he discutido en el captulo 14, ambos OPEN yCON-OPEN-FILE aceptar un argumento de palabra clave, : tipo de elemento , que controla la unidad bsica de transferencia de la corriente. Cuando se trabaja con archivos binarios, deber especificar (unsigned byte 8) . Un flujo de entrada se abri con tal : tipo de elemento devolver un nmero entero entre 0 y 255 cada vez que se pasa a READ-BYTE . Por el contrario, usted puede escribir bytes en un (unsigned byte 8) flujo de salida, pasando los nmeros comprendidos entre 0 y 255 paraESCRIBA-BYTE . Por encima del nivel de bytes individuales, la mayora de formatos binarios de utilizar un nmero ms bien pequeo de tipos de datos primitivos - los nmeros codificados de diversas maneras, las cadenas de texto, campos de bits, y as sucesivamente - que luego son integrados en las estructuras ms complejas. As que su primera tarea es definir un marco para la escritura de cdigo para leer y escribir los tipos de datos primitivos utilizados por un formato binario dado. Para poner un ejemplo sencillo, supongamos que usted est tratando con un formato binario que usa un entero de 16 bits sin signo como un tipo de datos primitivo. Para leer como un entero, te recomiendo que leas los dos bytes y luego combinarlos en un solo nmero al multiplicar un byte por 256, tambin conocido como 2 ^ 8, y agregarlo a la otro byte. Por ejemplo, suponiendo que el formato binario se especifica que dichas cantidades de 16 bits se almacenan en big-endian 3 forma, con el byte ms significativo en primer lugar, se puede leer como un nmero con esta funcin:
(Defun lectura U2 (en) (+ (* (Leer bytes en) 256) (lectura de bytes en)))

Sin embargo, Common Lisp proporciona una manera ms conveniente para llevar a cabo este tipo de bit. La funcin de LDB , cuyo nombre es sinnimo de bytes de carga, se puede utilizar para extraer y configurar (con SETF ) cualquier nmero de bits contiguos de un entero. 4 El nmero de bits y su posicin dentro del nmero entero se especifica con unespecificador de bytes creados con el BYTE funcin. BYTE

toma dos argumentos, el nmero de bits para extraer (o conjunto) y la posicin del bit de la derecha, donde el bit menos significativo est en la posicin cero. LDB tiene un especificador de bytes y entero, el de la que extraer los bits y devuelve el entero positivo representado por los bits extrados. Por lo tanto, se puede extraer el byte menos significativo de un entero de esta manera:
(LDB (de 8 bytes 0) # xabcd) ==> 205 y 205 es # xcd

Para obtener el octeto siguiente, tendr que utilizar un byte de especificador (8 bytes 8) de esta manera:
(LDB (de 8 bytes 8) # xabcd) ==> 171, 171 es el nmero xab

Usted puede utilizar LDB con SETF para establecer los bits especificados de un entero almacenado en un SETF lugar capaz.
CL-> USER (defvar * num * 0) * NUM * CL-> USER (setf (LDB (de 8 bytes 0) * nmero *) 128) 128 CL-USUARIO> * nmero * 128 CL-> USER (setf (LDB (de 8 bytes 8) * nmero *) 255) 255 CL-USUARIO> * nmero * 65408

Por lo tanto, tambin se puede escribir de lectura u2 as: 5


(Defun lectura U2 (en) (Let ((u2 0)) (Setf (LDB (de 8 bytes 8) u2) (lectura de bytes en)) (Setf (LDB (de 8 bytes 0) u2) (lectura de bytes en)) u2))

Para escribir un nmero como un entero de 16 bits, es necesario extraer los distintos bytes de 8 bits y grabarlos uno a la vez. Para extraer los bytes individuales, slo tiene que utilizarLDB con los especificadores de bytes mismos.
(Defun escribe-U2 (de un total de valor) (Escritura byte (LDB (de 8 bytes 8) valor) a) (Escritura byte (LDB (de 8 bytes 0) valor) a))

Por supuesto, usted tambin puede codificar enteros en muchas otras formas - con diferente nmero de bytes, con orden de bits diferente, y en el formato y sin signo. Las cadenas en archivos binarios Cadenas de texto son otro tipo de tipo de datos primitivo que encontrar en muchos formatos binarios. Cuando usted lee los archivos de un byte a la vez, no se puede leer y escribir cadenas directamente - que necesita para decodificar y codificar a un

byte a la vez, tal como lo hace con la codificacin binaria de nmeros. Y as como usted puede codificar un nmero entero de varias maneras, puede codificar una cadena de muchas maneras. Para empezar, el formato binario debe especificar cmo se codifican los caracteres individuales. Para traducir los bytes a caracteres, lo que necesita saber tanto qu carcter el cdigo de caracteres y lo que la codificacin que est utilizando. Un cdigo de caracteres define una asignacin de nmeros enteros positivos a los personajes. Cada nmero de la asignacin se denomina un punto de cdigo . Por ejemplo, el ASCII es un cdigo de caracteres que asigna los nmeros de 0 a 127 a los caracteres especiales utilizados en el alfabeto latino. Una codificacin de caracteres, por otro lado, define cmo los puntos de cdigo se representan como una secuencia de bytes en un medio byte orientado tales como un archivo. Para los cdigos que utilizan ocho o menos bits, tales como ASCII e ISO-8859-1, la codificacin es trivial - cada valor numrico se codifica como un nico byte. Casi tan sencillo son puros de doble byte, como codificaciones UCS-2, que se asignan entre los valores de 16 bits y caracteres. La nica razn por la codificacin de doble byte puede ser ms compleja que la de un solo byte codificaciones es que es posible que tambin necesitan saber si los valores de 16-bit se supone que deben ser codificados en formato big-endian o little-endian. De ancho variable codificaciones utilizan diferentes nmeros de octetos para diferentes valores numricos, hacindolos ms complejos, pero lo que les permite ser ms compactos en muchos casos. Por ejemplo, UTF-8, una codificacin diseado para su uso con el cdigo de caracteres Unicode, utiliza un solo octeto para codificar los valores 0-127, mientras que usando un mximo de cuatro octetos para codificar valores de hasta 1.114.111. 6 Desde los puntos de cdigo 0 a 127 el mapa a los mismos caracteres en Unicode como lo hacen en formato ASCII, una codificacin UTF-8 del texto que se componga slo de caracteres ASCII es tambin en la misma que la codificacin ASCII. Por otro lado, los textos que consisten en su mayora de personajes que requieren cuatro bytes en UTF-8 podra ser ms compactamente codificada en una recta de doble byte de codificacin. Common Lisp proporciona dos funciones para la traduccin entre cdigos de caracteres numricos y los objetos de caracteres: CODE-CHAR , que tiene un cdigo

numrico y regresa como un personaje, y el CODE-CHAR , que tiene un carcter y devuelve su cdigo numrico. La lengua estndar no especifica qu codificacin de caracteres una aplicacin debe utilizar, as que no hay ninguna garanta de que puede representar a todos los personajes que posiblemente pueden ser codificados en un formato de archivo determinado, como un personaje de Lisp. Sin embargo, casi todas las implementaciones actuales Common Lisp utiliza ASCII, ISO-8859-1 o Unicode como su cdigo de carcter nativo. Debido a que Unicode es un superconjunto ofISO-8859-1, que a su vez es un superconjunto de ASCII, si usted est utilizando un Lisp Unicode, CODE-CHAR y CHAR CODE- se puede utilizar directamente para la traduccin de cualquiera de esos tres cdigos de caracteres. 7 Adems de especificar una codificacin de caracteres, una cadena de codificacin tambin se debe especificar la forma de codificar la longitud de la cadena. Tres tcnicas se utilizan normalmente en formatos de archivo binario. El ms simple es no codificar, sino a dejar que estar implcito en la posicin de la cadena en alguna estructura ms grande: un elemento particular de un archivo siempre puede ser una cadena de una cierta longitud, o una cadena puede ser el ltimo elemento de una de longitud variable, cuya estructura de datos global de tamao determina cuntos bytes se dejan leer como datos de cadena. Ambas tcnicas se utilizan en las etiquetas ID3, como se ver en el prximo captulo. Las otras dos tcnicas se pueden utilizar para codificar cadenas de longitud variable sin depender de contexto. Uno es para codificar la longitud de la cadena seguido por los datos de caracteres - el analizador lee un valor entero (en algn formato entero especificado) y luego lee ese nmero de caracteres. Otra es para escribir los datos de caracteres seguidos por un delimitador que no puede aparecer en la cadena como un carcter nulo. Las diferentes representaciones tienen diferentes ventajas y desventajas, pero cuando ests tratando con formatos binarios ya especificados, no tendr ningn control sobre qu se utiliza la codificacin. Sin embargo, ninguna de las codificaciones es particularmente ms difcil de leer y escribir que cualquier otro. Aqu, por ejemplo, es una funcin que lee una cadena ASCII terminada en cero, asumiendo que su puesta en prctica de Lisp utiliza ASCII o uno de sus superseries como Unicode ISO-8859-1 o total como su codificacin de caracteres nativo:
(+ Defconstant nula + (cdigo-char 0))

(Defun lectura terminada en cero-ascii (en) (Con salida a cadena (s) (Bucle de char = (cdigo-char (lectura de bytes en)) hasta que (char = char + null +) hacer (write-char char s))))

El CON SALIDA-a cadena macro, que mencion en el captulo 14, es una manera fcil de construir una cadena cuando no se sabe cunto tiempo va a ser. Se crea unSTRING-STREAM y lo enlaza con el nombre de variable se especifica, s en este caso. Todos los caracteres escritos en la secuencia se recogen en una cadena, que luego se devuelve como el valor de la CON-SALIDA a cadena formulario. Para escribir una cadena de nuevo, slo tiene que traducir los caracteres de vuelta a los valores numricos que se pueden escribir con WRITE-BYTE y luego escribir el terminador nulo despus de que los contenidos de la cadena.
(Con cadena de defun escribe-terminada en cero-ascii () (Bucle de caracteres en cadena hacer (escritura de bytes (caracteres de cdigo de caracteres) a)) (Escritura de bytes (caracteres de cdigo + null +) a))

Como muestran estos ejemplos, el principal desafo intelectual - como es - de leer y escribir los elementos primitivos de los archivos binarios es entender exactamente cmo interpretar los bytes que aparecen en un archivo y para asignarlos a Lisp tipos de datos. Si un formato de archivo binario est bien especificado, esto debera ser un asunto sencillo. En realidad la escritura de funciones para leer y escribir es una codificacin determinada, como se suele decir, una simple cuestin de programacin. Ahora usted puede dar vuelta a la cuestin de la lectura y la escritura ms compleja de las estructuras de disco y la forma de asignar a los objetos de Lisp. Estructuras Compuestas Dado que los formatos binarios se utilizan generalmente para representar los datos de una manera que hace que sea fcil para asignar a las estructuras de datos en memoria, debe venir como ninguna sorpresa que compuestos en disco estructuras normalmente se definen de manera similar a la forma en lenguajes de programacin definen en el estructuras de memoria.Por lo general, un compuesto en el disco-estructura constar de un nmero de elementos con nombre, cada uno de los cuales es en s misma ya sea un tipo primitivo, tales como un nmero o una cadena, otra estructura de material compuesto, o, posiblemente, una coleccin de tales valores.

Por ejemplo, una etiqueta ID3 se define en la versin 2.2 de la especificacin consta de una cabecera compuesta por una cadena de tres caracteres ISO-8859-1, que es siempre "ID3", dos enteros sin signo de un byte que especifica la versin principal y revisin de la especificacin; ocho bits por valor de banderas booleanas; y cuatro bytes que codifican el tamao de la etiqueta en una codificacin particular a la especificacin ID3. Despus de la cabecera es una lista de marcos , cada uno de los cuales tiene su propia estructura interna. Despus de que los marcos son muchos bytes nulos que sean necesarios para rellenar la etiqueta a cabo con el tamao especificado en el encabezado. Si nos fijamos en el mundo a travs de la lente de la orientacin a objetos, estructuras de materiales compuestos se parecen mucho a las clases. Por ejemplo, podra escribir una clase para representar la etiqueta ID3.
(Defclass ID3-TAG () ((Identificador: initarg: Identificador: el identificador de acceso) (Versin principal: initarg: versin principal: versin principal de acceso) (Revisin: initarg: Revision de acceso) (Banderas: initarg: banderas: banderas de acceso) (Tamao: initarg: Tamao: El tamao de acceso) (Marcos: initarg: marcos: marcos de acceso)))

Una instancia de esta clase tendra un repositorio ideal para contener los datos necesarios para representar la etiqueta ID3. A continuacin, puede escribir funciones para leer y escribir instancias de esta clase. Por ejemplo, suponiendo la existencia de algunas otras funciones para la lectura de los tipos apropiados de datos primitivos, una lectura ID3-TAG en la funcin podra tener este aspecto:
(Defun lectura ID3-TAG (en) (Let ((etiqueta (make-instance 'ID3-TAG))) (Con ranuras (identificador de versin principal banderas de revisin de los marcos de tamao) etiqueta (Setf identificador (lectura-iso-8859-1-cadena: longitud 3)) (Setf versin principal (lectura-U1 en)) (Setf revisin (lectura-U1 en)) (Banderas setf (lectura-U1 en)) (Tamao setf (lectura ID3 codificada de tamao en el)) (Marcos setf (lectura de id3-marcos en: etiqueta de tamao de tamao))) etiqueta))

La escritura ID3-TAG en funcin de que se estructurara de manera similar Tendras utilizar los adecuados * Cul fue- funciones para escribir los valores almacenados en las ranuras de la ID3-TAG objeto. No es difcil ver cmo podra escribir las clases apropiadas para representar a todas las estructuras de datos compuestos en una especificacin, junto con foo lectura

yescritura-foo funciones para cada categora y para los tipos primitivos necesarios. Pero tambin es fcil de decir que toda la lectura y la escritura de funciones van a ser muy similar, difiriendo slo en los detalles de lo tipos que leen y los nombres de las ranuras que se los guarde all Es especialmente irritante si se considera que en el ID3 especificaciones que toma alrededor de cuatro lneas de texto para especificar la estructura de la etiqueta ID3, mientras que ya ha escrito dieciocho lneas de cdigo y ni siquiera han escritoescritura ID3-TAG todava. Lo que realmente me gustara es una manera de describir la estructura de algo as como una etiqueta ID3 en una forma que est tan comprimida como pseudocdigo de la especificacin pero que tambin se puede ampliar en el cdigo que define el ID3-TAG en la clase y las funciones que traducir entre bytes en el disco y las instancias de la clase. Suena como un trabajo para una macro. El diseo de las macros Puesto que usted ya tiene una idea aproximada de lo que codificar las macros se necesita para generar, el siguiente paso, de acuerdo con el proceso para escribir una macro que se describe en el captulo 8, es cambiar la perspectiva y pensar en lo que es una llamada a la macro debe ser similar . Dado que el objetivo es ser capaz de escribir algo tan comprimido como el pseudocdigo en la especificacin de ID3, puede empezar por ah. El encabezado de la etiqueta ID3 se especifica as:
Identificador ID3/file "ID3" ID3 versin de US $ 02 00 ID3 banderas% XX000000 ID3 tamao 4 *% 0xxxxxxx

En la notacin de la especificacin, esto significa que el "identificador de archivo", la ranura de la etiqueta ID3 es la cadena "ID3" en la norma ISO-8859-1 de codificacin. La versin consta de dos bytes, el primero de ellos-para esta versin de la especificacin - tiene el valor 2 y el segundo de los cuales - de nuevo para esta versin de la especificacin - es 0. La ranura de banderas es de ocho bits, de los cuales todos menos el primero dos son 0, y el tamao se compone de cuatro octetos, cada uno de los cuales tiene un 0 en el bit ms significativo. Parte de la informacin no es capturado por este pseudocdigo. Por ejemplo, exactamente cmo los cuatro bytes que codifican el tamao se han de interpretar se describe en unas pocas lneas de prosa. Del mismo modo, la especificacin describe en prosa como los marcos y el relleno posterior se almacena despus de esta

cabecera. Pero la mayor parte de lo que usted necesita saber para ser capaz de escribir cdigo para leer y escribir en la etiqueta ID3 se especifica mediante este pseudocdigo. Por lo tanto, usted debera ser capaz de escribir una versin de sexpresin de este pseudocdigo y hacer que se expandi a las definiciones de clase y la funcin que de otro modo tendra que escribir a mano - algo, tal vez, de esta manera:
(Define-binary-clase de ID3-TAG ((Archivo de identificadores (iso-8859-1-cadena: longitud de 3)) (Versin principal U1) (Revisin U1) (Banderas u1) (Tamao de ID3-TAG-size) (Marcos (marcos: id3-etiqueta de tamao de tamao))))

La idea bsica es que esta forma se define una clase ID3-TAG en forma similar a como lo hara con DEFCLASS , pero en lugar de especificar cosas como : initarg y: de acceso , cada especificacin de ranura formada por el nombre de la ranura archivo de identificadores de , versin principal , y as sucesivamente - y la informacin acerca de cmo ese espacio se representa en el disco. Dado que este es slo un poco de fantasear, no tiene que preocuparse de la macro exactamente cmodefinir-binary-clase sabr qu hacer con expresiones tales como (ISO-8859-1cadena: longitud de 3) y u1 , ID3-TAG de tamao , y(id3-marcos: etiqueta de tamao de tamao) , siempre y cuando cada expresin contiene la informacin necesaria para saber leer y escribir una codificacin de datos en particular, usted debera estar bien. Haciendo el sueo una realidad Bueno, basta fantasear con buen aspecto cdigo, ahora lo que necesita para llegar al trabajo por escrito definen-binary-clase - escribir el cdigo que a su vez, que la expresin concisa de lo que la etiqueta ID3 se parece a un cdigo que puede representar una en la memoria, leer uno del disco, y volver a escribirlos. Para empezar, debe definir un paquete de esta biblioteca. Aqu est el archivo del paquete que viene con la versin se puede descargar desde el sitio Web del libro:
(En el paquete: CL-usuario) (Defpackage: com.gigamonkeys.binary de datos (: Uso: common-lisp: com.gigamonkeys.macro utilidades) (: Exportacin: define-binary-clase : Define-etiquetado-binary-clase : Definir-de tipo binario : Lectura del valor

: : : : :

Write-valor * En curso de los objetos * Los padres de tipo Corriente binaria-objeto + + Null))

El COM.GIGAMONKEYS.MACRO-UTILIDADES paquete contiene los con los gensyms y una sola vez macros del Captulo 8. Puesto que usted ya tiene una versin manuscrita del cdigo que se desea generar, no debe ser demasiado difcil de escribir como una macro. Slo tienes que tomar en pequeas piezas, empezando con una versin de definir-binary-la clase que genera slo el DEFCLASS formulario. Si uno mira hacia atrs en la definicin binaria de la clase formulario, usted ver que tiene dos argumentos, el nombre de ID3-TAG y una lista de especificadores de ranura, cada uno de los cuales es a su vez una lista de dos elemento. De las piezas que necesita para construir la correspondiente DEFCLASS formulario. Es evidente que la mayor diferencia entre la definicin binaria de la clase formulario y un adecuado DEFCLASS formulario se encuentra en los especificadores de ranura. Un especificador de una sola ranura de definir-binary-clase se ve algo como esto:
(Versin principal U1)

Pero eso no es un especificador de ranura legal para un DEFCLASS . En su lugar, se necesita algo como esto:
(Versin principal: initarg: versin principal: versin principal de acceso)

Es bastante fcil. En primer lugar definir una funcin fcil de traducir un smbolo para el smbolo de la palabra clave correspondiente.
(Defun-como la palabra clave (simtrica) (pasante (smbolo cadena): palabra clave))

Ahora definimos una funcin que toma una definicin binaria de clase especificador de ranura y devuelve un DEFCLASS ranura especificador.
(Defun ranura-> defclass-slot (SPEC) (Let (nombre de ((primera especificacin))) `(, Nombre: initarg, (como la palabra clave-nombre): de acceso, nombre)))

Puede probar esta funcin en el REPL despus de cambiar a su nuevo paquete con una llamada a la in-package .
BINARY-DATA> (slot-> defclass ranura "(versin principal u1)) (Versin principal: INITARG: versin principal: Accessor versin principal)

Se ve bien. Ahora la primera versin de definir-binary-clase es trivial.

(Defmacro define-binary-el nombre de clase (slots) `(Defclass, el nombre de () , (Slot-> mapcar # 'tragaperras defclass ranuras)))

Esto es simple plantilla de estilo macro - definicin binaria de clase genera un DEFCLASS forma interpolando el nombre de la clase y una lista de especificadores de ranura construida mediante la aplicacin de la ranura-> defclass ranura a cada elemento de la lista de especificadores de las ranuras de la ladefinicin binaria de la clase formulario. Para ver exactamente lo que el cdigo esta macro genera, se puede evaluar esta expresin en el REPL.
(Macroexpand-1 '(define-binary-clase de ID3-TAG ((Identificador (iso-8859-1-cadena: longitud de 3)) (Versin principal U1) (Revisin U1) (Banderas u1) (Tamao de ID3-TAG-size) (Marcos (marcos: id3-etiqueta de tamao de tamao)))))

El resultado, ligeramente reordenada aqu para una mejor legibilidad, debera serle familiar, ya que es exactamente la definicin de la clase que usted escribi con la mano antes:
(Defclass ID3-TAG () ((Identificador: initarg: Identificador: el identificador de acceso) (Versin principal: initarg: versin principal: versin principal de acceso) (Revisin: initarg: Revision de acceso) (Banderas: initarg: banderas: banderas de acceso) (Tamao: initarg: Tamao: El tamao de acceso) (Marcos: initarg: marcos: marcos de acceso)))

Lectura de objetos binarios Lo siguiente que debe hacer -binary-definir la clase tambin generan una funcin que puede leer una instancia de la nueva clase. Mirando hacia atrs en lalectura ID3-TAG funcin que escrib antes, esto parece un poco ms complicado, ya que la lectura ID3-TAG no era tan regular - para leer el valor de cada ranura, usted tuvo que llamar a una funcin diferente. Sin mencionar, el nombre de la funcin, lectura ID3-TAG , mientras que deriva del nombre de la clase que est definiendo, no es uno de los argumentos para definir binario de clase y por lo tanto no est disponible para ser interpolados en una plantilla de la forma en que el nombre de la clase era. Usted podra hacer frente a ambos problemas mediante el diseo de y despus de una convencin de nomenclatura por lo que la macro se puede averiguar el nombre de la funcin a llamar basndose en el nombre del tipo en el especificador de

ranura. Sin embargo, esto requerira definir-binary-clase para generar el nombre de lectura ID3-TAG , la cual es posible, pero una mala idea. Las macros que crean definiciones globales en general, debe utilizar slo los nombres, que reciban de sus interlocutores; macros que generan los nombres de debajo de las sbanas puede causar difciles de predecir-y duro-a-debug - los conflictos de nombres cuando los nombres generados por pasar a ser el mismo que nombres usados en otros lugares.
8

Usted puede evitar estos inconvenientes tanto al notar que todas las funciones que leen un determinado tipo de valores tienen el mismo propsito fundamental, para leer un valor de un tipo especfico de una corriente. Hablando coloquialmente, se podra decir que son todas las instancias de una operacin genrica nica. Y el uso coloquial de la palabra genrica que le llevar directamente a la solucin a su problema: en lugar de definir un montn de funciones independientes, todos con diferentes nombres, se puede definir una funcin genrica nica,lectura de valor , con mtodos especializados para leer diferentes tipos de valores. Es decir, en lugar de definir las funciones de lectura-iso-8859-1-cadena y de lecturaU1 , se puede definir el valor de lectura como una funcin genrica de tomar dos argumentos necesarios, un tipo y un arroyo, y posiblemente algunos argumentos de palabras clave.
(Defgeneric lectura de valor (flujo tipo y clave) (: Documentacin "Leer un valor del tipo dado de la corriente."))

Mediante la especificacin y la llave sin ningn parmetro de palabras clave actuales, que permiten diferentes mtodos para definir sus propias claves y parmetros sin necesidad de que lo hagan. Esto quiere decir que cada mtodo especializado en lectura y el valor se tiene que incluir tanto y la llave o una y resto de parmetros en su lista de parmetros para que sea compatible con la funcin genrica. A continuacin, vamos a definir los mtodos que utilizan EQL specializers para especializar el tipo de argumento en el nombre del tipo que desea leer.
(Defmethod lectura valor ((tipo (EQL "iso-8859-1-cadena)) y en la longitud de la clave) ...) (Defmethod lectura valor ((tipo (EQL 'u1)) en y clave) ...)

Entonces usted puede hacer definir-binary-clase de generar una lectura de valor mtodo especializado en el nombre del tipo ID3-TAG , y que el mtodo puede ser

implementado en trminos de las llamadas a la lectura de valor con los tipos adecuados de ranura como el primer argumento. El cdigo que se desea generar va a tener el siguiente aspecto:
(Defmethod lectura valor ((tipo (eql ID3-TAG)) in & llave) (Let ((objeto (make-instance 'ID3-TAG))) (Con ranuras (identificador de versin principal banderas de revisin de los marcos de tamao) objeto (Setf identificador (lectura de valor "iso-8859-1-cadena: longitud 3)) (Setf versin principal (lectura de valor "en la U1)) (Setf revisin (lectura de valor "en la U1)) (Banderas setf (valor de lectura 'en u1)) (Tamao setf (valor de lectura 'id3 codificado de tamao en el)) (Marcos setf (valor de lectura 'id3-marcos en: etiqueta de tamao de tamao))) objeto))

Por lo tanto, tal y como lo necesita una funcin para traducir una definicin binaria de clase especificador de ranura a un DEFCLASS especificador de ranura con el fin de generar el DEFCLASS forma, ahora lo que necesita una funcin que toma una definicin binaria de clase especificador de ranura y genera la adecuada SETFforma, es decir, algo que tiene esto:
(Identificador (iso-8859-1-cadena: longitud de 3))

y devuelve el siguiente:
(Setf identificador (lectura de valor "iso-8859-1-cadena: longitud 3))

Sin embargo, hay una diferencia entre este cdigo y el DEFCLASS ranura especifica: se incluye una referencia a una variable de - el parmetro del mtodo de lalectura el valor mtodo - que no se deriva de la especificacin de la ranura. No tiene que ser llamado en , pero cualquiera sea el nombre que utiliza tiene que ser el mismo que el utilizado en lista de parmetros del mtodo y en las otras llamadas a leer-de valor . Por ahora se puede esquivar la cuestin de dnde proviene ese nombre mediante la definicin de la ranura-> read-el valor de tomar un segundo argumento del nombre de la variable de flujo.
(Corriente de espec defun ranura-> read-value () (Desestructuracin-bind (nombre (tipo y dems argumentos)) (normalizar las ranuras de las especificaciones de especificaciones) `(Setf, nombre (lectura de valor", el tipo, corriente, @ args))))

La funcin de normalizar las ranuras de las especificaciones normaliza el segundo elemento de la especificacin de ranura, la conversin de un smbolo como u1 a la lista (U1) por lo que la desestructuracin-BIND puede analizarlo. Se parece a esto:
(Defun normalizar las ranuras de las especificaciones (spec) (La lista (primera especificacin) (mklist (segunda especificacin))))

(Defun mklist (x) (if (listp x) x (lista x)))

Usted puede probar la ranura-> read-valor con cada tipo de ranura de especificador.
BINARY-DATA> (slot-> read-valor "(versin principal u1) flujo) (Setf versin principal (U1 LECTURA DE VALOR 'STREAM)) BINARY-DATA> (slot-> read-valor '(identificador (iso-8859-1-cadena: longitud 3)) flujo) (ISO-8859-1-STRING STREAM SETF IDENTIFICADOR (READ-valor ': DURACIN 3))

Con estas funciones usted est listo para aadir el valor de lectura de definirbinary-clase . Si usted toma la mano de lectura valor de mtodo y tira cualquier cosa que se relaciona a una clase en particular, uno se queda con este esqueleto:
(Defmethod lectura valor ((tipo (EQL ...)) y flujo de clave) (Let ((objeto (de creacin de ejemplo ...))) (Con ranuras (...) objeto de ... objeto)))

Todo lo que necesitas hacer es agregar este esqueleto a la define-binary-la clase de plantilla, en sustitucin de elipses con el cdigo que se llena en el esqueleto con los nombres correspondientes y el cdigo. Tambin querr reemplazar las variables de tipo , corriente , y objetos con los nombres de gensymed para evitar posibles conflictos con nombres de las ranuras, los 9 que se puede hacer con el con-gensyms macro desde el captulo 8. Adems, debido a una macro que ampliar en una sola forma, es necesario para envolver alguna forma todo el DEFCLASS y DEFMETHOD . progn es la forma habitual de utilizar para las macros que se expanden en mltiples definiciones, porque el trato especial que recibe de archivo cuando el compilador que aparece en el nivel superior de un archivo, como ya coment en el captulo 20. Por lo tanto, puede cambiar definir-binary-clase de la siguiente manera:
(Defmacro define-binary-el nombre de clase (slots) (Con-gensyms (typevar objectvar streamvar) `(Progn (Defclass, el nombre de () , (Slot-> mapcar # 'tragaperras defclass ranuras)) (Defmethod lectura de valor ((, typevar (EQL ', nombre)), streamvar y clave) (Let ((, objectvar (make-instance ', nombre))) (Con las ranuras, las ranuras (primeros mapcar # '), objectvar , @ (Mapcar # '(lambda (x) (slot-> read valor x)) streamvar ranuras)) , Objectvar)))))

Escribir objetos binarios La generacin de cdigo para registrar una instancia de una clase binaria se proceder de manera similar. En primer lugar se puede definir un valor de escritura funcin genrica.
(Defgeneric escritura de valor (valor de tipo de flujo y la llave) (: Documentacin ". Escribir un valor como el tipo de atencin a la corriente"))

A continuacin, defina una funcin de ayuda que se traduce en una definicin binaria de clase especificador de ranura en el cdigo que escribe utilizando la ranurade escritura de valor . Al igual que con la ranura-> read-valor de la funcin, esta funcin auxiliar debe tener el nombre de la variable de flujo como un argumento.
(Corriente de espec defun ranura-> write-value () (Desestructuracin-bind (nombre (tipo y dems argumentos)) (normalizar las ranuras de las especificaciones de especificaciones) `(Write-valor", el tipo, corriente, nombre, @ args)))

Ahora puede agregar un valor de escritura de plantilla a la definicin de clasebinary- macro.


(Defmacro define-binary-el nombre de clase (slots) (Con-gensyms (typevar objectvar streamvar) `(Progn (Defclass, el nombre de () , (Slot-> mapcar # 'tragaperras defclass ranuras)) (Defmethod lectura de valor ((, typevar (EQL ', nombre)), streamvar y clave) (Let ((, objectvar (make-instance ', nombre))) (Con las ranuras, las ranuras (primeros mapcar # '), objectvar , @ (Mapcar # '(lambda (x) (slot-> read valor x)) streamvar ranuras)) , Objectvar)) (Defmethod escritura de valor ((, typevar (EQL ', nombre)), streamvar, objectvar y clave) (Con las ranuras, las ranuras (primeros mapcar # '), objectvar , @ (Mapcar # '(lambda (x) (slot-> write-valor x)) streamvar slots))))))

Adicin de Sucesiones y Estructuras de la etiqueta Aunque esta versin de definir-binary-clase se encargar de estructuras independientes, formatos de archivos binarios suelen definir en disco estructuras que sera natural que el modelo con las subclases y superclases. As que es posible que desee ampliar definir-binary-clase para apoyar a la herencia. Una tcnica relacionada utilizada en los formatos binarios muchos es tener varias estructuras en disco cuyo tipo exacto slo puede determinarse mediante la lectura de algunos datos que indican cmo analizar los bytes siguientes. Por ejemplo, los

cuadros que componen la mayor parte de la etiqueta ID3 todos comparten una estructura de encabezado comn que consiste en un identificador de cadena y una longitud. Para leer un cuadro, te recomiendo que leas el identificador y utilizar su valor para determinar qu tipo de marco que estamos viendo y por lo tanto la manera de analizar el cuerpo de la estructura. La actual definicin de clase-binary- macro no tiene manera de manejar este tipo de lectura - se puede utilizar definir-binary-clase para definir una clase para representar a cada tipo de marco, pero no tendra manera de saber qu tipo de marco de leer sin leer por lo menos el identificador. Y si otro cdigo lee el identificador con el fin de determinar qu tipo de pasar a la lectura de valor , luego de que se rompa el valor de lectura , ya que est esperando para leer todos los datos que componen la instancia de la clase se crea una instancia. Usted puede resolver este problema mediante la adicin de la herencia para definirbinary-clase y luego escribir otra macro, define-etiquetado-binary-clase, para la definicin de "abstracto" las clases que no se crean instancias directamente, sino que puede ser especializada por valor de lectura mtodos que saben leer los datos suficientes para determinar qu tipo de clase para crear. El primer paso para la adicin de la herencia para definir-binary-clase es agregar un parmetro a la macro a aceptar una lista de superclases.
(Defmacro define-binary-clase (nombre (y dems) superclases slots) ...

Luego, en la DEFCLASS plantilla, interpolar el valor en lugar de la lista vaca.


(Defclass, nombre, superclases , ...)

Sin embargo, hay un poco ms que eso. Tambin es necesario cambiar los valores de lectura y escritura de valor para los mtodos de los mtodos generados en la definicin de una superclase puede ser utilizado por los mtodos generados como parte de una subclase a leer y escribir ranuras heredadas. La forma actual de valores de lectura obras es particularmente problemtico, ya que una instancia del objeto antes de llenarlo de - obviamente, no se puede tener el mtodo de la responsabilidad de leer los campos de la superclase de una instancia de un objeto mientras que el mtodo de la subclase de una instancia y se llena en un objeto diferente .

Puede solucionar este problema mediante el fraccionamiento de lectura de valor en dos partes, una responsable de instanciar la clase correcta de objeto y otro responsable de llenar espacios en un objeto existente. Por el lado de la escritura es un poco simple, pero puede utilizar la misma tcnica. As que vamos a definir dos nuevas funciones genricas, objeto de lectura y escritura a objetos , los que a la vez toman un objeto ya existente y un arroyo.Mtodos de estas funciones genricas sern responsables de la lectura o escritura de las ranuras especficas para la clase de objeto en el que estn especializados.
(Defgeneric lectura de objetos (flujo de objeto) (: Mtodo de combinacin de progn: ms especfica-ltima) (: Documentacin ". Rellene las ranuras de objetos de flujo")) (Defgeneric escritura-objeto (flujo de objeto) (: Mtodo de combinacin de progn: ms especfica-ltima) (: Documentacin "Escribe las ranuras de objeto en el flujo".))

La definicin de estas funciones genricas para usar el progn combinacin de mtodo con la opcin : ms especficos ltima le permite definir los mtodos que se especializan objeto binario de cada clase y hacer que se ocupan slo de las ranuras de realidad definidos en esa clase, la progn combinacin de mtodo de la voluntad combinar todos los mtodos aplicables por lo que el mtodo especializada en la clase menos especfico en la jerarqua se ejecuta primero, leer o escribir las ranuras definidas en esa clase, entonces el mtodo especializado en por lo siguiente subclase especfica, y as sucesivamente. Y puesto que todo el trabajo pesado para una clase especfica de ahora va a ser realizado porleer-objeto y la escritura a objetos , ni siquiera es necesario definir especializados lectura de valor y la escritura de valor mtodos, se puede definir mtodos que asumen por defecto el argumento de tipo es el nombre de una clase binario.
(Defmethod lectura valor ((el tipo de smbolo) y flujo de clave) (Let ((objeto (de creacin de instancia de tipo))) (Lectura de objeto de secuencia de objetos) objeto)) (Defmethod escritura-valor ((el tipo de smbolo) la cadena de valor y clave) (Assert tipo (valor typep)) (Escritura objeto de cadena de valor))

Tenga en cuenta cmo se puede utilizar MAKE-INSTANCIA como una factora de objetos genrico -, mientras que normalmente llamamos MARCA-INSTANCIA con un smbolo citado como el primer argumento, ya que normalmente saben

exactamente qu clase desea crear una instancia, se puede utilizar cualquier expresin que se evala un nombre de clase tal como, en este caso, el tipo de parmetro en la lectura-valor mtodo. Los cambios para definir-binary-clase para definir los mtodos de lectura-objeto y la escritura-objeto en lugar de leer-el valor y elvalor de escritura son bastante menores.
(Defmacro define-binary-el nombre de clase (ranuras superclases) (Con-gensyms (objectvar streamvar) `(Progn (Defclass, nombre, superclases , (Slot-> mapcar # 'tragaperras defclass ranuras)) (Defmethod lectura-objeto progn ((, objectvar, nombre), streamvar) (Con las ranuras, las ranuras (primeros mapcar # '), objectvar , @ (Mapcar # '(lambda (x) (slot-> read valor x)) streamvar ranuras))) (Defmethod escritura-objeto progn ((, objectvar, nombre), streamvar) (Con las ranuras, las ranuras (primeros mapcar # '), objectvar , @ (Mapcar # '(lambda (x) (slot-> write-valor x)) streamvar slots))))))

Mantener un registro de los slots heredados Esta definicin va a trabajar para muchos propsitos. Sin embargo, no se ocupa de una situacin bastante comn, es decir, cuando se tiene una subclase que necesita para referirse a las ranuras heredadas en sus especificaciones propias de ranura. Por ejemplo, con la definicin actual de definir-binary-clase , se puede definir una clase como esta:
(Define-binary-clase genrica-marco () ((Identificacin (iso-8859-1-cadena: longitud de 3)) (Tamao del u3) (Datos en bruto (de bytes: bytes de tamao))))

La referencia al tamao en la especificacin de los datos funciona de la manera que se espera debido a que las expresiones que leen y escriben los datos de la ranura se envuelven en un CON-Slots que enumera todas las ranuras de los objetos. Sin embargo, si se intenta dividir la clase en dos clases de esta manera:
(Define-binary-clase de marco () ((Identificacin (iso-8859-1-cadena: longitud de 3)) (Tamao del u3))) (Define-binary-clase genrica de marco (frame) (((Datos en bruto-bytes: tamao de bytes))))

obtendr una advertencia en tiempo de compilacin al compilar el marco genrico de la definicin y un error de ejecucin cuando se trate de usarlo porque no habr

ninguna variable lxico aparentemente el tamao de los objetos de lectura y escritura a objetos especializados en mtodos genricos marco . Lo que hay que hacer es un seguimiento de las franjas horarias definidas por cada clase binaria y, a continuacin incluyen ranuras heredadas en los CON-Slots se forma en losobjetos de lectura y escritura a objetos mtodos. La forma ms fcil hacer un seguimiento de este tipo de informacin es para colgar el smbolo que da nombre a la clase. Como he discutido en el captulo 21, cada objeto tiene un smbolo de la lista de propiedades asociada, que se puede acceder a travs de las funciones PLIST SMBOLO- y el GET . Se puede asociar de clave / valor pares con un smbolo, agregndolos a su lista de propiedades con SETF del GET . Por ejemplo, si el binario de la clase foo define tres ranuras - x , la Y , y Z - se puede realizar un seguimiento de este hecho mediante la adicin de unos espacios claves para el smbolo foo lista de propiedades 's con el valor (x yz) con este expresin:
(Setf (get 'foo' slots) '(xyz))

Usted quiere que esta contabilidad a ocurrir como parte de la evaluacin de la definicin binaria de la clase de foo . Sin embargo, no est claro dnde poner la expresin. Si la evaluacin cuando se calcula la expansin de la macro, que va a se evalan cuando se compila el binario define la clase la forma pero no si ms adelante cargar un archivo que contiene el cdigo compilado resultante. Por otro lado, si se incluye la expresin de la expansin, entonces no va a ser evaluado durante la compilacin, lo que significa que si se compila un archivo con varias definir-binary-clase formas, ninguna de la informacin acerca de lo que las clases de definir qu franjas horarias estar disponible hasta que todo el archivo se ha cargado, que es demasiado tarde. Esto es lo que el operador especial EVAL-CUANDO explic en el Captulo 20 es para el. Al envolver un formulario en un EVAL-AL , se puede controlar si se trata de evaluar en tiempo de compilacin, cuando el cdigo compilado se carga, o ambas cosas. Para casos como este en el que desea la ardilla de distancia alguna informacin durante la compilacin de una forma macro que tambin quieren estar disponible despus de la forma compilado se carga, se debe envolver en un EVAL-CUANDO as:
(Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get 'foo' slots) '(xyz)))

e incluyen la EVAL-CUANDO en la expansin generada por la macro. Por lo tanto, usted puede ahorrar tanto las ranuras y las superclases directas de una clase binaria mediante la adicin de esta forma a la expansin generada por definirbinary-clase :
(Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get ranuras ", de nombre ')', (mapcar # 'de primeras ranuras)) (Setf (get superclases ', de nombre') ', superclases))

Ahora usted puede definir tres funciones de ayuda para acceder a esta informacin. El primero simplemente devuelve directamente a las ranuras que define una clase binaria. Es una buena idea para devolver una copia de la lista ya que usted no desea otro tipo de cdigo para modificar la lista de las ranuras despus de la clase binaria se ha definido.
(Defun directa-slots (nombre) (Copia de la lista (obtener el nombre de las ranuras)))

La siguiente funcin devuelve los espacios heredados de las clases de otros binarios.
(Defun heredados-slots (nombre) (Bucle de super (obtener superclases nombre ') nconc (directa-Super Slots) nconc (heredados de Super Slots)))

Por ltimo, se puede definir una funcin que devuelve una lista con los nombres de todas las franjas horarias definidas directamente y heredado.
(Defun todos los slots (nombre) (Nconc (Direct-ranuras nombre) (nombre heredado de las ranuras)))

Cuando usted est calculando la expansin de un define-generic-binary-clase de forma, si desea generar un CON-Slots formulario que contiene los nombres de todas las ranuras de los definidos en la nueva clase y todas sus superclases. Sin embargo, no se puede utilizar todas las ranuras de tiempo que est generando la expansin ya que la informacin no estar disponible hasta despus de la expansin se compila. En su lugar, usted debe usar la siguiente funcin, que tiene la lista de especificadores de ranura y superclases pasan a definir-generic-binary-clase y los utiliza para calcular la lista de todas las ranuras de la nueva clase de:
(Defun nueva clase-todos los slots (ranuras superclases) (Nconc (mapcan # 'de todas las ranuras de superclases) (mapcar #' de primeras ranuras)))

Con estas funciones se define, puede cambiar la definicin binaria de clase para almacenar la informacin acerca de la clase que actualmente se est definiendo y

utilizar la informacin ya almacenada acerca de ranuras de las superclases 'para generar la CON-Slots formas que desea de esta manera:
(Defmacro define-binary-clase (nombre (y dems) superclases ranuras) (Con-gensyms (objectvar streamvar) `(Progn (Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get ranuras ", de nombre ')', (mapcar # 'de primeras ranuras)) (Setf (get superclases ', de nombre') ', superclases)) (Defclass, nombre, superclases , (Slot-> mapcar # 'tragaperras defclass ranuras)) (Defmethod lectura-objeto progn ((, objectvar, nombre), streamvar) (Con ranuras, por la nueva clase de todas las ranuras de superclases slots), objectvar , @ (Mapcar # '(lambda (x) (slot-> read valor x)) streamvar ranuras))) (Defmethod escritura-objeto progn ((, objectvar, nombre), streamvar) (Con ranuras, por la nueva clase de todas las ranuras de superclases slots), objectvar , @ (Mapcar # '(lambda (x) (slot-> write-valor x)) streamvar slots))))))

Estructuras con la etiqueta Con la capacidad de definir las clases de binarios que se extienden a otras clases binarios, usted est listo para definir una nueva macro para la definicin de clases para representar "con etiqueta" estructuras. La estrategia para la lectura de las estructuras con etiqueta ser definir un organismo especializado de lectura de valor mtodo que sabe leer los valores que componen el principio de la estructura y luego usar esos valores para determinar qu subclase instanciar. A continuacin, haremos una instancia de esa clase conMAKE-INSTANCIA , pasando los valores ya ha ledo como initargs, y pasar el objeto a objeto de lectura , lo que permite a la clase real del objeto para determinar cmo el resto de la estructura que se lee. La nueva macro, define-etiquetado-binary-clase , se ver como definir-binary-clase con la adicin de una : despacho opcin utilizada para especificar un formulario que se debe evaluar como el nombre de una clase binaria. El : envo forma sern evaluados en un contexto donde los nombres de los espacios definidos por la clase etiquetado estn obenlazados a variables que contienen los valores ledos desde el archivo. La clase cuyo nombre devuelve debe aceptar initargs que corresponden a los nombres de las ranuras definidas por la clase etiquetado. Esto es fcil de alcanzar si el : envo forma siempre como resultado el nombre de una clase que las subclases de la clase de etiquetado.

Por ejemplo, suponiendo que tiene una funcin, encontrar marco de la clase , que se asignar un identificador de cadena a una clase binario que representa un tipo particular de ID3 marco, es posible definir una clase binaria etiquetado, id3-marco , de esta manera:
(Define-etiquetados-binary-clase de id3-marco () ((Identificacin (iso-8859-1-cadena: longitud de 3)) (Tamao del u3)) (: Envo (encontrar-frame-la clase de id)))

La expansin de una definicin de etiquetado-binary-clase contendr un DEFCLASS y una escritura-objeto mtodo al igual que la expansin de ladefinicin binaria de la clase , pero en lugar de un objeto de lectura mtodo que va a contener un valor de lectura mtodo que se parece a esto:
(Defmethod lectura valor ((tipo (EQL 'id3-marco)) y flujo de clave) (Let ((id (iso-8859-1-cadena de valor de lectura 'stream: longitud de 3)) (Tamao (U3 lectura de valor "flujo))) (Let ((objeto (que de instancia (encontrar-frame-id de la clase): Identificacin Identificacin: tamao tamao))) (Lectura de objeto de secuencia de objetos) objeto)))

Dado que las expansiones de definir-etiquetado-binary-clase y definir binario de clase van a ser idnticos, excepto por el mtodo de lectura, se puede factorizar los bits comunes en una macro, la ayudante de definir-generic-binaria de la clase- , que acepta el leer mtodo como un parmetro e interpola ella.
(Defmacro define-generic-binary-clase (nombre (y resto superclases) ranuras de lectura y mtodo) (Con-gensyms (objectvar streamvar) `(Progn (Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get ranuras ", de nombre ')', (mapcar # 'de primeras ranuras)) (Setf (get superclases ', de nombre') ', superclases)) (Defclass, nombre, superclases , (Slot-> mapcar # 'tragaperras defclass ranuras)) , Mtodo de lectura (Defmethod escritura-objeto progn ((, objectvar, nombre), streamvar) (Declare (ignorable streamvar,)) (Con ranuras, por la nueva clase de todas las ranuras de superclases slots), objectvar , @ (Mapcar # '(lambda (x) (slot-> write-valor x)) streamvar slots))))))

Ahora usted puede definir tanto definir-binary-clase y definir de etiquetado-binaryclase para ampliar en una llamada adefinir-generic-binary-clase . Aqu est una

nueva versin de definir-binary-la clase que genera el mismo cdigo que la versin anterior cuando est totalmente expandida:
(Defmacro define-binary-clase (nombre (y dems) superclases ranuras) (Con-gensyms (objectvar streamvar) `(Define-generic-binary-clase, nombre, superclases, ranuras (Defmethod lectura-objeto progn ((, objectvar, nombre), streamvar) (Declare (ignorable streamvar,)) (Con ranuras, por la nueva clase de todas las ranuras de superclases slots), objectvar , @ (Mapcar # '(lambda (x) (slot-> read valor x)) streamvar slots))))))

Y aqu es definir-etiquetados-binary-clase , junto con dos nuevas funciones de ayuda que utiliza:
(Defmacro definen los etiquetados-binary-el nombre de clase (y resto (superclases) ranuras y opciones de descanso) (Con-gensyms (typevar objectvar streamvar) `(Define-generic-binary-clase, nombre, superclases, ranuras (Defmethod lectura de valor ((, typevar (EQL ', nombre)), streamvar y clave) (Let * (mapcar # '(lambda (x) (slot-> enlace)) x streamvar ranuras) (Let ((, objectvar (Marca de instancia , @ (O (cdr (assoc: las opciones de envo)) (Error "tendr que presentar: formulario de expedicin.")) , @ (Slot-> mapcan # 'arg palabra-ranuras)))) (Lectura-objeto, objectvar, streamvar) , Objectvar)))))) (Defun ranura-> enlace (flujo de especificaciones) (Desestructuracin-bind (nombre (tipo y dems argumentos)) (normalizar las ranuras de las especificaciones de especificaciones) `(, Nombre (lectura de valor", el tipo, corriente, @ args)))) (Defun ranura-> palabra-arg (SPEC) (Let (nombre de ((primera especificacin))) `(, (Como la palabra clave-nombre), nombre)))

Tipos primitivos binarios Mientras que definen-binary-clase y definir de etiquetado-binary-la clase que sea fcil de definir estructuras de materiales compuestos, que todava tiene que escribir lectura de valor y la escritura de valor mtodos para tipos de datos primitivos a mano. Usted puede decidir que vivir con eso, que especifica que los usuarios de la biblioteca tiene que escribir mtodos apropiados en la lectura del valor y la escritura de valor para apoyar a los tipos primitivos utilizados por las clases de sus binarios. Sin embargo, en lugar de tener que documentar cmo escribir una adecuada lectura de valor / escritura de valor par, que se puede brindar una macro para hacerlo automticamente. Esto tambin tiene la ventaja de hacer la abstraccin

creado por definir-binario-clase menos que gotea. En la actualidad,define-binaria de la clase depende de que los mtodos de lectura de valor y la escritura de valor se define de una manera particular, pero eso es realmente slo un detalle de implementacin. Mediante la definicin de una macro que genera los valores de lectura y escritura de valor mtodos de tipos primitivos, ocultar los detalles detrs de una abstraccin a controlar. Si ms tarde decide cambiar la aplicacin de la definicin binaria de la clase , usted puede cambiar su tipo primitivo que define el macro para cumplir los nuevos requisitos sin necesidad de realizar cambios en el cdigo que utiliza la biblioteca de datos binarios. As que usted debe definir una macro pasado, definir-de tipo binario , que generar lectura de valor y la escritura de valor mtodos para leer los valores representados por instancias de las clases existentes, en lugar de las clases definidas en definirbinary-clase . Para un ejemplo concreto, consideremos un tipo de los utilizados en el ID3-TAG en clase, una cadena de longitud fija codificado en ISO-8859-1 caracteres. Vamos a suponer, como lo hice antes, que la codificacin de caracteres nativo de tu Lisp es la norma ISO-8859-1 o un superconjunto, para que pueda utilizar CODE-CHAR y CHAR CODE- bytes para traducir a los personajes y la espalda. Como siempre, su meta es escribir una macro que le permite expresar slo la informacin esencial necesaria para generar el cdigo necesario. En este caso, hay cuatro piezas de informacin esencial: el nombre del tipo, iso-8859-1-secuencia , los y clave de los parmetros que deben ser aceptadas por los valores de lectura yescritura de valor mtodos, la longitud en este caso, el Cdigo para la lectura de un arroyo, y el cdigo para escribir en un arroyo. He aqu una expresin que contiene los cuatro elementos de informacin:
(Define-binaria de tipo iso-8859-1-cadena (longitud) (: Lector de (in) (Let ((string (cadena de longitud maquillaje))) (Dotimes (longitud i) (Setf (cadena char i) (cdigo-char (lectura de bytes en)))) cadena)) (: Escritor (con cadena) (Dotimes (longitud i) (Escribir byte (char-cdigo (cadena de caracteres i)) a))))

Ahora slo necesita una macro que puede desarmar este formulario y volverlo a armar en forma de dos DEFMETHOD s envuelto en una progn . Si se define la lista de parmetros para definir-de tipo binario de esta manera:

(Defmacro define-de tipo binario (especificacin de nombre (y dems argumentos) y cuerpo) ...

entonces dentro de la macro del parmetro de especificaciones ser una lista que contiene el lector y el escritor definiciones. A continuacin, puede utilizar ASOC para extraer los elementos de la especificacin con las etiquetas : lector de e : escritor y luego usar desestructurada-BIND para separar el RESTO de cada elemento. 10 Desde all es slo una cuestin de la interpolacin de los valores extrados en las plantillas backquoted de los valores de lectura y escritura de valor mtodos.
(Defmacro define-de tipo binario (nombre (y dems argumentos) y el cuerpo de especificaciones) (Con-gensyms (tipo) `(Progn , (Desestructuracin-bind ((en) y el cuerpo del cuerpo) (resto (assoc: lector de especificaciones)) `(Defmethod lectura de valor ((, tipo (EQL ', nombre)), y en clave, @ args) , El cuerpo de @)) , (Desestructuracin-bind ((de un total de valor) y el cuerpo del cuerpo) (resto (assoc: escritor de especificaciones)) `(Defmethod escritura-valor ((, tipo (EQL ', nombre)), a cabo, de valor y clave, @ args) , @ Cuerpo)))))

Observe cmo las plantillas backquoted se anidan: la plantilla ms externa comienza con la backquoted progn formulario. Esta plantilla se compone del smbolo progn y dos comas no cotizadas BIND desestructurada- expresiones. De este modo, la plantilla externa se rellena mediante la evaluacin de las BIND desestructurada- expresiones y la interpolacin de sus valores. Cada desestructurada-BIND expresin a su vez contiene otra plantilla comillas inversas, proteja el cual se utiliza para generar una de las definiciones de mtodo a ser interpolados en la plantilla ultraterrestre. Con esta macro se define, la define-de tipo binario forma dada anteriormente se expande a este cdigo:
(Progn (Defmethod lectura valor ((#: g1618 (EQL "iso-8859-1-cadena)) y en la longitud de la clave) (Let ((string (cadena de longitud maquillaje))) (Dotimes (longitud i) (Setf (cadena char i) (cdigo-char (lectura de bytes en)))) cadena)) (Defmethod escritura-valor ((#: g1618 (EQL "iso-8859-1-cadena)) con cadena y longitud de la clave) (Dotimes (longitud i) (Escribir byte (char-cdigo (cadena de caracteres i)) a))))

Por supuesto, ahora que tienes esta macro agradable para definir los tipos de binarios, es tentador para que haga el trabajo un poco ms. Por ahora slo debe hacer una pequea mejora que resultan ser bastante til cuando empiece a usar esta biblioteca para hacer frente a los formatos actuales, como las etiquetas ID3. Las etiquetas ID3, al igual que muchos otros formatos binarios, use un montn de tipos primitivos que son pequeas variaciones sobre un tema, como enteros sin signo en uno, dos, tres y cuatro bytes variedades. Por supuesto que podramos definir cada uno de esos tipos con los define-de tipo binario en su forma actual. O usted podra factorizar el algoritmo comn para la lectura y la escritura n bytes enteros sin signo en funciones de ayuda. Pero supongamos que ya haba definido un tipo binario, sin firmar, entero , que acepta un : bytes parmetro para especificar la cantidad de bytes a leer y escribir.Utilizando ese tipo, puede especificar un espacio que representa un entero sin signo de un byte con un especificador de tipo de (sin firmar-entero: 1 bytes) . Pero si un formato binario especial especifica un montn de ranuras de este tipo, que sera bueno para poder definir fcilmente un nuevo tipo - por ejemplo, u1 - lo que significa la misma cosa.Como resultado, es fcil de cambiar definir-de tipo binario para apoyar dos formas, una forma larga que consiste en una : lector y escritor: par y una forma corta que define un tipo binario en trminos de un tipo existente. El uso de un breve formulario define-de tipo binario , se puede definir u1 de esta manera:
(Define-de tipo binario u1 () (sin firmar-entero: 1 bytes))

que se expandir a lo siguiente:


(Progn (Defmethod lectura valor ((#: g161887 (u1 eql ')) #: g161888 y clave) (Lectura de valor "sin firmar-entero #: g161888: 1 bytes)) (Defmethod escritura-valor ((#: g161887 (u1 eql ')) #: g161888 #: g161889 y clave) (Write-valor "sin firmar-entero #: g161888 #: g161889: bytes 1)))

Para apoyar tanto a largo y corto-forma de definir la de tipo binario llamadas, hay que diferenciar en funcin del valor de la especificacin de argumento. Silas especificaciones son dos elementos de largo, que representa una llamada de formato largo, y los dos elementos debe ser el : lector de e : escritorespecificaciones, que se extrae como antes. Por otro lado, si es slo un tema de largo, el elemento uno debe ser un especificador de tipo, que necesita ser analizada de manera diferente.Usted puede utilizar ECASE para encender la LONGITUD de la

especificacin y luego analizar las especificaciones y generar una expansin apropiada, ya sea para la forma larga o la forma corta.
(Defmacro define-de tipo binario (nombre (y dems argumentos) y el cuerpo de especificaciones) (Ecase (especificacin de longitud) (1 (Con-gensyms (valor de tipo de flujo) (Desestructuracin-bind (derivado desde y dems derivados-args) (mklist (primera especificacin)) `(Progn (Defmethod lectura valor ((, tipo (EQL ', nombre)), la corriente y clave, @ args) (Lectura de valor ", que se deriven-de, arroyo, @ deriva-args)) (Defmethod escritura de valor ((, tipo (EQL ', nombre)), corriente, valor y clave, @ args) (Escritura de valor ", que se deriven-de, flujo, el valor, @ derivaargs)))))) (2 (Con-gensyms (tipo) `(Progn , (Desestructuracin-bind ((en) y el cuerpo del cuerpo) (resto (assoc: lector de especificaciones)) `(Defmethod lectura de valor ((, tipo (EQL ', nombre)), y en clave, @ args) , El cuerpo de @)) , (Desestructuracin-bind ((de un total de valor) y el cuerpo del cuerpo) (resto (assoc: escritor de especificaciones)) `(Defmethod escritura-valor ((, tipo (EQL ', nombre)), a cabo, de valor y clave, @ args) , @ Cuerpo)))))))

La pila de objetos actual Un ltimo de la funcionalidad que necesitar en el prximo captulo es una manera de conseguir el objeto binario que se lee o se escribe durante la lectura y la escritura. De manera ms general, al leer o escribir anidados objetos compuestos, es til ser capaz de obtener en cualquiera de los objetos que actualmente se leen o escriben. Gracias a las variables dinmicas yde todo: los mtodos, puede aadir esta mejora con una docena de lneas de cdigo. Para empezar, usted debe definir una variable dinmica que se llevar a cabo una pila de objetos en la actualidad est leyendo o escribiendo.
(* Los defvar en curso de los objetos * nil)

A continuacin, se puede definir : en torno a los mtodos de lectura-objeto y la escritura a objetos que empujar el objeto que se est leyendo o escribiendo en esta variable antes de invocar CALL-NEXT-MTODO .
(Defmethod lectura del objeto: todo (corriente de objeto) (Declare (flujo de ignorar)) (Let ((* progreso en los objetos * * (objeto de contras en curso de los objetos *)))

(Llamada de ltima mtodo))) (Defmethod escritura-objeto: en torno a (flujo de objeto) (Declare (flujo de ignorar)) (Let ((* progreso en los objetos * * (objeto de contras en curso de los objetos *))) (Llamada de ltima mtodo)))

Tenga en cuenta cmo volver a enlazar * en curso de los objetos * a una lista con un elemento nuevo en la parte delantera en lugar de asignar un nuevo valor. De esta manera, al final de la LET , despus de CALL-NEXT-mtodo que devuelve el valor antiguo de * en curso de los objetos * ser restaurada, apareciendo efectivamente el objeto de la pila. Con estos dos mtodos definidos, que se puede brindar dos funciones de conveniencia para conseguir los objetos especficos de la pila en curso. La funcinactual-binary-objeto volver la cabeza de la pila, el objeto cuya lecturaobjeto o la escritura objeto de mtodo ha sido invocado recientemente. El otro,los padres de tipo , toma un argumento que debe ser el nombre de una clase de objeto binario y devuelve el objeto de la ms reciente empuj de ese tipo, con el TYPEPfuncin que comprueba si un objeto dado es una instancia de un tipo particular .
(Defun corriente binaria-objeto () (* por primera vez en los avances de los objetos *)) (Defun tipo de padres y de los del tipo () (Encontrar-si # '(lambda (x) (x typep tipo)) * el progreso en los objetos *))

Estas dos funciones se pueden utilizar en cualquier cdigo al que se llamar dentro de la extensin dinmica de una lectura-objeto o de escritura a objetosllamada. Usted ver un ejemplo de cmo corriente binaria-objeto se puede utilizar en el prximo captulo. 11 Ahora usted tiene todas las herramientas que necesitan para hacer frente a una biblioteca de anlisis de ID3, por lo que ya est listo para pasar al siguiente captulo en el que voy a hacer justamente eso.

1 En ASCII, los primeros 32 caracteres no imprimibles son caracteres de control usadas originalmente

para controlar el comportamiento de una mquina de teletipo, haciendo que se hacen tales cosas como el sonido de la campana, copia de seguridad de un carcter, desplace a una nueva lnea, y mueva el carro hacia el principio de la lnea. De estos 32 caracteres de control, slo tres, la nueva lnea, retorno de carro, y horizontal, ficha, se encuentran normalmente en archivos de texto.

2 Algunos formatos de archivo binarios son estructuras en memoria de datos - en muchos sistemas

operativos, es posible convertir un archivo en la memoria, y lenguajes de bajo nivel como C, luego se puede tratar a la regin de memoria que contiene el contenido del archivo al igual que cualquier otra memoria, los datos escritos en esa rea de memoria se guardan en el archivo de base cuando est sin asignar. Sin embargo, estos formatos son dependientes de la plataforma desde la representacin en memoria, incluso de esos tipos de datos simples como enteros depende del hardware sobre el que se ejecuta el programa. Por lo tanto, cualquier formato de archivo que est destinado a ser porttil, debe definir una representacin cannica para todos los tipos de datos que utiliza y que se puede asignar a la actual representacin en memoria de datos en un determinado tipo de mquina o en un idioma determinado.
3 El trmino big-endian y su opuesto, little-endian , tomado de Jonathan Swift Los Viajes de Gulliver .,

se refieren a la forma en que se represent en un nmero multibyte en una secuencia ordenada de bytes, como en la memoria o en un archivo Por ejemplo, el nmero 43981, o abcd en hexadecimal, representado como una cantidad de 16 bits, se compone de dos bytes, ab y cd . No importa a un ordenador en qu orden estos dos bytes se almacenan siempre y cuando todos estn de acuerdo. Por supuesto, cada vez que hay una eleccin arbitraria que debe hacerse entre dos opciones igualmente buenas, lo nico que podemos estar seguros es que todo el mundo no va a estar de acuerdo. Durante ms de lo que siempre quisiste saber sobre l, y ver dnde estn los trminos big-endian y littleendian se aplic por primera vez de esta manera, lea "El Holy Wars y una splica por la paz" por Danny Cohen, disponible en http: / / khavrinen.lcs.mit.edu/wollman/ien-137.txt .
4 LDB y DPB , una funcin relacionada, fueron nombrados despus de las funciones DEC PDP-10 de

montaje que hizo esencialmente la misma cosa. Ambas funciones operan en nmeros enteros, como si estuvieran representadas con formato de complemento a dos, independientemente de la representacin interna utilizada por una implementacin particular de Common Lisp.
5 Common Lisp tambin proporciona funciones para el desplazamiento y enmascarar los bits de

enteros de una manera que puede ser ms familiar para los programadores de C y Java. Por ejemplo, podra escribirlectura U2 an una tercera va, utilizando las funciones, as:
(Defun lectura U2 (en) (Logior (ceniza (lectura de bytes en) 8) (lectura de bytes en)))

lo que sera ms o menos equivalente a este mtodo de Java:


pblica readU2 int (InputStream in) throws IOException { retorno (in.read () << 8) | (in.read ()); }

Los nombres LOGIOR y ASH son la abreviatura de lgico OR inclusivo y desplazamiento aritmtico . ASH se desplaza un nmero entero de un determinado nmero de bits a la izquierda cuando su segundo argumento es positivo o hacia la derecha si el segundo argumento es negativo. LOGIOR combina nmeros enteros por la lgica o Ing cada bit. Otra funcin, LOGAND , realiza un bit a bit y , lo que puede ser utilizado para enmascarar ciertos bits. Sin embargo, para los tipos de cambio de bit que tendr que hacer en este captulo y el siguiente, LDB y BYTE ser tanto ms conveniente y el estilo comn de ms idiomtico Lisp.

6 Originalmente, UTF-8 fue diseado para representar un cdigo de caracteres de 31 bits y se utiliza

hasta seis bytes por punto de cdigo. Sin embargo, el punto mximo de cdigo Unicode es # x10FFFF , por lo que una codificacin UTF-8 de Unicode requiere en la mayora de cuatro bytes por punto de cdigo.
7 Si es necesario analizar un formato de archivo que utiliza otros cdigos de caracteres, o si usted

necesita para analizar los archivos que contienen arbitrarias cadenas Unicode utilizando una aplicacin no Unicode-Common-Lisp, siempre se puede representar a estas cadenas en la memoria como vectores de cdigo entero puntos. No sern cadenas de Lisp, por lo que no ser capaz de manipular o compararlas con las funciones de cadena, pero an as ser capaz de hacer nada con ellos que se hace con vectores arbitrarios.
8 Por desgracia, el lenguaje en s mismo no siempre proporciona un buen modelo a este respecto: la

macro defstruct , que no discuto, ya que ha sido ampliamente superada por DEFCLASS , genera funciones con nombres que se genera en funcin del nombre de la la estructura que se le da. defstruct ejemplo 's mal lleva a muchos escritores de la nueva macro por mal camino.
9 Tcnicamente no hay posibilidad de que el tipo o el objeto en conflicto con los nombres de las

ranuras - en el peor, estara la sombra dentro de la CON-Slots formulario. Sin embargo, no duele nada simplemente GENSYM todos los nombres locales de las variables utilizadas dentro de una plantilla de macro.
10 Uso de ASOC para extraer el : lector de e : escritor elementos de la especificacin permite a los

usuarios de definir-de tipo binario para incluir los elementos de cualquier orden, si usted requiere el : lector de elemento que se est siempre en primer lugar, a continuacin, podra haber usado (el resto (primera especificacin)) para extraer el lector y el(resto (segunda especificacin)) para extraer el escritor. Sin embargo, el tiempo que requiere el : lector de e : escritor palabras clave para mejorar la legibilidad dedefinir de tipo binario formas, puede ser que tambin los utilizan para extraer los datos correctos.
11 El formato ID3 no requiere que el padre-de-tipo de la funcin, ya que es una estructura

relativamente plana. Esta funcin entra en su cuenta cuando tenga que analizar un formato compuesto por muchas de las estructuras anidadas cuyo anlisis depende de la informacin almacenada en estructuras de nivel superior. Por ejemplo, en el formato de archivo de clase Java, la estructura de clase de nivel superior del archivo contiene un conjunto de constantes que asigna valores numricos utilizados en otras subestructuras dentro del archivo de clase a los valores constantes que son necesarios al analizar las subestructuras. Si estuviera escribiendo un analizador de archivos de clase, puedes usar los padres-de-tipo en el cdigo que lee y escribe esas subestructuras para llegar a la clase de objeto de nivel superior del archivo y de all a la piscina constante.

25. Prctica: Un analizador de ID3


Con una biblioteca para analizar los datos binarios, usted est listo para escribir algo de cdigo para leer y escribir un formato binario real, el de las etiquetas ID3. Las etiquetas ID3 se utiliza para incrustar metadatos en archivos de audio MP3. Tratar con las etiquetas ID3 ser una buena prueba de la biblioteca de datos binarios, porque el formato ID3 es una verdad del mundo real en formato - una mezcla de ingeniera de los compromisos y decisiones idiosincrsicas de diseo que lo hace, independientemente de lo que se diga sobre l, obtener realizar el trabajo. En caso de que no la revolucin de intercambio de archivos, aqu est un resumen rpido de lo que las etiquetas ID3 son y cmo se relacionan con archivos MP3. MP3, tambin conocido como MPEG Audio Layer 3, es un formato para almacenar datos de audio comprimidos, diseado por los investigadores de Fraunhofer IIS y estandarizado por el Moving Picture Experts Group, un comit conjunto de la Organizacin Internacional de Normalizacin (ISO) y la Comisin Electrotcnica Internacional Commission (IEC). Sin embargo, el formato MP3, por s mismo, slo define la forma de almacenar datos de audio. Eso est bien siempre y cuando todos los archivos MP3 son gestionados por una nica aplicacin que puede almacenar metadatos externamente y hacer un seguimiento de los metadatos que va con los archivos. Sin embargo, cuando la gente empez a pasar alrededor de los archivos individuales de MP3 en Internet, a travs de sistemas de intercambio de archivos tales como Napster, pronto descubrieron que necesitaban una manera de integrar metadatos de los archivos MP3 en s. Debido a que el estndar MP3 fue codificado ya y un poco de software y hardware ya se haba escrito que supo decodificar el formato MP3 existente, cualquier plan para la incorporacin de la informacin en un archivo MP3 que tiene que ser invisible para los descodificadores de MP3. Introduzca ID3. El original formato ID3, inventado por el programador Eric Kemp, constaba de 128 bytes pegados en la final de un archivo MP3 en el que se haba ignorado por la mayora del software de MP3. Se compona de cuatro campos de 30 caracteres cada uno, uno para el ttulo de la cancin, el ttulo del lbum, el nombre del artista, y un comentario, un campo de ao de cuatro bytes, y un cdigo de gnero de un byte. Kemp siempre significados estndar para los cdigos de gnero primeros 80.

Nullsoft, los creadores de Winamp, un reproductor de MP3 popular, complementado ms tarde esta lista con otros gneros de 60 o menos. Este formato fue fcil de analizar, pero, evidentemente, bastante limitado. No tena forma de codificar los nombres de ms de 30 caracteres, sino que se limit a 256 gneros, y el significado de los cdigos de gnero tuvo que ser acordado por todos los usuarios de ID3-consciente de software. No haba ni siquiera una forma de codificar el nmero de pista de CD de un archivo MP3 en particular hasta que otro programador, Michael Mutschler, propuso incorporar el nmero de pista en el campo de comentarios, separada del resto del comentario de un byte nulo, por lo que existe ID3 software, que tenda a leer hasta el primer cero en cada uno de los campos de texto, lo ignoran. La versin de Kemp se llama ahora ID3v1 y ID3v1.1 Mutschler es. Limitado como eran, la versin 1 propuestas por lo menos una solucin parcial al problema de metadatos, por lo que fueron adoptados por muchos programas de extraccin (MP3, que tuvo que poner la etiqueta ID3 en los archivos MP3) y reproductores de MP3 (que extraer el informacin en la etiqueta ID3 para mostrar al usuario). 1 En 1998, sin embargo, las limitaciones eran realmente convertirse en molesto, y un nuevo grupo, liderado por Martin Nilsson, empez a trabajar en un esquema de etiquetado completamente nueva, que lleg a ser llamado ID3v2. El formato ID3v2 es extremadamente flexible, lo que permite muchos tipos de informacin que debe incluirse, con las limitaciones de longitud casi no hay. Tambin se aprovecha de ciertos detalles del formato MP3 para que las etiquetas ID3v2 para ser colocado al principio de un archivo MP3. ID3v2, sin embargo, ms de un desafo de interpretar que la versin 1 de etiquetas. En este captulo, vamos a usar la biblioteca de datos binarios de anlisis del captulo anterior para desarrollar un cdigo que puede leer y escribir las etiquetas ID3v2. O por lo menos usted hacer un esfuerzo razonable empezar - que era demasiado simple ID3v1, ID3v2 es de estilo barroco hasta el punto de ser completamente overengineered. Implementacin de todos los rincones de la especificacin, sobre todo si quieres apoyar a las tres versiones que se han especificado, sera una buena cantidad de trabajo. Sin embargo, usted puede pasar por alto muchas de las caractersticas de estas especificaciones ya que estn poco utilizadas "en la

naturaleza." Para empezar, usted puede pasar por alto, por ahora, una versin de su conjunto, 2,4, ya que no ha sido ampliamente adoptado y en su mayora slo aade ms flexibilidad innecesaria en comparacin con la versin 2.3. Me centrar en las versiones 2.2 y 2.3 ya que ambos son ampliamente utilizados y son lo suficientemente diferentes entre s para mantener las cosas interesantes. Estructura de un Tag ID3v2 Antes de que pueda empezar a cortar el cdigo, tendr que estar familiarizado con la estructura general de una etiqueta ID3v2. Una etiqueta comienza con un encabezado que contiene informacin sobre la etiqueta como un todo. Los tres primeros bytes de la cabecera de codificar la cadena "ID3" en la norma ISO-8859-1 caracteres. En otras palabras, que son los bytes 73, 68 y 51. A continuacin, se produce dos bytes que codifican la versin principal y revisin de la especificacin ID3 la etiqueta a la que pretende conformarse. Estn seguidos por un solo byte cuyos bits individuales son tratados como banderas. El significado de las banderas individuales dependen de la versin de la especificacin. Algunas de las banderas puede afectar la forma en que se analiza el resto de la etiqueta. La "versin principal" se utiliza realmente para grabar la versin de menor importancia de la especificacin, mientras que la "revisin" es la versin de la especificacin subminor. Por lo tanto, la "versin principal" campo de una etiqueta conforme a la especificacin 2.3.0 es 3. El campo de la revisin es siempre cero, ya que cada nueva especificacin ID3v2 ha topado con la versin menor, dejando la versin subminor en cero. El valor almacenado en el campo de la versin principal de la etiqueta tiene, como veremos, un efecto dramtico en cmo va a analizar el resto de la etiqueta. El ltimo campo en el encabezado de etiqueta es un nmero entero, codificado en cuatro bytes, pero utilizando slo siete bits de cada byte, que da el tamao total de la etiqueta, sin contar el encabezado. En la versin 2.3 etiquetas, el encabezado puede ser seguido por varios de cabecera extendidos campos, de lo contrario, el resto de la etiqueta de datos se divide en fotogramas . Los diferentes tipos de marcos de almacenar diferentes tipos de informacin, desde la simple informacin textual, como el nombre de la cancin, que las imgenes incrustadas. Cada marco comienza con un encabezado que contiene un identificador de cadena y un tamao. En la versin 2.3, la cabecera de la trama tambin contiene dos bytes por valor de

banderas y, en funcin del valor de una de las banderas, una opcin de un byte de cdigo que indica cmo el resto de la trama est encriptado. Los marcos son un ejemplo perfecto de una estructura de datos etiquetado - para saber cmo analizar el cuerpo de un marco, es necesario leer el encabezado y utilizar el identificador para determinar qu tipo de marco que est leyendo. El encabezado de la etiqueta ID3 no contiene ninguna indicacin directa del nmero de fotogramas que se encuentran en una etiqueta - la cabecera de etiqueta que indica qu tan grande es la etiqueta, pero es desde muchos cuadros son de longitud variable, la nica manera de saber cuntos cuadros contiene la etiqueta para leer los datos del bastidor. Tambin, el tamao dado en el encabezado de etiqueta puede ser mayor que el nmero real de bytes de datos del marco; los marcos puede ser seguido con suficientes bytes nulos a almohadilla de la etiqueta a cabo con el tamao especificado. Esto hace que sea posible para editores de etiquetas para modificar una etiqueta sin tener que reescribir el archivo MP3 conjunto. 2 Por lo tanto, las principales cuestiones que tienen que lidiar con estas leyendo el encabezado ID3, para determinar si usted est leyendo la versin 2.2 o etiqueta de 2,3, y la lectura de los datos de imagen, ya sea deteniendo, cuando usted ha ledo la etiqueta completa o cuando se ha golpe los bytes de relleno. Definicin de un paquete Al igual que las otras bibliotecas que hemos desarrollado hasta ahora, el cdigo que voy a escribir en este captulo, vale la pena poner en su propio paquete. Usted tendr que referirse a las funciones tanto de los datos binarios y las bibliotecas de nombre de ruta desarrollados en los captulos 24 y 15 y tambin se desea exportar los nombres de las funciones que componen la API pblica de este paquete. La definicin del paquete siguiente hace todo lo que:
(Defpackage: com.gigamonkeys.id3v2 (: Uso: common-lisp : Com.gigamonkeys.binary-datos : Com.gigamonkeys.pathnames) (: Las exportaciones : Lectura id3 :-P mp3 : ID3-p : lbum : El compositor : El gnero : Programa de codificacin

: : : : : : :

Artista Somos parte de la serie Realizar un seguimiento de La cancin Ao Tamao Traducido-gnero))

Como de costumbre, usted puede, y probablemente debera, cambiar el com.gigamonkeys parte del nombre del paquete a su propio dominio. Tipos enteros Puede comenzar por definir los tipos de binarios para leer y escribir varios de los tipos primitivos utilizados por el formato ID3, varios tamaos de enteros sin signo, y cuatro tipos de cadenas. ID3 utiliza enteros sin signo codificados en una, dos, tres y cuatro bytes. Si primero escribir un general sin signo de enteros de tipo binario que toma el nmero de bytes a leer como un argumento, a continuacin, puede utilizar la forma corta de definir-de tipo binario para definir los tipos especficos. El general-sin firmar nmero entero de tipo se parece a esto:
(Define-de tipo binario sin signo enteros (bytes) (: Lector de (in) (Bucle con el valor = 0 para personas de bajos bits downfrom (* 8 (1 - bytes)) a 0 en un 8 por hacer (Setf (LDB (de 8 bytes bajos bits)) (lectura de bytes en)) por ltimo, (valor de retorno))) (: Escritor (con valor) (Bucle de bajos bits downfrom (* 8 (1 - bytes)) a 0 en un 8 por hacer (escribir byte (LDB (de 8 bytes bajos bits)) a))))

Ahora usted puede utilizar la forma abreviada de definir-de tipo binario para definir un tipo para cada tamao de entero que se usa en el formato ID3 de esta manera:
(Define-de tipo binario u1 (Define-de tipo binario u2 (Define-binaria de tipo U3 (Define-U4 de tipo binario () () () () (sin (sin (sin (sin firmar-entero: firmar-entero: firmar-entero: firmar-entero: 1 bytes)) bytes 2)) 3 bytes)) 4 bytes))

Otro tipo tendr que ser capaz de leer y escribir es el valor de 28 bits utilizado en el encabezado. Este tamao se codifica con 28 bits en lugar de un mltiplo de 8, como de 32 bits, porque la etiqueta ID3 no puede contener el byte # xff seguido por un byte con los primeros 3 bits encendidos, ya que el patrn tiene un significado especial para MP3 decodificadores. Ninguno de los otros campos en el encabezado ID3 podra contener una secuencia de bytes, pero si el tamao de la etiqueta

codificada en forma regularsin firmar-nmero entero , que podra hacerlo. Para evitar esa posibilidad, el tamao se codifica utilizando solamente el fondo siete bits de cada byte, con el bit superior siempre cero. 3 Por lo tanto, se puede leer y escribir se parece mucho a un nmero entero sin signo, a excepcin del tamao de la especificacin de bytes que se pasa a LDB debe ser siete en vez de ocho. Esta similitud sugiere que si se aade un parmetro, bits por byte , a la ya existente -sin firmar nmero entero de tipo binario, que podra definir un nuevo tipo, ID3-TAG de tamao , mediante un formulario corto definir-de tipo binario . La nueva versin de signo-nmero entero es igual que la versin anterior, excepto con bits por byte usado en todas partes de la antigua versin cableada el nmero ocho. Se parece a esto:
(Define-de tipo binario sin signo-entero (bytes bits por byte) (: Lector de (in) (Bucle con el valor = 0 para personas de bajos bits downfrom (* bits por byte (1 - bytes)) a 0 bits por byte de hacer (Setf (LDB (byte bits por byte de bits)) (lectura de bytes en)) por ltimo, (valor de retorno))) (: Escritor (con valor) (Bucle de bajos bits downfrom (* bits por byte (1 - bytes)) a 0 bits por byte hacer (escribir byte (LDB (byte bits por byte de bits)) a))))

La definicin de ID3-TAG de tamao es entonces trivial.


(Define-binaria de tipo ID3-TAG-size () (sin firmar-entero: 4 bytes: bits por byte 7))

Tambin tendr que cambiar las definiciones de U1 a travs U4 para especificar ocho bits por byte de esta manera:
(Define-de tipo binario u1 (Define-de tipo binario u2 (Define-binaria de tipo U3 (Define-U4 de tipo binario () () () () (sin (sin (sin (sin firmar-entero: firmar-entero: firmar-entero: firmar-entero: bytes 1: bytes 2: bytes 3: 4 bytes: bits bits bits bits por por por por byte byte byte byte 8)) 8)) 8)) 8))

Tipos de cadenas de Los otros tipos de tipos primitivos que son ubicuas en el formato ID3 son cadenas. En el captulo anterior habl de algunas de las cuestiones que hay que considerar cuando se trata de cadenas en los archivos binarios, tales como la diferencia entre los cdigos de caracteres y codificaciones de caracteres. ID3 utiliza dos cdigos de caracteres diferentes, ISO 8859-1 y Unicode. ISO 8859-1, tambin conocido como Latin-1, es un cdigo de caracteres de ocho bits que se extiende ASCII con caracteres utilizados por las lenguas de Europa Occidental. En

otras palabras, los puntos de cdigo 0 a 127 el mapa a los mismos caracteres en ASCII y ISO 8859-1, 8859-1 ISO, pero tambin proporciona las asignaciones de puntos de cdigo hasta 255. Unicode es un cdigo de caracteres diseados para proporcionar un punto de cdigo para el personaje de casi todos los de todas las lenguas del mundo. Unicode es un superconjunto de la norma ISO 8859-1 de la misma manera que la norma ISO 8859-1 es un superconjunto de ASCII - los puntos de cdigo 0 a 255 el mapa a los mismos personajes, tanto en la norma ISO 8859-1 y Unicode. (Por lo tanto, Unicode tambin es un superconjunto de ASCII.) Dado que la norma ISO 8859-1 es un cdigo de caracteres de ocho bits, se codifica con un byte por cada carcter. Para las cadenas Unicode, ID3 utiliza la codificacin UCS-2 con una de las principales marcas de orden de bytes . 4 voy a discutir lo que es una marca de orden de bytes se encuentra en un momento. Lectura y escritura de estas dos codificaciones no es un problema - es slo una cuestin de leer y escribir nmeros enteros sin signo en varios formatos, y que acaba de terminar de escribir el cdigo para hacer eso. El truco es cmo traducir esos valores numricos a los objetos de carcter Lisp. La implementacin de Lisp que est utilizando probablemente usa Unicode o ISO 8859-1, como su cdigo de carcter interno. Y puesto que todos los valores de 0-255 mapa a los mismos personajes, tanto en la norma ISO 8859-1 y Unicode, puede usar Lisp CODE-CHAR y CHAR CODE- funciones para traducir esos valores en los cdigos de caracteres de ambos. Sin embargo, si su Lisp slo es compatible con la norma ISO 8859-1, entonces usted ser capaz de representar slo los primeros 255 caracteres Unicode como caracteres Lisp.En otras palabras, de una implementacin de Lisp, si intenta procesar una etiqueta ID3 que usa cadenas Unicode y si alguna de esas cadenas que contienen caracteres con puntos de cdigo superiores a 255, obtendr un error al tratar de traducir el cdigo apuntan a un carcter Lisp. Por ahora voy a asumir ya sea que usted est usando un Lisp basado en Unicode o no procesar todos los archivos que contienen caracteres fuera del rango de la norma ISO 8859-1. El otro problema con las cadenas de codificacin es la forma de saber cuntos bytes de interpretar como datos de caracteres. ID3 utiliza dos estrategias que mencion en el captulo anterior-algunas cadenas se terminan con un carcter nulo, mientras que otras cadenas se producen en los puestos donde se puede determinar el

nmero de bytes a leer, ya sea porque la cadena en esa posicin es siempre la misma longitud o porque la cadena est en el extremo de una estructura de material compuesto cuya global tamao que usted sabe. Tenga en cuenta, sin embargo, que el nmero de bytes no es necesariamente el mismo que el nmero de caracteres en la cadena. Poniendo todas estas variaciones en conjunto, el formato ID3 utiliza cuatro maneras de leer y escribir cadenas, dos personajes cruzaron con dos formas de delimitar los datos de cadena. Obviamente, gran parte de la lgica de la lectura y la escritura cuerdas ser bastante similar. Por lo tanto, usted puede comenzar por la definicin de dos tipos de binarios, uno para la lectura de las cadenas de una longitud especfica (en caracteres) y otro para la lectura de cadenas terminadas. Ambos tipos de tomar ventaja de que el argumento de tipo devalor de lectura y escritura de valor es ms que otra pieza de informacin, usted puede hacer el tipo de carcter de leer un parmetro de este tipo. Esta es una tcnica que vamos a usar unas cuantas veces en este captulo.
(Define-binary-tipo genrico de cadena (longitud de caracteres de tipo) (: Lector de (in) (Let ((string (cadena de longitud maquillaje))) (Dotimes (longitud i) (Setf (cadena de caracteres i) (lectura-valor de carcter de tipo de))) cadena)) (: Escritor (con cadena) (Dotimes (longitud i) (Write-valor de carcter de tipo de salida (cadena de caracteres i))))) (Define-binary-tipo genrico de terminacin de cadena (terminador de tipo carcter) (: Lector de (in) (Con salida a cadena (s) (Bucle de char = (lectura de valor de carcter de tipo de) hasta que (char = char terminador) hacer (write-char char s)))) (: Escritor (con cadena) (Bucle de caracteres en cadena hacer (write-valor de carcter de tipo de caracteres) por ltimo, (write-valor de carcter de tipo de terminador))))

Con este tipo disponible, no hay mucho que leer ISO 8859-1 cuerdas. Debido a que el carcter del tipo de argumento que se pasa a leer-el valor y laescritura el valor de una cadena genrica, debe ser el nombre de un tipo binario, es necesario definir un iso-8859-1-char de tipo binario. Esto tambin le da un buen lugar para poner un poco de cordura en la comprobacin de los puntos de cdigo de caracteres que se leen y escriben.

(Define-binaria de tipo iso-8859-1-char () (: Lector de (in) (Let ((cdigo (lectura de bytes en))) (O (cdigo-char el cdigo) (Error de "cdigo de carcter ~ d no es compatible" cdigo)))) (: Escritor (a char) (Let ((cdigo (char-char code))) (If (<= 0 # cdigo xff) (Escribir el cdigo de bytes) (Error de "carcter ilegal de la norma ISO-8859-1: Carcter: ~ c con el cdigo: ~ d" el cdigo de caracter)))))

Ahora la definicin de las normas ISO 8859-1 tipos de cadenas es trivial utilizando la forma abreviada de definir-de tipo binario de la siguiente manera:
(Define-binaria de tipo iso-8859-1-cadena (longitud) (Genrico de cadena: longitud longitud: el carcter de tipo "iso-8859-1-char)) (Define-binaria de tipo iso-8859-1-terminada en-cadena (terminador) (Genrica terminada en-cadena: Terminator Terminator: el carcter de tipo "iso8859-1-char))

Lectura de UCS-2 cadenas es slo un poco ms complejo. La complejidad surge porque se puede codificar un punto de UCS-2 del cdigo de dos maneras: byte ms significativo primero (big-endian) o byte menos significativo primero (little-endian). UCS-2 cuerdas por lo tanto, comenzar con dos bytes adicionales, llamados de la marca de orden de byte , formado por el valor numrico # xfeff codificada, ya sea en big-endian tipo o forma little-endian. Al leer un UCS-2 de cuerdas, de leer la marca de orden de bytes y, a continuacin, dependiendo de su valor, ya sea leer caracteres big-endian o little-endian. Por lo tanto, tendr dos diferentes UCS-2 tipos de caracteres. Sin embargo, slo se necesita una versin del cdigo de comprobacin de la cordura, por lo que se puede definir un tipo binario parmetros de esta manera:
(Define-binaria de tipo UCS-2-char (de intercambio) (: Lector de (in) (Let ((Cdigo (U2 lectura de valor "en el))) (Cuando el intercambio (setf cdigo (swap-bytes de cdigo))) (O (cdigo-char el cdigo) (error "cdigo de carcter ~ d no es compatible" cdigo)))) (: Escritor (a char) (Let ((cdigo (char-char code))) (A menos que (<= 0 # cdigo xffff) (Error de "carcter ilegal de codificacin UCS-2: ~ c con char-cdigo: ~ d" el cdigo de caracter)) (Cuando el intercambio (setf cdigo (swap-bytes de cdigo))) (Write-valor 'a U2 el cdigo))))

donde el intercambio de bytes funcin se puede definir de la siguiente manera, tomando ventaja de la LDB es SETF poder y por lo tanto ROTATEF capaz de:
(Defun intercambiar los bytes (cdigo)

(Assert (<= cdigo # xffff)) (Rotatef (LDB (de 8 bytes 0) cdigo) (LDB (de 8 bytes 8) Cdigo)) cdigo)

Uso de UCS-2-carbn , se pueden definir dos tipos de caracteres que se utilizarn como las de tipo carcter argumentos a las funciones de cadena genricos.
(Define-binaria de tipo UCS-2-ca-big-endian () (UCS-2-char: nula swap)) (Define-binaria de tipo UCS-2-ca-little-endian () (UCS-2-char: intercambio t))

Luego hay una funcin que devuelve el nombre del tipo de carcter que se utilizar en funcin del valor de la marca de orden de bytes.
(Defun UCS-2-ca-tipo (de orden de bytes de marcas) (Ecase de orden de bytes de marcas (# Xfeff 'UCS-2-ca-big-endian) (# Xfffe 'UCS-2-ca-little-endian)))

Ahora puede definir la longitud y el terminador-delimitados por los tipos de cadena de UCS-2-cadenas codificadas que se lea la marca de orden de bytes y lo utilizan para determinar qu variante de carcter UCS-2 para pasar como el carcter del tipo de argumento para lectura de valor y escritura de valor . La arruga nico es que se necesita para traducir la longitud del argumento, que es un nmero de bytes, el nmero de caracteres a leer, lo que representa para la marca de orden de bytes.
(Define-binaria de tipo UCS-2-cadena (longitud) (: Lector de (in) (Let ((orden de bytes de marcas (U2 lectura de valor "en el)) (Personajes (1 - (/ longitud 2)))) (Lectura de valor "Genrico de cadena de : Los caracteres de longitud : Tipo de carcter (UCS-2-caracteres de tipo de orden de bytes de marcas)))) (: Escritor (con cadena) (Write-valor 'a U2 # xfeff) (Escritura de valor "Genrico de cadena de cadena de : Longitud (cadena de longitud) : Tipo de carcter (UCS-2-caracteres de tipo # xfeff)))) (Define-binaria de tipo UCS-2-terminado-cadena (terminador) (: Lector de (in) (Let ((orden de bytes de marcas (U2 lectura de valor "en el))) (Lectura de valor Genrica terminada en-cadena en : Terminator Terminator : Tipo de carcter (UCS-2-caracteres de tipo de orden de bytes de marcas)))) (: Escritor (con cadena) (Write-valor 'a U2 # xfeff) (Escritura de valor Genrica terminada en-cadena a cadena : Terminator Terminator : Tipo de carcter (UCS-2-caracteres de tipo # xfeff))))

ID3 Tag Header Con los tipos primitivos bsicos hecho, ya est listo para cambiar a una vista de alto nivel y empezar a definir las clases de binarios para representar primero la etiqueta ID3 en su conjunto y, a continuacin los cuadros individuales. Si en primer lugar el pliego de ID3v2.2, vers que la estructura bsica de la etiqueta es la siguiente cabecera:
Identificador ID3/file "ID3" ID3 versin de US $ 02 00 ID3 banderas% XX000000 ID3 tamao 4 *% 0xxxxxxx

seguido por los datos del marco y el relleno. Como ya ha definido los tipos de binarios a leer y escribir todos los campos en la cabecera, la definicin de una clase que puede leer el encabezado de la etiqueta ID3 es slo una cuestin de ponerlos juntos.
(Define-binary-clase de ID3-TAG () ((Identificador (iso-8859-1-cadena: longitud de 3)) (Versin principal U1) (Revisin U1) (Banderas u1) (Tamao de ID3-TAG-size)))

Si usted tiene algunos archivos MP3 por ah, puede probar esta gran parte del cdigo y tambin ver qu versin de las etiquetas ID3 de tus MP3 contienen. En primer lugar usted puede escribir una funcin que lee un ID3-TAG , ya que se acaba de definir, desde el principio de un archivo. Tenga en cuenta, sin embargo, que las etiquetas ID3 no estn obenlazados a aparecer al principio de un archivo, aunque en estos das casi siempre lo hacen. Para encontrar la etiqueta ID3 en otras partes de un archivo, puede escanear el archivo en busca de la secuencia de bytes 73, 68, 51 (en otras palabras, la cadena "ID3"). 5 Por ahora probablemente pueda salirse con la suya asumiendo las etiquetas son la primera cosa en el archivo.
(Defun lectura ID3 (archivo) (Con-de archivos abiertos (en el archivo: el elemento de tipo "(unsigned byte 8)) (Lectura de valor "ID3-TAG en)))

En la parte superior de esta funcin se puede construir una funcin que toma un nombre de archivo e imprime la informacin en el encabezado de etiqueta junto con el nombre del archivo.
(Defun show-etiquetas de cabecera (archivo) (Con ranuras (identificador de versin principal de revisin tamao de las banderas) (lectura ID3 del archivo) (Formato t "~ a del ~ D ~ d ~ 8, '0 B ~ d bytes -. ~ A ~%"

identificador de revisin a la versin banderas de tamao (lo suficiente-namecadena archivo))))

Imprime la salida que tiene este aspecto:


ID3V2> (se presenta en etiquetas de cabecera "/ usr2/mp3/Kitka/Wintersongs/02 Cesta.mp3 Byla") ID3 2.0 00000000 2165 bytes - Kitka/Wintersongs/02 Byla Cesta.mp3 NIL

Por supuesto, para determinar las versiones de ID3 son ms comunes en la biblioteca de MP3, que sera ms prctico tener una funcin que devuelve un resumen de todos los archivos MP3 en un directorio determinado. Usted puede escribir una con bastante facilidad utilizando el directorio de pie- funcin definida en el captulo 15. En primer lugar definir una funcin auxiliar que evala si un nombre de archivo dado tiene un mp3 de extensin.
(Defun mp3-p (archivo) (Y (No (directorio-ruta-p archivo)) (Cadena-igual "mp3" (ruta de acceso del tipo de archivo))))

A continuacin, usted puede combinar espectculo-tag-cabecera y p-mp3 con el piedirectorio para imprimir un resumen de la cabecera ID3 de cada archivo en un directorio determinado.
(Defun show-tag-encabezados (dir) (Walk-dir # 'show-tag-encabezado: test #' mp3-p))

Sin embargo, si usted tiene un montn de archivos MP3, puede que slo quieren un recuento de la cantidad de etiquetas ID3 de cada versin que tienes en tu coleccin de MP3. Para obtener esta informacin, puede escribir una funcin como esta:
(Defun contar las versiones (dir) (Let (((versiones mapcar # '(lambda (x) (x contras 0))' (2 3 4)))) (FLET ((nmero de versin (archivo) (Incf (cdr (assoc (versin principal (lectura ID3 del archivo)) las versiones))))) (Walk-dir # 'recuento de la versin: test #' mp3-p)) versiones))

Otra de las funciones que necesita en el captulo 29 es una que comprueba si un archivo en realidad comienza con la etiqueta ID3, que se puede definir as:
(Defun id3-p (archivo) (Con-de archivos abiertos (en el archivo: el elemento de tipo "(unsigned byte 8)) (String = "id3" (lase valor "iso-8859-1-cadena: longitud 3))))

ID3 Marcos Como ya he comentado anteriormente, la mayor parte de la etiqueta ID3 se divide en cuadros. Cada marco tiene una estructura similar a la de la etiqueta como un todo. Cada marco comienza con un encabezado que indica qu tipo de marco que es y el tamao del fotograma en bytes. La estructura de la cabecera de la trama cambi ligeramente de la versin 2.2 y la versin 2.3 del formato ID3, y, finalmente, tendr que hacer frente a las dos formas. Para empezar, usted puede centrarse en analizar la versin 2.2 marcos. El encabezado de un marco 2,2 consta de tres bytes que codifican un tres-de caracteres ISO 8859-1 cadena seguida de un entero sin signo de tres bytes, que especifica el tamao del marco en bytes, excluidos el encabezado de seis bytes. La cadena identifica el tipo de trama que es, lo que determina la forma de analizar los datos siguientes del tamao. Este es exactamente el tipo de situacin para la que ha definido la definicin de etiquetado-binary-clase de macro. Se puede definir una clase de etiqueta que lee el encabezado de la trama y luego transmite a la clase apropiada de hormign con una funcin que se asigna a un ID de nombres de clase.
(Define-etiquetados-binary-clase de id3-marco () ((Identificacin (iso-8859-1-cadena: longitud de 3)) (Tamao del u3)) (: Envo (encontrar-frame-la clase de id)))

Ahora ya ests listo para comenzar a implementar clases concretas del marco. Sin embargo, la especificacin define un buen nmero - 63 en la versin 2.2 y an ms en las especificaciones posteriores. Incluso teniendo en cuenta los tipos de trama que comparten una estructura comn que es equivalente, igualmente encontrar 24 tipos de marcos nicos en la versin 2.2. Pero slo unos pocos de estos son usados "en la naturaleza." As que en lugar de ponerse a trabajar de inmediato la definicin de clases para cada uno de los tipos de tramas, usted puede comenzar a escribir una clase de marco genrico que le permite leer los fotogramas de una etiqueta sin necesidad de analizar los datos dentro de los propios marcos.Esto le dar una forma de descubrir lo que los marcos estn realmente presentes en los archivos MP3 que desea procesar. Usted necesitar esta clase al final de todos modos porque la especificacin permite marcos experimentales que usted necesitar para ser capaz de leer sin analizar. Desde el campo de tamao de la cabecera de la trama le dice exactamente cuntos bytes de largo es el marco, se puede definir un marco genrico de la clase que se

extiendeid3-marco y aade un solo campo, los datos , que contendr una matriz de bytes.
(Define-binary-clase genrica-marco (ID3-marco) ((Los datos (primas-bytes: el tamao el tamao))))

El tipo del campo de datos, crudos-bytes , slo tiene que contener una matriz de bytes. Se puede definir as:
(Define-binaria de tipo prima-bytes (tamao) (: Lector de (in) (Let ((buf (make-matriz de tamao: elemento de tipo '(sin signo de 8 bytes-)))) (Lectura de la secuencia buf en) buf)) (: Escritor (a buf) (Escritura de la secuencia a cabo buf)))

Por el momento, tendr que todas las tramas que se lea como marco genrico de s, por lo que puede definir el marco de encontrar-de la clase utilizada en funcin de id3-frame s ' : envo de expresin para que siempre regrese genrica-marco , con independencia de el marco de Identificacin .
(Defun encontrar-frame-clase (id) (Declare (ignorar id)) "Genrico-frame)

Ahora es necesario modificar ID3-TAG por lo que voy a leer despus de los marcos de los campos de cabecera. Slo hay un poco difcil de leer los datos de la base: a pesar de la cabecera de etiqueta que indica cuntos bytes de largo es la etiqueta, ese nmero se incluye el relleno que puede seguir los datos de la base. Desde la cabecera de la etiqueta no dice cuntos cuadros contiene la etiqueta, la nica manera de saber cuando se ha golpeado el relleno es la bsqueda de un byte nulo en lo que espera un identificador de marco. Para controlar esto, se puede definir un tipo binario, id3-marcos , que ser responsable de leer el resto de una etiqueta, la creacin de objetos de marco para representar a todos los fotogramas que encuentra, y luego saltar por encima de cualquier relleno. Este tipo se tomar como parmetro el tamao de la etiqueta, que puede utilizar para evitar la lectura ms all del final de la etiqueta. Pero el cdigo de la lectura tambin tendr que detectar el comienzo del relleno que puede seguirlos partidos de datos de la etiqueta de marco. En lugar de llamarlectura el valor directamente en el ID3-marcos : lector , usted debe utilizar una funcin de marco de lectura , que va a definir para volver NIL cuando se detecta el relleno, de lo contrario devolver un id3-marco objeto de leer con valor de lectura . Suponiendo

que se define el marco de lectura por lo que slo lee un byte ms all del final del ltimo fotograma con el fin de detectar el inicio del relleno, se puede definir el ID3frames tipo binario de esta manera:
(Define-binaria de tipo ID3-frames (etiqueta de tamao) (: Lector de (in) (Bucle con leer la etiqueta de tamao = mientras que (plusp de leer) de frame = (lectura en el marco) Mientras el marco de hacer (DECF de leer (+ 6 (tamao))) recopilar marco por ltimo, (repeticin de bucle (1 - leer) hacer (leer bytes en)))) (: Escritor (marcos de salida) (Bucle con a = escribir la etiqueta de tamao para marco en marcos hacer (write-valor 'id3-marco con marco) (DECF a escribir (+ 6 (tamao))) por ltimo, (repeticin de bucle a escribir hacer (write-byte 0 a cabo)))))

Puede utilizar este tipo para agregar un marco a la ranura de ID3-TAG .


(Define-binary-clase de ID3-TAG () ((Identificador (iso-8859-1-cadena: longitud de 3)) (Versin principal U1) (Revisin U1) (Banderas u1) (Tamao de ID3-TAG-size) (Marcos (marcos: id3-etiqueta de tamao de tamao))))

La deteccin de relleno de etiquetas Ahora lo nico que queda es poner en prctica el marco de lectura . Esto es un poco complicado ya que el cdigo que realmente lee bytes desde el stream es de varias capas por debajo de lectura marco . Lo que realmente me gustara hacer en el marco de lectura se lee un byte y devolver NIL si se trata de un nulo y otra leer un cuadro con valor de lectura .Lamentablemente, si usted lee el byte en el marco de lectura , entonces no estar disponible para ser ledo por valor de lectura . 6 Resulta que esta es una oportunidad perfecta para usar el sistema de estado - puede comprobar los bytes nulos en el cdigo de bajo nivel que lee de la corriente y la seal de una condicin cuando se lee un valor nulo; marco de lectura puede tratar la condicin de por la anulacin de la pila antes de que ms bytes se leen. Adems de girar hacia fuera a ser una solucin ordenada al problema de detectar el inicio de relleno de la etiqueta, esto es tambin un ejemplo de cmo puede utilizar condiciones para fines distintos de manejo de errores.

Puedes empezar por definir un tipo de condicin a ser sealada por el cdigo de bajo nivel y manejada por el cdigo de alto nivel. Esta condicin no tiene por qu cualquiera de las ranuras - slo tiene una clase distinta de la condicin para que usted sepa ningn otro cdigo ser de sealizacin o de su manipulacin.
(Definir condicin en el relleno () ())

A continuacin, debe definir un tipo binario cuyos : lector lee un nmero determinado de bytes, en primer lugar la lectura de un solo byte y sealizacin de un relleno encondiciones si el byte es nula y de lo contrario la lectura de los bytes restantes como iso-8859-1-cadena y combinndolo con el primer byte ledo.
(Define-binary-tipo de trama-id (longitud) (: Lector de (in) (Let ((primer byte (byte de lectura))) (Cuando (= primer byte 0) (seal "en el relleno)) (Let ((resto (lectura de valor "iso-8859-1-cadena: longitud (1 - longitud)))) (Concatenar 'Cadena (cadena (cdigo-char primer byte)) el resto)))) (: Escritor (con ID) (Write-valor "iso-8859-1-cadena a partir Identificacin: longitud longitud)))

Si se redefine id3-marco para hacer el tipo de su Identificacin ranura marco de Identificacin del lugar de la norma ISO-8859-1-secuencia , la condicin se indicar siempre id3-frame 's de lectura de valor mtodo lee un byte nulo en lugar de al principio de un marco.
(Define-etiquetados-binary-clase de id3-marco () ((Identificacin del (marco-id: longitud 3)) (Tamao del u3)) (: Envo (encontrar-frame-la clase de id)))

Ahora, todo el marco de lectura tiene que hacer es envolver un llamado a la lectura de valor en un MANIPULADOR-CASE que maneja el relleno encondiciones de volver NIL .
(Defun marco de lectura (en) (Manejador de los casos (lase valor 'id3-marco en el) (En el relleno () nil)))

Con marco de lectura definida, ahora se puede leer una versin completa de etiquetas ID3 2.2, lo que representa marcos con las instancias de genricos-marco . En la seccin "Marcos Es usted realmente necesita?" seccin, que va a hacer algunos experimentos en el REPL para determinar qu clases de marco que necesita para poner en prctica.Pero primero vamos a aadir soporte para la versin 2.3 las etiquetas ID3.

El apoyo de varias versiones de ID3 En la actualidad, ID3-TAG se define utilizando define-binary-clase , pero si quieres apoyar a varias versiones de ID3, tiene ms sentido usar unadefinicin de etiquetado-binary-clase que distribuye en la versin principal- valor. Como resultado, todas las versiones de ID3v2 tienen la misma estructura hasta el campo de tamao. Por lo tanto, se puede definir una clase binaria etiquetado como el siguiente que define esta estructura bsica y luego transmite a la versin especfica de su caso subclase:
(Define-etiquetados-binary-clase de ID3-TAG () ((Identificador (iso-8859-1-cadena: longitud de 3)) (Versin principal U1) (Revisin U1) (Banderas u1) (Tamao de ID3-TAG-size)) (: Despacho (Ecase versin principal (2 'id3v2.2-tag) (3 'ID3v2.3-tag))))

La versin 2.2 y las etiquetas de la versin 2.3 se diferencian en dos maneras. En primer lugar, la cabecera de una etiqueta de la versin 2.3 puede ampliarse con un mximo de cuatro campos opcionales encabezado extendido, segn lo determinado por los valores en el campo de las banderas. En segundo lugar, el formato de trama ha cambiado entre la versin 2.2 y la versin 2.3, lo que significa que tendr que utilizar diferentes clases para representar a la versin 2.2 fotogramas y la versin 2.3 cuadros correspondientes. Puesto que el nuevo ID3-TAG en clase se basa en la que escribi originalmente para representar a la versin 2.2 las etiquetas, no es de extraar que el nuevoid3v2.2etiqueta de la clase es trivial, que hereda la mayora de sus ranuras de la nueva ID3TAG en la clase y la adicin de la ranura de una falta, los marcos .Debido a que la versin 2.2 y la versin 2.3 etiquetas utilizan distintos formatos de marco, usted tendr que cambiar el ID3-frames tipo que se parametriza con el tipo de trama para leer. Por ahora, se supone que va a hacer eso y agregar una : estructura de tipo argumento de la id3 marcos descriptor de tipos como este:
(Define-binary-clase id3v2.2-etiqueta (ID3-TAG) ((Marcos (marcos: id3-tag-size: tamao del marco de tipo 'id3v2.2-marco))))

El ID3v2.3-etiqueta de clase es un poco ms compleja debido a los campos opcionales. Los primeros tres de los cuatro campos opcionales se incluyen cuando el sexto bit enlas banderas se establece. Son un nmero entero de cuatro bytes que

especifica el tamao de la cabecera extendida, por valor de dos bytes de banderas, y otro entero de cuatro bytes que especifica cuntos bytes de relleno se incluyen en la etiqueta. 7 El cuarto campo opcional, incluido cuando el decimoquinto bits de las banderas de cabecera extendidos est establecido, es una comprobacin cclica de cuatro bytes de redundancia (CRC) del resto de la etiqueta. La biblioteca de datos binarios no proporciona ningn tipo de apoyo especial para los campos opcionales en una clase binaria, pero resulta que los tipos de parmetros binarios regulares son suficientes. Puede definir un tipo parametrizado con el nombre de un tipo y un valor que indica si un valor de ese tipo en realidad debera ser ledo o escrito.
(Define-de tipo binario opcional (tipo si) (: Lector de (in) (Cuando si (lectura en el valor de tipo))) (: Escritor (con valor) (Cuando if (write-valor de tipo de valor))))

Uso de si el nombre del parmetro se parece un poco extrao en ese cdigo, pero hace que las opcionales descriptores de tipo muy legible. Por ejemplo, aqu est la definicin deID3v2.3-tag con opcionales ranuras:
(Define-binary-ID3v2.3 clase de etiqueta (ID3-TAG) ((Extended-header-size (opcional: u4 tipo ': if (extended-p banderas))) (Extra-flags (opcional: u2 tipo ': if (extended-p banderas))) (Relleno de tamao (opcional: u4 tipo ': if (extended-p banderas))) (CRC (opcional: u4 tipo ': si (CRC-p banderas extra-flags))) (Marcos (marcos: id3-tag-size: tamao del marco de tipo 'ID3v2.3-marco))))

, donde p prolongada y CRC-p son funciones de ayuda que ponen a prueba el bit apropiado del valor de los indicadores que estn pasado. Para probar si un bit individual de un nmero entero que se encuentra, puede utilizar LOGBITP , otra funcin de bits haciendo girar. Se necesita un ndice y un entero y devuelve true si el bit especificado se encuentra en el nmero entero.
(Defun extendida-p (banderas) (logbitp 6 banderas)) (Defun CRC-p (banderas extra-banderas) (Y (extended-p banderas) (15 logbitp extra-banderas)))

Al igual que en la clase versin etiqueta 2,2, la ranura de fotogramas se define para ser de tipo id3-frames , pasando el nombre de el tipo de trama como un parmetro. Usted, sin embargo, que hacer unos pequeos cambios a los marcos de id3- y leermarco para apoyar el extra de tipo marco de parmetros.
(Define-binaria de tipo ID3-frames (etiqueta de tamao de tipo de trama) (: Lector de (in)

(Bucle con leer la etiqueta de tamao = mientras que (plusp de leer) de frame = (lase marco de tipo de trama en el) Mientras el marco de hacer (DECF de leer (+ (marco-header-tamao del fotograma) (tamao))) recopilar marco por ltimo, (repeticin de bucle (1 - leer) hacer (leer bytes en)))) (: Escritor (marcos de salida) (Bucle con a = escribir la etiqueta de tamao para marco en marcos hacer (escribir un valor de tipo de trama a trama) (DECF a escribir (+ (marco-header-tamao del fotograma) (tamao))) por ltimo, (repeticin de bucle a escribir hacer (write-byte 0 a cabo))))) (Defun lectura del marco (frame-escribe) (Manejador de los casos (lase valor de tipo de trama en el) (En el relleno () nil)))

Los cambios estn en las llamadas a la lectura y el marco y la escritura de valor , donde usted necesita para pasar el tipo de trama y argumento, en el clculo del tamao del marco, donde es necesario utilizar una funcin de marco de encabezado de tamao en lugar de la valor literal 6 desde la cabecera de la trama cambia de tamao entre la versin 2.2 y la versin 2.3. Puesto que la diferencia en el resultado de esta funcin se basa en la clase del marco, que tiene sentido lo definen como una funcin genrica como esta:
(Defgeneric marco de encabezado de tamao (frame))

Vas a definir los mtodos necesarios en funcin genrica que en la prxima seccin despus de definir las clases de nuevo marco. Diferentes versiones de las clases de la base Base Donde antes se ha definido una clase base nica para todos los marcos, que ahora tendr dos clases, id3v2.2-marco y ID3v2.3 marco . El id3v2.2-marco clase ser esencialmente el mismo que el original id3-marco clase.
(Define-etiquetados-binary-clase id3v2.2-marco () ((Identificacin del (marco-id: longitud 3)) (Tamao del u3)) (: Envo (encontrar-frame-la clase de id)))

El ID3v2.3-marco , por otro lado, requiere ms cambios. El identificador de cuadro y tamao de los campos se extendieron en la versin 2.3 de tres a cuatro bytes cada uno, y dos bytes por valor de banderas se han aadido. Adems, el marco, al igual que la etiqueta de la versin 2.3, puede contener campos opcionales, controlados por los valores de tres de las banderas del marco. 8 Con estos cambios en mente,

usted puede definir la versin 2.3 de la clase base del marco, junto con algunas funciones auxiliares, de esta manera:
(Define-etiquetados-binary-clase ID3v2.3-marco () ((Identificacin del (marco-id: Longitud 4)) (U4 tamao) (Banderas u2) (Descomprimido-size (opcional: u4 tipo ': if (fotograma comprimido banderas p))) (Cifrado rgimen (opcional: tipo 'u1: si (estructura-cifrada-p banderas))) (Agrupacin de identidad (opcional: tipo 'u1: si (marco-agrupados-p banderas)))) (: Envo (encontrar-frame-la clase de id))) (Defun marco comprimido-p (banderas) (logbitp 7 banderas)) (Defun marco cifrada-p (banderas) (logbitp 6 banderas)) (Defun marco agrupan-p (banderas) (logbitp 5 banderas))

Con estas dos clases definidas, ahora se puede poner en prctica los mtodos de la funcin genrica marco de encabezado de tamao .
(Defmethod marco-header-size ((marco id3v2.2-marco)) 6) (Defmethod marco-header-size ((marco ID3v2.3-marco)) 10)

Los campos opcionales en un marco de la versin 2.3 no se cuentan como parte de la cabecera de este clculo, puesto que ya est incluido en el valor de la estructura de tamao . Diferentes versiones de las clases del marco de hormign En la definicin original, genrico marco de una subclase id3-marco . Pero ahora id3-marco ha sido sustituido por las dos clases de base especfica de la versin, losid3v2.2-marco y ID3v2.3 marco . Por lo tanto, es necesario definir dos nuevas versiones de genricos-marco , uno para cada clase de base. Una forma de definir estas clases sera as:
(Define-binary-clase genrica-frame-v2.2 (id3v2.2-frame) ((Los datos (primas-bytes: el tamao el tamao)))) (Define-binary-clase genrica-frame-v2.3 (ID3v2.3-frame) ((Los datos (primas-bytes: el tamao el tamao))))

Sin embargo, es un poco molesto que estas dos clases son iguales a excepcin de su superclase. No es tan malo en este caso, ya que slo hay un campo adicional. Pero si usted toma este enfoque a otras clases de estructura de hormign, aquellos que tienen una estructura interna ms compleja que es idntico entre las dos versiones de ID3, la duplicacin ser ms molesto.

Otro enfoque, y el uno que realmente debe utilizar, es definir una clase genricamarco como un mixin : una clase destinada a ser utilizada como un superclase junto con una de las clases base especficas versin-para producir un concreto, especfico de la versin marco de la clase. La nica parte difcil de este enfoque es que si genrica-marco no se aplica ninguna de las clases de estructura de base, entonces usted no puede hacer referencia al tamao de la ranura en su definicin. En su lugar, debe utilizar laactual-binary-objeto de la funcin que se discuti al final del captulo anterior para acceder al objeto que est en medio de la lectura o la escritura y la pasan al tamao .Y usted necesita para dar cuenta de la diferencia en el nmero de bytes de el tamao del marco total que se sobraron, en el caso de una versin 2,3 del marco, si cualquiera de los campos opcionales estn incluidos en el marco. Por lo tanto, debe definir una funcin genrica de datos de bytes con los mtodos que hacen lo correcto, tanto para la versin 2.2 y la versin 2.3 cuadros.
(Define-binary-clase genrica-marco () (((Datos en bruto-bytes: el tamao (bytes de datos (actual-binary-objeto)))))) (Defgeneric de datos-bytes (marco)) (Defmethod de datos-bytes ((marco id3v2.2-marco)) (Tamao)) (Defmethod de datos-bytes ((marco ID3v2.3-marco)) (Let (((banderas banderas marco))) (- (Tamao) (If (fotograma comprimido p banderas) 4 0) (If (marco-cifrado-p banderas) 1 0) (If (frame-agrupados-p banderas) 1 0))))

A continuacin, puede definir las clases de hormign que se extienden a una de las clases base y especficos de la versin genrica de marco para definir la versin clases especficas de marcos genricos.
(Define-binary-clase genrica-frame-v2.2 (id3v2.2-marco genrico-marco) ()) (Define-binary-clase genrica-frame-v2.3 (ID3v2.3-marco genrico-marco) ())

Con estas clases definidas, se puede redefinir el marco de encontrar-clase para devolver la clase correcta versionado basado en la longitud del identificador.
(Defun encontrar-frame-clase (id) (Ecase (Identificacin de longitud) (3 'genrica-frame-v2.2) (4 'genrica-frame-v2.3)))

Lo que Marcos Es usted realmente necesita? Con la capacidad de leer tanto la versin 2.2 y la versin 2.3 las etiquetas que utilizan marcos genricos, ya est listo para comenzar las clases de ejecucin que representan los cuadros especficos que le interesan. Sin embargo, antes de empezar a bucear, usted debe tomar un respiro y averiguar lo que los marcos que realmente importa, ya que, como he mencionado antes, la especificacin ID3 especifica muchos cuadros que casi nunca se utilizan. Por supuesto, lo que usted se preocupa por los marcos depende de qu tipo de aplicaciones que est interesado en escribir. Si usted est interesado principalmente en la extraccin de informacin de las etiquetas ID3 existentes, entonces usted necesita implementar slo las clases que representan a los marcos que contienen la informacin que te interesa. Por otro lado, si usted quiere escribir un editor de etiquetas ID3, puede ser necesario para apoyar a todos los marcos. En vez de adivinar qu imgenes sern de gran utilidad, puede utilizar el cdigo que ya ha escrito a hurgar un poco en el REPL y ver lo que las tramas se utiliza realmente en su propio MP3s. Para empezar, se necesita una instancia de ID3-TAG , que se puede obtener con la lectura ID3 funcin.
ID3V2> (lectura id3 "/ usr2/mp3/Kitka/Wintersongs/02 Byla Cesta.mp3") # @ <ID3V2.2-TAG #x727b2912>

Puesto que usted va a querer jugar con este objeto un poco, debe guardarlo en una variable.
ID3V2> (defparameter * id3 * (lectura id3 "/ usr2/mp3/Kitka/Wintersongs/02 Byla Cesta.mp3")) * ID3 *

Ahora usted puede ver, por ejemplo, cuntos cuadros que tiene.
ID3V2> (longitud (marcos * id3 *)) 11

No muchos - vamos a echar un vistazo a lo que son.


ID3V2> (marcos * id3 *) (# # #x72dabdda> <GENERIC-FRAME-V2.2 @ # # #x72dabfa2> <GENERIC-FRAME-V2.2 @ # # #x72dac16a> <GENERIC-FRAME-V2.2 @ # # #x72dac32a> <GENERIC-FRAME-V2.2 @ # # #x72dac4f2> <GENERIC-FRAME-V2.2 @ # @ <GENERIC-FRAME-V2.2 #x72dac7b2>) @ @ @ @ @ <GENERIC-FRAME-V2.2 <GENERIC-FRAME-V2.2 <GENERIC-FRAME-V2.2 <GENERIC-FRAME-V2.2 <GENERIC-FRAME-V2.2 #x72dabec2> #x72dac08a> #x72dac24a> #x72dac40a> #x72dac632>

Bueno, eso no es muy informativo. Lo que realmente quiero saber es qu tipo de cuadros estn ah. En otras palabras, usted quiere saber el ID s de esos marcos, que se puede obtener con un simple MAPCAR de esta manera:
ID3V2> (Identificacin del mapcar # '(marcos * id3 *)) ("TT2" "TP1" por "TAL", "TRK" "TPA" "tye" "TCO" "TEN" "COM", "COM", "com")

Si usted mira para arriba estos identificadores en la especificacin ID3v2.2, usted descubrir que todas las tramas con identificadores que comienzan con T son los marcos de texto de la informacin y tienen una estructura similar. Y COM es el identificador para los marcos de comentario, que tienen una estructura similar a la de tramas de informacin de texto. Los marcos de texto en particular la informacin en que aqu resultan ser los marcos para representar el ttulo de la cancin, artista, lbum, pista, parte de la serie, ao, gnero, y el programa de codificacin. Por supuesto, esto es slo un archivo MP3. Tal vez otros cuadros se utilizan en otros archivos. Es bastante fcil de descubrir. En primer lugar definir una funcin que combina la anterior MAPCAR expresin con una llamada a la lectura ID3 y envuelve todo el asunto en un DELETE-DUPLICADOS para mantener las cosas ordenadas. Vas a tener que utilizar una : prueba el argumento de la cadena # '= a BORRAR-DUPLICADOS para especificar que desea que dos elementos que se consideran la misma si son la misma cadena.
(Defun marco de tipos (archivo) (Delete-duplicados ('id (marcos de lectura (ID3 del archivo))): test #' # mapcar cadena =))

Esto debera dar la misma respuesta, salvo que slo uno de cada cdigo de identificacin cuando se pasa el mismo nombre.
ID3V2> (estructura de tipo "/ usr2/mp3/Kitka/Wintersongs/02 Cesta.mp3 Byla") ("TT2" "TP1" por "TAL", "TRK" "TPA" "tye" "TCO" "TEN" "COM")

A continuacin, puede usar el Captulo 15 del paseo de directorio , junto con la funcin mp3-p para encontrar todos los archivos MP3 en un directorio y combinar los resultados de llamar a los tipos de marcos en cada archivo. Recordemos que Nenlace es la versin de reciclaje de la enlace funcin, ya que los tipos de marcohace una nueva lista para cada archivo, esto es seguro.
(Defun marco de tipos-en-dir (dir) (Let ((identificadores ())) (FLET ((recoger (archivo) (Setf IDS (nenlace IDS (marcos tipo de archivos): cadena de prueba # '=))))

(Walk-dir # "recoger: test # 'mp3-p)) ids))

Ahora bien, pasar el nombre de un directorio, y le dir el conjunto de identificadores que se utilizan en todos los archivos MP3 en el directorio. Puede tardar unos segundos dependiendo de la cantidad de archivos MP3 que tengas, pero probablemente obtendr algo similar a esto:
ID3V2> (frame-tipos-de-dir "/ usr2/mp3 /") ("TCON" "COMM" "TRCK de la" "TIT2" "TPE1" "TALB" "TCP", "TT2" "TP1" por "TCM" "TAL", "TRK" "TPA" "tye" "TCO" "TEN" "COM")

Los identificadores de cuatro letras son la versin 2.3 equivalentes de la versin 2.2 los identificadores que habl anteriormente. Dado que la informacin almacenada en esos marcos es exactamente la informacin que necesita en el captulo 27, tiene sentido para poner en prctica las clases slo para los cuadros realmente utilizados, a saber, la informacin de texto y marcos de comentario, que usted har en los prximos dos secciones . Si ms adelante decide que desea apoyar otro tipo de marco, que es sobre todo una cuestin de la traduccin de las especificaciones ID3 en las definiciones de las clases apropiadas binarios. Marcos de texto de informacin Todas las tramas de informacin de texto consisten en dos campos: un solo byte que indica que la codificacin cadena se utiliza en el marco y una cadena codificada en los bytes restantes del marco. Si el byte de codificacin es igual a cero, la cadena est codificada en la norma ISO 8859-1, y si la codificacin es una parte, la cadena es una cadena de UCS-2. Usted ya ha definido los tipos de binarios que representan los cuatro tipos diferentes de cadenas, dos codificaciones diferentes cada uno con dos diferentes mtodos de delimitacin de la cadena. Sin embargo, definir-binario-clase no proporciona ninguna instalacin directa para la determinacin del tipo de valor para leer sobre la base de otros valores en el objeto. En su lugar, se puede definir un tipo binario que se pasa el valor del byte de codificacin y que luego lee o escribe el tipo adecuado de la cadena. Mientras ests definiendo este tipo, tambin se puede definir a tomar dos parmetros, : longitud y : terminador , y escoger el tipo correcto de la cadena a partir del cual se suministra el argumento. Para implementar este nuevo tipo,

primero debe definir una serie de funciones auxiliares. Los dos primeros devolver el nombre del tipo de cadena apropiado basado en el byte de codificacin.
(Defun no terminada-tipo (codificacin) (Ecase codificacin (0 "iso-8859-1-cadena) (1 'UCS-2-cadena))) (Defun terminada-tipo (codificacin) (Ecase codificacin (0 "iso-8859-1-terminada en-cadena) (1 'UCS-2-terminado-string)))

Luego de cadena args utiliza la codificacin de bytes, la longitud, y el terminador para determinar algunos de los argumentos que se pasarn al valor de lectura yescritura de valor por el : lector de e : escritor de ID3 codificada cuerdas . Uno de los argumentos de longitud y de terminacin decadena de argumentos debe ser siempre NIL .
(Defun cadena args (codificacin de longitud de terminacin) (Cond (Longitud (Los valores (no terminada-tipo de codificacin): longitud de longitud)) (Terminador (Los valores (terminado en el tipo de codificacin): Terminator Terminator))))

Con los ayudantes, la definicin de id3-codificado-secuencia es simple. Un detalle a tener en cuenta es que la palabra clave - ya sea : la longitud o: Terminator - usado en la llamada a la lectura y el valor y el valor de escritura es un elemento ms de los datos devueltos por cadena args . Aunque las palabras clave en las listas de argumentos casi siempre son literales palabras, no tiene que ser.
(Define-de tipo binario codificado id3 cuerdas (codificacin de longitud de terminacin) (: Lector de (in) (Multiple-value-bind (arg palabra clave type) (Cadena-args codificacin de longitud de terminacin) (Lectura en el valor del tipo de palabra arg))) (: Escritor (con cadena) (Multiple-value-bind (arg palabra clave type) (Cadena-args codificacin de longitud de terminacin) (Escritura-el tipo de valor a la palabra clave de cadena arg))))

Ahora se puede definir un texto de informacin de clase mixin, tanto la forma en que se define el marco genrico antes.
(Define-binary-clase text-info-marco () ((Codificacin U1) (La informacin (ID3 codificada-cadena: codificacin de codificacin: longitud (bytes a la izquierda-1)))))

Al igual que cuando se define el marco genrico , es necesario acceder al tamao del marco, en este caso, para calcular el : longitud ha de pasar a laID3 codificada cuerdas . Debido a que usted tendr que hacer un clculo similar en la prxima clase se define, se puede seguir adelante y definir una funcin auxiliar,bytesizquierda , que utiliza corriente binaria-objeto de llegar al tamao del marco.
(Defun bytes a la izquierda (en bytes a leer) (- Tamao ((actual-binary-objeto)) bytes de lectura))

Ahora bien, como lo hizo con el genrico marco de mixin, se pueden definir dos clases especficas de la versin de concreto con un mnimo de cdigo duplicado.
(Define-binary-clase text-info-frame-v2.2 (id3v2.2-marco de texto-info-marco) ()) (Define-binary-clase text-info-frame-v2.3 (ID3v2.3-marco de texto-info-marco) ())

Para conectar estas clases, usted necesita modificar encontrar-frame-clase para devolver el nombre de la clase apropiada cuando la ID indica que el marco es un marco de informacin de texto, es decir, siempre que el ID empieza con T y no es TXX o TXXX .
(Defun encontrar-frame-clase (nombre) (Cond ((Y (char = (char nombre 0) # \ T) (No (nombre de usuario "(" TXX "" TXXX "): test # 'cadena =))) (Ecase (nombre largo) (3 'text-info-frame-v2.2) (4 'text-info-frame-v2.3))) (T (Ecase (nombre largo) (3 'genrica-frame-v2.2) (4 'genrica-frame-v2.3)))))

Opina Marcos Otro tipo de marco utilizado es el marco de comentario, que es como un marco de informacin de texto con algunos campos adicionales. Al igual que un marco de informacin de texto, se inicia con un solo byte que indica la codificacin de cadena que se utiliza en el marco. Eso byte es seguido por un tres-de caracteres ISO 8859-1 cadena (independientemente del valor del byte de codificacin de cadena), que indica qu idioma el comentario es en el uso de un cdigo ISO-639-2, por ejemplo, "eng" para Ingls o "jpn" para los japoneses. Ese campo es seguido por dos cadenas codificadas como se indica por el primer byte. El primero es una cadena terminada en null que contiene una descripcin del comentario. La segunda, que ocupa el resto del marco, es el texto del comentario en s.

(Define-binary-clase de comentario-marco () ((Codificacin U1) (Idioma (ISO-8859-1-cadena: longitud de 3)) (Descripcin (ID3-codificado-cadena: codificacin de codificacin: terminador nulo + +)) (Texto (ID3 codificada cuerdas : Codificacin de : Longitud (bytes-izquierda (+ 1; codificacin 3, el lenguaje (Codificado-serie-longitud de la descripcin de codificacin t)))))))

Al igual que en la definicin del texto-info mixin, puede utilizar octetos-izquierda para calcular el tamao de la cadena final. Sin embargo, desde la descripcin de campo es una cadena de longitud variable, el nmero de bytes ledos antes del comienzo de texto no es una constante. Para empeorar las cosas, el nmero de bytes utilizados para codificar la descripcin depende de la codificacin. Por lo tanto, debe definir una funcin auxiliar que devuelve el nmero de bytes utilizados para codificar una cadena determinada de la cadena, el cdigo de codificacin, y un booleano que indica si la cadena se termina con un personaje extra.
(Defun codificado-string-length (cadena de codificacin terminado) (Let ((caracteres (+ (longitud de la cadena) (si es terminado 1 0)))) (* Caracteres (ecase codificacin (0 1) (1 2)))))

Y, como antes, se puede definir la especfica de la versin concreta del marco clases de comentarios y conectarlos a encontrar-frame-clase .
(Define-binary-clase de comentario-frame-v2.2 (id3v2.2 marco comment-marco) ()) (Define-binary-clase de comentario-frame-v2.3 (ID3v2.3 marco comment-marco) ()) (Defun encontrar-frame-clase (nombre) (Cond ((Y (char = (char nombre 0) # \ T) (No (nombre de usuario "(" TXX "" TXXX "): test # 'cadena =))) (Ecase (nombre largo) (3 'text-info-frame-v2.2) (4 'text-info-frame-v2.3))) (Cadena (= el nombre de "COM") 'comentario-frame-v2.2) (Cadena (= el nombre de "COMM") 'comentario-frame-v2.3) (T (Ecase (nombre largo) (3 'genrica-frame-v2.2) (4 'genrica-frame-v2.3)))))

Extraer informacin de una etiqueta ID3 Ahora que tienes la habilidad bsica de leer y escribir etiquetas ID3, tiene una gran cantidad de direcciones que podra tomar este cdigo. Si se quiere desarrollar un completo editor de etiquetas ID3, que ser necesario implementar clases especficas

para todos los tipos de trama. Tambin haba necesidad de definir los mtodos para la manipulacin de la etiqueta y los objetos del marco de una manera coherente (por ejemplo, si cambia el valor de una cadena en un texto-informacin-marco , es probable que tenga que ajustar el tamao); ya que el cdigo actual, no hay nada para asegurarse de que esto ocurra. 9 O, si usted slo tiene que extraer ciertas piezas de informacin acerca de un archivo MP3 de la etiqueta ID3 - lo que quieras cuando se desarrolla un servidor de streaming de MP3 en los captulos 27, 28 y 29 - usted necesita para escribir las funciones que encontrar los cuadros correspondientes y extraer la informacin que desea. Por ltimo, para hacer de este cdigo de produccin de calidad, que tendra que estudiar minuciosamente las especificaciones de ID3 y tratar con los detalles que saltan sobre el inters del espacio. En particular, algunas de las banderas, tanto en la etiqueta y el cuadro puede afectar la forma en el contenido de la etiqueta o en un marco que se lee, a menos que escribir algo de cdigo que hace lo correcto cuando las etiquetas estn definidas, puede haber etiquetas ID3 este cdigo no ser capaz de analizar correctamente. Sin embargo, el cdigo de este captulo debe ser capaz de analizar casi todos los archivos MP3 que usted realmente encontrar. Por ahora se puede terminar con unas pocas funciones para extraer las piezas individuales de informacin de un ID3-TAG . Usted necesitar estas funciones en el captulo 27 y, probablemente, en otro cdigo que utiliza esta biblioteca. Pertenecen a esta biblioteca, ya que dependen de los detalles del formato ID3 que los usuarios de esta biblioteca no tiene por qu preocuparse. Para obtener, por ejemplo, el nombre de la cancin del MP3 de la que un ID3-TAG se extrajo, es necesario encontrar el marco ID3 con un identificador especfico y luego extraer el campo de la informacin. Y algunas piezas de informacin, tales como el gnero, pueden requerir ms de decodificacin. Por suerte, todos los marcos que contienen la informacin que importa son los marcos de texto de informacin, por lo que la extraccin de una pieza particular de informacin sobre todo se reduce a utilizar el identificador de la derecha para buscar el marco adecuado. Por supuesto, las ID3 autores decidieron cambiar todos los identificadores entre ID3v2.2 y ID3v2.3, as que tendr que dar cuenta de eso.

Nada demasiado complejo - slo tiene que averiguar el camino correcto para llegar a las diferentes piezas de informacin. Esto es un poco perfecta de cdigo para desarrollar de forma interactiva, tanto la forma en que descubri lo que las clases del marco que necesitaba para poner en prctica. Para empezar, se necesita un ID3TAG objeto para jugar. Asumiendo que tiene un MP3 por ah, puede utilizar lectura id3 de esta manera:
ID3V2> (defparameter * id3 * (lectura id3 "Kitka/Wintersongs/02 Byla Cesta.mp3")) * ID3 * ID3V2> * ID3 * # @ <ID3V2.2-TAG #x73d04c1a>

sustitucin de Kitka/Wintersongs/02 Cesta.mp3 Byla con el nombre de su MP3. Una vez que tenga su ID3-TAG objeto, se puede empezar a jugar. Por ejemplo, usted puede comprobar fuera de la lista de objetos con el marco marcos de la funcin.
ID3V2> (marcos * id3 *) (# @ <TEXT-INFO-FRAME-V2.2 #x73d04cca> # @ <TEXT-INFO-FRAME-V2.2 #x73d04dba> # @ <TEXT-INFO-FRAME-V2.2 #x73d04ea2> # @ <TEXT-INFO-FRAME-V2.2 #x73d04f9a> # @ <TEXT-INFO-FRAME-V2.2 #x73d05082> # @ <TEXT-INFO-FRAME-V2.2 #x73d0516a> # @ <TEXT-INFO-FRAME-V2.2 #x73d05252> # @ <TEXT-INFO-FRAME-V2.2 #x73d0533a> # @ <COMMENT-FRAME-V2.2 #x73d0543a> # @ <COMMENT-FRAME-V2.2 #x73d05612> # @ <COMMENT-FRAME-V2.2 #x73d0586a>)

Ahora suponga que desea extraer el ttulo de la cancin. Es probablemente en uno de esos marcos, pero para encontrarlo, es necesario encontrar el marco de la "TT2" identificador.Bueno, se puede comprobar fcilmente lo suficiente como para ver si la etiqueta contiene una trama mediante la extraccin de todos los identificadores de esta manera:
ID3V2> (Identificacin del mapcar # '(marcos * id3 *)) ("TT2" "TP1" por "TAL", "TRK" "TPA" "tye" "TCO" "TEN" "COM", "COM", "com")

Ah est, el primer fotograma. Sin embargo, no hay garanta de que siempre ser el primer fotograma, por lo que probablemente debera mirar hacia arriba en lugar de por el identificador de posicin. Eso tambin es sencillo utilizando el ENCONTRAR funcin.
ID3V2> (find "TT2" (marcos * id3 *): 'cadena =: tecla # "# prueba de Identificacin) # @ <TEXT-INFO-FRAME-V2.2 #x73d04cca>

Ahora, para obtener la informacin real en el marco, haga lo siguiente:


ID3V2> (de la informacin (buscar "TT2" (marcos * id3 *): 'cadena =: tecla # "test # id))

"Byla Cesta ^ @"

Epa. Eso ^ @ es como Emacs imprime un carcter nulo. En una maniobra que recuerda a este truco que se convirti en el ID3v1 ID3v1.1, la informacin de la ranura de un marco de informacin de texto, aunque no oficialmente una cadena terminada en cero, puede contener un valor nulo, y los lectores ID3 se supone hacer caso omiso de todos los caracteres despus de la nula . Por lo tanto, se necesita una funcin que toma una cadena y devuelve el contenido hasta el primer carcter nulo, si las hubiere. Eso es muy fcil usando el+ + nula constante de la biblioteca de datos binarios.
(Defun hasta nulo (cadena) (Subseq cadena de 0 (posicin + + cadena nula)))

Ahora usted puede conseguir el ttulo.


ID3V2> (hasta nulo (la informacin (buscar "TT2" (marcos * id3 *): 'cadena =: tecla # "test # id))) "Byla Cesta"

Se poda concluir que el cdigo en una funcin denominada cancin que tiene un ID3-TAG como un argumento, y que te har. Sin embargo, la nica diferencia entre este cdigo y el cdigo que va a utilizar para extraer las otras piezas de informacin que usted necesita (como el nombre del lbum, el artista y el gnero) es el identificador. Por lo tanto, es mejor dividir el cdigo un poco. Para empezar, usted puede escribir una funcin que slo encuentra un marco dado un ID3-TAG y un identificador de esta manera:
(Defun encontrar-marco (id3 id) (Encontrar Identificacin (marcos ID3): 'cadena =: tecla # "test # id)) ID3V2> (find-marco * id3 * "TT2") # @ <TEXT-INFO-FRAME-V2.2 #x73d04cca>

A continuacin, el otro fragmento de cdigo, la parte que se extrae la informacin de un texto-informacin-marco , pueden ir en otra funcin.
(Defun get-text-info (Identificacin ID3) (Let ((marco (frame-encontrar Identificacin ID3))) (Cuando marco (hasta nulo (estructura de la informacin))))) ID3V2> (get-text-info * id3 * "TT2") "Byla Cesta"

Ahora la definicin de la cancin es slo una cuestin de pasar el identificador de la derecha.


(Defun cancin (ID3) (get-text-info id3 "TT2"))

ID3V2> (cancin * id3 *) "Byla Cesta"

Sin embargo, esta definicin de la cancin slo funciona con la versin 2.2 las etiquetas ya que el identificador cambiado de "TT2" a "TIT2" entre la versin 2.2 y la versin 2.3. Y todas las otras etiquetas tambin ha cambiado. Dado que el usuario de esta biblioteca no tiene que saber acerca de las diferentes versiones del formato ID3 para hacer algo tan simple como obtener el ttulo de la cancin, probablemente debera manejar esos detalles para ellos. Una forma sencilla es cambiar de marco encontramos a tomar no slo un identificador nico, sino una lista de identificadores de esta manera:
(Defun encontrar-frame (ID3 IDS) (Encontrar-si # '(lambda (x) (Buscar (id x) ids: test #' cadena =)) (marcos ID3)))

A continuacin, cambie obtener informacin-texto- algo por lo que puede tomar uno o ms identificadores utilizando una y resto de parmetros.
(Defun obtener informacin-texto (ID3 y resto IDS) (Let ((marco (frame-encontrar los identificadores ID3))) (Cuando marco (hasta nulo (estructura de la informacin)))))

Entonces el cambio que se necesita para permitir que la cancin para apoyar tanto a la versin 2.2 y la versin 2.3 las etiquetas es slo una cuestin de aadir el identificador de la versin 2.3.
(Defun cancin (ID3) (get-text-info id3 "TT2" "TIT2"))

Entonces slo tiene que buscar la versin apropiada 2.2 y la versin 2.3 del marco de los identificadores de los campos para los que quieren ofrecer una funcin de acceso. stos son los que necesitar en el captulo 27:
(Defun lbum (ID3) (get-text-info id3 "TAL" "TALB")) (Artista defun (ID3) (get-text-info id3 "TP1" por "TPE1")) (Defun pista (ID3) (get-text-info id3 "TRK" "elemento TRCK")) (Defun aos (ID3) (get-text-info id3 "tye" "TYER de" "TDRC")) (Gnero defun (ID3) (get-text-info id3 "TCO" "TCON"))

La arruga ltima es que la forma en que el gnero se almacena en los marcos de TCO o TCON no siempre es legible. Recordemos que en ID3v1, los gneros se almacena como un solo byte que codifica un gnero particular de una lista fija. Desafortunadamente, los cdigos de vivir en ID3v2 - si el texto del marco de gnero es un nmero entre parntesis, el nmero se supone que debe ser interpretada

como un cdigo de gnero ID3v1. Pero, de nuevo, los usuarios de esta biblioteca, probablemente no le importar que la historia antigua.Por lo tanto, usted debe proporcionar una funcin que traduce automticamente el gnero. La funcin siguiente se utiliza el gnero de la funcin se acaba de definir para extraer el texto de gnero real y luego comprueba si comienza con un parntesis de apertura, la decodificacin de la versin de un cdigo de gnero con una funcin que va a definir en un momento si lo hace:
(Defun traducida-gnero (ID3) (Let ((gnero (gnero de id3))) (Si (y el gnero (char = # \ ((gnero de caracteres 0))) (Traducir-v1-gnero de gnero) gnero)))

Desde una versin un cdigo de gnero es en efecto slo un ndice en una matriz de nombres estndar, la forma ms fcil de implementar traducir-v1-gnero es la de extraer el nmero de la cadena de gnero y lo utilizan como un ndice en una matriz real.
(Gnero traducir defun-v1-gnero () (* Los Aref ID3-V1-gneros * (anlisis sintctico-entero gnero: inicio 1: basura permite t)))

Entonces todo lo que tiene que hacer es definir el conjunto de nombres. La siguiente matriz de nombres incluye la versin oficial 1 80 gneros, adems de los gneros creados por los autores de Winamp:
Defparameter (* ID3-V1-gneros * # ( ;; Estos son los oficiales ID3v1 gneros. "Blues", "Classic Rock", "Pas" "Dance", "Disco", "Funk", "Grunge" "Hip-Hop", "Jazz" "Metal", "New Age", "Oldies", "Otros", "Pop", "R & B", "Rap" "Reggae" "Rock", "Techno", "Industrial", "Alternativa", "Ska" "Death Metal", "Travesuras", "Banda sonora", "Euro-Techno", "ambiente" "Trip-Hop", "Vocal", "Jazz + Funk", "Fusion", "Trance", "Clsico" "Instrumental", "Acid", "Casa", "Juego", "Sound Clip", "Evangelio", "Ruido" "AlternRock", "Bajo", "Alma", "Punk", "Espacio", "meditativo" "Pop Instrumental", "Rock Instrumental", "tnico" "Gothic", "Darkwave" "Techno-Industrial", "Electrnica", "Pop-Folk", "eurodance" "Dream" "Southern Rock", "Comedia", "Cult", "Gangsta", "Top 40", "Rap cristiano" "Pop / Funk", "Jungle", "nativos americanos", "Cabaret", "New Wave" "Psychadelic" "Rave", "Showtunes", "Tragedia", "Lo-Fi", "Tribal" "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll" "Hard Rock" ;; Estos fueron compuestos por los autores de Winamp, pero portado en ;; La especificacin ID3. "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion" "Bebob" "Amrica", "Revival", "Celtic", "Bluegrass", "Avantgarde" "Gothic Rock" "Rock Progresivo", "Psychedelic Rock" "Rock Sinfnico" "Slow Rock", "Big Band", "Coro", "Easy Listening", "Acstico", "Humor" "Discurso", "Chanson", "Opera", "Msica de Cmara", "Sonata", "Symphony"

"Booty Bass", "Primus", "Porn Groove", "Stira" "Slow Jam", "Club" "Tango", "Samba", "Folklore", "Balada", "power ballad", "Alma Rtmica" "Freestyle", "Duet", "Punk Rock", "Drum Solo", "a capella", "Euro-House" "Dance Hall" ;; Estos tambin fueron inventados por la gente de Winamp, pero ignorado por el ;; ID3 autores. "Goa", "Drum & Bass", "Club-House", "Hardcore", "Terror", "independiente" "Britpop" "Negerpunk" "Punk Polsk" "Beat", "Gangsta Rap cristiano" "Heavy Metal" "Metal Negro", "Crossover", "msica cristiana contempornea" "Christian Rock", "Merengue", "Salsa", "Thrash Metal", "Anime", "J-Pop" "Synthpop"))

Una vez ms, es probable que se siente como que escribi un montn de cdigo de este captulo. Pero si se pone todo en un archivo, o si se descarga la versin desde el sitio Web de este libro, vers que no es justo que muchas lneas, la mayora de los dolores de escribir esta biblioteca se deriva de tener que entender las complejidades de la el formato ID3 s mismo.De todos modos, ahora usted tiene una pieza importante de lo que te convertirs en un servidor de streaming de MP3 en los captulos 27, 28 y 29. La otra parte importante de la infraestructura que usted necesita es una manera de escribir del lado del servidor web de software, el tema del siguiente captulo.

1 Ripear es el proceso mediante el cual se convierte en una cancin en un CD de audio a un archivo

MP3 en su disco duro. En estos das la mayora de software de extraccin tambin se recupera automticamente la informacin sobre las canciones que se arrancaron de las bases de datos en lnea, como Gracenote (apellido de soltera de la base de datos de Compact Disc [CDDB]) o FreeDB, que luego se incorpora en los archivos de MP3 como etiquetas ID3.
2 Casi todos los sistemas de archivos ofrecen la posibilidad de sobrescribir bytes existentes de un

archivo, pero pocos, en su caso, proporcionar una manera de agregar o quitar datos al comienzo o en medio de un archivo sin tener que volver a escribir el resto del archivo. Dado que las etiquetas ID3 se suelen almacenar en el comienzo de un archivo, para volver a escribir una etiqueta ID3 sin alterar el resto del archivo que debe sustituir la etiqueta antigua con una nueva etiqueta de exactamente la misma longitud. Al escribir las etiquetas ID3 con una cierta cantidad de relleno, tiene una mejor oportunidad de ser capaz de hacerlo - si la nueva etiqueta tiene ms datos que los de la etiqueta original, se utiliza menos el relleno, y si es ms corto, se utiliza ms.
3 Los datos de la base despus de la cabecera ID3 tambin podra contener la secuencia ilegal. Eso ha

impedido utilizar un esquema diferente que se enciende mediante una de las banderas en la cabecera de la etiqueta. El cdigo de este captulo no tiene en cuenta la posibilidad de que este indicador se podra establecer, en la prctica rara vez se utiliza.
4 En ID3v2.4, UCS-2 se sustituye por el prcticamente idntico UTF-16 y UTF-16BE y UTF-8 se agregan

como codificaciones adicionales.

5 La versin 2,4 del formato ID3 tambin admite la colocacin de un pie de pgina al final de una

etiqueta, que le hace ms fcil para encontrar una etiqueta anexa al final de un archivo.
6 secuencias de caracteres soportan dos funciones, CHAR PEEK y LEDO CHAR- , ya sea de lo que

sera una solucin perfecta a este problema, pero secuencias binarias apoyar a ninguna de las funciones equivalentes.
7 Si una etiqueta tena un encabezado extendido, se puede utilizar este valor para determinar dnde

estn los datos de la base debe terminar. Sin embargo, si el encabezado extendido no se utiliza, usted tendra que usar el viejo algoritmo de todos modos, por lo que no vale la pena agregar el cdigo para hacerlo de otra manera.
8 Estas banderas, adems de controlar si los campos opcionales estn incluidos, puede afectar el

anlisis sintctico del resto de la etiqueta. En particular, si el sptimo bit de las banderas se establece, entonces los datos de fotogramas real est comprimido utilizando el algoritmo zlib, y si el sexto bit est establecido, los datos estn cifrados. En la prctica, estas opciones son raramente, si acaso, que se utiliza, por lo que puede salirse con la suya haciendo caso omiso de ellas por ahora. Pero eso sera una zona en la que tendra que dirigirse a hacer de esto una calidad de produccin ID3 biblioteca. Una solucin sencilla sera un medio para cambiar encontrar marco de la clase a aceptar un segundo argumento y pasar las banderas, y si el marco es comprimido o cifrado, se puede crear instancias de un marco genrico para almacenar los datos.
9 Asegurar que tipo de consistencia entre los campos sera una aplicacin de multa por : despus de

los mtodos de las funciones genricas de acceso. Por ejemplo, se podra definir esta : despus demtodo para mantener el tamao en sincrona con la informacin de cadena:
(Defmethod (setf la informacin): despus de (valor (caja de texto-info-marco)) (Declare (ignorar el valor)) (Con ranuras (informacin de codificacin de tamao) el marco (Tamao setf (codificado cuerdas de longitud nula codificacin de la informacin))))

26. Programacin Web con AllegroServe: Prctica


En este captulo veremos una forma de desarrollar programas basados en Web en Common Lisp, utilizando el cdigo abierto AllegroServe servidor Web. Esto no pretende ser una introduccin completa a AllegroServe. Y no estoy seguro que va a cubrir algo ms que un pequeo rincn del tema ms amplio de la programacin Web. Mi objetivo aqu es suficiente para cubrir los aspectos bsicos del uso AllegroServe que usted ser capaz de, en el captulo 29, para desarrollar una aplicacin para navegar por una coleccin de archivos MP3 y streaming a un cliente de MP3. Del mismo modo, este captulo servir como una breve introduccin a la programacin web para gente nueva con el tema. Una introduccin de 30 segundos a la programacin Web del lado del servidor Mientras que la programacin Web hoy en da suele implicar un gran nmero de estructuras de software y protocolos diferentes, los pedacitos de base de la programacin web no han cambiado mucho desde que se inventaron en la dcada de 1990. Para aplicaciones sencillas, como el que usted escribe en el captulo 29, es necesario comprender slo unos pocos conceptos clave, as que voy a revisar rpidamente aqu. Los programadores experimentados web podemos pasar por alto o pasar el resto de esta seccin. 1 Para empezar, es necesario comprender las funciones del navegador de Internet y el juego en el servidor Web de programacin web. Mientras que un navegador moderno viene con un montn de campanas y silbatos, la funcionalidad bsica de un navegador de Internet para solicitar las pginas Web de un servidor web y luego hacer ellos. Generalmente, las pginas se escribirn en el idioma de marcado de hipertexto (HTML), que indica al navegador cmo mostrar la pgina, incluyendo dnde insertar las imgenes en lnea y enlaces a otras pginas Web. HTML se compone de texto marcado con etiquetas que le dan al texto una estructura que utiliza el navegador al representar la pgina. Por ejemplo, un documento HTML simple sera el siguiente:
<html> <head> <title> Hola </ title> </ Head> <body> <p> Hola, mundo! </ p> <p> Esta es una imagen: <img src="some-image.gif"> </ p>

<p> Este es un enlace <a href="another-page.html"> </ a> a otra pgina. </ p> </ Body> </ Html>

Figura 26-1 muestra cmo el navegador hace que esta pgina.

Figura 26-1. Ejemplo de pgina Web

El navegador y el servidor se comunican mediante un protocolo llamado Protocolo de transferencia de hipertexto (HTTP). Mientras que usted no necesita preocuparse por los detalles del protocolo, es la comprensin de que vale la pena consiste enteramente en una secuencia de solicitudes iniciadas por el navegador y las respuestas generadas por el servidor. Es decir, el navegador se conecta al servidor web y enva una solicitud que incluya, al menos, la direccin deseada y la versin de HTTP que el navegador habla. El navegador tambin puede incluir los datos de su solicitud, as es como el navegador enva los formularios HTML en el servidor. Para responder a una solicitud, el servidor enva una respuesta formada por un conjunto de encabezados y un cuerpo. Los encabezados contienen informacin sobre el cuerpo, tales como qu tipo de datos que es (por ejemplo, HTML, texto plano, o una imagen), y el cuerpo es de los propios datos, que luego se prestan por el navegador. El servidor tambin puede enviar una respuesta de error diciendo al navegador que su solicitud no pudo ser respondida por alguna razn. Y eso es bastante. Una vez que el navegador ha recibido la respuesta completa del servidor, no hay comunicacin entre el navegador y el servidor hasta que la prxima vez que el navegador decide pedir a una pgina desde el servidor. 2 Esta es la principal limitacin de la programacin web - no hay manera para el cdigo que se ejecuta en el servidor de afectar a lo que el usuario ve en su navegador a menos que el explorador emite una solicitud de nuevo al servidor. 3 Algunas pginas Web, llamadas estticas pginas, son simplemente archivos HTML almacenados en el servidor Web y servido cuando sea solicitado por el navegador. dinmicospginas, por el contrario, consisten en HTML que se genera cada vez que se solicite la pgina en un navegador. Por ejemplo, una pgina dinmica se pueden generar mediante la consulta de una base de datos y luego la construccin de HTML para representar los resultados de la consulta. 4

Cuando se genera la respuesta a una solicitud, del lado del servidor de cdigo tiene cuatro partes principales de informacin para actuar sobre. La primera pieza de informacin es la direccin URL solicitada. Tpicamente, sin embargo, la direccin es utilizada por el propio servidor Web para determinar lo que el cdigo es responsable de generar la respuesta. A continuacin, si la URL contiene un signo de interrogacin, todo despus de que el signo de interrogacin se considera que es una cadena de consulta , que suele ser ignorado por el servidor Web, salvo que lo pone a disposicin el cdigo de la generacin de la respuesta. La mayor parte del tiempo de la cadena de consulta contiene un conjunto de pares clave / valor. La peticin del navegador tambin puede contener datos de envo , que tambin por lo general consiste de pares de clave / valor. Post data se suele utilizar para presentar los formularios HTML. Los pares clave / valor proporcionados en la cadena de consulta o de los datos enviados son llamados colectivamente los parmetros de consulta . Por ltimo, con el fin de encadenar una secuencia de peticiones individuales desde el mismo navegador, el cdigo se ejecuta en el servidor puede establecer una cookie , el envo de una cabecera especial en su respuesta al navegador que contiene un bit de datos opacos llamado cookies . Despus se coloca una cookie por un servidor en particular, el navegador enviar la cookie con cada solicitud se enva al servidor. El navegador no se preocupa por los datos de la cookie - slo se hace eco de vuelta al servidor para el cdigo del lado del servidor para interpretar sin embargo, quiere. Estos son los elementos primitivos en la parte superior de los cuales se construye el 99 por ciento de la programacin Web del lado del servidor. El navegador enva una solicitud, el servidor encuentra algn cdigo para manejar la peticin y lo ejecuta, y el cdigo utiliza los parmetros de consulta y galletas para determinar qu hacer. AllegroServe Puede servir contenido Web mediante Common Lisp en un nmero de maneras, hay al menos tres de cdigo abierto servidores web escritas en Common Lisp, as como plug-ins tales como mod_lisp 5 y Lisplets 6 que permiten que el servidor web Apache o Java ninguna contenedor de servlets de delegar las peticiones a un servidor de Lisp se ejecuta en un proceso separado. Para este captulo, vamos a usar una versin de cdigo abierto del servidor Web AllegroServe, escrito originalmente por John Foderaro a Franz Inc.. AllegroServe

est incluido en la versin de Allegro disponible de Franz para su uso con este libro. Si usted no est usando Allegro, puede utilizar PortableAllegroServe, un tenedor amistoso de la base de cdigo AllegroServe, que incluye una capa de compatibilidad que permite Allegro PortableAllegroServe para funcionar en la mayora de los Lisps comunes. El cdigo que voy a escribir en este captulo y en el captulo 29 debera funcionar tanto en la vainilla y el AllegroServe PortableAllegroServe. AllegroServe ofrece un modelo de programacin similares en espritu a Java Servlets - cada vez que un navegador solicita una pgina, AllegroServe analiza la solicitud y se ve un objeto, denominado entidad , que se ocupa de la solicitud. Algunas clases de entidad proporcionados como parte de AllegroServe saber cmo servir contenido esttico - cualquiera de los archivos individuales o el contenido de un rbol de directorios. Otros, los que me pasan la mayor parte de este captulo discutiendo, ejecutar cdigo arbitrario Lisp para generar la respuesta. 7 Pero antes de llegar a eso, usted necesita saber cmo empezar AllegroServe y configurarlo para servir a unos pocos archivos. El primer paso es cargar el cdigo AllegroServe en su imagen Lisp. En Allegro, slo tiene que escribir (requiere: aserve) . En Lisps otros (o en Allegro), puede cargar PortableAllegroServe cargando el archivoINSTALL.lisp en la parte superior de la portableaserve rbol de directorios. Cargando AllegroServe crear tres nuevos paquetes, NET.ASERVE ,NET.HTML.GENERATOR y NET.ASERVE.CLIENT . 8 Despus de cargar el servidor, se inicia con la funcin de inicio en el NET.ASERVE paquete. Para tener un fcil acceso a los smbolos exportados desde NET.ASERVE , desdeCOM.GIGAMONKEYS.HTML (un paquete voy a discutir en un momento), y del resto de Common Lisp, debe crear un nuevo paquete para jugar en la misma familia :
CL-> USER (defpackage: com.gigamonkeys.web (: Uso: CL: net.aserve: com.gigamonkeys.html)) # <El COM.GIGAMONKEYS.WEB package>

Ahora cambie a ese paquete con esta in-Package expresin:


CL-> USER (en el paquete: com.gigamonkeys.web) # <El COM.GIGAMONKEYS.WEB package> WEB>

Ahora usted puede utilizar los nombres exportados desde NET.ASERVE sin calificacin. La funcin de arranque inicia el servidor. Se necesita un gran nmero de parmetros de palabra clave, pero el nico que tiene que pasar es : el puerto , el

cual especifica el puerto de escucha. Usted probablemente tendr que usar un puerto alto como 2001 en lugar del puerto por defecto para servidores HTTP, 80, porque en los sistemas operativos derivados de Unix slo el usuario root puede escuchar en los puertos por debajo de 1024. Para ejecutar AllegroServe escuchando en el puerto 80 en Unix, tendra que empezar a Lisp como root y luego utilizar el : setuid y : setgid parmetros que contar empezar acambiar su identidad despus de abrir el puerto. Usted puede iniciar un servidor escuchando en el puerto 2001 de esta manera:
WEB> (inicio: el puerto 2001) # <WSERVER Puerto 2001 @ #x72511c72>

El servidor se est ejecutando en el Lisp. Es posible que usted obtendr un error que dice algo sobre "El puerto ya est en uso" cuando intenta iniciar el servidor. Esto significa que el puerto 2001 ya est en uso por algn otro servidor en su mquina. En ese caso, la ms sencilla es utilizar un puerto diferente, el suministro de un argumento diferente para empezar y luego usar ese valor en vez de 2001 en la URL utilizadas en este captulo. Usted puede seguir interactuando con Lisp a travs de la REPL porque AllegroServe comienza sus propios hilos para manejar las solicitudes de exploradores. Esto significa, entre otras cosas, que se puede utilizar el REPL para obtener una vista a las entraas de su servidor mientras se est ejecutando, lo que hace que la depuracin y prueba de un montn ms fcil que si el servidor es un cuadro negro completo. Asumiendo que se est ejecutando Lisp en la misma mquina como su navegador, se puede comprobar que el servidor est activo y funcionando sealando su navegador ahttp://localhost:2001/ . En este punto, usted debe conseguir una pgina no encontrado mensaje de error en el navegador, ya que no han publicado nada. Pero el mensaje de error ser de AllegroServe, sino que voy a decir que en la parte inferior de la pgina. Por otro lado, si el navegador muestra un dilogo de error que dice algo as como "La conexin fue rechazada al intentar ponerse en contacto con localhost: 2001", que significa ya sea que el servidor no se est ejecutando o que se inici con un puerto diferente 2001. Ahora usted puede publicar algunos archivos. Suponga que tiene un archivo de hello.html en el directorio / tmp / html con el siguiente contenido:
<html>

<head> <title> Hola </ title> </ Head> <body> <p> Hola, mundo! </ p> </ Body> </ Html>

Puedes publicar de forma individual con la publicacin de archivos funcin.


WEB> (publicar-file: ruta "/ hello.html": el archivo "/ tmp / html / hello.html") # @ <NET.ASERVE::FILE-ENTITY #x725eddea>

El : ruta argumento es la ruta que aparece en la URL solicitada por el navegador, mientras que el : archivo argumento es el nombre del archivo en el sistema de archivos.Despus de evaluar la publicacin de archivos expresin, puede apuntar su navegador a http://localhost:2001/hello.html , y debe mostrar una pgina como algo que la figura 26-2.

Figura 26-2. http://localhost:2001/hello.html

Tambin puede publicar un rbol de directorio completo de archivos a travs de la publicacin de directorio de funcin. En primer lugar vamos a aclarar a la entidad ya publicados con la siguiente llamada a publicar-archivo :
WEB> (publicar-file: ruta "/ hello.html": eliminar t) NIL

Ahora usted puede publicar todo el directorio / tmp / html / directorio (y todos sus subdirectorios), con la publicacin de directorio de funcin.
WEB> (publicacin de directorio: el prefijo "/": lugar de destino "/ tmp / html /") # @ <NET.ASERVE::DIRECTORY-ENTITY #x72625aa2>

En este caso, el : prefijo argumento especifica el comienzo de la parte de la ruta de la URL que deben ser manejadas por esta entidad. Por lo tanto, si el servidor recibe una solicitud de http://localhost:2001/foo/bar.html , la ruta es / foo / bar.html , que comienza con / . Este camino se traduce a un nombre de fichero, sustituyendo el prefijo, / , con el destino, / tmp / html / . Por lo tanto, la direccin URL http://localhost:2001/hello.html todava se traducir en una solicitud para el archivo / tmp / html / hello.html .

Generacin de Contenido Dinmico con AllegroServe Publicacin de las entidades que generan contenido dinmico es casi tan simple como publicar contenido esttico. Las funciones de publicacin y lapublicacin de prefijo son los anlogos de la dinmica de publicacin de archivos y publicacin de directorios- . La idea bsica de estas dos funciones es que se publica una funcin que ser llamada para generar la respuesta a una solicitud de una direccin URL especfica o cualquier URL con un prefijo determinado. La funcin se llama con dos argumentos: un objeto que representa la solicitud y la entidad publica. La mayor parte del tiempo que usted no necesita hacer nada con el objeto de entidad si no es para pasar a lo largo de un par de macros voy a discutir en un momento. Por otro lado, va a utilizar el objeto de solicitud para obtener la informacin presentada por el navegador - incluidos los parmetros de consulta en la direccin URL o los datos publicada utilizando un formulario HTML. Para un ejemplo trivial de utilizar una funcin para generar contenido dinmico, vamos a escribir una funcin que genera una pgina con un nmero aleatorio diferente cada vez que se solicita.
(Defun de nmeros aleatorios (entidad de la solicitud) (Con-http-respuesta (entidad de la solicitud: el tipo de contenido "text / html") (Con-http-cuerpo (entidad de la solicitud) (Formato (Peticin-respuesta-secuencia de la solicitud) "<html> ~ @ <head> <title> aleatorio </ title> </ head> ~ @ <body> ~ @ <p> de nmeros aleatorios: ~ d </ p> ~ @ </ Body> ~ @ </ Html> ~ @ " (Aleatorio 1000)))))

Las macros con-http-respuesta y con el cuerpo-http- son parte de AllegroServe. El ex comienza el proceso de generar una respuesta HTTP y se puede utilizar, como aqu, para especificar las cosas tales como el tipo de contenido que sern devueltos. Tambin se ocupa de diversas partes de HTTP, como tratar con If-Modified-ya que las solicitudes. El con-http-cuerpo realmente enva los encabezados de respuesta HTTP y luego ejecuta su cuerpo, que debe contener cdigo que genera el contenido de la respuesta. En con-http-respuesta , pero antes de la con-http-cuerpo , usted puede agregar o cambiar los encabezados HTTP que se enva en la respuesta. La funcinde solicitud y respuesta de la corriente tambin es parte de AllegroServe y

devuelve la corriente a la que debe escribir la salida de la intencin de ser enviado al navegador. Como muestra esta funcin, slo puede utilizar FORMATO HTML para imprimir a la secuencia devuelta por solicitud y respuesta de la corriente . En la siguiente seccin, te voy a mostrar las formas ms convenientes para generar programacin HTML. 9 Ahora est listo para publicar esta funcin.
WEB> (publicar: ruta "/ de nmeros aleatorios": la funcin "de nmeros aleatorios) # @ <COMPUTED-ENTITY #x7262bab2>

Como lo hace en la publicacin de archivos funcin, el : ruta argumento especifica la ruta de la URL que se traducir en esta funcin que se invoca. El: La funcin de argumento especifica el nombre o la funcin de un objeto real. Usando el nombre de una funcin, como se muestra aqu, le permite redefinir la funcin de tarde, sin publicar y tener AllegroServe utilizar la definicin de una funcin nueva. Despus de evaluar el llamado a la publicacin , se puede utilizar en el navegadorhttp:// localhost: 2001/random-number para obtener una pgina con un nmero aleatorio en ella, como se muestra en la Figura 26-3.

Figura 26-3. http://localhost:2001/random-number

Generacin de HTML Aunque el uso de FORMATO para emitir HTML funciona bien para las pginas simples que he discutido hasta ahora, ya que empezar a construir las pginas ms elaboradas que sera bueno tener una forma ms concisa para generar HTML. Varias bibliotecas estn disponibles para la generacin de cdigo HTML de una representacin de s-expresin, incluida una, HTMLGen, que se incluye con AllegroServe. En este captulo vamos a usar una biblioteca llamada FOO, 10 que est vagamente inspirado en HTMLGen Franz y cuya aplicacin usted se ver con ms detalle en los captulos 30 y 31. Por ahora, sin embargo, usted slo necesita saber cmo utilizar FOO. Generacin de HTML dentro de Lisp es bastante natural, ya que s-expresiones y HTML son esencialmente isomorfos. Puede representar los elementos HTML con sexpresiones mediante el tratamiento de cada elemento en HTML como una lista de

"etiquetado" con un elemento apropiado en primer lugar, como un smbolo de la palabra el mismo nombre que la etiqueta HTML. De este modo, el cdigo HTML <p> foo </ p> est representado por el s-expresin (: p "foo") . Debido a que anidan los elementos HTML de las listas de la misma manera que en s-expresiones hacerlo, este sistema se extiende a HTML ms complejo. Por ejemplo, este cdigo HTML:
<html> <head> <title> Hola </ title> </ Head> <body> <p> Hola, mundo! </ p> </ Body> </ Html>

podra ser representado con la siguiente s-expresin:


(: Html (: La cabeza (ttulo de "Hola")) (: Cuerpo (: p "Hola, mundo")))

Elementos HTML con atributos de complicar las cosas un poco, pero no de una manera insuperable. FOO admite dos formas de incluir los atributos de una etiqueta. Uno de ellos es simplemente seguir el primer elemento de la lista con pares de palabra clave / valor. El primer elemento que sigue a un par de palabras clave / valor que no se trata de un smbolo clave marca el comienzo de los contenidos del elemento. Por lo tanto, usted representa este cdigo HTML:
href="foo.html"> <a Este es un enlace </ a>

con el siguiente s-expresin:


(: A: href "foo.html" "Este es un enlace")

La otra sintaxis FOO apoyos consiste en agrupar el nombre de etiqueta y de atributos en su propia lista como esta:
((: A: ". Este es el enlace" href "foo.html"))

FOO puede utilizar la representacin de s-expresin de HTML de dos maneras. La funcin de emitir-html toma un HTML s-expresin y enva el cdigo HTML correspondiente.
WEB> (emiten-html '(: html (: cabeza (ttulo de "Hola")) (: cuerpo (: p "Hola, mundo")))) <html> <head> <title> Hola </ title> </ Head> <body>

<p> Hola, mundo! </ p> </ Body> </ Html> T

Sin embargo, emiten-html no es siempre el camino ms eficiente para generar HTML, ya que su argumento debe ser una completa s-expresin de la representacin de HTML que se generar. Mientras que es fcil de construir este tipo de representacin, que no siempre es particularmente eficiente. Por ejemplo, supongamos que quera hacer una pgina HTML que contiene una lista de 10.000 nmeros aleatorios. Se puede construir el s-expresin que utiliza una plantilla de acento grave y luego pasar a emitir-html como este:
(Emiten-html `(: Html (: La cabeza (: Title "nmeros aleatorios")) (: El cuerpo (: H1 "nmeros aleatorios") (: P, @ (repeticin de bucle 10000 recoger (al azar 1000) recoger "")))))

Sin embargo, esto tiene que construir un rbol que contiene una lista 10000elemento antes de que incluso puede comenzar HTML emisor, y el conjunto de sexpresin se convertir en basura tan pronto como el HTML se emite. Para evitar esta ineficiencia, FOO tambin proporciona una macro html , que te permite insertar fragmentos de cdigo Lisp en medio de un archivo HTML s-expresin. Los valores literales, como cadenas y nmeros en la entrada de html estn interpolados en el HTML de salida. Del mismo modo, los smbolos se tratan como referencias a variables, y se genera el cdigo para emitir su valor en tiempo de ejecucin. As, tanto de estos:
(Html (: p "foo")) (Let ((x "foo")) (html (: px)))

emitir el siguiente:
<p> foo </ p>

Lista de las formas que no comienzan con un smbolo de la palabra clave se supone que son de cdigo y se insertan en el cdigo generado. Cualquier valora las declaraciones de cdigo incrustados se tendr en cuenta, pero el cdigo HTML puede emitir ms llamando html s mismo. Por ejemplo, para emitir el contenido de una lista en formato HTML, puede escribir lo siguiente:
(Html (: ul (dolist (tema (lista 1 2 3)) (html (: elemento li)))))

que emitir el cdigo HTML siguiente:


<ul> <li> 1 </ li> <li> 2 </ li> <li> 3 </ li> </ Ul>

Si desea emitir el valor de un formulario de lista, se debe envolver en el pseudotag : impresin . As, esta expresin:
(Html (: P (+ 1 2)))

genera este cdigo HTML despus de la informtica y descartando el valor 3 :


</ p>

Para emitir el 3 , debe escribir lo siguiente:


(Html (: P (: print (+ 1 2))))

O usted podra calcular el valor y lo almacena en una variable fuera de la llamada a html como este:
(Let ((x (+ 1 2))) (html (: px)))

Por lo tanto, puede utilizar el cdigo HTML macro para generar la lista de nmeros aleatorios como esta:
(Html (: Html (: La cabeza (: Title "nmeros aleatorios")) (: El cuerpo (: H1 "nmeros aleatorios") (: P (repeticin de bucle de 10 hacer (html (: print (random 1000)) ""))))))

La versin macro ser un poco ms eficiente que la emite-html versin. No slo no tiene que generar una s-expresin que representa toda la pgina, tambin gran parte del trabajo que se emiten-html hace en tiempo de ejecucin de interpretar el s-expresin se hace una vez, cuando la macro se expande, en lugar de cada vez se ejecuta el cdigo. Usted puede controlar que el producto generado por tanto html y emiten html se enva con la macro -html con la salida , que es parte de la biblioteca FOO. Por lo tanto, puede utilizar los con-html-salida y html macros de FOO volver a escribir de nmeros aleatorios de esta manera:
(Defun de nmeros aleatorios (entidad de la solicitud) (Con-http-respuesta (entidad de la solicitud: el tipo de contenido "text / html") (Con-http-cuerpo (entidad de la solicitud)

(Con-html-output ((peticin-respuesta-secuencia de la solicitud)) (Html (: Html (: La cabeza (ttulo de "Random")) (: El cuerpo (: P "de nmeros aleatorios:" (: print (random 1000))))))))))

Macros HTML Otra caracterstica de FOO es que le permite definir HTML "macros" que se traducen en formas arbitrarias HTML S-expresiones que el html macro entiende. Por ejemplo, supongamos que con frecuencia se encuentre escribiendo pginas de este formulario:
(: Html (: La cabeza (ttulo de "algn ttulo")) (: El cuerpo (: H1 "algn ttulo") ... cosas ... ))

Se podra definir una macro HTML para capturar ese patrn de esta manera:
(Define-html-macro: estndar de pginas ((y ttulo clave) y el cuerpo del cuerpo) `(: Html (: La cabeza (ttulo, el ttulo)) (: El cuerpo (: H1, el ttulo) , El cuerpo de @)))

Ahora usted puede utilizar el "tag" : estndar de pgina en su s-expresin de HTML, y va a ser ampliado antes de ser interpretado o compilado. Por ejemplo, el texto siguiente:
(Html (: estndar de la pgina (ttulo de "Hola") (: P ". Hola, mundo")))

genera el cdigo HTML siguiente:


<html> <head> <title> Hola </ title> </ Head> <body> <h1> Hola </ h1> <p> Hola, mundo. </ p> </ Body> </ Html>

Parmetros de la consulta Por supuesto, la generacin de la salida HTML es slo la mitad de la programacin Web. La otra cosa que hay que hacer es conseguir la entrada del usuario. Como dije en la "A Intro 30-En segundo lugar a la programacin del lado del servidor Web",

cuando un navegador solicita una pgina desde un servidor Web, puede enviar los parmetros de consulta en las URL y los datos enviados, los cuales actan como entrada a el cdigo del lado del servidor. AllegroServe, al igual que la mayora de los marcos de programacin en web, se encarga de analizar estas dos fuentes de entrada para ti. En el momento en sus funciones publicados son llamados, todos los pares clave / valor de la cadena de consulta y / o datos de envo han sido decodificadas y se coloca en un alista que se puede recuperar desde el objeto de la peticin con la funcin de solicitud de consulta- . La siguiente funcin devuelve una pgina que muestra todos los parmetros de consulta que recibe:
(Entidad de la solicitud defun show-consulta-params () (Con-http-respuesta (entidad de la solicitud: el tipo de contenido "text / html") (Con-http-cuerpo (entidad de la solicitud) (Con-html-output ((peticin-respuesta-secuencia de la solicitud)) (Html (: Estndar de pginas (: Title "parmetros de consulta") (En caso de (solicitud de consulta previa solicitud) (Html (: Table: una frontera (Bucle for (k. V) en la (solicitud de consulta previa solicitud) hacer (html (: tr (: td k) (: td v)))))) (Html (: P ". No hay parmetros de la consulta"))))))))) (Publicar: ruta "/ show-query-params": la funcin 'show-query-params)

Si usted da su navegador una URL con una cadena de consulta en el mismo como el siguiente:
http://localhost:2001/show-query-params?foo=bar&baz=10

usted debe volver a una pgina similar a la que se muestra en la Figura 26-4.

Figura 26-4. http://localhost:2001/show-query-params?foo=bar&baz=10

Para generar algunos datos de envo, usted necesita un formulario HTML. La siguiente funcin genera una forma simple, que presenta sus datos paraconsulta demostrar-params :
(Defun simple formulario (entidad de la solicitud) (Con-http-respuesta (entidad de la solicitud: el tipo de contenido "text / html") (Con-http-cuerpo (entidad de la solicitud) (Let ((* html de salida * (peticin-respuesta-secuencia de la solicitud))) (Html (: Html (: La cabeza (ttulo de "forma simple"))

(: El cuerpo (Forma: mtodo "POST": la accin de consulta "/ show-params" (: Mesa (: Tr (: td "Foo") (: TD (: de entrada: el nombre de "foo": tamao 20))) (: Tr (: td "Contrasea") (: TD (: de entrada: el nombre de "contrasea": el tipo "password": tamao 20)))) (: P (: de entrada: el nombre de "enviar": tipo de "enviar": valor "correcto") (: De entrada :: Tipo "reset": el valor "Reset")))))))))) (Publicar: ruta "/ simple forma": la funcin de "simple-forma)

Dirija su navegador a http://localhost:2001/simple-form , y usted debera ver una pgina como la de la figura 26-5. Si rellena el formulario con el "abc" y los valores de "def", haciendo clic en el botn Aceptar le llevar a una pgina como la de la figura 26-6.

Figura 26-5. http://localhost:2001/simple-form

Figura 26-6. Resultado de la presentacin de la forma simple

Sin embargo, la mayora de las veces no ser necesario iterar sobre todos los parmetros de la consulta, usted querr seleccionar los parmetros individuales. Por ejemplo, es posible que desee modificar de nmeros aleatorios por lo que el valor lmite se pasa al AZAR puede ser suministrado a travs de un parmetro de consulta. En ese caso, se utiliza la funcin de peticiones y consultas de valor , que tiene el objeto de solicitud y el nombre del parmetro cuyo valor desea y devuelve el valor como una cadena o NILsi no hay tal parmetro ha sido facilitada. Una versin parametrizable de nmeros aleatorios podra tener este aspecto:
(Defun de nmeros aleatorios (entidad de la solicitud) (Con-http-respuesta (entidad de la solicitud: el tipo de contenido "text / html") (Con-http-cuerpo (entidad de la solicitud) (Let * ((* html de salida * (solicitud y respuesta de la corriente peticin)) (Lmite de cuerdas (o de (solicitud de consulta de valor peticin de "lmite") "")) (Lmite (o el (anlisis sintctico-entero-lmite cadena: basura permite t) 1000))) (Html (: Html (: La cabeza (ttulo de "Random")) (: El cuerpo (: P "de nmeros aleatorios:" (: print (lmite al azar))))))))))

Debido a peticiones y consultas de valor puede devolver NIL o una cadena vaca, lo que tiene que hacer frente a esos dos casos, cuando se analiza el parmetro en un nmero para pasar al AZAR . Usted puede hacer frente a una NIL de valor al enlazar lmite de cuerdas , que le vincule a "" si no hay un "lmite" parmetro de consulta.A continuacin, puede utilizar el : basura permitido argumento para PARSE-INTEGER para asegurarse de que devuelve NIL (si no se puede analizar un nmero entero de la cadena dada) o un entero. En la seccin "Un marco es una pequea aplicacin," que va a desarrollar algunas macros para hacer ms fcil para hacer frente a agarrar los parmetros de consulta y convertirlos a diferentes tipos. Galletas En AllegroServe puede enviar una cabecera Set-Cookie que le dice al navegador para guardar una cookie y enviarla junto con las solicitudes posteriores llamando a la funcinset-cookie-encabezado en el cuerpo de con-http-respuesta , pero antes de la llamada a la con- http-cuerpo . El primer argumento de la funcin es el objeto de la peticin, y el resto de argumentos son argumentos clave utilizados para establecer las distintas propiedades de la cookie. Los nicos dos que tienen que pasar son la : nombre y: valor de los argumentos, los cuales deben ser cadenas. Los otros argumentos posibles que afectan a la cookie se enva al navegador son : expira , laruta de acceso: y : de dominio y seguros: . De stos, tiene que preocuparse slo de : expira . Se controla el tiempo que el navegador debe guardar la cookie. Si : vence es NIL (por defecto), el navegador guarda la cookie slo hasta que sale. Otros valores posibles son : nunca , lo que significa que la cookie se debe mantener para siempre, o un tiempo universal devuelto porGET-UNIVERSAL-TIME o ENCODE-Tiempo Universal- . Una : que expire . de cero indica al cliente que deseche de inmediato una cookie existente 11 Despus de configurar una cookie, puede utilizar la funcin de conseguir-los valores de las cookies para obtener una alist que contiene un par nombre / valor para cada cookie enviada por el navegador. A partir de ese alist, usted puede seleccionar los valores individuales de galletas con ASOC y el CDR . La siguiente funcin muestra los nombres y valores de todas las cookies enviadas por el navegador:
(Defun muestran las cookies (entidad de la solicitud) (Con-http-respuesta (entidad de la solicitud: el tipo de contenido "text / html") (Con-http-cuerpo (entidad de la solicitud)

(Con-html-output ((peticin-respuesta-secuencia de la solicitud)) (Html (: Estndar de pginas (Ttulo de "cookies") (If (null (get-cookies-valores peticin)) (Html (: P ". No cookies")) (Html (: Mesa (Bucle for (clave. Valor) en (get-galletas-valores peticin) hacer (html (: tr (: td clave) (: valor de td))))))))))))) (Publicar: ruta "/ show-cookies": la funcin 'show-cookies)

La primera vez que cargue la pgina http://localhost:2001/show-cookies debe decir "No cookies", como se muestra en la Figura 26-7, ya que no se ha configurado ninguna todava.

Figura 26-7. http://localhost:2001/show-cookies sin las cookies

Para configurar una cookie, es necesario otra funcin, como la siguiente:


(Defun set-cookie (solicitud de la entidad) (Con-http-respuesta (entidad de la solicitud: el tipo de contenido "text / html") (Set-Cookie-encabezado de la solicitud: nombre de "MyCookie": valor "Un valor de la cookie") (Con-http-cuerpo (entidad de la solicitud) (Con-html-output ((peticin-respuesta-secuencia de la solicitud)) (Html (: Estndar de pginas (Ttulo de "Juego de Cookie") (: ". Conjunto Cookie" p) (: P (: a:. Href "/ show-cookies", "Mira tarro de las galletas")))))))) (Publicar: ruta "/ Set-Cookie": la funcin "Set-Cookie)

Si se introduce la URL http://localhost:2001/set-cookie , el navegador mostrar una pgina como la de la figura 26-8. Adems, el servidor enva un encabezado SetCookie con una cookie llamada "MyCookie" con "Un valor de la cookie" como su valor. Si hace clic en el enlace Mira tarro de las galletas , usted ser llevado a la/ show-cookies de la pgina donde puedes ver la nueva cookie, como se muestra en la Figura 26-9. Debido a que no se ha especificado una : vence el argumento, el navegador seguir enviando la cookie con cada solicitud hasta que clausura el navegador.

Figura 26-8. http://localhost:2001/set-cookie

Figura 26-9. http://localhost:2001/show-cookies despus de establecer una cookie

Un marco es una pequea aplicacin Aunque AllegroServe proporciona acceso relativamente sencillo a todas las instalaciones bsicas necesarias para escribir cdigo de servidor web (el acceso a los parmetros de consulta tanto de cadena de consulta de la URL y los datos de envo, la posibilidad de configurar las cookies y recuperar sus valores, y, por supuesto , la capacidad de generar la respuesta enviada de vuelta al navegador), hay una buena cantidad de cdigo repetitivo molesto. Por ejemplo, todas las funciones de generacin de cdigo HTML que escribe se va a tomar los argumentos solicitud y la entidad y, a continuacin se contienen las llamadas acon-http-respuesta , con-http-respuesta , y - si usted va a utilizar para generar FOO HTML - con-html-salida . Luego, en las funciones que necesitan para llegar a los parmetros de consulta, habr un montn de llamadas a peticin de consulta-relacin calidad-precio y ms cdigo para convertir la cadena volvi a cualquier tipo que realmente desea. Por ltimo, es necesario recordar a la publicacin de la funcin. Para reducir la cantidad de repetitivo tiene que escribir, puede escribir un pequeo marco en la parte superior de la AllegroServe para que sea ms fcil de definir las funciones que manejan las solicitudes de una determinada URL. El enfoque bsico ser el de definir una macro, la funcin de definir-url- , que usted va a utilizar para definir las funciones que automticamente se publicarn a travs de publicacin . Esta macro se expandir en una DEFUN que contiene el texto modelo adecuado, as como a publicar el cdigo de la funcin en una direccin URL del mismo nombre. Tambin nos encargamos de generar cdigo para extraer los valores de los parmetros de consulta y las cookies y para unirse a las variables declaradas en la lista de parmetros de la funcin. Por lo tanto, la forma bsica de una definicin-url-funcin de la definicin es la siguiente:
(Define-url-funcin de nombre de ( solicitud de del cuerpo ) consulta de parmetros *)

donde el cuerpo es el cdigo para emitir el cdigo HTML de la pgina. Va a ser envuelto en una llamada a Foo html macro, por lo que para las pginas simples puede contener nada ms que HTML s-expresin. Dentro del cuerpo, las variables de los parmetros de la consulta estar sujeto a los valores de los parmetros de consulta con el mismo nombre o de una cookie. En el caso ms simple, el valor de un parmetro de consulta ser la cadena de toma del parmetro de consulta o en el campo despus de los datos del mismo nombre. Si el parmetro de consulta se especifica una lista, tambin puede especificar una conversin automtica de tipos, un valor por defecto, y si hay que buscar y guardar el valor del parmetro en una cookie. La sintaxis completa de una consultaparmetro es como sigue:
Nombre | ( nombre del tipo [ por defecto de valor ] [ rigidez ])

El tipo debe ser un nombre reconocido por definir-url-funcin . Voy a discutir en un momento la forma de definir los nuevos tipos. El valor por defecto debe ser un valor del tipo dado. Por ltimo, la pegajosidad , si fue suministrado, indica que el valor del parmetro debe ser tomada de una cookie apropiado nombre si no hay parmetro de consulta se suministra y que una cabecera Set-Cookie se deben enviar en la respuesta que guarda el valor en la cookie con el mismo nombre . Por lo tanto, un parmetro pegajosa, despus de ser especificado explcitamente un valor a travs de un parmetro de consulta, mantendr ese valor en las solicitudes posteriores de la pgina, incluso cuando no hay parmetro de consulta se suministra. El nombre de la cookie utilizada depende del valor de pegajosidad : con un valor de : global , la cookie ser el mismo nombre que el parmetro. De este modo, las diferentes funciones que utilizan parmetros a nivel global pegajosos con el mismo nombre que comparten el valor. Si rigidez es : Paquete , entonces el nombre de la cookie se construye a partir del nombre del parmetro y el paquete de nombre de la funcin, lo que permite que las funciones en el mismo paquete para compartir los valores, pero no tiene que preocuparse de pisar fuerte en los parmetros de las funciones en otros paquetes . Por ltimo, un parmetro con una pegajosidad valor de : locales usarn una galleta hecha a partir del nombre del parmetro, el paquete de el nombre de funcin, y el nombre de funcin, lo que lo convierte nico para esa funcin.

Por ejemplo, puede utilizar definir-url-funcin para reemplazar a la anterior de once lnea de definicin de pginas al azar con esta versin de cinco lneas:
(Define-url-la funcin de nmeros aleatorios (peticin (lmite de nmero entero 1000)) (: Html (: La cabeza (ttulo de "Random")) (: El cuerpo (: P "de nmeros aleatorios:" (: print (lmite al azar))))))

Si desea que el argumento de lmite a ser pegajosos, podra cambiar la declaracin lmite (lmite entero de 1000: local) . La puesta en prctica Voy a explicar la aplicacin de la definicin-url-la funcin de arriba hacia abajo. La misma macro se parece a esto:
(Defmacro define-url-funcin (params solicitud de nombre (y dems) y el cuerpo del cuerpo) (Con-gensyms (entidad) (Let ((params (mapcar # 'normalizar-param params))) `(Progn (Defun nombre, (, solicitud, la entidad) (Con-http-respuesta (, solicitud, la entidad: el tipo de contenido "text / html") (Let * ((enlaces, los @ param params de solicitar el nombre)) , @ (Set-cookies-params solicitar el nombre de cdigo) (Con-http-cuerpo (, solicitud, la entidad) (Con-html-output ((solicitud y respuesta de la corriente, peticin)) (Html, body @)))))) (Publicar: ruta de acceso, (formato nulo "/ ~ (~ a ~)" nombre): la funcin ', nombre)))))

Vamos a tomar poco a poco, comenzando con las primeras lneas.


(Defmacro define-url-funcin (params solicitud de nombre (y dems) y el cuerpo del cuerpo) (Con-gensyms (entidad) (Let ((params (mapcar # 'normalizar-param params)))

Hasta aqu slo ests a punto de generar cdigo. Usted GENSYM un smbolo que se utilizar ms adelante como el nombre del parmetro de la entidad en el DEFUN . A continuacin, la normalizacin de los parmetros, la conversin de smbolos sencillos a la lista de la forma de utilizar esta funcin:
(Defun normalizar-param (param) (Param etypecase (Param lista) (El smbolo `(, nula param string nulo))))

En otras palabras, se declara un parmetro con slo un smbolo es lo mismo que declarar un parmetro no pegajoso, cadena con un valor predeterminado.

Luego viene la progn . Usted debe ampliar en un progn porque es necesario para generar el cdigo para hacer dos cosas: definir una funcin con DEFUN y llamar a publicar .Debe definir la primera funcin por lo que si hay un error en la definicin, la funcin no ser publicada. Las dos primeras lneas de la DEFUN son slo repetitivo.
(Defun nombre, (, solicitud, la entidad) (Con-http-respuesta (, solicitud, la entidad: el tipo de contenido "text / html")

Ahora lo hace el verdadero trabajo. Las siguientes dos lneas de generar los enlaces para los parmetros especificados en definir-url-funcin que no sea pedido y el cdigo que llama a Set-Cookie-encabezado por los parmetros rgidos. Por supuesto, el trabajo real es realizado por las funciones auxiliares que veremos en un momento. 12
(Let * ((enlaces, los @ param params de solicitar el nombre)) , @ (Set-cookies-params solicitar el nombre de cdigo)

El resto es repetitivo ms justo, poner el cuerpo de la definicin-url-la funcin de definicin en el contexto adecuado de con-http-cuerpo ,con-html de salida- , y html macros. Luego viene el llamado a la publicacin .
(Publicar: ruta de acceso, (cero formato "/ ~ (~ a ~)" nombre): la funcin ', nombre)

La expresin (cero formato "/ ~ (~ a ~)" nombre) se evala en el momento de expansin de la macro, lo que genera una cadena formada por /, seguido por una versin en minsculas del nombre de la funcin que est a punto de definir. Esa cadena se convierte en el : ruta argumento de la publicacin, mientras que el nombre de la funcin se interpola como : la funcin de argumento. Ahora echemos un vistazo a las funciones auxiliares utilizados para generar el DEFUN formulario. Para generar enlaces de parmetros, que deben reproducirse en los parametros y recoger un fragmento de cdigo para cada uno, generados por parmetros de enlace . Ese fragmento ser una lista que contiene el nombre de la variable que se unen y el cdigo que va a calcular el valor de esa variable. La forma exacta de cdigo utilizado para calcular el valor depender del tipo del parmetro, si es que es pegajosa, y el valor predeterminado, si los hubiere. Debido a que ya se normalizaron los parmetros del, puede utilizar BIND desestructurada- de desarmarlas en el parmetro de enlace.
(Defun parmetros de enlaces (nombre de la funcin-params solicitud)

(Bucle de parmetros en los parametros recoger (param vinculante nombre-funcin param peticin))) (Defun param vinculante (funcin-param name peticin) (Desestructuracin-bind (tipo de nombre predeterminado opcional y pegajoso) param (Let ((consulta de nombre (smbolo-> query-name nombre)) (Cookie de nombre (smbolo-> galletas-nombre de la funcin de nombre pegajoso nombre))) `(, Nombre (o (String-> tipo ', el tipo (solicitud de consulta de valor, la consulta de nombre, peticin)) , @ (Si el nombre de galleta(Lista `(string-> tipo ', el tipo (get-galleta-valor, solicitud, cookies nombre)))) , Por defecto)))))

La funcin string-> Tipo , que se utiliza para convertir cadenas obtenidos a partir de los parmetros de consulta y galletas para el tipo deseado, es una funcin genrica con la siguiente firma:
(Defgeneric cadena> Tipo de valor del tipo ())

Para hacer un nombre en particular se puede usar como un nombre de tipo de un parmetro de consulta, slo tiene que definir un mtodo en cadena> Tipo . Tendr que definir al menos un mtodo especializado en el smbolo de cadena ya que es el tipo predeterminado. Por supuesto, eso es bastante fcil. Dado que los navegadores a veces, presentar los formularios con cadenas vacas para indicar que no se proporcion el valor para un determinado valor, usted desea convertir una cadena vaca NIL ya que este mtodo hace:
(Defmethod string-> (tipo ((cadena de eql ')) valor) (Y (plusp (valor de longitud)) el valor))

Usted puede agregar las conversiones de otros tipos necesarios para su aplicacin. Por ejemplo, para hacer nmero entero se puede usar como un tipo de parmetro de consulta para que pueda manejar el lmite de los parmetros de la pgina al azar , se puede definir este mtodo:
(Defmethod string-> (tipo ((entero eql ')) valor) (Anlisis sintctico-entero (o el valor ""): basura permite t))

Otra funcin auxiliar que se utiliza en el cdigo generado por el parmetro de enlace es conseguir-cookies-valor , que es slo un poco de azcar en todo elgetCOOKIE-valores de funcin proporcionada por AllegroServe. Se parece a esto:
(Defun get-galleta-valor (el nombre de solicitud) (Cdr (nombre de enlace (get-cookie-valores de peticin): cadena de prueba # '=)))

Las funciones que calculan el parmetro de consulta y los nombres de las galletas son igualmente sencillo.
(Smbolo-> defun consulta de nombre (smbolo) (Cadena-downcase smbolo)) (Defun smbolo-> galletas-name (nombre-funcin smbolo pegajoso) (Let ((nombre-paquete (paquete-nombre (smbolo de paquete nombre-funcin)))) (Cuando pegajosa (Ecase pegajosa (: global (Cadena-downcase smbolo)) (: Paquete de (Cero formato "~ (~ a: ~ a ~)" nombre-smbolo)) (: Local (Formato nulo "~ (~ a: ~ a: ~ a ~)" nombre-nombre-funcin smbolo))))))

Para generar el cdigo que establece las cookies para los parmetros rgidos, de nuevo un bucle sobre la lista de parmetros, esta vez recogiendo un fragmento de cdigo para cada parmetro pegajosa. Usted puede utilizar el que ya se recogen LOOP formularios para recopilar slo los no- NIL valores devueltos por set-cookiecdigo .
(Defun set-cookies-cdigo (nombre-funcin de solicitud params) (Bucle de parmetros en los parametros cuando (set-cookie-cdigo de la funcin-param name peticin) recogerlo)) (Defun set-cookie-cdigo (nombre-funcin param peticin) (Desestructuracin-bind (tipo de nombre predeterminado opcional y pegajoso) param (Declare (ignorar por defecto de tipo)) (Si pegajosa `(Cuando, el nombre de (Set-Cookie-header , La solicitud de : Nombre, (smbolo-> galletas-nombre de la funcin de nombre pegajoso nombre) : Valor (princ a cadena, el nombre))))))

Una de las ventajas de la definicin de macros, en trminos de funciones de ayuda de este tipo es que es fcil de asegurarse de que los bits de cdigo que est generando ve bien. Por ejemplo, puede comprobar que el siguiente set-cookiecdigo :
(Set-Cookie-cdigo de solicitud "foo" "(entero x 20: local))

genera algo como esto:


(Cuando x (SET-COOKIE-jefe de la peticin : NOMBRE "com.gigamonkeys.web: foo: x" : VALOR (princ a cadena X)))

Suponiendo que este cdigo se producir en un contexto donde x es el nombre de una variable, esto se ve bien. Una vez ms, han permitido que las macros para destilar el cdigo que necesita para escribir a su esencia - en este caso, los datos que desea extraer de la solicitud y el cdigo HTML que desea generar. Dicho esto, este marco no est destinado a ser el alfa y omega de los entornos de aplicaciones Web - es slo un poco de azcar para que sea un poco ms fcil de escribir aplicaciones simples como el que usted escribe en el captulo 29 . Pero antes de poder llegar a eso, tiene que escribir las entraas de la solicitud de que el captulo 29 de aplicacin ser la interfaz de usuario. Vamos a empezar en el prximo captulo con una versin mejorada de la base de datos que escribi en el captulo 3, esta vez para hacer un seguimiento de los datos ID3 extrados de los archivos MP3.

1 Los lectores nuevos a la programacin Web, probablemente tendr que complementar esta

introduccin con una mayor profundidad-tutorial o dos. Usted puede encontrar un buen conjunto de tutoriales en lnea enhttp://www.jmarshall.com/easy/ .
2 Carga de una sola pgina Web de hecho puede implicar mltiples peticiones - para hacer que el

cdigo HTML de una pgina que contiene las imgenes en lnea, el navegador debe solicitar cada imagen por separado y luego inserte cada uno en el lugar apropiado en el cdigo HTML representado.
3 Gran parte de la complejidad en torno a la programacin Web es el resultado de tratar de evitar

esta limitacin fundamental para proporcionar una experiencia de usuario que es ms como la interactividad proporcionada por las aplicaciones de escritorio.
4 Desafortunadamente, dinmica es un tanto sobrecargada en el mundo de la Web. La frase HTML

dinmico se refiere a HTML con cdigo embebido, por lo general en el lenguaje JavaScript, que puede ser ejecutado en el navegador sin necesidad de mayor comunicacin con el servidor Web. Se utiliza con cierta discrecin, HTML dinmico puede mejorar la usabilidad de una aplicacin basada en Web, ya que, incluso con conexiones a Internet de alta velocidad, lo que hace una peticin a un servidor Web, recibir la respuesta, y la prestacin de la nueva pgina puede tomar una cantidad notable de tiempo. Para confundir ms las cosas, las pginas generadas dinmicamente (es decir, generados en el servidor) tambin podra contener HTML dinmico (la ejecucin de cdigo en el cliente.) A los efectos de este libro, me quedo con la generacin de dinmica no dinmico simple y llano HTML.
5 http://www.fractalconcept.com/asp/html/mod_lisp.html 6 http://lisplets.sourceforge.net/

7 AllegroServe tambin proporciona un marco llamado Webactions que es anloga a JSP en el mundo

Java - en lugar de escribir cdigo que genera HTML, con Webactions que usted escribe las pginas que son esencialmente HTML con un poco de magia foo que se convierte en cdigo que se ejecuta cuando el la pgina est servido. No voy a cubrir Webactions en este libro.
8 Cargando PortableAllegroServe crear algunos otros paquetes para las bibliotecas de

compatibilidad, pero los paquetes que le interesan son los tres.


9 El ~ @ seguido de una nueva lnea dice FORMATO hacer caso omiso de los espacios en blanco

despus de la nueva lnea, que le permite el cdigo de guin muy bien sin necesidad de aadir un montn de espacio en blanco para el cdigo HTML. Desde los espacios en blanco no suele ser significativa en HTML, esto no importa en el navegador, pero hace que el cdigo HTML generado por mirar un poco ms agradable a los seres humanos.
10 FOO es un acrnimo recursivo tautolgica de salida FOO salidas . 11 Para ms informacin sobre el significado de los otros parmetros, consulte la documentacin

AllegroServe y RFC 2109, que describe el mecanismo de la cookie.


12 Es necesario utilizar LET * en lugar de un LET para permitir que las formas del valor por defecto

para los parmetros que se refieren a los parmetros que aparecen anteriormente en la lista de parmetros. Por ejemplo, podra escribir lo siguiente:
(Define-url-funcin (peticin (entero x 10) (entero y (* 2 x))) ...)

y el valor de y , si no explcitamente suministrado, sera el doble del valor de x .

27. Prctica: Una base de datos MP3


En este captulo usted volver a la primera idea explorada en el captulo 3 de la construccin de una base de datos en memoria de las estructuras de datos bsicos de Lisp. Esta vez su objetivo es mantener la informacin que usted va a extraer de una coleccin de archivos MP3 usando la biblioteca ID3v2 del captulo 25. A continuacin, vamos a usar esta base de datos en los captulos 28 y 29 como parte de un servidor basado en Web streaming de MP3. Por supuesto, esta vez alrededor de usted puede utilizar algunas de las caractersticas del lenguaje que ha aprendido ya que el captulo 3 para crear una versin ms sofisticada. La base de datos El problema principal con la base de datos en el captulo 3 es que slo hay una mesa, la lista almacenada en la variable * db * . Otra es que el cdigo no sabe nada acerca de qu tipo de valores se almacenan en diferentes columnas. En el captulo 3 te saliste con la que al utilizar el muy de propsito general EQUAL mtodo para comparar los valores de la columna cuando se selecciona las filas de la base de datos, pero usted ha estado en problemas si hubiera querido para almacenar los valores que no pudieron ser comparados con EQUAL o si haba querido ordenar las filas en la base de datos ya que no hay funcin ordenadora que es tan general como EQUAL . Esta vez va a resolver ambos problemas mediante la definicin de una clase, la tabla , para representar tablas de bases de datos individuales. Cada tabla de ejemplo, se compondr de dos ranuras: una para almacenar los datos de la tabla y otro para informacin sobre las columnas en la tabla que las operaciones de base de datos ser capaz de utilizar.La clase tiene este aspecto:
(Defclass tabla () ((Filas: las filas de acceso: initarg: filas: initForm (make-filas)) (Esquema: el esquema de acceso: initarg: esquema)))

Al igual que en el captulo 3, que pueden representar las filas individuales con plists, pero esta vez vamos a crear una abstraccin que haga que un detalle de implementacin se puede cambiar ms adelante sin demasiados problemas. Y esta vez va a almacenar las filas en un vector en lugar de una lista ya que algunas operaciones que no queremos apoyar, tales como el acceso aleatorio a las filas de un

ndice numrico y la capacidad de ordenar una tabla, puede ser implementado de manera ms eficiente con vectores. La funcin de hacer de las filas se utilizan para inicializar la filas de la ranura puede ser un contenedor simple alrededor HAZ-ARRAY que construye un vaco, el vector ajustable, con un puntero de relleno.
El paquete de
El paquete para el cdigo que va a desarrollar en este captulo es la siguiente: (Defpackage: com.gigamonkeys.mp3 base de datos (: Uso: common-lisp : Com.gigamonkeys.pathnames : Com.gigamonkeys.macro utilidades : Com.gigamonkeys.id3v2) (: Exportacin: * default-table-size * : * Mp3 esquema * : * Mp3 * : Columna : Columna-valor : Delete-all-filas : Delete-filas : Hacer las filas : Extracto de esquema : En : Insertar filas : Carga de base de datos : Hacer que la columna de : Make-esquema : Mapa de las filas : Juego : No-anulable : Ensima fila : Aleatorio de seleccin : Esquema de : Seleccione : La reproduccin de mesa : Sort-filas : Mesa : Mesa de tamao : Con columnas de valores)) El : El uso seccin le da acceso a las funciones y macros cuyos nombres se exportan desde los paquetes definidos en el captulo 15, 8 y 25 y las exportaciones: las exportaciones de la seccin de la API de esta biblioteca se ofrecen, que se usar en el captulo 29. (Defparameter * default-table-* El tamao de 100) (Defun hacer las filas (y opcional (* El tamao de default-table-size *)) (Make-matriz de tamao: regulable t: relleno triple 0))

Para representar el esquema de una tabla, es necesario definir otra clase, la columna , cada instancia de la cual contendr la informacin sobre una columna de la tabla: su nombre, cmo comparar los valores en la columna por la igualdad y realizar el pedido, un valor por defecto, y un funcin que se utilizar para

normalizar los valores de la columna al insertar datos en la tabla y cuando se consulta la tabla. El esquema de la ranura llevar a cabo una lista de columnas de los objetos. La definicin de clase tiene este aspecto:
(Defclass columna () ((Nombre : Lector de nombre : Initarg: nombre) (Igualdad-predicado : Lector de la igualdad-predicado : Initarg: la igualdad-predicado) (Comparador : Lector de comparacin : Initarg: comparador) (Por defecto el valor : Lector por defecto el valor : Initarg: por defecto el valor : InitForm cero) (Valor normalizador : Lector de valor normalizador : Initarg: valor normalizador : InitForm # '(lambda (columna v) (declare (ignorar la columna)) v))))

El predicado de igualdad- y la comparacin ranuras de una columna de objeto de mantener las funciones utilizadas para comparar los valores de la columna dada la equivalencia y el pedido. Por lo tanto, una columna que contiene los valores de cadena pueden tener = STRING como su predicado de la igualdad y lacadena de texto < como comparador , mientras que un nmero de columnas que contienen podra tener = y < . Los de valor por defecto- y valor normalizador- ranuras se utilizan cuando la insercin de filas en la base de datos y, en el caso de valor-normalizador , cuando se consulta la base de datos. Cuando se inserta una fila en la base de datos, si no se proporciona un valor para una columna en particular, puede utilizar el valor almacenado en la columna de 's -valor por defecto de la ranura. Entonces, el valor impaga o de otra manera - se normaliza hacindola pasar y el objeto de columna para la funcin almacenada en el valor-normalizador ranura. Se pasa la columna en caso de que el valor normalizador de la funcin tiene que usar algunos datos relacionados con el objeto de columna. (Vas a ver un ejemplo de esto en la siguiente seccin.) Tambin se debe normalizar los valores pasados en las consultas antes de compararlos con los valores en la base de datos.

Por lo tanto, el valor normalizador de la responsabilidad 's es principalmente para devolver un valor que puede ser segura y correcta pasa a la igualdad-predicado yde comparacin funciones. Si el valor normalizador no puede entender por un valor adecuado para volver, se puede sealar un error. La otra razn para normalizar los valores antes de almacenarlas en la base de datos es para guardar la memoria y ciclos de CPU. Por ejemplo, si usted tiene una columna que va a contener los valores de cadena, pero el nmero de series diferentes que se almacena en la columna es pequeo - por ejemplo, la columna de gnero en la base de datos MP3 - usted puede ahorrar espacio y la velocidad utilizando el valor normalizador de pasante de las cuerdas (traducir todas las string = valores a un objeto de cadena nica). Por lo tanto, tendr slo en forma de cadenas, ya que hay muchos valores distintos, independientemente de cuntas filas hay en la tabla, y se puede utilizar EQL para comparar los valores de columna en lugar de la lenta STRING = . 1 Definicin de un esquema Por lo tanto, para hacer una instancia de la tabla , que necesita para construir una lista de las columnas de objetos. Se puede construir la lista a mano, utilizando LISTA yMAKE INSTANCIA- . Pero pronto se dar cuenta de que ests haciendo con frecuencia a muchos objetos de la columna con la misma comparacin y combinaciones de predicado de igualdad-. Esto se debe a la combinacin de un comparador y predicado de igualdad esencialmente define un tipo de columna. Sera bueno si hubiera una manera de dar los nombres de esos tipos que le permiten decir simplemente que una determinada columna es una columna de cadena, en lugar de tener que especificar <STRING como su comparacin y= STRING como su predicado de igualdad. Una forma es definir una funcin genrica, que-columna , de esta manera:
(Defgeneric que la columna de (nombre de tipo opcional y por defecto el valor))

Ahora usted puede poner en prctica los mtodos de esta funcin genrica que se especializan en el tipo de EQL specializers y el retorno de la columna los objetos con las ranuras llenas con los valores adecuados. Esta es la funcin genrica y los mtodos que definen los tipos de columna para el tipo de nombres de cadena y el nmero de :
(Defmethod que la columna de (nombre (tipo (cadena eql ')) y opcional default-valor)

(Marca de instancia "Columna : Nombre : # Comparador 'cadena < : La igualdad-predicado # 'cadena = : Por defecto el valor por defecto de valor : Valor normalizador # 'no acepta valores NULL)) (Defmethod que la columna de (nombre (tipo (nmero eql ')) y opcional default-valor) (Marca de instancia "Columna : Nombre : Comparador # '< : La igualdad-predicado # '= : Por defecto el valor por defecto de valor))

La siguiente funcin, que no acepta valores NULL , utilizado como valor normalizador para la cadena de columnas, simplemente devuelve el valor que se le da menos que el valor es NIL , en cuyo caso se indica un error:
(Defun-no anulable (columna de valor) (O el valor (de error "Columna ~ uno no puede ser nulo" (Nombre de la columna))))

Esto es importante porque cadena < y = CADENA indicar un error si se llama a NIL , es mejor coger los valores de los malos antes de entrar en la mesa ms que al intentar usarlos. 2 Otro tipo de columna que necesita para que la base de datos MP3 es un internado de cadena cuyos valores estn internados como se indic anteriormente. Ya que se necesita una tabla hash en la que los valores internos, debe definir una subclase de la columna , internados los valores de la columna , que aade una ranura cuyo valor es la tabla hash se utiliza para internar. Para llevar a cabo la internacin actual, tambin tendr que proporcionar una : initForm de valor normalizador de la funcin que los internos el valor en la columna de los internados los valores de la tabla hash. Y debido a que una de las razones principales a los valores internos es permitir que usted utilice EQL como predicado de igualdad, tambin debe agregar un : initForm para la igualdadpredicado de eql # ' .
(Defclass internado-los valores de la columna (columna) ((Internados los valores : Lector de internamiento de los valores : InitForm (make-hash-table: test # 'igual)) (Igualdad-predicado: eql initForm # ') (Valor normalizador: initForm # 'pasante para la columna))) (Defun pasante para la columna (valor de la columna) (Let ((hash (internados los valores de la columna))) (O (GetHash (no anulable columna de valor) de hash)

(Setf (hash GetHash valor) de valor))))

A continuacin, puede definir una columna de marca mtodo especializado en el nombre del internado-string que devuelve una instancia deinternado-los valores de la columna .
(Defmethod que la columna de (nombre (tipo (eql internado-string)) y opcional el valor por defecto) (Marca de instancia "Internados los valores de la columna : Nombre : # Comparador 'cadena < : Por defecto el valor por defecto de valor))

Con estos mtodos definidos en la columna de hacer , ahora se puede definir una funcin, hacen del esquema , que crea una lista de columnas de los objetos de una lista de especificaciones de la columna que consiste en un nombre de columna, escriba un nombre de columna, y, opcionalmente, un defecto valor.
(Defun crea-esquema (especificaciones) (Mapcar # '(lambda (columna-spec) (aplicar #' make-columna de la columna de las especificaciones)) especificaciones))

Por ejemplo, puede definir el esquema de la tabla que se utilizar para almacenar los datos extrados de archivos MP3, como esto:
(* Defparameter mp3 esquema * (Make-esquema '((: Cadena de archivo) (: Gnero de internados-cadena "desconocido") (: Artista internado-cadena "desconocido") (: lbum internado-cadena "desconocido") (: Cadena de la cancin) (: Nmero de pista 0) (: El nmero de ao 0) (: ID3-el tamao del nmero))))

Para hacer una tabla real para la celebracin de informacin acerca de MP3, se pasa mp3 * esquema * como : esquema de initarg de MAKE-INSTANCIA .
(Defparameter * mp3 * Mesa (make-instance ': esquema de mp3 * esquema *))

Insertar valores Ahora est listo para definir la operacin de la primera tabla, insertar fila , que toma un plist de los nombres y los valores y una mesa y agrega una fila a la tabla que contiene los valores dados. La mayor parte del trabajo se hace en una funcin auxiliar, normalizar la fila , que construye un plist con un valor en default, normalizado para cada columna, utilizando los valores de los nombres y los valores si est disponible y el valor por defecto para la columna cuando no.

(Defun insercin de registro (nombres y los valores de la tabla) (Vector de empuje se extienden (normalizar la fila de nombres-y-valores (tabla de esquema)) (filas de la tabla))) (Defun normalizar la fila (los nombres de las y los valores de esquema) (Circular para la columna en el esquema el nombre = (Nombre de la columna) para el valor = (o (GETF nombres-y los valores de nombre) (por defecto valor de la columna)) recoger el nombre recoger (para la normalizacin de la columna columna de valor)))

Vale la pena definir una funcin auxiliar por separado, para la normalizacin de la columna , que toma un valor y una columna de objeto y devuelve el valor normalizado porque vas a necesitar para llevar a cabo la normalizacin misma en los argumentos de la consulta.
(Defun normalizacin para la columna (valor de la columna) (Funcall (valor normalizador de la columna) columna de valor))

Ahora est listo para combinar el cdigo de base de datos con cdigo de los captulos anteriores para construir una base de datos de los datos extrados de los archivos MP3. Se puede definir una funcin, archivo-> fila , que utiliza lectura id3 de la biblioteca de ID3v2 para extraer la etiqueta ID3 de un archivo y lo convierte en un plist que se puede pasar a la fila de insercin .
(Defun archivo-> fila (archivo) (Let ((ID3 (ID3 del archivo de lectura))) (Lista de : Archivo (namecadena (truename archivo)) : El gnero (traducido-gnero ID3) : Artista (artista ID3) : Disco (lbum ID3) : Cancin (cancin ID3) : Pista (analizar-track (pista ID3)) : Ao (anlisis sintctico-ao (ao ID3)) : ID3-size (tamao ID3))))

Usted no tiene que preocuparse por la normalizacin de los valores desde el inserto fila se encarga de eso para usted. Usted, sin embargo, tiene que convertir los valores de cadena devueltos por la pista y el ao en nmeros. El nmero de pista en la etiqueta ID3 a veces se almacenan como la representacin ASCII del nmero de pista y, a veces como un nmero seguido de una barra seguida del nmero total de pistas en el lbum. Puesto que slo se preocupan por el nmero de pista actual, se debe utilizar el : final argumento para PARSE-INTEGER para especificar que se debe analizar slo hasta la barra, en su caso. 3
(Pista de defun-parse pista () (Cuando la pista (nmero entero de anlisis-pista: final (posicin # \ / pista))))

(Defun parse aos (ao) (Cuando el ao (anlisis sintctico-entero ao)))

Por ltimo, puedes poner todas estas funciones juntas, junto con el paseo de directorio de la biblioteca porttil rutas y mp3-p de la biblioteca ID3v2, para definir una funcin que carga una base de datos MP3 con datos extrados de todos los archivos MP3 que se puede encontrar en un directorio determinado.
(Defun de carga de base de datos (dir db) (Let ((conteo de 0)) (Walk-directorio dir # '(Lambda (archivo) (Princ # \.) (Incf cuenta) (Insercin de registro (archivo-> fila de archivo) db)) : Test # 'mp3-p) (Formato t "del ~ del ~ d & Loaded archivos en la base de datos." Cuenta)))

Consulta de la Base de Datos Una vez que haya cargado la base de datos con los datos, usted necesitar una manera de consultar. Para la aplicacin de MP3 tendr una funcin de consulta un poco ms sofisticado de lo que escribi en el captulo 3. En esta ocasin quiere no slo ser capaz de seleccionar las filas coincidan con los criterios particulares, sino tambin para limitar los resultados a las columnas en particular, para limitar los resultados a las filas nicas, y tal vez para ordenar las filas por columnas particulares. En consonancia con el espritu de la teora de base de datos relacional, el resultado de una consulta ser una nueva tabla de objeto que contiene las filas y columnas deseadas. La funcin de consulta que voy a escribir, seleccionar , est vagamente inspirado en el SELECT declaracin del Lenguaje de Consulta Estructurado (SQL). Tomar cinco parmetros de palabra clave: : de , los : columnas y donde: y : distintos , y : por fin- . El : de argumento es la tabla de objeto que desea consultar. El: columnas argumento especifica las columnas que deben incluirse en el resultado. El valor debe ser una lista de nombres de columna, un solo nombre de columna, o unacamiseta , por defecto el, es decir, devolver todas las columnas. El : en el argumento, si existe, debe ser una funcin que acepta una fila y devuelve true si se debe incluir en los resultados. En un momento, voy a escribir dos funciones, a juego y en el que las funciones de retorno adecuadas para su uso como : dnde argumentos. El: orden por el argumento, si se las suministra, debe ser una lista de nombres de columna,

los resultados se ordenan por las columnas mencionadas. Al igual que el: columnas de argumento, se puede especificar una sola columna utilizando slo el nombre, lo que equivale a una lista de un elemento que contiene el mismo nombre. Por ltimo, el : distinta argumento es un booleano que indica si se debe eliminar las filas duplicadas de los resultados. El valor por defecto : distinta es NIL . Estos son algunos ejemplos del uso de seleccin :
;; Seleccione todas las filas donde el: columna de artista es "Green Day" (Seleccione: * de * mp3: si (a juego * mp3 *: artista de "Green Day")) ;; Seleccione una lista ordenada de artistas con canciones del gnero "Rock" (Seleccione : Columnas: artista : De * mp3 * : Si (a juego * mp3 *: gnero "Rock") : Distintos t : Orden por: artista)

La implementacin de seleccionar a sus funciones de ayuda inmediata es la siguiente:


(Defun de seleccin (y la llave (columnas t), desde donde distintos order-by) (Let ((filas (filas de la)) (Esquema (esquema de))) (Cuando en (Filas setf (restringir las filas de las filas donde))) (A menos que (t columnas eql) (Setf esquema (extracto del esquema (columnas mklist) del esquema)) (Setf filas (columnas de esquema de proyecto de filas))) (Cuando distinta (Setf filas (filas distintas de las filas de esquema))) (Cuando por fin (Setf (filas ordenadas filas de filas de esquema (mklist order-by)))) (Vase el cuadro que de instancia ': filas filas: el esquema de esquema))) (Defun mklist (cosa) (Si (lo listp) cosa (cosa que la lista))) (Defun extracto del esquema (las columnas de los nombres de esquema) (Bucle de C en la columna de nombres de recolectar (encontrar-de esquema de columna c))) (Defun encuentra la columna de (nombre de columna del esquema) (O (encuentra la columna de nombre de esquema: nombre de la tecla # ') (Error de "No hay columna: ~ a en el esquema: ~ un" esquema de nombrecolumna))) (Defun restringir las filas (filas donde) (Quitar-si-no donde las filas)) (Defun proyecto de columnas (filas de esquema) (Mapa de vectores "(extractor de esquema) filas))

(Defun distinta de filas (filas de esquema) (Remove-duplica las filas (fila: Prueba de la igualdad-probador de esquema))) (Defun ordenados de filas (filas del esquema para-by) (Ms o menos (copy-ss filas) (fila comparador de orden por el esquema)))

Por supuesto, la parte realmente interesante de la seleccin es la forma de implementar las funciones de extraccin , la fila de igualdad de probador- , yla fila de comparacin- . Como se puede ver por la forma en que estamos acostumbrados, cada una de estas funciones debe devolver una funcin. Por ejemplo, los proyectos de columnas utiliza el valor devuelto por el extractor como el argumento de la funcin a la MAP . Dado que el objetivo de los proyectos de las columnas es para devolver un conjunto de filas con valores de columna slo algunos, se puede inferir que extractora devuelve una funcin que toma una fila como un argumento y devuelve una nueva fila que contiene slo las columnas especificadas en el esquema que ha pasado . He aqu cmo usted puede ponerlo en prctica:
(Defun extractor (esquema) (Let (((esquema de nombres mapcar # nombre))) # '(Lambda (fila) (Bucle de c en los nombres de recopilar c recolectar (fila GETF c)))))

Tenga en cuenta cmo se puede hacer el trabajo de extraccin de los nombres del esquema fuera del cuerpo del clausura: desde el clausura ser llamado muchas veces, que quiere que haga el trabajo lo menos posible cada vez que se llama. Las funciones de fila del probador la igualdad y la fila de comparacin- se aplican de manera similar. Para decidir si dos filas son equivalentes, es necesario aplicar el predicado de igualdad adecuado para cada columna de los valores de las columnas correspondientes. Recuerde del Captulo 22 que el LOOP clusula siempredevolver NIL tan pronto como un par de valores falla su prueba o har que el LOOP para volver T .
(Defun fila de la igualdad-tester (esquema) (Let (((esquema de nombres mapcar # nombre)) (Pruebas (igualdad-predicado mapcar # 'esquema))) # '(Lambda (ab) (Circular el nombre en los nombres y la prueba en las pruebas de siempre (prueba de funcall (GETF un nombre) (nombre de GETF b))))))

Dos filas de pedido es un poco ms complejo. En Lisp, las funciones de comparacin devuelve true si el primer argumento debe ser ordenada por delante del segundo y NIL lo contrario. Por lo tanto, un NIL puede significar que el segundo argumento

debe ser ordenada antes de la primera o que son equivalentes. Usted quiere que sus comparadores de filas que se comporten de la misma manera: de retorno T si la primera fila se deben ordenar por delante del segundo y NIL lo contrario. As, al comparar dos filas, usted debe comparar los valores de las columnas que usted est de clasificacin de, en orden, usando el comparador adecuado para cada columna. En primer lugar llamar a la comparacin con el valor de la primera fila como el primer argumento. Si la comparacin devuelve true, lo que significa la primera fila definitivamente debe ser ordenada por delante de la segunda fila, por lo que puede regresar inmediatamente T . Pero si la comparacin columna devuelve NIL , entonces usted necesita para determinar si esto es debido a que el segundo valor debe ordenar por delante del primer valor o porque son equivalentes. As que usted debe llamar a la comparacin de nuevo con los argumentos inversos. Si la comparacin devuelve true este tiempo, significa que el tipo la segunda columna de valor antes de la primera y por lo tanto la segunda fila por delante de la primera fila, para que pueda volver NIL inmediatamente. De lo contrario, los valores de las columnas son equivalentes, y lo que necesita para pasar a la siguiente columna. Si usted recibe a travs de todas las columnas sin valor de una fila de haber ganado la comparacin, a continuacin, las filas son equivalentes, y se vuelve NIL . Una funcin que implementa este algoritmo es el siguiente:
(Defun fila de comparacin (columna los nombres de esquema) (Let (((comparador comparadores mapcar # '(extracto de esquema de columna de nombres de esquema)))) # '(Lambda (ab) (Circular para el nombre en la columna de nombres para comparador en comparadores por un valor = (GETF un nombre) para el valor b = (b GETF nombre) cuando (comparador funcall un valor-b-value) return t cuando (comparador funcall b-el valor de un valor) RETURN NIL por ltimo (cero retorno)))))

Funciones Compartidas El : dnde argumento para seleccionar puede ser cualquier funcin que toma un objeto de fila y devuelve true si se debe incluir en los resultados. En la prctica, sin embargo, que rara vez se necesita toda la potencia de cdigo arbitrario a expresar los criterios de consulta. As que usted debe ofrecer dos funciones, a juego y en el que se basarn las funciones de consulta que le permiten expresar los tipos

comunes de las consultas y que se ocupan de la utilizacin de los predicados de igualdad de propios y normalizadores de valor para cada columna. El hospicio de consulta funcin constructora se emparejan , que devuelve una funcin que coincide con las filas con valores de la columna especficos. Ya has visto cmo se han utilizado en los ejemplos anteriores de seleccin . Por ejemplo, esta llamada a la coincidencia :
(Que coinciden con * mp3 *: artista de "Green Day")

devuelve una funcin que corresponda a las filas cuyos : artista de valor es "Green Day". Tambin puede pasar varios nombres y los valores, la funcin devuelve coincide en que todas las columnas coinciden. Por ejemplo, el siguiente devuelve un clausura de filas que coincide con la que el artista es "Green Day" y el lbum es "American Idiot":
(Que coinciden con * mp3 *: artista de "Green Day": lbum "American Idiot")

Usted tiene que pasar a juego el objeto de tabla, ya que necesita tener acceso al esquema de la tabla con el fin de llegar a los predicados de igualdad y las funciones de valor Normalizer para las columnas que coincide en contra. Usted construye la funcin que devuelve a juego de funciones ms pequeas, cada una responsable de la correspondiente valor de una columna. Para construir estas funciones, debe definir una funcin, la columna-comparador , que tiene una columna de objeto y un valor sin normalizar que desea hacer coincidir y devuelve una funcin que acepta una sola fila y devuelve true cuando el valor de la columna que figura en la fila coincide con la versin normalizada del valor dado.
(Defun columna comparador (valor de la columna) (Let ((nombre de (Nombre de la columna)) (Predicado (la igualdad predicado de la columna)) (Normalizado (normalizar para la columna de columna de valor))) # '(Lambda (fila) (predicado funcall (nombre GETF fila) normalizado))))

A continuacin, crear una lista de coincidencia de las funciones de columna para los nombres y valores que le interesan con las siguientes funciones,la columna de comparadores :
(Defun columna de comparadores (esquema de nombres-y-valores) (Bucle de (nombre y valor) en los nombres de las y los valores por cddr # ' cuando el valor de recopilar (Columna-comparador (encontrar la columna de nombre de esquema) de valor)))

Ahora usted puede aplicar a juego . Una vez ms, tenga en cuenta que hacer un trabajo lo ms posible fuera de la clausura con el fin de hacerlo una sola vez en lugar de una vez por cada fila de la tabla.
(Coincidencia de defun (mesa y dems nombres-y-valores) "Construir una funcin que corresponde en las filas con los valores de determinadas columnas." (Let ((comparadores (columna-comparadores (esquema de la tabla) los nombres y los valores))) # '(Lambda (fila) (Todos los # '(lambda (comparador) (fila funcall comparador)) comparadores))))

Esta funcin es un poco de un laberinto de curvas de los clausuras, pero vale la pena contemplar por un momento para hacerse una idea de las posibilidades de la programacin con funciones como objetos de primera clase. El trabajo de adaptacin es devolver una funcin que se invoca en cada fila de una tabla para determinar si se debe incluir en la nueva tabla. Por lo tanto, a juego devuelve un clausura con un parmetro, la fila . Recordemos ahora que la funcin CADA toma una funcin determinante como su primer argumento y devuelve true si y slo si, la funcin devuelve el valor true cada vez que se aplica a un elemento de la lista que se pasa en TODOS LOS segundo argumento 's. Sin embargo, en este caso, la lista que se pasa a TODOS en s es una lista de funciones, los comparadores de columna. Lo que quiero saber es que cada comparador columna, cuando se invoca en la fila que se est probando actualmente, devuelve true. Por lo tanto, como argumento determinante a TODOS , se pasa otro clausura que funcall s del comparador columna, pasando la fila. Otra de las funciones de coincidencia que de vez en cuando encontrars til es en el , que devuelve una funcin que coincide con las filas en una columna en particular se encuentra en un conjunto determinado de valores. Usted define en tomar dos argumentos: un nombre de columna y una tabla que contiene los valores que desea hacer coincidir. Por ejemplo, suponga que desea encontrar todas las canciones de la base de datos MP3 que tienen nombres al igual que una cancin interpretada por las Dixie Chicks. Usted puede escribir que, cuando la clusula utilizando en el y un sub selecciona de esta manera: 4
(Seleccione : '(Columnas: Artista: Cancin) : De * mp3 * : Si (en: la cancin (Seleccione : Columnas: la cancin

: De * mp3 * : Si (a juego * mp3 *: artista "Dixie Chicks"))))

Aunque las consultas son ms complejas, la definicin de en es mucho ms simple que la de juego .
(Defun en (nombre-columna de tabla) (Let (prueba de ((la igualdad-predicado (encontrar la columna de nombre-columna (tabla de esquema)))) (Los valores ('# lista' mapa (lambda (r) (r GETF nombre-columna)) (filas de la tabla)))) # '(Lambda (fila) (Miembros (fila GETF nombre-columna) los valores de: prueba de ensayo))))

Llegar a los resultados Desde seleccin vuelve otra mesa , hay que pensar un poco acerca de cmo desea llegar a la fila individual y los valores de las columnas de una tabla. Si ests seguro de que nunca volver a cambiar la forma de representar los datos en una tabla, puede simplemente hacer que la estructura de una parte de la tabla de la API - que la tabla tiene una ranura de filas que es un vector de plists - y utilizar todas las funciones normales de Common Lisp para la manipulacin de vectores y plists para llegar a los valores de la tabla. Pero que la representacin es realmente un detalle interno que es posible que desee cambiar. Adems, no necesariamente quiere otro cdigo manipulacin de las estructuras de datos directamente - por ejemplo, usted no quiere que nadie utilice SETF poner un valor de la columna sin normalizar en una fila. Por lo tanto, podra ser una buena idea definir una abstracciones que proporcionan las operaciones que desea apoyar. Entonces, si usted decide cambiar la representacin interna ms tarde, tendr que cambiar slo la puesta en prctica de estas funciones y macros. Y mientras que Common Lisp no permite prevenir absolutamente la gente de conseguir en "internos" de datos, proporcionando una API oficial de que por lo menos dejar claro dnde est el lmite. Probablemente la cosa ms comn que usted tendr que hacer con los resultados de una consulta es para repetir las filas individuales y extraer los valores especficos de la columna. As que hay que proporcionar una forma de hacer ambas cosas sin tocar la filas vector directamente o mediante GETF para llegar a los valores de columna en una fila. Por el momento estas operaciones son triviales de implementar, sino que son meramente contenedores de todo el cdigo que iba a escribir si no tienen estas abstracciones. Puede proporcionar dos formas de iterar sobre las filas de una tabla:

Una macro de hacer filas , que ofrece una construccin de bucle de base, y la funcin de unmapa de las filas , lo que genera una lista que contiene los resultados de la aplicacin de una funcin para cada fila de la tabla. 5
(Defmacro hacer las filas ((fila de la tabla) y del cuerpo del cuerpo) `(Bucle para, a travs de la fila (fila, tabla) lo hace, el cuerpo @)) (Defun mapa de filas (tabla de fn) (Bucle de fila a travs de (filas de la tabla) recoger (funcall fila fn)))

Para llegar a valores de columna individuales dentro de una fila, usted debe proporcionar una funcin, la columna de valor , que tiene una fila y un nombre de columna y devuelve el valor apropiado. Una vez ms, es una envoltura alrededor de la trivial cdigo que iba a escribir de otra manera. Pero si se cambia la representacin interna de una tabla ms adelante, los usuarios de la columna de valor no tiene que ser ms sabios de la.
(Defun columna valor (la fila de nombre de columna) (Fila GETF nombre-columna))

Mientras que la columna de valor es una abstraccin suficiente para ir a parar los valores de columna, a menudo se desea para llegar a los valores de varias columnas a la vez.As que usted puede proporcionar un poco de azcar sintctico, una macro, con columnas de valores , que se une un conjunto de variables a los valores extrados de una fila con los nombres de palabras clave correspondientes. As, en lugar de escribir esto:
(Hacer las filas (fila de la tabla) (Let (cancin ((columna-valor de la fila: la cancin)) (Artista (columna-valor de la fila: artista)) (lbum (columna-valor de la fila: lbum))) (Formato t "~ ~ a por una de ~ a ~%" lbum artista de la cancin)))

simplemente hay que escribir lo siguiente:


(Hacer las filas (fila de la tabla) (Con columnas de valores (lbum de la cancin del artista) la fila (Formato t "~ ~ a por una de ~ a ~%" lbum artista de la cancin)))

Una vez ms, la ejecucin real no es complicado si se utiliza el una sola vez macro desde el captulo 8.
(Defmacro con columnas los valores de ((y resto VARs) y la fila del cuerpo del cuerpo) (Una sola vez (fila) `(Vamos, (columna de enlaces fila vars), el cuerpo de @))) (Defun columna de enlaces (fila vars) (Bucle de v en Vars recopilar `(, v (columna de valor, fila (como palabra clavev)))))

(Defun-como la palabra clave (smbolo) (Pasante (smbolo de nombre de smbolo): palabra clave))

Finalmente, usted debe proporcionar abstracciones para obtener el nmero de filas de una tabla y para acceder a una fila determinada por el ndice numrico.
(Tabla defun tabla de tamao () (Longitud (filas de la tabla))) (Cuadro N defun ensima fila () (Aref (filas de la tabla) n))

Otras operaciones de base de datos Por ltimo, deber poner en prctica algunas operaciones otras bases de datos que va a necesitar en el captulo 29. Los dos primeros son los anlogos de la sentencia SQL DELETEcomunicado. La funcin de eliminar las filas se utiliza para eliminar filas de una tabla que coincidan con determinados criterios. Al igual que seleccione , se requiere: a partir de ya : cuando los argumentos de palabras clave. A diferencia de seleccin , que no devuelve una nueva tabla - que en realidad modifica la tabla que se pasa como : del argumento.
(Defun eliminar las filas y la llave (de donde) (Circular con filas = (filas de) con la tienda-idx = 0 para la lectura de 0 idx para la fila a travs de las filas do (setf (filas Aref lectura idx) nil) a menos que (funcall en fila) hacer (Setf (filas Aref tienda idx) fila) (Incf tienda idx) por ltimo, (setf (puntero de relleno filas) tienda-idx)))

En aras de la eficiencia, es posible que desee proporcionar una funcin separada para eliminar todas las filas de una tabla.
(Defun eliminar de todas las filas (tabla) (Setf (filas de la tabla) (maquillaje filas * default-table-size *)))

Las restantes operaciones de tabla en realidad no se asignan a las operaciones normales de bases de datos relacionales, pero ser til en la aplicacin del navegador de MP3. La primera es una funcin para ordenar las filas de una tabla en su lugar.
(Defun tipo de filas (de mesa y resto de las columnas de nombres) (Setf (filas de la tabla) (sort (filas de la tabla) (comparacin de fila de las columnas de los nombres de tabla de esquema ()))) tabla)

Por otro lado, en la aplicacin del navegador de MP3, tendr una funcin que baraja las filas de una tabla en su lugar con la funcin nshuffle-vector del Captulo 23.
(Tabla defun shuffle-table () (Nshuffle-vector (filas de la tabla)) tabla)

Y, por ltimo, una vez ms a los efectos de el navegador de archivos MP3, usted debe proporcionar una funcin que selecciona n filas al azar, la devolucin de los resultados como una tabla nueva. Tambin utiliza nshuffle-vector junto con una versin de una muestra al azar basado en el algoritmo S de Donald Knuth El Arte de la Programacin de Computadoras, Volumen 2: Algoritmos Seminumerical , tercera edicin (Addison-Wesley, 1998) que he comentado en el captulo 20.
(Defun aleatorio de seleccin (tabla n) (Marca de instancia 'Mesa : Esquema (tabla de esquema) : Filas (nshuffle-vector (una muestra al azar (filas de la tabla) n)))) (Defun una muestra al azar (vector n) "Basado en el algoritmo S de Knuth. TAOCP, vol. 2. P. 142" (Bucle con seleccionado = (make-array n: relleno triple 0) para idx desde 0 hacer (Circular con el que la seleccin = (- n (longitud seleccionada)) para el resto = (- (longitud del vector) idx) mientras que (> = (* restante (al azar 1.0)) para seleccionar-) hacer (incf idx)) (Vector de empuje (Aref vector idx) seleccionado) cuando (= (longitud seleccionada) n) de retorno seleccionado))

Con este cdigo estar listo, en el captulo 29, para crear una interfaz web para navegar por una coleccin de archivos MP3. Pero antes de llegar a eso, es necesario implementar la parte del servidor que archivos MP3 corrientes que utilizan el protocolo Shoutcast, que es el tema del siguiente captulo.

1 La teora general detrs de los objetos internar es que si vas a comparar un valor particular,

muchas veces, vale la pena pagar el costo de internar a la misma. El valor normalizador se ejecuta una vez cuando se inserta un valor en la tabla y, como veremos, una vez al comienzo de cada consulta. Ya que una consulta puede implicar la invocacin de la igualdad de predicado de una vez por fila de la tabla, el coste amortizado de internar a los valores rpidamente se aproxima a cero.
2 Como siempre, la primera causal de la exposicin concisa de los libros de programacin es un

correcto manejo, en el cdigo de produccin que probablemente querrs para definir el tipo de error propio, como el siguiente, y sealar que en su lugar:
(Error de 'ilegal-columna-valor: valor: columna de la columna)

Entonces te gustara que pensar en donde usted puede agregar reinicios que podran ser capaces de recuperarse de esta condicin. Y, por ltimo, en cualquier aplicacin que usted podra establecer manejadores de condiciones que elegir entre los reinicios.
3 Si los archivos MP3 tienen datos con formato incorrecto en los bastidores de las orugas y el ao,

PARSE-INTEGER puede ser seal de un error. Una manera de lidiar con esto es pasar PARSEINTEGER el: basura permitido argumento de T , lo que har que se ignore cualquier basura no numrico que sigue al nmero y devolver NIL , si no hay ningn nmero se puede encontrar en la cadena. O, si usted quiere que la prctica en el uso del sistema de condicin, se podra definir un error y sealar que de estas funciones cuando los datos no se ha formado, as como establecer unos pocos se reinicia para permitir que estas funciones para recuperarse.
4 Esta consulta tambin devuelve todos los temas interpretados por las Dixie Chicks. Si desea

limitarlo a canciones de otros artistas que las Dixie Chicks, se necesita una ms compleja : en donde funcionar.Desde el : donde el argumento puede ser cualquier funcin, es ciertamente posible, porque puede eliminar canciones de las Dixie Chicks propios con esta consulta:
(Let * ((Dixie-pollitos (que coinciden con * mp3 *: artista "Dixie Chicks")) (Del mismo tema (en: cancin (seleccione: columnas: Cancin: de * mp3 *: cuando los polluelos de Dixie-))) (Consulta # '(lambda (fila) (y (not (funcall Dixie-pollitos fila)) (funcall misma cancin de la fila))))) (Seleccione: columnas '(: Artista: Cancin): a partir de MP3 * *: en la consulta))

Esto, obviamente, no es tan conveniente. Si se va a escribir una aplicacin que tena que hacer un montn de preguntas complejas, es posible que desee considerar la posibilidad dar con un lenguaje de consulta ms expresivo.
5 La versin de LOOP cabo en el MIT antes de Common Lisp fue normalizado incluido un

mecanismo para ampliar el LOOP gramtica para apoyar iteracin sobre nuevas estructuras de datos. Algunas comunes implementaciones de Lisp que heredaron de su LOOP aplicacin de ese cdigo base todava puede apoyar a esa instalacin, lo que hara hacer las filas y filas de mapas necesarios menos.

28. Prctico: un servidor Shoutcast


En este captulo se va a desarrollar otra parte importante de lo que eventualmente va a ser una aplicacin basada en Web para archivos MP3 en streaming, es decir, el servidor que implementa el protocolo de streaming Shoutcast para realmente MP3 a los clientes, tales como iTunes, XMMS, 1 o Winamp. El Protocolo de Shoutcast El protocolo Shoutcast fue inventado por la gente de Nullsoft, los creadores del software de Winamp MP3. Fue diseado para apoyar la difusin de audio por Internet - DJ Shoutcast enviar datos de audio desde sus computadoras personales a un servidor Shoutcast central que se da la vuelta y los arroyos que a todos los detectores conectados. El servidor al que va a construir es de hecho slo la mitad de un servidor Shoutcast verdad - usted utiliza el protocolo que los servidores Shoutcast utilizar para escuchar archivos MP3 a los oyentes, pero su servidor ser capaz de servir slo las canciones que ya estn almacenados en el sistema de archivo de la equipo donde se ejecuta el servidor. Usted tiene que preocuparse por slo dos partes del protocolo de Shoutcast: la peticin realizada por un cliente con el fin de comenzar a recibir un arroyo y el formato de la respuesta, incluyendo el mecanismo por el cual los metadatos acerca de qu cancin se est reproduciendo actualmente est integrado en la corriente . La solicitud inicial del cliente de MP3 con el servidor Shoutcast tiene un formato normal de solicitud HTTP. En respuesta, el servidor Shoutcast enva una respuesta de helado que se ve como una respuesta HTTP, excepto con la cadena de "hielo" 2 en lugar de la cadena de la versin normal de HTTP y con diferentes encabezados. Despus de enviar las cabeceras y una lnea en blanco, el servidor transmite una cantidad potencialmente infinita de datos MP3. La nica cosa difcil sobre el protocolo Shoutcast es la manera de los metadatos de las canciones transmitidos en flujo est incrustado en los datos enviados al cliente. El problema que enfrentan los diseadores de Shoutcast es proporcionar una forma para que el servidor Shoutcast para comunicar informacin de nuevos ttulos para el cliente cada vez que empez a tocar una nueva cancin para que el cliente poda

ver en su interfaz de usuario. (Recuerde que en el captulo 25 que el formato MP3 no hace ninguna provisin para los metadatos de la codificacin.) Mientras que uno de los objetivos de diseo de ID3v2 haba sido para que sea ms adecuado para su uso cuando se transmite MP3, la gente de Nullsoft decidi seguir su propia ruta e inventar un nuevo esquema que es bastante fcil de implementar tanto en el lado del cliente y el servidor. Eso, por supuesto, era ideal para ellos, ya que tambin fueron los autores de su propio MP3 del cliente. Su plan era simplemente ignorar la estructura de archivos MP3 e incrustar un pedazo de s mismo de la delimitacin de los metadatos de cada n bytes. El cliente se encargar de despojar a estos metadatos por lo que no se tratan como datos MP3. Puesto que los metadatos enviado a un cliente que no est listo para que causar problemas en el sonido, el servidor debe enviar los metadatos slo si la solicitud original del cliente contiene un especial de Icy-Metadatos de cabecera. Y para que el cliente sepa con qu frecuencia debe esperar que los metadatos, el servidor debe enviar de nuevo un cabezazo Icy-Metaint cuyo valor es el nmero de bytes de datos MP3 que se enviarn entre cada pedazo de metadatos. El contenido bsico de los metadatos es una cadena de la forma "StreamTitle = ' ttulo ', "donde el ttulo es el ttulo de la cancin actual y no puede contener marcas de comillas simples.Esta carga se codifica como una matriz de longitud delimitada de bytes: un byte se enva indicando el nmero de bloques de 16 bytes seguir, y luego de que muchos bloques se envan.Ellos contienen la carga til de la cadena como una cadena ASCII, con el bloque final rellena con bytes nulos segn sea necesario. Por lo tanto, el ms pequeo fragmento de metadatos legal es de un solo byte, cero, cero indicando los bloques siguientes. Si el servidor no es necesario para actualizar los metadatos, se puede enviar como un trozo de vaco, sino que debe enviar al menos un byte por lo que el cliente no tire los datos reales de MP3. Fuentes de canciones Debido a que un servidor Shoutcast tiene que tener canciones en streaming al cliente durante el tiempo que est conectado, es necesario proporcionar su servidor con una fuente de canciones a las que recurrir. En la aplicacin basada en Web, cada cliente conectado tendr una lista de reproduccin que puede ser manipulado a travs de la interfaz web. Sin embargo, en el inters de evitar acoplamiento

excesivo, debe definir una interfaz que el servidor Shoutcast puede utilizar para obtener canciones para jugar. Usted puede escribir una implementacin sencilla de esta interfaz de vez en cuando uno ms complejo, como parte de la aplicacin web que crear en el captulo 29.
El paquete de
El paquete para el cdigo que va a desarrollar en este captulo es la siguiente: (Defpackage: com.gigamonkeys.shoutcast (: Uso: common-lisp : Net.aserve : Com.gigamonkeys.id3v2) (: Las exportaciones: la cancin : Archivo Ttulo : Id3 de tamao : Encontrar el canto de cdigo : Corriente de la cancin : An en curso-p : Tal vez-se mueven a la siguiente cancin : * Cancin de cdigo de tipo *))

La idea detrs de la interfaz es que el servidor Shoutcast se encuentra una fuente de canciones basadas en una identificacin extrada del objeto de la peticin AllegroServe. A continuacin, puede hacer tres cosas con la fuente de la cancin que se le da. Consigue la cancin actual de la fuente Dgale a la fuente de la cancin que se ha hecho con la cancin actual Pregunte a la fuente si la cancin se le dio anteriormente sigue siendo la cancin actual La ltima operacin es necesaria porque puede haber maneras - y ser en el captulo 29 - para manipular la fuente de las canciones fuera del servidor Shoutcast. Usted puede expresar las operaciones del servidor Shoutcast necesita con las funciones genricas siguientes:
(Defgeneric corriente cancin (fuente) (: Documentacin "Volver la cancin se est reproduciendo o nula".)) (Defgeneric tal vez-se mueven a la siguiente cancin (fuente de la cancin) (: Documentacin "Si la cancin dada es todava el actual uno actualizar el valor devuelto por la corriente de la cancin. ")) (Defgeneric an en curso-p (fuente de la cancin) (: Documentacin "Devuelve true si la cancin es siempre la misma, como la cancin actual."))

La funcin de tal medida-a la prxima cancin se define la forma en que es por lo que los controles a la operacin solo si la cancin es actual y, si lo es, se mueve la fuente de la cancin a la siguiente cancin. Esto ser importante en el captulo

siguiente, cuando lo que necesita para implementar una fuente de la cancin que se puede manipular de forma segura a partir de dos temas diferentes. 3 Para representar la informacin sobre una cancin que necesita el servidor Shoutcast, puede definir una clase, la cancin , con ranuras para contener el nombre del archivo MP3, el ttulo de enviar en los metadatos de Shoutcast, y el tamao de la etiqueta ID3 para que puede saltar al servir el archivo.
(Defclass cancin () ((Archivo: archivo lector: initarg: archivo) (Ttulo en DVD lector: initarg ttulo) (ID3-size: lector de id3-size: initarg: ID3-size)))

El valor devuelto por la corriente de la cancin (y por lo tanto el primer argumento de que an en curso-p , ytal vez-se mueven a la siguiente cancin- ) ser una instancia de la cancin . Adems, es necesario definir una funcin genrica que puede utilizar el servidor para encontrar una fuente de cancin basada en el tipo de fuente que desea y el objeto de solicitud. Los mtodos se especializar el tipo de parmetro a fin de devolver distintos tipos de fuente de la cancin y se tire toda la informacin que necesitan de la solicitud objeto de determinar la fuente de regresar.
(Defgeneric encontrar-cancin-fuente (tipo de solicitud) (: Documentacin "Encuentra la cancin de cdigo del tipo dado a la solicitud dada."))

Sin embargo, a los efectos de este captulo, se puede utilizar una aplicacin trivial de esta interfaz que utiliza siempre el mismo objeto, una cola simple de objetos de la cancin que se puede manipular desde el REPL. Puede comenzar por la definicin de una clase, simple cancin de cola , y una variable global, * canciones * , que contiene una instancia de esta clase.
(Defclass simple cancin-cola () ((Canciones: canciones de acceso: initForm (make-array 10: regulable t: relleno triple 0)) (ndice: El ndice de acceso: initForm 0))) (* Defparameter canciones * (make-instance 'simple cancin-cola))

A continuacin, puede definir un mtodo de encontrar el canto de cdigo que se especializa tipo con un EQL specializer en el smbolo de Singleton y devuelve la instancia almacenada en * Las canciones * .
(Defmethod encuentra el canto de cdigo ((tipo (singleton eql ')) bajo peticin) (Declare (ignorar la peticin)) * Canciones *)

Ahora slo tiene que poner en prctica los mtodos en las tres funciones genricas que el servidor Shoutcast va a usar.
(Defmethod corriente cancin ((fuente simple-cancin-cola)) (Cuando (array-en el terreno de juego-p (fuente de canciones) (fuente de ndice)) (Aref (fuente de canciones) (fuente de ndice)))) (Defmethod an en curso-p (cancin (fuente simple-cancin-cola)) (Cancin eql (actual-cancin de origen))) (Defmethod tal vez-se mueven a la siguiente cancin (cancin (fuente simple-cancincola)) (Cuando (an en curso-p fuente de la cancin) (Incf (fuente de ndice))))

Y para propsitos de prueba que debe proporcionar una forma de agregar canciones a la cola.
(Defun agrega-archivo-a-canciones (archivo) (Vector de empuje se extienden (archivo-> archivo de la cancin) (* canciones canciones) *)) (File-> defun cancin (archivo) (Let ((ID3 (ID3 del archivo de lectura))) (Marca de instancia 'Cancin : Archivo (namecadena (truename archivo)) : Ttulo (nula en formato "~ a ~ a por una de ~" (cancin ID3) (artista ID3) (lbum ID3)) : ID3-size (tamao ID3))))

Implementacin de Shoutcast Ahora est listo para implementar el servidor Shoutcast. Dado que el protocolo Shoutcast se basa libremente en HTTP, puede implementar el servidor como una funcin dentro de AllegroServe. Sin embargo, ya que se necesita para interactuar con algunas de las caractersticas de bajo nivel de AllegroServe, no se puede utilizar ladefinicin-url-funcin de macro del Captulo 26. En su lugar, tiene que escribir una funcin regular que tiene este aspecto:
(Defun shoutcast (entidad de la solicitud) (Con-http-respuesta (Entidad de la solicitud: Content-Type "audio/MP3": tiempo de espera * de tiempo de espera de los segundos *) (Prepare-helada-respuesta de solicitud * los metadatos del intervalo *) (Let ((quiere-metadata-p (cabecera de la ranura de valor de pedido: hielometadatos))) (Con-http-cuerpo (entidad de la solicitud) (Juego de canciones (Solicitud de un socket peticin) (Encontrar-cancin-cancin-fuente * tipo de fuente * peticin) (Si se quiere-metadata-p * los metadatos del intervalo *))))))

A continuacin, publique esa funcin en la ruta / stream.mp3 de esta manera: 4

(Publicar: ruta "/ stream.mp3": shoutcast funcin ')

En la llamada a con-http-respuesta , adems de la habitual solicitud y la entidad argumentos, tiene que pasar : el tipo de contenido y eltiempo de espera: los argumentos. El : Content-Type argumento le dice AllegroServe cmo establecer el encabezado Content-Type se enva. Y el: tiempo de espera argumento especifica el nmero de segundos AllegroServe da la funcin de generar su respuesta. Por defecto los tiempos AllegroServe a cabo cada solicitud despus de cinco minutos. Debido a que usted va a transmitir una secuencia esencialmente sin fin de archivos MP3, es necesario mucho ms tiempo. No hay manera de saber AllegroServe que nunca el tiempo de espera de la solicitud, por lo que debe establecerse en el valor de * de tiempo de espera de los segundos * , que se puede definir a un valor suficientemente grande, como el nmero de segundos en diez aos.
(* Los defparameter tiempo de espera de los segundos * (* 60 60 24 7 52 10))

Luego, dentro del cuerpo de la con-http-respuesta y antes de la llamada a la conhttp-cuerpo que har que las cabeceras de respuesta para ser enviada, es necesario manipular la respuesta que enviar AllegroServe. La funcin de preparacinhelada-respuesta encapsula las manipulaciones necesarias: el cambio de protocolo de la cadena del valor predeterminado de "http" a "hielo" y la adicin de las cabeceras especficas Shoutcast. 5 Tambin es necesario, con el fin de evitar un error en iTunes, decir AllegroServe no utilizar fragmentada Transfer-Encoding . 6 Las funciones de solicitud y respuesta cuerdas protocolo , la URI de la solicitud- , yrespuesta-header-ranura de valor son parte de AllegroServe.
(Defun prepare-helada-respuesta (solicitud de los metadatos de intervalo) (Setf (peticin-respuesta-protocolo-cadena de la solicitud) "hielo") (Bucle para (kv) en (hacia atrs `((: | Helada-metaint |, (princ a cadena metadatos del intervalo)) (: | Helada-preaviso1 | "<BR> Esta corriente, bla, bla, bla, <BR>") (: | Helada-notice2 | "Ms bla") (: | Helada-nombre | "MyLispShoutcastServer") (: | Helada-gnero | "Desconocido") (: | Helada-url |, (Request-URI peticin)) (: | Helada-pub | "1"))) do (setf (respuesta-header-slot-valor de pedido k) v)) ;; ITunes, a pesar de que dijo hablar HTTP/1.1, no entiende ;; Fragmentada Transfer-Encoding. Grrr. As que simplemente apagarla. (Turn-off-fragmentada-Transfer-Encoding peticin)) (Defun apagado-fragmentada-Transfer-Encoding (bajo peticin) (Setf (peticin-respuesta-la estrategia de solicitud) (Quitar: fragmentada (peticin-respuesta-la estrategia de solicitud))))

Dentro de la con-http-cuerpo de shoutcast , en realidad transmitir los datos MP3. Las funciones de play-canciones lleva la corriente a la que debe escribir los datos, la fuente de la cancin, y el intervalo de metadatos que debe usar o NIL si el cliente no quiere que los metadatos. La corriente es la toma de corriente obtenida del objeto de la peticin, la fuente de la cancin se obtiene llamando a encontrar el canto de cdigo , y el intervalo de metadatos viene de la variable global* los metadatos del intervalo * . El tipo de fuente de la cancin es controlado por la variable * cancin de cdigo de tipo * , que por ahora se puede establecer para singleton con el fin de utilizar la simple cancin de cola que ha implementado con anterioridad.
(* Defparameter metadatos del intervalo * (expt 2 12)) (* Defparameter cancin de cdigo de tipo * 'singleton)

La funcin de reproduccin las canciones en s no hace mucho - se realiza un bucle llamando a la funcin de reproduccin actual , que hace todo el trabajo pesado de enviar el contenido de un archivo MP3, saltndose la etiqueta ID3 y la incorporacin de metadatos ICY. La arruga slo es que se necesita hacer un seguimiento de cundo se debe enviar los metadatos. Puesto que usted debe enviar trozos de metadatos en una intervalos fijos, independientemente del momento en que ocurra cambiar de un archivo MP3 a la siguiente, cada vez que se llama el juego de corriente que hay que decir cuando los metadatos es la prxima, y cuando regrese, debe informarle a usted lo mismo por lo que puede pasar la informacin a la siguiente llamada a jugar a la corriente . Si el juego-actual consigue NIL de la fuente de cancin, devuelve NIL , que permite alplay-canciones LOOP a fin. Adems de manejar el bucle, jugar de canciones tambin proporciona un MANIPULADOR-CASE para interceptar el error que se marc cuando el cliente MP3 se desconecta del servidor y una de las escrituras en el zcalo, en el juego de corriente , no. Desde el MANIPULADOR-CASE est fuera de la LOOP , el manejo del error de romper el bucle, lo que permite el juego de las canciones para volver.
(Defun play-canciones (corriente de la cancin-origen de metadatos de intervalo) (Manejador de caso (Circular para la prxima metadatos = intervalo de metadatos entonces (el juego de corriente corriente cancin de cdigo prxima metadatos intervalo de metadatos)

mientras que la prxima metadatos) (Error (e) (en formato * traza de salida * "Se detect el error en el juego de canciones: ~ un" e))))

Por ltimo, usted est listo para poner en prctica el juego de corriente , que en realidad enva los datos de Shoutcast. La idea bsica es que usted consigue la cancin actual de la fuente de la cancin, abrir el archivo de la cancin, y luego un bucle para leer los datos desde el archivo y la escritura a la toma hasta que llegue al final del archivo o la cancin actual ya no es la cancin actual. Slo hay dos complicaciones: una es que usted necesita para asegurarse de que envan los metadatos en el intervalo correcto. La otra es que si el archivo comienza con la etiqueta ID3, que desea que pase. Si usted no se preocupe demasiado acerca de la eficiencia de E / S, se puede implementar de play-actual as:
(Defun juego de corriente continua (la cancin de cdigo siguiente metadatos metadatos del intervalo) (Let (cancin ((actual-Cancin-source))) (Cuando la cancin (Let (metadatos ((make-hielo-los metadatos (ttulo de la cancin)))) (Con-open-archivo (mp3 (archivo de la cancin)) (A menos que (archivo posiciones MP3 (ID3-tamao de la cancin)) (El error "No se puede saltar a la posicin d ~ en ~ a" (ID3 de tamao cancin) (archivo de la cancin))) (Bucle para byte = (leer byte ninguna ninguna mp3) al mismo tiempo (y de bytes (an en curso-p Cancin de cdigo)) hacer (Escribir byte a byte) (DECF prxima metadatos) cuando (y (zerop prxima metadatos) de metadatos del intervalo) hacer (Escritura de la secuencia de metadatos) (Setf prxima metadatos metadatos del intervalo)) (Tal vez-se mueven a la siguiente cancin Cancin de cdigo))) prxima metadatos)))

Esta funcin se pone la cancin actual de la fuente de la cancin y se pone un tampn que contiene los metadatos que tendr que enviar al pasar el ttulo a lacomposicin de hielo-los metadatos . A continuacin, se abre el archivo y salta ms all de la etiqueta ID3 utilizando la forma de dos argumentos deFILEPOSICIN . Luego comienza la lectura de bytes desde el archivo y escribir a la secuencia de la solicitud. 7 Va a romper el ciclo ya sea cuando llega al final del archivo o cuando los cambios actuales de la fuente de la cancin de la cancin de salir de debajo de ella. Mientras tanto, cada vez que la prxima metadatos llega a cero (si se supone que debes enviar los metadatos en absoluto), escribe los metadatos a la corriente y se restablecela prxima metadatos . Una vez que termine el bucle, se comprueba si la

cancin sigue siendo la cancin actual de la fuente de cancin, si lo es, lo que significa que se rompi fuera del circuito, ya que leer todo el archivo, en cuyo caso se le dice a la fuente de la cancin para mover a la siguiente cancin. De lo contrario, se rompi fuera del circuito debido a un cambio de la cancin actual de debajo de ella, y vuelve solo. En cualquier caso, devuelve el nmero de bytes que quedan antes de los metadatos es la prxima para que pueda ser aprobada en la siguiente llamada a jugar a la corriente . 8 La funcin make-helada-metadatos , que toma el ttulo de la cancin actual y genera una matriz de bytes que contiene un trozo con el formato correcto de los metadatos ICY, tambin es sencillo. 9
(Defun crea-hielo-los metadatos (ttulo) (Let * ((texto (formato nulo "StreamTitle = '~ a';" (sustituto # \ # Espacio \ "ttulo))) (Bloques (techo (texto largo) 16)) (Tampn (make-array (1 + (* bloques de 16)) : Elemento de tipo '(sin signo byte 8) : Primer elemento 0))) (Setf (tampn Aref 0) cuadras) (Circular para char a travs de texto para i desde el 1 de do (setf (Aref tampn i) (char-char code))) tampn))

Dependiendo de cmo su particular Lisp maneja sus flujos, as como cuntos clientes de MP3 que desea servir a la vez, la versin simple del juego de corriente puede ser o no ser lo suficientemente eficiente. El problema potencial con la puesta en prctica simple es que tienes que llamar a LEA-BYTE y Escribe un byte- por cada byte a transferir. Es posible que cada llamada puede resultar en una llamada al sistema relativamente caro para leer o escribir un byte. E incluso si Lisp implementa sus propias emisiones con el bfer interno, no por lo que cada llamada aREAD-BYTE o Escribe un byte- resulta en una llamada al sistema, las llamadas de funcin todava no son libres. En particular, en las implementaciones que brindan extensibles usuario secuencias utilizando los llamados flujos de Gray, READ-BYTE y BYTE-WRITE puede resultar en una llamada a la funcin genrica bajo las sbanas de su expedicin en la clase de la argumentacin corriente. Mientras que el envo funcin genrica es normalmente lo suficientemente rpido que usted no tiene que preocuparse por ello, no es un poco ms caro que una llamada a la funcin genrica y por lo tanto algo que

necesariamente quieren hacer varios millones de veces en pocos minutos si se puede evitar . De manera ms eficiente, aunque un poco ms compleja, forma de implementar el juego de corriente es a leer y escribir varios bytes a la vez usando las funcionesde secuencia LEER y ESCRIBIR Secuencia . Esto tambin le da la oportunidad para que coincida con la lectura de ficheros con el tamao de bloque natural del sistema de archivos, lo que probablemente le dar el mejor rendimiento de disco. Por supuesto, no importa qu tamao de bfer que usar, mantener un registro de cundo se debe enviar los metadatos se convierte en un poco ms complicado. Una versin ms eficiente de juego de corriente que utiliza SECUENCIA DE LECTURA yESCRITURA SECUENCIA- podra tener este aspecto:
(Defun juego de corriente continua (la cancin de cdigo siguiente metadatos metadatos del intervalo) (Let (cancin ((actual-Cancin-source))) (Cuando la cancin (Let (metadatos ((make-hielo-los metadatos (ttulo de la cancin))) (Buffer (de creacin de matriz de tamao: elemento de tipo '(sin signo de 8 bytes-)))) (Con-open-archivo (mp3 (archivo de la cancin)) (Etiquetas ((write-tampn (extremo inicial) (Si los metadatos del intervalo (Escritura-buffer-con-los metadatos extremo inicial) (Escritura de la secuencia de bfer: de inicio de inicio: end))) (Escritura-buffer-con-los metadatos (extremo inicial) (Cond ((> Siguiente-los metadatos (- Fin)) (Escritura de la secuencia de bfer: de inicio de inicio: final final) (DECF prxima metadatos (- Fin))) (T (Let ((media (+ comenzar la prxima metadatos))) (Escritura de la secuencia de bfer: de inicio de inicio: media final) (Escritura de la secuencia de metadatos) (Setf prxima metadatos metadatos del intervalo) (Escritura-buffer-con-los metadatos finales centro)))))) (Multiple-value-bind (skip-skip-bloques de bytes) (En el piso (ID3 de tamao cancin) (bfer de longitud)) (A menos que (archivo mp3 de posicin (* saltar los bloques (bfer de longitud))) (El error "No se puede pasar por alto del ~ D ~ d bloques de bytes". saltan los bloques (bfer de longitud))) (bucle para el final = (lase secuencia de bfer mp3) para la puesta en saltar-bytes = 0 entonces hacer (escritura-buffer extremo inicial) al mismo tiempo (y (= final (bfer de longitud)) (An en curso-p Cancin de cdigo)))

(Tal vez-se mueven a la siguiente cancin Cancin de cdigo))))) prxima metadatos)))

Ahora ya ests listo para poner todas las piezas juntas. En el prximo captulo que voy a escribir una interfaz web para el servidor Shoutcast desarrollado en este captulo, con la base de datos MP3 de Captulo 27 como la fuente de las canciones.

1 La versin de XMMS se entregan con Red Hat 8.0 y 9.0 y Fedora ya no sabe cmo reproducir

archivos MP3, porque la gente de Red Hat estaban preocupados por los problemas de licencia relacionados con el codec MP3. Para obtener una XMMS con soporte para MP3 en estas versiones de Linux, se puede agarrar la fuente de la http://www.xmms.org y construir por s mismo. O bien, consultehttp://www.fedorafaq.org/~~V # xmms-mp3 para obtener informacin sobre otras posibilidades.
2 Para confundir ms las cosas, no hay un protocolo diferente de streaming llamado Icecast . No

parece haber ninguna conexin entre la cabecera del ICY utilizado por el protocolo Shoutcast y Icecast.
3 Tcnicamente, la puesta en prctica en este captulo tambin va a ser manipulado a partir de dos

temas: el hilo AllegroServe que ejecuta el servidor Shoutcast y el hilo REPL. Pero usted puede vivir con la condicin de carrera, por ahora. Voy a discutir el uso de bloqueo para hacer hilo de cdigo seguro en el prximo captulo.
4 Otra cosa que usted puede querer hacer mientras se trabaja en este cdigo es de evaluar la forma

(net.aserve :: depuracin en: notrap) . Esto le dice a AllegroServe para no capturar los errores sealados por el cdigo, que le permitir corregirlas en el normal depurador de Lisp. En BABA esto abrir un buffer depurador BABA al igual que cualquier otro error.
5 cabeceras Shoutcast se envan generalmente en minsculas, por lo que necesita para escapar de los

nombres de los smbolos de palabras clave utilizadas para identificar a AllegroServe para mantener al lector Lisp desde su conversin a maysculas. Por lo tanto, usted escribira : | helada-metaint | en lugar de : hielo-metaint . Tambin puede escribir : \ i \ c \ y-\ m \ e \ t \ a \ i \ n \ t, pero eso sera una tontera.
6 La funcin de apagado-fragmentada-Transfer-Encoding es un poco chapuza. No hay forma de

desactivar la codificacin de transferencia fragmentada a travs de API oficial de AllegroServe sin especificar una longitud de contenido, ya que cualquier cliente que se anuncia como un cliente HTTP/1.1, que iTunes lo hace, se supone que lo entiendo. Pero esto hace el truco.
7 La mayora del software de MP3 juego mostrar los metadatos en algn lugar de la interfaz de

usuario. Sin embargo, el programa XMMS en Linux por defecto no lo hace. Para obtener XMMS para mostrar metadatos Shoutcast, presione Ctrl + P para ver el panel de Preferencias. Luego, en el Audio I / O pestaa de Plugins (la ficha ms a la izquierda en la versin 1.2.10), seleccionar el MPEG Layer 1/2/3 Player (libmpg123.so ) y pulsa el botn Configurar. A continuacin, seleccione la pestaa de

Streaming en la ventana de configuracin, y en la parte inferior de la ficha en la seccin de Shoutcast / Icecast, marque la casilla "Habilitar Shoutcast / Icecast streaming ttulo de" caja.
8 La gente que vienen a Common Lisp del esquema podra preguntarse por qu el juego de corriente

no puede llamarse a s mismo de forma recursiva. En el esquema que funcione bien ya que las implementaciones de Scheme son requeridos por la especificacin del Plan para apoyar "un nmero ilimitado de llamadas de cola activas." Comunes implementaciones de Lisp se les permite tener esta propiedad, pero no es requerido por el estndar del lenguaje. As, en Common Lisp la forma idiomtica para escribir bucles es una construccin de bucle, no con la recursividad.
9 Esta funcin supone, al igual que otro tipo de cdigo que has escrito, que la codificacin de su

puesta en prctica de carcter interno de Lisp es ASCII o un superconjunto de ASCII, as que usted puede usarCHAR-CODE para traducir Lisp CARCTER objetos de bytes de datos ASCII.

29. Prctica: Un Navegador de MP3


El ltimo paso en la construccin de la aplicacin de streaming de MP3 es proporcionar una interfaz web que permite a un usuario para buscar las canciones que desea escuchar y aadirlos a una lista de reproduccin que el servidor Shoutcast se basar en solicitudes de MP3, el usuario de los clientes del canal URL . Para este componente de la aplicacin, podrs reunir varios bits de cdigo de los captulos anteriores: la base de datos MP3, la definen-url-la funcin de macro del captulo 26, y, por supuesto, el servidor Shoutcast s mismo. Listas de reproduccin La idea bsica detrs de la interfaz ser que cada cliente de MP3 que se conecta al servidor Shoutcast tiene su propia lista de reproduccin , que sirve como la fuente de las canciones para el servidor Shoutcast. Una lista de reproduccin tambin proporcionar instalaciones ms all de los que necesita el servidor Shoutcast: a travs de la interfaz Web del usuario ser capaz de agregar canciones a la lista de reproduccin, borrar canciones ya estn en la lista de reproduccin, y reordenar la lista de reproduccin de seleccin y arrastrando los pies. Se puede definir una clase para representar listas de reproduccin de esta manera:
(Defclass lista de reproduccin () ((Id: Identificacin de acceso: initarg: id) (Canciones de la mesa: la mesa de acceso canciones: initForm (make-lista de reproduccin de mesa)) (Corriente de la cancin: acceso corriente cancin: initForm * empty-playlistcancin *) (Actual-idx: acceso actual-idx: initForm 0) (Ordenacin: orden de acceso: initForm: lbum) (Aleatoria: La reproduccin de acceso: initForm: ninguno) (Repeticin: repeticin de acceso: initForm: ninguno) (User-agent: acceso user-agent: initForm "Desconocido") (Bloqueo: bloqueo de lector: initForm (make-proceso-lock))))

La Identificacin de una lista de reproduccin es la clave se extrae del objeto de la peticin pasa a buscar el canto de cdigo cuando se est buscando una lista de reproduccin. En realidad no necesitamos almacenar en la lista objeto, sino que hace que la depuracin un poco ms fcil si usted puede encontrar a partir de un objeto de lista de reproduccin arbitraria lo que su Identificacin es. El corazn de la lista de reproduccin es la mesa de canciones ranura, lo que llevar a cabo una mesa de objeto. El esquema para esta tabla ser la misma que para la

base de datos MP3 principal. La funcin make-lista de reproduccin de mesa , que se utiliza para inicializar las canciones de mesa , es simplemente esto:
(Defun crea-playlist-table () (Vase el cuadro que de instancia ": esquema de * mp3-esquema *))

El paquete de
Puede definir el paquete para el cdigo de este captulo con el siguiente DEFPACKAGE : (Defpackage: com.gigamonkeys.mp3 navegador (: Uso: common-lisp : Net.aserve : Com.gigamonkeys.html : Com.gigamonkeys.shoutcast : Com.gigamonkeys.url-funcin : Com.gigamonkeys.mp3 base de datos : Com.gigamonkeys.id3v2) (: Importacin-a partir de: acl-socket : Ipaddr a puntos : Host remoto) (: Importacin-a partir de: multiprocesamiento : Make-proceso-de bloqueo : Con-proceso-bloqueo) (: Exportacin: inicio-MP3-navegador)) Debido a que esta es una aplicacin de alto nivel, que utiliza una gran cantidad de paquetes de bajo nivel. Tambin importa tres smbolos de la ACL-SOCKET paquete y dos ms deMultiproceso , ya que slo necesita las exportaciones esos dos paquetes de cinco y no los smbolos de otros 139.

Al almacenar la lista de las canciones como una tabla, puede utilizar las funciones de base de datos del captulo 27 de manipular la lista de reproduccin: se pueden agregar a la lista de reproduccin con la insercin de filas , eliminar canciones con borrado de filas , y reordenar la lista de reproduccin con laordenacin de las filas y la reproduccin aleatoria de mesa . Las corrientes de canciones y actual idx- ranuras de realizar un seguimiento de qu cancin se est reproduciendo: corriente de la cancin es una verdadera cancin de objeto, mientras que actual-idx es el ndice en la mesa de canciones de la fila que representa la cancin actual. Usted ver en la seccin "La manipulacin de la lista de reproduccin" cmo asegurarse de que la cancin actual se actualiza cada vez que actual-idx cambios. El ordenamiento y la reproduccin aleatoria ranuras de mantener la informacin acerca de cmo las canciones de las canciones de mesa deben ser ordenados. El pedido tiene ranura para una palabra clave que explica cmo la canciones de mesa deben ser ordenados cuando no se mezclan. Los valores permitidos son: gnero , : artista , : disco , y : la cancin . El shuffle de ranura tiene una de las palabras clave :

ninguno , : la cancin , o : lbum , que especifica cmo las canciones de mesa debe ser mezclado, en todo caso. La repeticin de la ranura tambin tiene una palabra clave, una de : ninguno , : la cancin , o : todos , que especifica el modo de repeticin para la lista. Sila repeticin es : ninguno , despus de la ltima cancin de la canciones de mesa se ha jugado, la corriente de la cancin se remonta a un MP3 por defecto. Cuando : la repeticin es : la cancin , la lista de reproduccin se sigue presentando la misma corriente de la cancin para siempre. Y si es: todos , despus de la ltima cancin, la cancin actual se remonta a la primera cancin. El agente de usuario slot mantiene el valor de la cabecera User-Agent enviada por el cliente de MP3 en su solicitud de la corriente. Usted necesidad de aferrarme a este valor exclusivamente para su uso en la interfaz web - la cabecera User-Agent identifica el programa que hizo la solicitud, para que pueda mostrar el valor de la pgina que muestra todas las listas de reproduccin para que sea ms fcil saber qu lista con la que va de conexin cuando varios clientes conectados. Por ltimo, el bloqueo de slot mantiene un bloqueo de proceso creada con la funcin make-proceso-de bloqueo , que es parte de Allegro Multiproceso paquete.Tendr que usar ese bloqueo en ciertas funciones que manipulan lista de objetos para asegurarse de que slo un hilo en un momento dado manipula un objeto de lista de reproduccin. Se puede definir la macro siguiente, construida sobre el proceso con el bloqueo de macros de Multiproceso , para dar una forma fcil de envolver un cuerpo de cdigo que se debe realizar mientras se mantiene una lista de reproduccin de bloqueo:
(Defmacro con-lista-cerrada ((lista de reproduccin) y el cuerpo del cuerpo) `(Con proceso de bloqueo ((bloqueo, lista de reproduccin)) , El cuerpo de @))

El proceso con el bloqueo de macro adquiere acceso exclusivo a la cerradura determinado proceso y luego ejecuta las formas del cuerpo, liberar el bloqueo despus. De forma predeterminada, con el proceso de bloqueo permite bloqueos recursivos, es decir, el mismo hilo con seguridad puede adquirir la misma cerradura varias veces.

Listas de reproduccin como las fuentes de canciones Para utilizar la lista s como una fuente de canciones para el servidor Shoutcast, usted tendr que poner en prctica un mtodo de la funcin genricaencuentra el canto de cdigo del captulo 28. Puesto que usted va a tener varias listas de reproduccin, se necesita una manera de encontrar el ms adecuado para cada cliente que se conecta al servidor. La parte de la cartografa es fcil - se puede definir una variable que contiene una EQUAL tabla hash que se puede utilizar para asignar un identificador de la lista de objetos.
(* Defvar listas de reproduccin * (make-hash-table: test # 'igual))

Tambin tendr que definir un proceso de bloqueo para proteger el acceso a esta tabla hash de esta manera:
(* Defparameter listas de bloqueo * (make-proceso-de bloqueo: el nombre de "listas de reproduccin-lock"))

A continuacin, defina una funcin que busca una lista de reproduccin da una identificacin, la creacin de una nueva lista de reproduccin objeto si es necesario y utilizar , con proceso de bloqueo para asegurar que slo un hilo a la vez manipula la tabla hash. 1
(Defun lista-lookup (id) (Con proceso de bloqueo (* listas de bloqueo *) (O (GetHash id * listas *) (Setf (GetHash Identificacin * listas *) (make-instance 'lista de reproduccin: id id)))))

A continuacin, se puede implementar encontrar el canto de cdigo en la parte superior de esa funcin y otra, lista de reproduccin-id , que toma un objeto de la peticin AllegroServe y devuelve el identificador correspondiente lista de reproduccin. El encontrar el canto de cdigo funcin tambin es donde se agarra la cadena de agente de usuario del objeto de la peticin y de alijo en el objeto de lista de reproduccin.
(Defmethod encuentra el canto de cdigo ((tipo (lista de reproduccin eql ')) bajo peticin) (Let ((lista de reproduccin (bsqueda, lista de reproduccin (playlist-ID de la solicitud)))) (Con-playlist-bloqueado (lista de reproduccin) (Let ((user-agent (cabecera de la ranura de valor de pedido: User-Agent))) (Cuando el agente de usuario (setf (user-agent lista de reproduccin) de agente de usuario)))) lista de reproduccin))

El truco, entonces, es cmo poner en prctica lista de reproduccin-id , la funcin que extrae el identificador del objeto de la peticin. Usted tiene un par de opciones,

cada una con diferentes implicaciones para la interfaz de usuario. Puede tirar de la informacin que desee salir del objeto de la peticin, pero sin embargo, usted decide para identificar al cliente, que necesita de alguna manera para que el usuario de la interfaz Web para engancharse a la derecha lista de reproduccin. Por ahora usted puede tomar un enfoque que "simplemente funciona", siempre y cuando slo hay un cliente de MP3 por cada mquina se conecta al servidor, y siempre y cuando el usuario est navegando por la interfaz web de la mquina que ejecuta el cliente de MP3: vamos a usar el direccin IP de la mquina cliente como el identificador. De esta manera usted puede encontrar la lista de reproduccin para una solicitud, independientemente de si la solicitud es desde el cliente de MP3 o un navegador Web. Usted, sin embargo, proporcionar una manera en la interfaz web para seleccionar una lista de reproduccin diferente desde el navegador, por lo que la nica limitacin real de esta decisin pone en la aplicacin es que slo puede haber un cliente conectado MP3 por cada direccin IP del cliente. 2 La puesta en prctica de la lista de reproduccin-id es el siguiente:
(Solicitud de defun lista-id () (Ipaddr a puntos (host remoto (peticin de solicitud de socket))))

La funcin de solicitud de sockets es parte de AllegroServe, mientras host remoto y ipaddr a puntos- son parte de la biblioteca toma de Allegro. Para hacer una lista de reproduccin puede utilizar como una fuente de la cancin por el servidor Shoutcast, es necesario definir los mtodos de la cancin actual ,an en curso-p , y tal vez-se mueven a la siguiente cancin que se especializan a su fuente de parmetro en la lista . Lacorriente de la cancin mtodo ya se atendidos: por definir el acceso corriente de la cancin en la ranura del mismo nombre, automticamente tiene unacorriente de la cancin mtodo especializado en la lista que devuelve el valor de esa ranura. Sin embargo, para los accesos a la lista de reproduccinseguro para subprocesos, es necesario cerrar la lista de reproduccin antes de acceder al corriente de la cancin de la ranura. En este caso, la forma ms fcil es definir una : todo mtodo como el siguiente:
(Defmethod corriente cancin: alrededor de ((playlist)) (Con-playlist-bloqueado (lista de reproduccin) (llamada-next-mtodo)))

Implementar todava en curso-p es tambin bastante simple, suponiendo que usted puede estar seguro de que actual-cancin se actualiza con una nuevacancin de objeto slo cuando la cancin actual en realidad los cambios. Una vez ms, es

necesario adquirir el bloqueo de proceso para asegurarse de que obtiene una visin consistente de la lista de reproduccin estado s.
(Defmethod an en curso-p (cancin (playlist)) (Con-playlist-bloqueado (lista de reproduccin) (Cancin eql (actual-cancin lista de reproduccin))))

El truco, entonces, es asegurarse de que la actual cancin de ranura se actualiza en el momento adecuado. Sin embargo, la cancin actual puede cambiar en un nmero de maneras. La ms obvia es cuando el servidor Shoutcast llama tal vez-se mueven a la siguiente cancin . Sin embargo, tambin puede cambiar cuando las canciones se agregan a la lista de reproduccin, cuando el servidor Shoutcast se ha quedado sin canciones, o incluso si el modo de la lista de reproduccin de repeticin se cambia. En lugar de tratar de escribir cdigo especfico para cada situacin para determinar si se debe actualizar la cancin actual , se puede definir una funcin,actualizacin de corriente, si es necesario- , que actualiza la cancin actual , si el tema objeto de la cancin actual no coincide con el archivo que el actual-idx ranura dice debera estar jugando. Entonces, si se llama a esta funcin despus de cualquier manipulacin de la lista de reproduccin que podra poner esas dos franjas horarias fuera de sincronizacin, usted puede estar seguro de mantener vigente el canto conjunto correctamente. Aqu hayactualizacin de corriente, si es necesario- y sus funciones de ayuda:
(Defun actualizacin de corriente-si-es necesario (lista de reproduccin) (A menos que (archivo igual ((No hay cancin de la lista)) (Archivo-de-corriente-idx lista de reproduccin)) (Reset-actual-cancin lista de reproduccin))) (Defun archivo-por-actual-idx (lista de reproduccin) (Si (a-fin-p lista de reproduccin) cero (Columna de valor (ensima fila (actual-idx lista de reproduccin) (canciones de mesa lista de reproduccin)): archivo))) (Defun al-final-p (lista de reproduccin) (> = (Actual-idx lista de reproduccin) (cuadro de tamao (las canciones de mesa lista de reproduccin))))

No es necesario aadir a estas funciones de bloqueo, ya que va a ser llamado slo de las funciones que se encargarn de cerrar la primera lista de reproduccin. La funcin de restablecimiento de la corriente, la cancin presenta una arruga ms: porque desea que la lista de reproduccin para ofrecer un sinfn de archivos MP3 para el cliente, usted no desea establecer alguna corriente de la cancin de NIL . En

cambio, cuando una lista de reproduccin de las canciones se queda sin jugar cuando las canciones de mesa est vaco o despus de la ltima cancin que se escucha y repeticin se establece : ninguno - entonces usted necesita para establecer actual-cancin a una cancin especial, cuyo archivo es un MP3 de silencio 3 y cuyo ttulo explica por qu no se reproduce la msica. Aqu hay un cdigo para definir dos parmetros, * empty-playlist-cancin * y * al final de su lista de reproduccin, canciones * , cada conjunto de una cancin con el archivo con el nombre por el silencio * mp3 * como su archivo y un ttulo apropiado:
(* Defparameter silencio-mp3 * ...) (Defun crea-en silencio-cancin (ttulo y opcional (archivo * silencio-mp3 *)) (Marca de instancia 'Cancin : Archivo : Ttulo del ttulo : Id3 de tamao (si (ID3-p archivo) (tamao (lectura ID3 del archivo)) 0))) (* Defparameter vaco-lista-cancin * (make-en silencio-cancin "lista de reproduccin vaca".)) (* Defparameter al final de su lista de reproduccin-cancin * (make-en silenciocancin "Al final de la lista."))

restablecimiento de la corriente-cancin utiliza estos parmetros cuando la corriente idx no apunta a una fila de canciones de mesa . De lo contrario, se establece actual-cancin a una cancin de objeto que representa la fila actual.
(Defun-reset corriente cancin (lista de reproduccin) (Setf (No hay cancin de la lista) (Cond ((Vaco-p lista de reproduccin) * empty-playlist-cancin *) ((A-fin-p lista de reproduccin) * al final de su lista de reproduccin, canciones *) (T (fila-> cancin (ensima fila (actual-idx lista de reproduccin) (canciones de mesa lista de reproduccin))))))) (Defun row-> cancin (cancin-db-entrada) (Con columnas de valores (archivo de la cancin lbum artista ID3 de tamao) la cancin de entrada de DB(Marca de instancia 'Cancin : Archivo : Ttulo (nula en formato "~ ~ a por una de ~ un" lbum de artista de la cancin) : Id3 id3 de tamao de tamao))) (Defun vaco-p (lista de reproduccin) (Zerop (cuadro de tamao (las canciones de mesa lista de reproduccin))))

Ahora, por fin, se puede implementar el mtodo en movimiento-tal vez a la prxima cancin que se mueve corriente idx a su siguiente valor, basado en el modo de

repeticin de la lista de reproduccin, y luego llama a update-actual-si es necesario- . No se cambia de corriente idx cuando ya est al final de la lista de reproduccin, ya que queremos que siga su valor actual, por lo que va a apuntar a la siguiente cancin que aadir a la lista de reproduccin. Esta funcin se debe cerrar la lista de reproduccin antes de manipularlo ya que es llamado por el cdigo del servidor Shoutcast, que no hace ningn bloqueo.
(Defmethod tal vez-se mueven a la siguiente cancin (cancin (playlist)) (Con-playlist-bloqueado (lista de reproduccin) (Cuando (an en curso-p cancin de la lista) (A menos que (a-fin-p lista de reproduccin) (Ecase (repeticin de reproduccin) (: Cancin) no cambia nada; (: Ninguno (incf (actual-idx lista de reproduccin))) (: Todos (setf (current-idx lista de reproduccin) (Mod (1 + (actual-idx lista de reproduccin)) (Vase el cuadro de tamao (las canciones de mesa lista de reproduccin))))))) (Actualizacin de corriente, si es necesario-lista))))

La manipulacin de la lista de reproduccin El resto del cdigo es la lista funciones usadas por la interfaz web para manipular listas de reproduccin de objetos, incluyendo agregar y eliminar canciones, seleccin y arrastrando los pies, y establecer el modo de repeticin. Al igual que en las funciones de ayuda en la seccin anterior, usted no necesita preocuparse por el bloqueo en estas funciones, ya que, como se ver, la cerradura se puede adquirir en la funcin de interfaz web que llama a estos. Adicin y eliminacin es principalmente una cuestin de la manipulacin de la canciones de mesa . El nico trabajo adicional que tiene que hacer es mantener lacorriente de la cancin y la actual idx- en sincrona. Por ejemplo, cada vez que la lista est vaca, su actual idx ser cero, y la cancin actual- ser la lista de reproduccin * vaco *-cancin . Si se agrega una cancin a una lista vaca, entonces el ndice de cero se encuentra en los lmites, y debe cambiar lacancin actual, a la cancin que acaba de agregar. De la misma manera, cuando has jugado todas las canciones de una lista de reproduccin y lacorriente de la cancin es * al final de su lista de reproduccin, canciones * , la adicin de una cancin debe causaractual cancin que se restablezca. Todo esto realmente significa, sin embargo, es que se necesita para llamar a update-actual-si es necesario- en los puntos apropiados. Para aadir canciones a una lista de reproduccin es un poco complicada debido a la forma de la interfaz web se comunica que las canciones que aadir. Por razones

que discutiremos en la siguiente seccin, el cdigo de la interfaz web no slo le puede dar un simple conjunto de criterios a utilizar en la seleccin de canciones de la base de datos. Por el contrario, le da el nombre de una columna y una lista de valores, y que se supone que aadir todas las canciones de la base de datos principal, donde la columna dada tiene un valor en la lista de valores. Por lo tanto, para aadir las canciones correctas, lo que necesita para construir la primera un objeto de tabla que contiene los valores deseados, que luego se puede utilizar con una en la consulta en la base de datos de cancin. Por lo tanto, add-canciones es el siguiente:
(Defun complementos de canciones (playlist nombre de columna, valores) (Let ((Mesa (make-instancia 'Mesa : Esquema (extracto del esquema (lista nombre-columna) (esquema de * mp3 *))))) (Dolist (valores V) (insercin de registro (lista de nombre-columna v) la tabla)) (Hacer las filas (fila (seleccione: * de * mp3: si (en la tabla nombre-columna))) (Insertar fila de la fila (canciones de mesa lista de reproduccin)))) (Actualizacin de corriente, si es necesario-lista de reproduccin))

Eliminacin de canciones es un poco ms simple, slo tienes que ser capaz de eliminar canciones de la mesa de canciones que coincidan con determinados criterios - ya sea una cancin en particular o todas las canciones de un gnero en particular, de un artista concreto, o de un lbum en particular. Por lo tanto, puede proporcionar unborrado canciones de funcin que toma la palabra clave / valor, que se utilizan para la construccin de una coincidencia : que la clusula se puede pasar a lade borrado de filas la funcin de base de datos. Otra complicacin que surge al eliminar canciones es que actual-idx posible que necesite cambiar. Suponiendo que la cancin actual no es uno de los acaba de eliminar, que le gustara que siga siendo la cancin actual. Pero si las canciones antes de que en la mesa de canciones se borran, va a estar en una posicin diferente en la tabla despus de la eliminacin. As que despus de una llamada a eliminar las filas , lo que necesita para buscar la fila que contiene la cancin actual y restablecer corriente idx . Si la cancin actual ha sido borrado, entonces, a falta de algo mejor que hacer, puede restablecer la corriente idx a cero. Despus de la actualizacin actual-idx , pidiendoactualizacin de corriente, si es necesario- se har cargo de la actualizacin de la cancin actual . Y si actual-idx cambiado, pero an apunta a la misma cancin, la cancin actual- se qued solo.
(Defun borrar las canciones (lista de reproduccin y descanso de los nombres y valores)

(Delete-filas : A partir de (canciones de mesa lista de reproduccin) : Si (se aplican a juego # '(canciones de la mesa lista de reproduccin) y los nombres de los valores)) (Setf (current-idx lista de reproduccin) (o 0 (posicin de lista de reproduccin actual-))) (Actualizacin de corriente, si es necesario-lista de reproduccin)) (Defun posicin de la corriente (lista de reproduccin) (Let * ((Mesa (canciones de mesa lista de reproduccin)) (Comparador (coincidencia de la tabla: archivo (archivo (cancin de la lista)))) (Pos 0)) (Hacer las filas (fila de la tabla) (Cuando (fila funcall comparador) (Ida y vuelta-desde la posicin de la corriente pos)) (Pos incf))))

Tambin puede proporcionar una funcin para borrar por completo la lista de reproduccin, que utiliza delete-all-filas y no tiene que preocuparse por encontrar la cancin actual, ya que, evidentemente, ha sido eliminado. La llamada a actualizar la corriente, si es necesario- se har cargo de la creacin actual-cancinde NIL .
(Defun clara lista de reproduccin (playlist) (Delete-all-filas (canciones de mesa lista de reproduccin)) (Setf (current-idx lista de reproduccin) 0) (Actualizacin de corriente, si es necesario-lista de reproduccin))

La clasificacin y barajar la lista de reproduccin que se relacionan en la lista de reproduccin siempre bien ordenados , o arrastrando los pies. La reproduccin aleatoriaranura dice si la lista debe ser mezclado y si es as cmo. Si se establece que : ninguno , a continuacin, la lista de reproduccin se ordenan segn el valor de la orden de la ranura. Cuando aleatorio es : la cancin , la lista ser al azar permutada. Y cuando se establece en : lbum , la lista de lbumes de forma aleatoria permutada, pero las canciones de cada disco se enumeran en el orden de las pistas. As, el tipo-lista funcin, que ser llamado por el cdigo de la interfaz Web cuando el usuario selecciona un nuevo orden, tiene que dar orden a la ordenacin deseada y un conjunto aleatorio de : ninguno antes de llamar al orden, lista de reproduccin , lo que realmente hace la clase . Al igual que en eliminar las canciones , es necesario utilizar la posicin de la corriente para restablecer corriente idx a la nueva ubicacin de la cancin actual. Sin embargo, esta vez no es necesario llamar a update-actual-si es necesario- ya que usted sabe la cancin actual se encuentra todava en la mesa.
(Defun-tipo lista (lista de pedidos) (Setf (ordenar la lista) el pedido) (Setf (shuffle lista de reproduccin): ninguno) (Fin de lista de reproduccin-playlist)

(Setf (current-idx lista de reproduccin) (posicin de lista de reproduccin actual-)))

En fin, lista de reproduccin , puede utilizar la funcin de base de datos de clasificacin de las filas para llevar a cabo realmente el tipo, pasando por una lista de las columnas para ordenar por la base en el valor del pedido .
(Defun orden lista de reproduccin (playlist) (Aplicar # 'sort-filas (canciones de mesa lista de reproduccin) (Caso (pedido lista de reproduccin) (: Gnero '(: Gnero: lbum: pista)) (: Artista (: Artista: Album: pista)) (: lbum '(: lbum: pista)) (: Cancin '(: cancin)))))

La funcin de reproduccin aleatoria, lista de reproduccin , llamado por el cdigo de la interfaz Web cuando el usuario selecciona un nuevo modo de reproduccin aleatoria, funciona de manera similar, excepto que no es necesario cambiar el valor del pedido . As, cuando shuffle lista de reproduccin se llama con un aleatorio de : ninguna , la lista de reproduccin vuelve a ser ordenados de acuerdo con el orden ms reciente. Arrastrando los pies por las canciones es sencillo - simplemente llame a barajar la mesa de canciones de mesa . Arrastrando los pies por los lbumes es un poco ms complicado, pero todava no la ciencia de cohetes.
(Defun shuffle lista de reproduccin (shuffle lista de reproduccin) (Setf (shuffle lista de reproduccin) shuffle) (Shuffle caso (: Ninguno (para-playlist)) (: Cancin (shuffle por cancin de la lista)) (: lbum (seleccin aleatoria por lbum de la lista))) (Setf (current-idx lista de reproduccin) (posicin de lista de reproduccin actual-))) (Defun aleatorio por cancin (lista de reproduccin) (Seleccin aleatoria de mesa (canciones de mesa lista de reproduccin))) (Defun aleatoria por lbum (lista de reproduccin) (Let ((nueva tabla (hacer-lista de reproduccin de mesa))) (Hacer las filas (lbum fila (barajan los nombres de los lbumes, lista de reproduccin)) (Hacer las filas de la cancin ((canciones-de-lbum lista de reproduccin (valores de columnas de la fila lbum: lbum))) (Insertar fila de la nueva cancin de mesa))) (Setf (canciones de mesa lista de reproduccin) la nueva tabla))) (Defun barajan los nombres de los lbumes (lista de reproduccin) (Seleccin aleatoria de mesa (Seleccione : Columnas: album : A partir de (canciones de mesa lista de reproduccin) : Distintos t))) (Defun canciones-de-lbum (lbum de la lista) (Seleccione : A partir de (canciones de mesa lista de reproduccin)

: Si (a juego (canciones de mesa lista de reproduccin): lbum lbum) : Order-by: pista))

La manipulacin ltimo que necesita para apoyar el modo de ajuste es la lista de reproduccin de repeticin. La mayora de las veces no es necesario tomar ninguna medida especial al establecer la repeticin - el valor entra en juego slo en tal medida-a la prxima cancin . Sin embargo, es necesario actualizar la cancin actual,como resultado del cambio de la repeticin de una situacin, es decir, si actual-idx es al final de una lista de reproduccin vaca y repeticin est siendo cambiado a : cancin o : todos . En ese caso, usted quiere seguir jugando, ya sea la repeticin de la ltima cancin, o empezar por el principio de la lista de reproduccin.Por lo tanto, debe definir una : despus de mtodo en la funcin genrica (setf repeticin) .
(Defmethod (setf repeticin): despus de (valor (playlist)) (Si (y (a-fin-p lista de reproduccin), no ((vaco-p lista de reproduccin))) (Valor ecase (: Cancin (setf (current-idx lista de reproduccin) (1 - (cuadro de tamao (las canciones de mesa lista de reproduccin))))) (: Ninguno) (: Todos (setf (current-idx lista de reproduccin) 0))) (Actualizacin de corriente, si es necesario-lista)))

Ahora usted tiene todos los bits subyacentes que necesita. Todo lo que queda es el cdigo que se proporciona una interfaz de usuario basada en Web para navegar por la base de datos y la manipulacin de listas de reproduccin MP3. La interfaz consta de tres funciones principales definidas con definir-url-funcin : una para navegar por la base de datos de la cancin, una para la visualizacin y la manipulacin de una nica lista de reproduccin, y uno para la inclusin de todas las listas de reproduccin disponibles. Pero antes de llegar a la escritura de estas tres funciones, es necesario comenzar con algunas funciones de ayuda HTML y las macros que van a utilizar. Tipos de consultas de parmetros Puesto que usted va a utilizar define-url-funcin , es necesario definir algunos mtodos de la cadena-> tipo de funcin genrica del captulo 28, quedefine-urlfuncin utiliza para convertir los parmetros de cadena de consulta en los objetos de Lisp. En esta aplicacin, usted necesitar mtodos para convertir cadenas en nmeros enteros, los smbolos de palabras clave y una lista de valores. Los dos primeros son bastante simples.

(Defmethod string-> (tipo ((entero eql ')) valor) (Anlisis sintctico-entero (o el valor ""): basura permite t)) (Defmethod string-> (tipo ((palabra eql ')) valor) (Y (plusp (valor de longitud)) (pasante (cadena-upcase valor): la palabra clave)))

La ltima cadena-> tipo de mtodo es algo ms complejo. Por razones que llegaremos en un momento, tendr que generar pginas que muestran un formulario que contiene un campo oculto cuyo valor es una lista de cadenas. Puesto que usted es responsable de generar el valor en el campo oculto , y para analizar lo que al regresar, se puede utilizar cualquier codificacin es conveniente. Usted podra utilizar las funciones de escritura en cuerdas y LEA-DE CUERDAS- , que utilizan la impresora Lisp y el lector a leer y escribir datos desde y hacia las cadenas, a excepcin de la representacin impresa de las cadenas puede contener comillas y otros caracteres que pueden causar problemas cuando se incrusta en el atributo de valor de un INPUT elemento. Por lo tanto, tendrs que escapar de los personajes de alguna manera. En lugar de tratar de llegar con su propio sistema de escape, slo puede utilizar la base 64, una codificacin de uso general para proteger los datos binarios enviados a travs de correo electrnico. AllegroServe viene con dos funciones, base64-codificar y decodificar Base64- , los que hacen la codificacin y decodificacin para ti, as que todo lo que tienes que hacer es escribir un par de funciones: uno que codifica un objeto Lisp, convirtindola en una cadena legible con ESCRIBE a cadena y luego la base codificacin de 64 ella y, por el contrario, otro para decodificar una cadena por la base 64 que la decodificacin y pasar el resultado a read-from-STRING . Usted querr para envolver las llamadas a ESCRIBA a cadena yread-from-STRING en el CON-STANDARD-IO-La sintaxis para asegurarse de que todas las variables que afectan a la impresora y el lector se ajustan a sus valores normales. Sin embargo, debido a que va a ser la lectura de los datos que se vienen de la red, que sin duda querr desactivar una caracterstica del lector - la capacidad de evaluar de cdigo arbitrario Lisp, mientras que la lectura! 4 Puede definir su propia macro con-safe-io-la sintaxis , que se envuelve el cuerpo en sus formasCON-STANDARD-IO-SINTAXIS envuelto alrededor de un LET que se une * LEA-EVAL * de NIL .
(Defmacro con-safe-io-sintaxis (y el cuerpo del cuerpo) `(Con estndar-io-sintaxis (Let ((* lectura eval * nil)) , El cuerpo de @)))

A continuacin, las funciones de codificacin y decodificacin son triviales.

(Defun obj-> base64 (obj) (Base64-encode (con-safe-io-sintaxis (escritura a cadena obj)))) (Defun base64-> obj (cadena) (Ignorar los errores (Con-safe-io-sintaxis (lectura de cuerdas (base 64 de decodificacin de cadenas)))))

Por ltimo, puede utilizar estas funciones para definir un mtodo en cadena> Tipo que define la conversin para el tipo de parmetro de consulta en base64 lista .
(Defmethod string-> tipo ((tipo (EQL "base-64-list)) valor) (Let ((obj (base64-> valor de obj))) (If (obj listp) nula obj)))

Repetitivo HTML A continuacin es necesario definir algunas macros HTML y funciones de ayuda para hacer ms fcil para dar las diferentes pginas de la aplicacin una apariencia consistente.Usted puede comenzar con una macro HTML que define la estructura bsica de una pgina en la aplicacin.
(Define-html-macro: mp3-navegador de pginas ((y ttulo clave (ttulo de cabecera)) y el cuerpo del cuerpo) `(: Html (: La cabeza (Ttulo, el ttulo) (: Link: rel "estilo": el tipo "text / css": href "mp3-browser.css")) (: El cuerpo (Estndar en la cabecera) (Cuando, cabecera (html (: H1: la clase "ttulo", encabezado))) , @ Cuerpo (Estndar en pie))))

Usted debe definir estndares de cabecera y pie de pgina estndar como funciones separadas por dos razones. En primer lugar, durante el desarrollo puede volver a definir las funciones y ver el efecto inmediatamente, sin tener que recompilar las funciones que utilizan el formato mp3, navegador de pginas macro. En segundo lugar, resulta que una de las pginas que va a escribir ms adelante no se define con : mp3, navegador de pginas , pero todava tendr la cabecera estndar y pies de pgina. Se ver as:
(Defparameter * r * 25) (Defun norma-header () (Html ((: P: clase "barra de herramientas") "[" (: A: href (enlace "/ ver": lo que "gnero") "Todos los gneros") "]" "[" (: A: href (enlace "/ ver": lo que "gnero": al azar * r *) "gneros azar") "]" "[" (: A: href (enlace "/ ver": lo "artista") "Todos los artistas") "]" "[" (: A: href (enlace "/ ver": lo "artista": al azar * r *) "artistas al azar") "]"

"[" (: A: href (enlace "/ ver": lo "disco") "Todos los lbumes") "]" "[" (: A: href (enlace "/ ver": lo "disco": al azar * r *) "discos al azar") "]" "[" (: A: href (enlace "/ ver": lo "cancin": al azar * r *) "canciones al azar") "]" "[" (: A: href (enlace "/ lista de reproduccin") "Playlist") "]" "[" (: A: href (enlace "/ todas las listas-") "Todas las listas de reproduccin") "]"))) (Defun estndar pie de pgina () (Html (: h) ((: P: "." Clase "pie") "MP3 Browser v" * versin principal * * de menor importancia-la versin *)))

Un par de pequeas macros HTML y funciones auxiliares automatizar otros patrones comunes. El : table-row macro HTML hace que sea ms fcil generar el cdigo HTML de una sola fila de una tabla. Se utiliza una caracterstica de FOO que voy a discutir en el captulo 31, uno y atributos de los parmetros, lo que hace que los usos de la macro va a ser analizada como normales s-expresin de formularios HTML, con todos los atributos recogidos en una lista que quedar obenlazado a la y los atributos deparmetro. Se parece a esto:
(Define-html-macro: table-row (y attrs atributos y valores de descanso) `(: Tr, attrs @, @ (bucle para recoger los valores de v en` (: td, v))))

Y el enlace de la funcin genera una direccin URL de nuevo en la aplicacin que se utiliza como HREF atributo con un un elemento, la construccin de una cadena de consulta de un conjunto de pares de palabra clave / valor y asegurarse de que todos los caracteres especiales son caracteres de escape. Por ejemplo, en vez de escribir esto:
(: A: href "? Ver lo que el artista y el gnero = = Ritmo +% 26 + Blues" "artistas")

usted puede escribir lo siguiente:


(: A: href (enlace "ver": lo "artista": gnero "Rhythm & Blues") "Artistas")

Se parece a esto:
(Enlace defun (de destino y dems atributos) (Html (Atributo: (: Formato de "~ a ~ @ [{~ ~ (~ a ~) = ~ a ~ ^ & ~} ~?]" Objetivo (atributos mapcar # 'de urlencode)))))

URL codificar las claves y valores, se utiliza la funcin auxiliar urlencode , que es una envoltura alrededor de la funcin de codificar-form-urlencoded , que es una funcin pblica de la AllegroServe. Esto es - por un lado - una mala forma, ya que el nombre de codificacin-form-urlencoded no se exporte desde NET.ASERVE , es posible que codifican-form-urlencoded pueden desaparecer o se renombran de

debajo de ti. Por otro lado, mediante este smbolo dejados por el momento le permite conseguir el trabajo hecho por el momento, antes del envasado de codificacin-form-urlencoded en su propia funcin, a aislar el cdigo enrevesada de una funcin, que se puede reescribir si tuvieras a.
(Defun urlencode (cadena) (Net.aserve :: encode-form-urlencoded cadena))

Por ltimo, es necesario la hoja de estilos CSS mp3-browser.css utilizados por : mp3navegador de pginas . Como no hay nada dinmico al respecto, es probablemente ms fcil de publicar slo un archivo esttico, con publicacin de archivos .
(Publicar-archivo: ruta "/ mp3-browser.css": archivo nombre de archivo : El contenido de tipo "text / css")

Una hoja de estilo de la muestra se incluye en el cdigo fuente de este captulo en el sitio web del libro. Vas a definir una funcin, al final de este captulo, que se inicia la aplicacin del navegador de MP3. Va a hacerse cargo de, entre otras cosas, la publicacin de este archivo. La pgina Examinar La funcin primera URL generar una pgina para navegar por la base de datos MP3. Sus parmetros de consulta dicen que el tipo de cosa que el usuario est navegando y proporcionar los criterios de lo que elementos de la base de datos que est interesado pulg voy a dar una forma de seleccionar las entradas de la base de datos que responden a un gnero especfico, el artista o lbum. En aras de la casualidad, tambin puede proporcionar una manera de seleccionar un subconjunto aleatorio de elementos coincidentes. Cuando el usuario est navegando en el nivel de las canciones individuales, el ttulo de la cancin ser un enlace que hace que la cancin que se aade a la lista de reproduccin. De lo contrario, cada artculo ser presentado con enlaces que permiten al usuario navegar por el elemento de la lista por alguna otra categora. Por ejemplo, si el usuario est navegando por gneros, la entrada "Blues" contendr enlaces para buscar todos los lbumes, artistas, y canciones de los Blues. Adems, la pgina de torrents contar con una "Aadir todos" botn que agrega cada cancin corresponde a la bsqueda de la pgina a la lista del usuario. La funcin se parece a esto:
(Define-url-la funcin de navegar (Solicitud (lo que la palabra clave: gnero) lbum del artista gnero (entero aleatorio))

(Let * ((Los valores (los valores para la pgina de lbum del artista lo que genero al azar)) (Ttulo (de exploracin de la pgina, el ttulo del lbum lo aleatorio artista de gnero)) (De una sola columna (si (lo que eql: la cancin): archivo de lo que)) (Los valores de cadena (valores-> 64 de base de una sola columna de valores))) (Html (En formato mp3, navegador de pginas (Ttulo del ttulo) ((: Forma: el mtodo "POST": la accin "lista") (: De entrada: nombre de los "valores": el tipo "ocultos": el valor de los valores de cadena) (: De entrada: el nombre de "qu": el tipo "oculto": el valor de una sola columna) (: De entrada: el nombre de "accin": tipo de "oculto": Valor: complementos de canciones) (: De entrada: el nombre de "enviar": tipo de "enviar": valor "Aadir todos")) (: Ul (do-filas (valores de la fila) (list-item para la pgina de lo que la fila)))))))

Esta funcin se inicia mediante la funcin de los valores de fines de la pgina para obtener una tabla que contiene los valores que necesita presentar. Cuando el usuario se encuentre navegando por la cancin de - cuando el lo que el parmetro es : la cancin - que desea seleccionar filas completas de la base de datos. Pero cuando se est navegando por gnero, artista o lbum, que desea seleccionar slo los valores distintos para la misma categora. La funcin de base de datos de seleccin hace la mayor parte del trabajo pesado, con valores de fines de la pgina principal responsable de pasar los argumentos adecuados en funcin del valor de lo que . Aqu es tambin donde se selecciona un subconjunto aleatorio de las filas coincidentes, si es necesario.
(Defun los valores para la pgina (lo que lbum del artista gnero al azar) (Let ((los valores (Seleccione : De * mp3 * : Las columnas (SI (EQL lo que: cancin) t lo) : Si (coincidentes con * mp3 *: gnero de gnero: Artista: lbum lbum) : Distinto (no (lo que eql: cancin)) : Order-by (if (EQL qu: la cancin) '(: lbum: pista) lo que)))) (Si es al azar (random-seleccin de valores aleatorios) los valores)))

Para generar el ttulo de la pgina de exploracin, se pasa los criterios de navegacin a la siguiente funcin, de exploracin de la pgina, el ttulo :
(Defun de exploracin de pgina del ttulo (lo que Random Album artista de gnero) (Con salida a cadena (s) (Cuando el azar (formato de s "~: (~ ~ r) Random" al azar)) (Formato de s "~: (~ a ~ ~ p)" lo aleatorio) (Cuando (o el lbum gnero de artista) (Cuando (no (lo que eql: cancin)) (princ "con canciones" s)) (Cuando el gnero (en formato s "en el gnero ~ un" gnero)) (Cuando el artista (formato s "por el artista ~ un" artista))

(Cuando el disco (formato s "en el lbum de ~ un" lbum)))))

Una vez que tenga los valores que desea presentar, lo que necesita hacer dos cosas con ellos. La tarea principal, por supuesto, es dar a conocer ellos, lo que sucede en elhacer de las filas , lo que deja la representacin de cada fila a la funcin de objetos de lista-para-la pgina . Esta funcin hace : canciones filas de una manera y todas las otras clases otra manera.
(Defun lista de tems para la pgina (lo que la fila) (Si (lo que eql: cancin) (Con columnas de valores (archivo de la cancin lbum artista gnero) la fila (Html (: Li (: A: href (enlace "playlist": Archivo: accin "add-canciones") (: cancin b)) "desde" (: A: href (enlace "ver": lo que: la cancin: lbum lbum) lbum) "por" (: A: href (enlace "ver": lo que: la cancin: artista) del artista) "en el gnero" (: A: href (enlace "ver": lo que: la cancin: gnero de gnero) Gnero)))) (Let ((valor (columna de valor de la fila lo que))) (Html (: Li valor "-" (Exploracin de enlace: el gnero lo que el valor) (Browse-link: artista cul es el valor) (Browse-link: lbum qu valor) (Exploracin de enlace: la cancin qu valor)))))) (Defun de exploracin de enlace (nuevo-lo cul es el valor) (A menos que (EQL nuevo-lo que lo que) (Html "[" (: A: href (enlace "ver": lo nuevo-lo cul es el valor) (: formato de "~ (~ en ~)" nuevo-lo)) "]")))

La otra cosa en la exploracin de pgina es un formulario con varios ocultos ENTRADA campos y una "Aadir todos" botn de enviar. Usted necesidad de utilizar un formulario HTML en lugar de una de manera regular para mantener la aplicacin sin estado - para asegurarse de que toda la informacin necesaria para responder a una solicitud se presenta en la propia solicitud. Debido a la pgina de resultados exploracin puede ser en parte al azar, usted debe presentar una buena cantidad de datos para el servidor para poder reconstruir la lista de canciones para aadir a la lista de reproduccin. Si no permitir que la pgina de bsqueda para devolver los resultados generados al azar, usted no necesitara mayor cantidad de datos - slo poda presentar una solicitud para agregar canciones a cualquier criterio de bsqueda utiliza la pgina de torrents. Pero si se ha aadido canciones de esa manera, con criterios que incluyen una aleatoria argumento, entonces iba a

terminar la adicin de un conjunto diferente de las canciones al azar que el usuario estaba viendo en la pgina cuando llegan a la opcin "Agregar todo". La solucin va a utilizar es el devolver un formulario que tiene suficiente informacin guardados en un escondido ENTRADA elemento para permitir que el servidor de reconstruir la lista de canciones que coincidan con los criterios de exploracin de pginas. Esa informacin es la lista de valores devueltos por los valores-por-pgina y el valor de laqu parmetro. Aqu es donde se utiliza la base 64 de lista de tipo de parmetro, la funcin de los valores-> base64 extractos de los valores de una columna especificada de la tabla devuelta por los valores de fines de la pgina en una lista y luego hace una base de 64 cadena codificada de ese lista para incorporar en el formulario.
(Defun valores> Base-64 (los valores de las columnas de tabla) (FLET ((valor (r) (columna-valor de la columna r))) (Obj-> base64 (MAP-filas # 'valor de los valores de tabla))))

Cuando este parmetro se vuelve como el valor de la valores de parmetro de consulta a una funcin de direccin URL que declara los valores a ser de tipobase64-list , que va a ser automticamente convertidos de nuevo a una lista. Como se ver en un momento, esa lista puede ser utilizado para construir una consulta que va a devolver la lista correcta de las canciones. 5 Cuando ests navegando por : la cancin , se utilizan los valores de la : Archivo de la columna, ya que identificar de forma exclusiva las canciones reales, mientras que los nombres de las canciones no. La lista de reproduccin Esto me lleva a la funcin de direccin URL siguiente, lista de reproduccin . Esta es la pgina ms complejo de los tres - es responsable de mostrar el contenido actual de la lista del usuario, as como para proporcionar la interfaz para manipular la lista de reproduccin. Sin embargo, con la mayor parte de la contabilidad tediosa a cargo dedefinir-url-funcin , no es demasiado difcil ver cmo la lista funciona. Aqu est el principio de la definicin, con slo la lista de parmetros:
(Define-url-funcin de lista de reproduccin (Solicitud de (Lista de reproduccin-string id (lista de reproduccin-Identificacin del pedido): el paquete) (Palabra clave de accin); lista de reproduccin accin de la manipulacin (Qu palabra clave: archivo), para: add-canciones de accin (Valores de base de la lista de 64); " un archivo, por: add-canciones y canciones: delete-acciones gnero; para: eliminar canciones de la accin artista, "

lbum; " (Orden por palabra clave), para: tipo de accin (Palabra clave aleatoria), para: mezclar la accin (Repeticin de palabras clave)); para: conjunto de acciones de repeticin

Adems de la obligatoria peticin de parmetro, lista de reproduccin toma una serie de parmetros de consulta. El ms importante de alguna manera esla lista-id , que identifica lo que la lista objeto de la pgina debe mostrar y manipular. Para este parmetro, se puede tomar ventaja de definir-url-funcin 's "parmetro pegajosa" caracterstica. Normalmente, la lista de reproduccin-id no se proporciona explcitamente, por defecto el valor devuelto por lalista de reproduccin-id funcin, es decir, la direccin IP de la mquina cliente en la que el navegador se est ejecutando. Sin embargo, los usuarios tambin pueden manipular a sus listas de reproduccin de mquinas diferentes a las que ejecutan sus clientes MP3, permitiendo que este valor se especifica explcitamente. Y si se especifica una vez,definir-url-funcin de los arreglos para que se "pegue" al poner una cookie en el navegador. Ms tarde va a definir una funcin de direccin URL que genera una lista de todas las listas, que los usuarios pueden utilizar para elegir una que no sea el de las mquinas que estn navegando por la lista de reproduccin. La accin de parmetro especifica una accin a tomar sobre el objeto que el usuario la lista de reproduccin. El valor de este parmetro, que se convertir en un smbolo de la palabra clave para usted, puede ser : add-canciones , las canciones: delete- y : claro , la clasificacin: y : shuffle , oconjunto: la repeticin- . El complemento de las canciones: la accin es utilizado por el "Aadir todas" en la pgina de exploracin y tambin por los enlaces utilizados para aadir canciones individuales. Las dems acciones son utilizados por los enlaces de la pgina de lista de reproduccin propia. El archivo , lo que , y los valores de los parmetros se utilizan con el complemento de las canciones: la accin. Al declarar los valores a ser de tipo base-64-list , la definen-url-funcin de la infraestructura se encargar de descifrar el valor presentado por la opcin "Agregar" o forma. Los dems parmetros se utilizan con otras acciones, como se indica en los comentarios. Ahora vamos a ver el cuerpo de la lista de reproduccin . Lo primero que tienes que hacer es utilizar la lista de reproduccin-id para buscar el objeto de cola y luego adquirir el bloqueo de la lista de reproduccin con las dos lneas siguientes:
(Let ((lista de reproduccin (playlist-lookup-id)))

(Con-playlist-bloqueado (lista de reproduccin)

Desde la lista de bsqueda, se crear una nueva lista de reproduccin si es necesario, esto siempre devolver una lista de reproduccin de objetos. A continuacin, usted toma el cuidado de cualquier manipulacin de la cola es necesario, el envo en el valor de la accin de los parmetros con el fin de llamar a una de laslistas de reproduccin funciones.
(Caso de la accin (: Add-canciones (canciones de la lista complemento lo (o los valores (archivo de lista)))) (: Delete (borrar canciones-cancioneslista de reproduccin : Archivo: Gnero Gnero : Artista: lbum lbum)) (: Clara (clear-playlist)) (: Sort (sort-playlist-por fin)) (: Reproduccin aleatoria (shuffle-lista de reproduccin aleatoria lista de reproduccin)) (: Conjunto de repeticin (setf (repeticin de reproduccin) la repeticin)))

Todo lo que queda de la lista de reproduccin funcin es la generacin de HTML real. Una vez ms, se puede utilizar elformato mp3, navegador de pginas HTML macro para asegurarse de que la forma bsica de la pgina coincide con las otras pginas de la aplicacin, aunque esta vez se pasa NADA a la : cabecera argumento para dejar de lado el H1 de cabecera . Aqu est el resto de la funcin:
(Html (En formato mp3, navegador de pginas (Ttulo (: formato de "lista de reproduccin - ~ a" (Identificacin lista de reproduccin)): ninguna de cabecera) (Lista de reproduccin, barra de herramientas de lista de reproduccin) (If (empty-p lista de reproduccin) (Html (: P (: ". Vaco" i))) (Html ((: Table: clase "lista") (: Table-row "#", "Cancin", "lbum", "Artista", "Gnero") (Let ((idx 0) (Actual-idx (actual-idx lista de reproduccin))) (Hacer las filas (fila (las canciones de mesa lista de reproduccin)) (Con columnas de valores (archivo de pista de la cancin lbum artista de gnero) la fila (Let ((fila de estilo (if (= idx actual-idx) "ahora jugar", "normal"))) (Html ((: Table-row: fila de clase de estilo) rastrear (: Cancin progn (delete-canciones-link: Archivo)) (: lbum progn (delete-canciones-link: lbum)) (: Artista progn (delete-canciones-link: artista)) (: Gnero progn (delete-canciones-link: gnero de gnero))))) (Incf idx))))))))))))

La funcin de lista de reproduccin, la barra de herramientas genera una barra de herramientas que contiene enlaces a lista de reproduccinpara llevar a cabo las

diferentes acciones: las manipulaciones. Y delete-canciones-link genera un enlace a la lista de reproduccin con la : accinconjunto de parmetros : borrar las canciones y los argumentos apropiados para eliminar un archivo individual o todos los archivos en un lbum, un artista en particular o en un gnero especfico.
(Defun lista-barra de herramientas (lista de reproduccin) (Let ((actual-repeat (repeticin de reproduccin)) (Actual-sort (ordenar lista de reproduccin)) (Corriente aleatoria (shuffle lista de reproduccin))) (Html (: P: clase "lista de reproduccin, barra de herramientas" (: I "Ordenar por:") "[" (Ms o menos-playlist-botn de "gnero" actual-clase) "|" (Ms o menos-playlist-botn de "artista" actual-clase) "|" (Ms o menos-playlist-botn de "lbum" actual-clase) "|" (Ms o menos-playlist-botn de "cancin" actual-clase) "]" (: I "Shuffle") "[" (Lista de reproduccin-shuffle-botn de "ninguna" corriente aleatoria) "|" (Lista de reproduccin-shuffle-botn de "cancin" actual-shuffle) "|" (Lista de reproduccin-shuffle-botn de "lbum" corriente aleatoria) "]" (: I "Repetir:") "[" (Lista de reproduccin-repetidamente el botn "ninguno" en curso de repeticin) "|" (Lista de reproduccin de repeticin, botn de "cancin" actual de la repeticin) "|" (Lista de reproduccin de repeticin, botn de "todos" en curso de repeticin) "]" "[" (: A: href (enlace "lista de reproduccin": la accin "claro") "Clear") "]")))) (Defun lista-botn (argumento de accin nuevo valor actual del valor) (Let ((etiqueta (cadena de valor capitalizar nueva))) (Si (string-equal nuevo valor actual del valor) (Html (: etiqueta b)) (Html (: a: href (enlace "playlist": argumento de la accin nueva accin de valor) la etiqueta))))) (Defun-playlist-tipo botn (de la orden por la corriente de clase) (Lista de reproduccin-botn: Ordenar por: orden-por orden-por el actual-clase)) (Defun lista-shuffle-botn (seleccin aleatoria de corriente shuffle) (Lista de reproduccin-botn: shuffle: shuffle actual-shuffle)) (Defun lista-repetidamente el botn (repeticin de curso de repeticin) (Lista de reproduccin-botn: la repeticin: Repeticin de corriente de la repeticin)) (Defun eliminar-canciones-link (qu valor) (Html "[" (: a: href (enlace "playlist": accin: Eliminar canciones de lo que el valor) "x") "]"))

Encontrar una lista de reproduccin La ltima de las tres funciones de URL es la ms sencilla. Se presenta una tabla con todas las listas de reproduccin que se han creado. Normalmente los usuarios no

tendrn que utilizar esta pgina, pero durante el desarrollo que le da una visin til sobre el estado del sistema. Tambin proporciona el mecanismo para elegir una lista de reproduccin diferente - cada ID de la lista es un vnculo a la lista la pgina con una explcita lista de reproduccin-Identificacin del parmetro de consulta, que luego se hizo pegajosa por la lista de reproduccin funcin de direccin URL. Tenga en cuenta que usted necesita para adquirir la * listas de bloqueo * para asegurarse de que las listas de reproduccin * Los * tabla hash no cambia de debajo de ti mientras ests interactuando sobre ella.
(Define-la funcin url-todas las listas de reproduccin (bajo peticin) (En formato mp3, navegador de pginas (Ttulo de "Todas las listas de reproduccin") ((: Table: clase "todas las listas de") (: Table-row "Lista de reproduccin" "canciones #" "agente de usuario ms reciente") (Con proceso de bloqueo (* listas de bloqueo *) (Bucle de la lista son los valores hash de * Las listas de reproduccin * do (Html (: Table-row (: A: href (enlace "playlist": lista de reproduccin-ID (Identificacin lista de reproduccin)) (: print (Identificacin lista de reproduccin))) (: De impresin (vase el cuadro de tamao (las canciones de mesa lista de reproduccin))) (: Print (user-agent lista de reproduccin)))))))))

Ejecucin de la aplicacin Y eso es todo. Para utilizar esta aplicacin, slo tiene que cargar la base de datos de MP3 con la carga de base de datos de la funcin del captulo 27, a publicar la hoja de estilos CSS, establezca * cancin de cdigo de tipo * de la lista para encontrar el canto de cdigo utiliza las listas de reproduccin en lugar de la fuente de singleton cancin se define en el captulo anterior, y empezar a AllegroServe. La siguiente funcin se encarga de todos estos pasos para usted, despus de rellenar los valores adecuados para los dos parmetros * mp3-dir * , que es el directorio raz de tu coleccin de MP3, y * mp3-CSS * , el nombre del archivo de la CSS de hojas de estilo:
(* Defparameter mp3-dir * ...) (* Defparameter mp3-CSS * ...) (Defun principio-mp3-navegador () (Carga de base de datos * mp3 ** mp3-dir *) (Publicar-archivo: ruta "/ mp3-browser.css": archivo mp3 *-css *: tipo de contenido "text / css") (Setf * cancin de cdigo de tipo * 'lista de reproduccin) (Net.aserve :: depuracin en: notrap) (Net.aserve: inicio: el puerto 2001))

Cuando se llama a esta funcin, se imprimir puntos, mientras que se carga la informacin ID3 de tus archivos ID3. A continuacin, se puede apuntar a su cliente de MP3 en la siguiente direccin:
http://localhost:2001/stream.mp3

y dirija su navegador en algn buen lugar de partida, como este:


http://localhost:2001/browse

que le permitir comenzar a navegar por la categora por defecto, Gnero. Despus de agregar algunas canciones a la lista de reproduccin, puede pulsar Play en el cliente de MP3, y debe empezar a reproducir la primera cancin. Obviamente, usted puede mejorar la interfaz de usuario en cualquiera de varias maneras-por ejemplo, si usted tiene un montn de archivos MP3 en su biblioteca, puede ser til ser capaz de navegar por artistas o lbumes por la primera letra de sus nombres . O tal vez usted podra agregar una "Play lbum completo" para la pgina de lista de reproduccin que hace que la lista de reproduccin para poner de inmediato todas las canciones del mismo lbum como la cancin que est sonando en la parte superior de la lista de reproduccin. O usted podra cambiar la lista de clase, as que en vez de jugar el silencio cuando no hay canciones en cola, toma una cancin al azar de la base de datos. Pero todas esas ideas caen en el mbito del diseo de aplicaciones, que no es realmente el tema de este libro. En cambio, los dos captulos siguientes se reducir de nuevo al nivel de infraestructura de software para cubrir la forma de la biblioteca de la generacin de HTML FOO funciona.

1 Las complejidades de la programacin concurrente estn fuera del mbito de este libro. La idea

bsica es que si usted tiene mltiples hilos de control - como usted en esta solicitud con algunos hilos de ejecucin del shoutcast funcin y otros temas que responden a las solicitudes del explorador entonces usted necesita para asegurarse de que slo un hilo en un momento manipula un objeto con el fin de evitar que un hilo de ver el objeto en un estado incoherente, mientras que otro hilo est trabajando en ello. En esta funcin, por ejemplo, si dos clientes nuevos MP3 se conecta al mismo tiempo, que haba tanto tratar de aadir una entrada en las listas de reproduccin * Los * y pueden interferir unos con otros. El proceso con el bloqueo se asegura de que cada hilo tiene acceso exclusivo a la tabla hash para el tiempo suficiente para hacer el trabajo que tiene que hacer.
2 Este enfoque tambin supone que cada mquina cliente tiene una direccin IP nica. Este supuesto

debe mantener, siempre y cuando todos los usuarios estn en la misma LAN, pero no podrn ejercer si el cliente se conecta desde detrs de un firewall que hace la traduccin de direcciones de red. Implementacin de esta aplicacin fuera de una LAN se requiere algunas modificaciones, pero si

desea implementar esta aplicacin en el Internet en general, es mejor que saber lo suficiente acerca de las redes de averiguar un esquema apropiado a ti mismo.
3 Desafortunadamente, debido a problemas de licencias en todo el formato MP3, no est claro que es

legal para m para ofrecerle como un MP3 sin tener que pagar derechos de licencia de Fraunhofer IIS. Yo tengo la ma, como parte del software que vena con mi Slimp3 de Slim Devices. Usted puede agarrar de su repositorio de Subversion a travs de la Web enhttp://svn.slimdevices.com/ * checkout * / trunk / server / HT ML/EN/html/silentpacket.mp3? rev = 2 . O comprar un Squeezebox, la nueva versin, sin hilos de Slimp3, y obtendr silentpacket.mp3 como parte del software que viene con l. O encontrar un MP3 de la pieza de John Cage 4'33 " .
4 El lector es compatible con una poco de la sintaxis, #. , que hace que el siguiente s-expresin que se

evala en tiempo de lectura. Esto es til en ocasiones en el cdigo fuente, pero obviamente se abre un agujero de seguridad grande cuando usted lee los datos no confiables. Sin embargo, puede desactivar esta sintaxis mediante el establecimiento de * LEA-EVAL * a NIL , lo que har que el lector para sealar un error si se encuentra con #. .
5 Esta solucin tiene sus desventajas - si un browse la pgina devuelve un montn de resultados, una

buena cantidad de datos que van y vienen bajo las sbanas. Adems, las consultas de base de datos no son necesariamente las ms eficientes. Pero s quiere mantener la aplicacin sin estado. Un enfoque alternativo es la ardilla de distancia, en el lado del servidor, la informacin sobre los resultados devueltos por laexploracin y, a continuacin, cuando una solicitud para aadir canciones entrar, encontrar el bit apropiado de la informacin con el fin de volver a crear el conjunto correcto de las canciones. Por ejemplo, usted podra salvar a la lista de valores en lugar de enviarlo de regreso en el formulario. O usted puede copiar el AZAR-ESTADO objeto antes de generar los resultados de exploracin de modo que puede volver a crear las mismas "al azar" los resultados. Pero este enfoque hace que sus propios problemas. Por ejemplo, a continuacin, haba necesidad de preocuparse acerca de cundo puede deshacerse de la informacin de recaudo de distancia-, nunca se sabe cuando el usuario puede pulsar el botn Atrs de su navegador para volver a una pgina de torrents de edad y despus haga clic en el botn Aadir " todos los botones ".Bienvenido al maravilloso mundo de la programacin Web.

30. Prctico: una biblioteca de la generacin de HTML, el intrprete


En este captulo y el siguiente que le eche un vistazo bajo el cap del generador FOO HTML que usted ha estado utilizando en los ltimos captulos. FOO es un ejemplo de un tipo de programacin que es muy comn en Common Lisp y relativamente poco frecuente en no Lisp idiomas, a saber, el lenguaje orientado a la programacin. En lugar de proporcionar una API construida principalmente de funciones, clases y macros, FOO ofrece procesadores de lenguaje para un lenguaje de dominio especfico que se puede incorporar en sus programas de Common Lisp. FOO ofrece dos procesadores de lenguaje para el mismo s-expresin del lenguaje. Uno de ellos es un intrprete que tiene un FOO "programa" como los datos y la interpreta para generar HTML. El otro es un compilador que compila expresiones FOO, posiblemente con cdigo embebido Common Lisp, en Common Lisp que genera HTML y ejecuta el cdigo incrustado. El intrprete se expone como la funcin de emitir-html y el compilador como la macro HTML , que se utiliza en los captulos anteriores. En este captulo veremos algunas de las infraestructuras compartidas entre el intrprete y el compilador y luego en la aplicacin de la intrprete. En el siguiente captulo, le mostrar cmo funciona el compilador. El diseo de un lenguaje de dominio especfico El diseo de un lenguaje embebido requiere dos pasos: en primer lugar, el diseo del lenguaje que te permitir expresar las cosas que quiere expresar, y segundo, implementar un procesador o procesadores, que acepta un "programa" en ese idioma y, o bien lleva a cabo la acciones indicadas por el programa o traduce el programa en comn de cdigo Lisp que va a realizar comportamientos equivalentes. As, el primer paso es disear el lenguaje HTML de generacin. La clave para disear un buen dominio del lenguaje especfico es lograr el justo equilibrio entre expresividad y concisin. Por ejemplo, una gran fuerza expresiva, pero no muy concisa "lenguaje" para generar HTML es el lenguaje de las cadenas literales HTML. Las formas jurdicas "," de esta lengua son cadenas que contienen HTML literal. Los

procesadores de lenguaje para este "lenguaje" podra procesar estas formas con slo emitir tal cual.
(* Defvar html de salida ** estndar de salida *) (Defun emiten-html (html) "Un intrprete para el lenguaje HTML literal." (Escritura de la secuencia * html html de salida *)) (Defmacro html (html) "Un compilador para el lenguaje HTML literal." `(Write-secuencia, HTML * html de salida *))

Este "lenguaje" es muy expresiva, ya que puede expresar cualquier . HTML que pueda desear para generar un Por otro lado, este lenguaje no gana una gran cantidad de puntos por su concisin, ya que proporciona una compresin cero - la entrada es su salida. Para disear un lenguaje que le da un poco de compresin til sin sacrificar la expresividad demasiado, es necesario identificar los detalles de la salida que son redundantes o poco interesante. A continuacin, puede hacer que los aspectos de la produccin implcita en la semntica de la lengua. Por ejemplo, debido a la estructura de HTML, cada etiqueta de apertura se empareja con una etiqueta de clausura correspondiente. 2 Al escribir HTML a mano, tienes que escribir las etiquetas de clausura, pero se puede mejorar la concisin de su HTML de generacin de lenguaje haciendo las etiquetas de clausura implcito. Otra manera de obtener la concisin, a un costo ligeramente expresividad es hacer que los procesadores de lenguaje responsable de la adicin adecuada los espacios en blanco entre los elementos - lneas en blanco y el sangrado. Cuando se est generando HTML mediante programacin, por lo general no se preocupan mucho acerca de qu elementos tienen los saltos de lnea antes o despus de ellos o acerca de si los distintos elementos se aplica sangra en relacin a los elementos de sus padres. Dejar que el procesador de lenguaje insertar espacios en blanco de acuerdo con alguna regla significa que usted no tiene que preocuparse por ello. Como resultado, FOO realidad compatible con dos modos: uno que utiliza la cantidad mnima de espacio en blanco, lo que le permite generar el cdigo HTML muy eficiente y compacto, y otro que genera cdigo HTML con un formato agradable con elementos de diferentes aberturas y separado de otros elementos de acuerdo a su papel.

Otro detalle que mejor se mueve en el procesador de lenguaje es el escapar de ciertos personajes que tienen un significado especial en HTML como < , > , y la y . Obviamente, si generar HTML con slo imprimir las cadenas a un arroyo, entonces le toca a usted para sustituir todas las apariciones de los caracteres de la cadena con las secuencias de escapeadecuadas, < , > y y . Pero si el procesador de lenguaje se puede saber qu cadenas se emite en forma de datos de elementos, entonces puede hacerse cargo de forma automtica escapar de esos personajes para usted. El lenguaje FOO Por lo tanto, suficiente teora. Te voy a dar una visin general rpida de la lengua implementado por FOO, y entonces mirars en la aplicacin de los dos procesadores de lenguaje FOO - el intrprete, en este captulo, y el compilador, en el siguiente. Al igual que Lisp en s, la sintaxis bsica del lenguaje de FOO se define en trminos de las formas compuestas de objetos de Lisp. El lenguaje define cmo cada forma jurdica FOO se traduce en HTML. Las formas ms sencillas FOO son auto-evaluacin de objetos de Lisp, como cadenas, nmeros y smbolos de palabras clave. 3 Tendr una funcin de autoevaluacin-p que comprueba si un objeto dado, es la auto-evaluacin con fines de Foo.
(Defun auto-evaluacin-p (formulario) (Y (forma de un tomo) (if (forma symbolp) (formulario keywordp) t)))

Los objetos que satisface este requisito se emitir mediante la conversin a cadenas con Princ a cadena y luego escapar todos los caracteres reservados, como < , > , o y .Cuando el valor se est emitiendo como un atributo, los personajes " , y " se escap tambin. Por lo tanto, puede invocar el html macro en un objeto de autoevaluacin para emitir a* html de salida * (que inicialmente est obenlazado a * NORMAS DE SALIDA * ). Tabla 30-1 muestra cmo unos pocos diferentes que evalan los valores de auto-ser la salida.
Tabla 30-1. FOO de salida para la Auto-Evaluacin de los objetos

FOO Formulario "Foo" 10 : Foo "Foo y bar"

HTML generado foo 10 FOO foo y bar

Por supuesto, la mayora de HTML se compone de elementos etiquetados. Las tres piezas de informacin que describen cada elemento es la etiqueta, un conjunto de

atributos, y un cuerpo que contiene el texto y / o ms elementos HTML. . Por lo tanto, usted necesita una manera de representar estas tres piezas de informacin como objetos de Lisp, preferentemente aquellos que el lector de Lisp que ya sabe leer 4 Si te olvidas de los atributos de un momento, no hay una asignacin obvia entre las listas de Lisp y elementos HTML: cualquier elemento HTML puede ser representada por una lista cuyo PRIMER es un smbolo donde el nombre es el nombre de la etiqueta del elemento y cuya REST es una lista de auto-evaluacin de los objetos o las listas que representan a otros elementos HTML. As:
<p> Foo </ p> <==> (: p "Foo") <p> <i> Ahora </ i> es el tiempo </ p> <==> (: P (: i "ahora") "es el tiempo")

Ahora el nico problema es que al apretar en los atributos. Como la mayora de los elementos no tienen atributos, sera agradable si usted podra utilizar la sintaxis anterior para elementos sin atributos. FOO ofrece dos maneras de anotar los elementos con los atributos. La primera es simplemente incluir los atributos de la lista inmediatamente despus del smbolo, alternando smbolos de palabras clave de nombres de los atributos y los objetos que representan las formas de valores de atributos. El cuerpo del elemento comienza con el primer elemento de la lista que est en una posicin de ser un nombre de atributo y no es un smbolo de esa palabra. As:
> HTML (html (: p "foo")) <p> foo </ p> NIL > HTML (html (: p "foo" (: i "bar") "baz")) <p> <i> foo bar </ i> baz </ p> NIL > HTML (html (: P: El estilo de "foo" "Foo")) <p style='foo'> Foo </ p> NIL > HTML (html (: p: id "x": el estilo de "foo" "Foo")) <p id='x' style='foo'> Foo </ p> NIL

Para aquellos que prefieren la delineacin un poco ms evidente entre los atributos del elemento y su cuerpo, FOO soporta una sintaxis alternativa: si el primer elemento de una lista en s es una lista con una palabra clave como el primer elemento, a continuacin, la lista exterior representa un elemento HTML con la palabra clave que indica la etiqueta, con el RESTOde la lista anidada como los atributos, y con el RESTO de la lista exterior como del cuerpo. Por lo tanto, podra escribir los ltimos dos expresiones como esta:
> HTML (html ((: P: El estilo de "foo") "Foo"))

<p style='foo'> Foo </ p> NIL > HTML (html ((: p: id "x": el estilo de "foo") "Foo")) <p id='x' style='foo'> Foo </ p> NIL

Las siguientes pruebas de la funcin si un objeto dado coincide con cualquiera de estas sintaxis:
(Defun cons-form-p (forma y opcional (test # 'keywordp)) (Y (forma consp) (O (prueba funcall (forma de coche)) (Y (consp (forma de coche)) (prueba de funcall (forma Caar))))))

Usted debe parametrizar la prueba de la funcin, porque ms tarde tendrs que poner a prueba los mismos dos sintaxis con un predicado un poco diferente en el nombre. Para completamente abstracto las diferencias entre las dos variantes de sintaxis, puede definir una funcin, analizan-cons-forma , que tiene una forma y lo analiza en tres elementos, la etiqueta, el plist atributos, y la lista del cuerpo, devolviendo como mltiples valores. Contra el cdigo que realmente evala las formas a utilizar esta funcin y no tener que preocuparse por la sintaxis se utiliza.
(Defun analizan-cons-forma (sexp) (Si (consp (primera sexp)) (Parse-explcita-atributos-sexp sexp) (Parse-implcita-atributos-sexp sexp))) (Defun-parse explcita-atributos-sexp (sexp) (Desestructuracin-bind ((etiqueta y dems atributos) y el cuerpo del cuerpo) sexp (Etiqueta de los valores de los atributos del cuerpo))) (Defun analizan los implcitos de los atributos-sexp (sexp) (Bucle con tag = (primera sexp) para el descanso en el (resto sexp) por # 'cddr al mismo tiempo (y (keywordp (primer descanso)) (segundos de descanso)) cuando (segundos de descanso) recoger (primer descanso) en los atributos y recoger (segundos de descanso) en los atributos final finalmente, (ida y vuelta (etiqueta de los valores de los atributos de reposo))))

Ahora que tiene el lenguaje bsico especificado, se puede pensar en cmo se est en realidad va a poner en prctica los procesadores de lenguaje. Cmo se obtiene a partir de una serie de formularios HTML FOO a la deseada? Como he mencionado anteriormente, se le ejecucin de dos procesadores de lenguaje para Foo: un intrprete, que camina de un rbol de FOO forma y emite el cdigo HTML correspondiente directamente y un compilador que camina de un rbol y lo traduce a cdigo Lisp Comn que va a emitir el mismo HTML. Tanto el intrprete y el

compilador se construir en la parte superior de una base comn de cdigo, que proporciona soporte para cosas como el escape de los caracteres reservados y la generacin de salida muy bien recortada, as que tiene sentido empezar por ah. Escapar caracteres El primer bit de la fundacin tendr que poner es el cdigo que sabe cmo escapar de caracteres con un significado especial en HTML. Hay tres caractersticas, tales caracteres, y no deben aparecer en el texto de un elemento o un atributo de valor, son < , > , y la y . En los valores de texto de los elementos o atributos, estos personajes deben ser reemplazados con las entidades de caracteres de referencia < , > , y, y . Del mismo modo, en valores de los atributos, las comillas utilizado para delimitar el valor que hay que escapar, ' con ' y " con" . Adems, cualquier personaje puede ser representado por una entidad de referencia de carcter numrico que consiste en un signo, seguido por un fuerte firmar, seguido por el cdigo numrico como un nmero entero de base 10, y seguido por un punto y coma. Estos escapes numricos se utilizan a veces para incluir caracteres no ASCII en el HTML.
El paquete de
Desde FOO es una biblioteca de bajo nivel, el paquete a desarrollar en no se basa en el cdigo externo mucho - apenas la dependencia habitual de los nombres de la commonlisp paquete y, casi como de costumbre, los nombres de la macro- escribir las macros de COM.GIGAMONKEYS.MACRO-UTILIDADES . Por otro lado, el paquete necesita para exportar todos los nombres necesarios por el cdigo que utiliza FOO. Aqu est la DEFPACKAGE de la fuente que se puede descargar desde el sitio Web del libro: (Defpackage: com.gigamonkeys.html (: Uso: common-lisp: com.gigamonkeys.macro utilidades) (: Export: con-html-salida : En html de estilo : Define-html-macro : Html : Emiten-html : Y atributos))

La siguiente funcin acepta un solo carcter y devuelve una cadena que contiene una entidad de referencia de carcter para que el personaje:
(Defun de escape-char (char) (Caso de caracteres (# \ & "&") (# \ <"<") (# \> ">") (# \ '"'") (# \ "" "") (T (cero formato "& # ~ d;" (char-char code)))))

Usted puede utilizar esta funcin como base para una funcin, de escape , que toma una cadena y una secuencia de caracteres y devuelve una copia del primer argumento con todas las ocurrencias de los personajes en el segundo argumento reemplaza con la entidad correspondiente carcter devuelto por fuga -char .
(Defun de escape (para-escape) (FLET ((necesidades de escape-p (char) (char a encontrar-escape))) (Con salida a cadena (a) (Bucle para empezar a = 0, entonces (1 + pos) para la pos = (posicin #-si las necesidades de escape-p en: inicio de inicio) hacer (escribir la secuencia de salida: arranque de inicio: finales pos) cuando las organizaciones lo hacen (escritura de la secuencia (de escape-char (char en pos)) a) mientras que pos))))

Tambin se pueden definir dos parmetros: * Los elementos de fugas * , que contiene los caracteres que necesita para escapar de datos de los elementos normales, y* Los atributos se escapa * , que contiene el conjunto de caracteres que se escaparon en los valores de atributos.
(* Defparameter elemento de escape * "<> &") (* Defparameter atributos de escape * "<> y \" '")

He aqu algunos ejemplos:


HTML> (escapar "foo y bar" * Los elementos de fugas *) "Foo y bar" HTML> (escapar "foo y 'bar'" * Los elementos de fugas *) "Foo y 'bar'" HTML> (escapar "foo y 'bar'" * Los atributos se escapa *) "Foo y 'bar'"

Finalmente, usted necesitar una variable, se escapa * Los * Los , que se enlazar con el conjunto de caracteres que hay que escapar. Se establece inicialmente en el valor delos elementos * escapa * , pero cuando la generacin de atributos, ser, como se ver, debe volver a vincular el valor de los atributos * escapa * .
(* Defvar escapa ** elemento de escape *)

Sangra de la impresora Para hacer frente a la generacin de salida muy bien recortada, puede definir una clase de sangrado, la impresora , que se envuelve alrededor de un flujo de salida, y las funciones que utilizan una instancia de esa clase para emitir las cadenas a la corriente mientras se registran cuando se est en el principio de la lnea . La clase tiene este aspecto:
(Defclass sangra-impresora () ((Salida a cabo de acceso: initarg: a)

(A partir de la lnea-p: acceso comienzo de la lnea-p: initForm t) (Sangra: la sangra de acceso: initForm 0) (Sangrado-p:-p sangra de acceso: initForm t)))

La funcin principal que opera en la sangra, la impresora s es emitir , que tiene la impresora y una cadena que emite la cadena de flujo de salida de la impresora, no perder de vista cuando se emite una nueva lnea para que pueda restablecer el principio de la lnea-p ranura.
(Defun emiten (cadena ip) (Bucle para empezar a = 0, entonces (1 + pos) para la pos = (posicin # \ cadena de nueva lnea: comienzo de inicio) hacer (emiten / no-cadena de saltos de lnea ip: inicio de inicio: finales pos) cuando las organizaciones lo hacen (emiten nueva lnea-ip) mientras POS))

Para emitir en realidad la cadena, que utiliza la funcin de emitir o no los saltos de lnea , que emite cualquier sangrado es necesario, a travs de la ayudante deguinsi-es necesario , a continuacin, escribe la cadena en la secuencia. Esta funcin tambin puede ser llamado directamente por otro cdigo para emitir una cadena que se sabe que no contienen nuevas lneas.
(Defun emiten / sin saltos de lnea (IP cuerdas y clave (inicio 0) final) (Guin, si es necesario-ip) (Escritura de la secuencia de cadena (de un total IP): inicio de inicio: final final) (A menos que (zerop (- (o al final (cadena de longitud)) de inicio)) (Setf (a partir de la lnea-p ip) nil)))

El ayudante de guin, si es necesario, los controles comienzo de la lnea-p y la sangra de p- para determinar si es necesario para emitir la sangra y, si son ambas verdaderas, emite tantos espacios como se indica por el valor de la sangra . El cdigo que utiliza la sangra, la impresora puede controlar el sangrado mediante la manipulacin de la sangra y la sangra de p- ranuras. Aumentar y disminuir sangra cambia el nmero de espacios a la izquierda, mientras que la creacinsangra-p a NIL puede desactivar temporalmente el sangrado.
(Defun guin-si-es necesario (IP) (Cuando (y (a partir de la lnea-p ip) (sangrado-p ip)) (Repeticin de bucle (IP sangrado) hacer (write-char # \ espacio (de un total ip))) (Setf (a partir de la lnea-p ip) nil)))

Las dos ltimas funciones en las impresoras sangra- API se emiten-salto de lnea y emiten-freshline , que son utilizados para emitir un carcter de nueva lnea, similar a la % ~ y del ~ y FORMATO directivas. Es decir, la nica diferencia es que emiten nueva lnea- siempre emite un salto de lnea, mientras queemiten-freshline lo hace

slo si comienzo de la lnea-p es falsa. As, varias llamadas a emitir-Freshline sin ningn tipo de intervenir emiten s no resultar en una lnea en blanco. Esto es til cuando una pieza de cdigo quiere generar una salida que debe terminar con un salto de lnea, mientras que otra pieza de cdigo quiere generar una salida que debe comenzar en una nueva lnea, pero usted no quiere una lnea en blanco entre los dos bits de salida .
(Defun emiten-de nueva lnea (IP) (Write-char # \ nueva lnea (de un total IP)) (Setf (a partir de la lnea-p ip) t)) (Defun-emiten freshline (ip) (A menos que (a partir de la lnea-p ip) (salto de lnea emiten-ip)))

Con estos preliminares fuera del camino, usted est listo para llegar a las entraas del procesador de FOO. Procesador de interfaz HTML Ahora est listo para definir la interfaz que va a ser utilizada por el procesador de lenguaje FOO para emitir HTML. Se puede definir esta interfaz como un conjunto de funciones genricas, dado que tiene dos implementaciones, una que en realidad emite HTML y otra que el html macro se puede utilizar para recopilar una lista de acciones que deban llevarse a cabo, lo que puede ser optimizado y compilado en cdigo que emite la misma salida de una manera ms eficiente. Voy a llamar a este conjunto de funciones genricas de la interfaz de backend . Se compone de las siguientes ocho funciones genricas:
(Defgeneric prima-cadena (cadena del procesador y saltos de lnea opcional-p)) (Defgeneric nueva lnea (procesador)) (Defgeneric freshline (procesador)) (Defgeneric guin (procesador)) (Defgeneric Quitar sangra (procesador)) (Defgeneric alternar sangra (procesador)) (Defgeneric embed-valor (valor de procesador)) (Defgeneric embed del cdigo (el cdigo del procesador))

Si bien varias de estas funciones tienen correspondencia evidente para la impresora sangra- funciones, es importante entender que estas funciones genricas definir las operaciones abstractas que son utilizados por los procesadores de lenguaje FOO, y

no siempre se ejecutar en trminos de las llamadas a la sangra, la impresora funciones. Dicho esto, tal vez la forma ms fcil de entender la semntica de estas operaciones abstractas es mirar a las implementaciones concretas de los mtodos especializados enhtml-muy-de la impresora , la clase utilizada para generar cdigo HTML legible. El back-end de la impresora Bastante Puede comenzar por la definicin de una clase con dos ranuras - una para sostener una instancia de sangrado, la impresora y otro para mantener el ancho de la ficha el nmero de espacios que desea aumentar la sangra para cada nivel de anidamiento de los elementos HTML.
(Defclass html-bonito-impresora () ((La impresora: Impresora de acceso: initarg: impresora) (Tab-width: acceso tab-width: initarg: tab-width: initForm 2)))

Ahora usted puede implementar mtodos especializados en html-muy-printer en las ocho funciones genricas que conforman la interfaz de gestin. Los procesadores de FOO utilizar la prima de cadena de la funcin de emitir las cadenas que no necesitan escapar de caracteres, ya sea porque usted quiere realmente para emitir los caracteres que normalmente se reservan o porque todos los caracteres reservados se han escapado ya ha. Por lo general, prima-secuencia se invoca con cadenas que no contienen nuevas lneas, por lo que el comportamiento por defecto es usar emiten / sin saltos de lnea de no ser la persona que llama no especifica un NILsaltos de lnea-p argumento.
(Defmethod prima cuerdas ((pp html-bonito-impresora) y cadena de saltos de lnea opcional-p) (En caso de nuevas lneas-p (Emiten (impresora pp) de la cadena) (Emiten / no-saltos de lnea (impresora pp) de la cadena)))

Las funciones de nueva lnea , la Freshline y guin , Unindent , y alternar sangra aplicar manipulaciones relativamente sencillos de la subyacente-la sangra de la impresora . La arruga slo es que la impresora genera una salida HTML muy bonita cuando la variable dinmica * muy * es cierto. Cuando es NIL, se debe generar el cdigo HTML compacto sin espacios en blanco innecesarios. Por lo tanto, estos mtodos, con la excepcin de la nueva lnea , todos los check * muy *antes de hacer nada: 5
(Defmethod nueva lnea ((pp html-muy-printer))

(Emiten lnea nueva (impresora pp))) (Defmethod freshline ((pp html-muy-printer)) (* Muy * cuando (emiten-freshline (impresora pp)))) (Defmethod guin ((pp html-muy-printer)) (* Muy * cuando se (Incf (sangrado (impresora pp)) (tab-width pp)))) (Defmethod Quitar sangra ((pp html-muy-printer)) (* Muy * cuando se (DECF (sangrado (impresora pp)) (tab-width pp)))) (Defmethod alternar sangra ((pp html-muy-printer)) (* Muy * cuando se (Con ranuras (sangrado-p) (impresora pp) (Setf sangra-p (no indentando-p)))))

Por ltimo, las funciones de insercin de valor y de insercin de cdigo, se utilizan slo por el compilador FOO - Insertar valor se utiliza para generar cdigo que se va a emitir el valor de una expresin Lisp Comn, mientras que el cdigo embed- se utiliza para incrustar un poco de cdigo que se ejecuta y se desecha el resultado. En el intrprete, no se puede evaluar de manera significativa incrustado cdigo Lisp, por lo que los mtodos en estas funciones siempre sealar un error.
(Defmethod embed-valor ((pp html-bonito-impresora) valor) (El error "No se puede integrar los valores en la interpretacin de valor:. ~ S" valor)) (Defmethod embed del cdigo ((pp html-bonito-impresora) de cdigo) (El error "No se puede incrustar cdigo en la interpretacin de cdigo:. ~ S" cdigo))

Uso de condiciones de tener su pastel y comrselo tambin


Un enfoque alternativo sera utilizar EVAL para evaluar expresiones Lisp en el intrprete. El problema con este enfoque es que EVAL no tiene acceso a la entorno lxico. Por lo tanto, no hay manera de hacer algo parecido a este trabajo: (Let ((x 10)) (emiten-html '(: px))) cuando x es una variable lxica. El smbolo x que se pasa a emitir-html en tiempo de ejecucin tiene una relacin particular con la variable lxica llamado con el mismo smbolo. El compilador de Lisp se encarga de las referencias a x en el cdigo para referirse a la variable, pero despus de que el cdigo es compilado, ya no hay necesariamente una relacin entre el nombre x y variable que. Esta es la razn principal por la que cuando se piensa en IC es la solucin a su problema, usted est probablemente equivocado. Sin embargo, si x es una variable dinmica, declara con defvar o DEFPARAMETER (y es probable que el nombre * x * en lugar de x ), IC podra conseguir en su valor. Por lo tanto, podra ser til para permitir al intrprete FOO usar EVAL en algunas situaciones. Pero es una mala idea usar siempre EVAL . Usted puede obtener lo mejor de ambos mundos mediante la combinacin de la idea de usar EVAL con la condicin de sistema. En primer lugar definir una serie de clases de error que se puede sealar cundo embed relacin calidad-precio y el cdigo de insercin, se les llama en el intrprete.

(Definir acondicionado incorporado-lisp-en-intrprete (de error) ((Formulario: initarg: formulario: formulario de lectura))) (Definir el valor-condicin-en-intrprete (embedded-lisp-en-intrprete) () (: Informe (Lambda (cs) (Formato de s "No se puede integrar los valores en la interpretacin de valor:. ~ S" (Formulario C))))) (Definir el cdigo de condicin-en-intrprete (embedded-lisp-en-intrprete) () (: Informe (Lambda (cs) (Formato de s "No se puede incrustar cdigo en la interpretacin de cdigo:. ~ S" (Formulario C))))) Ahora puede implementar incorporar valor y la incrustacin de cdigo para sealar los errores y ofrecer un reinicio que va a evaluar la forma con IC . (Defmethod embed-valor ((pp html-bonito-impresora) valor) (Reinicio de los casos (error de "valor-en-intrprete: valor del formulario) (Evaluar () : Informe (". EVAL ~ s en el entorno lxico nula" lambda (s) (s formato de valor)) (Prima de cadena pp (escape (princ a cadena (valor eval)) * escapa *) t)))) (Defmethod embed del cdigo ((pp html-bonito-impresora) de cdigo) (Reinicio de los casos (error de "cdigo-en-intrprete: el cdigo del formulario) (Evaluar () : Informe (lambda (s) (formato de s ". EVAL ~ s en el entorno lxico nulo" el cdigo)) (Cdigo eval)))) Ahora usted puede hacer algo como esto: HTML> (defvar * x * 10) *X* HTML> (emiten-html '(: p * x *)) y usted se dej caer en el depurador con este mensaje: No se puede integrar los valores de la hora de interpretar. Valor: * X * [Estado del tipo de valor en una intrprete] Reinicia: 0: [evaluar] IC * X * en el entorno lxico nulo. 1: [abortar] manejo Abortar solicitud BABA. 2: [abortar] Abortar por completo de este proceso. Si se llama a los Evalan el reinicio, embed valor se EVAL * x * , obtener el valor 10 , y generar este cdigo HTML: <p> 10 </ p> Entonces, para su conveniencia, usted puede proporcionar las funciones de reinicio funciones que invocan el evaluate reiniciar-en ciertas situaciones. El evaluate funcin de reinicio incondicional invoca la reanudacin, mientras que eval-dinmicos las variables y cdigos de eval- lo invocan slo si la forma en que la condicin es una dinmica de cdigo variable o potencial. (Defun evaluar (y condicin opcional) (Declare (ignorar la condicin)) (Invoke-restart 'evaluar)) (Defun eval-dinmicos (las variables y la condicin opcional) (Cuando (y (symbolp (condicin de la forma)) (boundp (condicin de la forma))) (Evaluar))) (Defun eval-cdigo (y condicin opcional) (Cuando (consp (condicin formulario)) (Evaluar))) Ahora usted puede utilizar MANIPULADOR-BIND para crear un manejador para invocar automticamente los Evalan el reinicio para usted. HTML> (manejador-bind ((valor-en-intrprete # 'evaluar)) (emiten-html' (: p * x *))) <p> 10 </ p> T

Por ltimo, se puede definir una macro para proporcionar una sintaxis ms agradable para los controladores de enlace de los dos tipos de errores. (Defmacro con la dinmica-evaluacin ((y el cdigo de valores de clave) y el cuerpo del cuerpo) `(Manejador-bind ( , @ (Si los valores de `((valor-en-intrprete # 'evaluar))) , @ (Si el cdigo `((cdigo en el intrprete de # 'evaluar)))) , El cuerpo de @)) Con esta macro definida, puede escribir lo siguiente: HTML> (con-dinmica-evaluacin (: valores de t) (emiten-html '(: p * x *))) <p> 10 </ p> T

La regla de clculo bsico Ahora para conectar la lengua FOO a la interfaz de procesador, todo lo que necesita es una funcin que toma un objeto y lo procesa, la invocacin de las funciones del procesador adecuadas para generar el cdigo HTML. Por ejemplo, cuando se les da una forma simple como esto:
(: P "Foo")

esta funcin podra ejecutar esta secuencia de llamadas en el procesador:


(Freshline procesador) (Prima de cadena de procesador "<p" nil) (Prima de cadena de tratamiento ">" nil) (Prima de cadena de procesador de "Foo" nil) (Prima de cadena de procesador "</ p>" nil) (Freshline procesador)

Por ahora se puede definir una funcin sencilla que simplemente verifica si un formulario es, en realidad, una forma jurdica FOO y, si lo es, la que entrega a la funcin delos procesos sexp-html para su procesamiento. En el prximo captulo, vamos a aadir algunas campanas y silbidos a esta funcin para que pueda manejar las macros y los operadores especiales. Pero por ahora se ve as:
(Defun proceso (la forma del procesador) (If (sexp-html-P) (Proceso-sexp-html forma de procesador) (Error de "formato incorrecto en la forma FOO: ~ s" forma)))

La funcin de sexp-html-p determina si el objeto dado es una expresin legal de FOO, ya sea un formulario de auto-evaluacin o un cons formato adecuado.
(Forma sexp defun-html-p () (O (auto-evaluacin-P) (cons-form-forma p)))

Auto-evaluacin de las formas son fciles de manejar: basta con convertir en una cadena con Princ a cadena y escapar de los personajes de las variablesse escapa *

Los * , que, como usted recordar, inicialmente vinculado al valor del elemento * de escape * . Pros formas de pasar fuera delos procesos cons-sexp-html .
(Defun proceso sexp-html (forma del procesador) (En caso de (auto-evaluacin-P) (Prima de cadena de procesador (de escape (princ a cadena formulario) * escapa *) t) (Proceso de-cons-sexp-html forma de procesador)))

La funcin de los procesos cons-sexp-html es el responsable de emitir la etiqueta de apertura, los atributos, el cuerpo, y la etiqueta de clausura. La complicacin principal es que para generar bastante HTML, es necesario emitir nuevas lneas y ajustar la sangra de acuerdo con el tipo de elemento que se emite. Puede clasificar todos los elementos definidos en HTML en una de tres categoras: bloque, el prrafo, y en lnea. Los elementos de bloque-como cuerpo y ul - se emiten con lneas frescas antes y despus de que tanto su apertura y el clausura etiquetas y con sus contenidos con sangra un nivel. Elementos de prrafo, como p , li , y blockquote - se emiten con una nueva lnea antes de la etiqueta de apertura y despus de la etiqueta de clausura. Los elementos en lnea son simplemente emiten en lnea. Los siguientes tres parmetros de una lista de los elementos de cada tipo:
(* Defparameter elementos en bloque * '(: Cuerpo: colgroup: dl: fieldset: Forma: cabeza: html: mapa: NoScript: Objeto : Ol: optgroup: script:: pre seleccin: estilo: tabla: tbody: tfoot: thead : Tr: ul)) (* Defparameter prrafo-elementos * '(: rea: base: cita: br: botn: Leyenda: col: dd: div: dt: h1 : H2: H3: H4: H5: h6: h: Entrada: li: link: meta: opcin: p: param : Td: rea de texto: XX: el ttulo)) (* Defparameter en lnea de los elementos * '(: A: abbr: acrnimo de: Direccin: B: BDO: grande: citar: Cdigo: del: DFN: em : I: img: complementos: kbd: etiqueta: la leyenda: q: samp: pequea: el rango de: fuerte: sub : Sup: tt: var))

Las funciones de bloque-p-elemento y el apartado de elementos-p prueba de si una determinada etiqueta es un miembro de la lista correspondiente. 6
(Defun bloque-elemento-p (etiqueta) (encontrar la etiqueta * Los elementos en bloque *)) (Defun el apartado de elementos-p (etiqueta) (* Prrafo encontrar la etiqueta de los elementos *))

Dos otras categorizaciones con sus propios predicados son los elementos que siempre estn vacas, como br y horas , y los tres elementos, antes , el estilo , y lasecuencia de comandos , en los que los espacios en blanco se supone que debe ser

preservado. Los primeros se manejan de manera especial cuando se genera HTML normal (en otras palabras, no XHTML), ya que no se supone que tienen una etiqueta de clausura. Y al emitir las tres etiquetas en las que se conserva el espacio en blanco, usted puede desactivar temporalmente el sangrado para que la impresora no aade prcticamente todos los espacios que no forman parte del contenido real del elemento.
Defparameter (* empty-elementos * '(: rea: base: br: col: hr: img: Entrada: link: meta: param)) (* Defparameter preservar los espacios en blanco los elementos de * '(: pre: script: el estilo)) (Defun elemento vaco-p (etiqueta) (encontrar la etiqueta * Los vacos de los elementos *)) (Defun preservar el espacio en blanco-p (etiqueta) (etiqueta de encontrar un espacio en blanco * Conservar los elementos *))

La ltima pieza de informacin que necesita cuando la generacin de HTML es si se est generando XHTML ya que afecta la forma en que emiten los elementos vacos.
(Defparameter * xhtml * nil)

Con toda esa informacin, usted est listo para procesar un formulario de FOO contras. Se utiliza de anlisis-cons-forma para analizar la lista en tres partes, el smbolo de la etiqueta, un plist posiblemente vaca de atributos pares clave / valor, y una lista posiblemente vaca de formas corporales. A continuacin, emite la etiqueta de apertura, el cuerpo, y la etiqueta de clausura con las funciones de ayuda emiten-libre de marcas , emitir-elemento de la carrocera , y emiten cerca-tag- .
(Defun proceso cons-sexp-html (forma del procesador) (Cuando (cadena = * escapa ** atributo de los escapes *) (El error "No se puede utilizar en formas contras atributos: ~ a" la forma)) (Multiple-value-bind (cuerpo de la etiqueta atributos) (anlisis sintctico-consforma de la forma) (Emiten-open-etiqueta del procesador atributos del cuerpo de etiquetas) (Emiten elemento de cuerpo cuerpo de la etiqueta del procesador) (Emiten de cerca-cuerpo de la etiqueta etiqueta de procesador)))

En emiten-open-etiqueta tiene que llamar a freshline en su caso y luego emitir los atributos con los atributos de los emiten . Usted necesita para pasar el cuerpo del elemento para emitir-open-etiqueta de modo que cuando se est emitiendo XHTML, sabe si hay que terminar la etiqueta con /> o > .
(Defun emiten abierta-Tag (etiqueta del procesador cuerpo P-atributos) (Cuando (o (prrafo-elemento-p etiqueta) (bloque-elemento-p etiqueta)) (Freshline procesador)) (Prima de cadena de procesador (nula en formato "<~ (~ a ~)" tag)) (Emiten los atributos de los atributos del procesador)

(Prima de cadena de procesador (si (y * xhtml * (no el cuerpo-p)) "/>" ">")))

En emiten los atributos de los nombres de los atributos no son evaluadas, ya que deben ser smbolos de palabras clave, pero usted debe invocar el alto nivel deproceso de la funcin de evaluar los valores de los atributos, la enlace * escapa * de * los atributos de fugas * . Como una conveniencia para especificar los atributos booleanos, cuyo valor debe ser el nombre del atributo, si el valor es T -no es cualquier valor real, pero en realidad T -entonces se reemplaza el valor con el nombre del atributo.7
(Defun emiten los atributos (atributos de procesador) (Bucle para (kv) en los atributos de cddr # 'hacer (Prima de cadena de procesador (en formato nulo "~ (~ a ~) =" "k)) (Let ((* escapa ** atributos escapa *)) (Procesador de proceso (si (EQL vt) (cadena downcase k) v))) (Prima de cadena del tratamiento "" ")))

Emitir el cuerpo del elemento es similar a la emisin de los valores de los atributos: puede recorrer el rgano que convoque el proceso para evaluar cada formulario. El resto del cdigo est dedicado a emitiendo lneas frescas y ajustando la sangra como apropiado para el tipo de elemento.
(Defun cuerpo emiten elemento de cuerpo (etiqueta de procesador) (Cuando (bloque-elemento-p etiqueta) (Freshline procesador) (Procesador de guin)) (Cuando (conservar un espacio en blanco-p etiqueta) (toggle-procesador de sangrado)) (Dolist (cuerpo del elemento) (elemento proceso de procesador)) (Cuando (conservar un espacio en blanco-p etiqueta) (toggle-procesador de sangrado)) (Cuando (bloque-elemento-p etiqueta) (Procesador de Unindent) (Procesador de freshline)))

Por ltimo, emiten-cerca de etiqueta , como probablemente era de esperar, emite la etiqueta de clausura (a menos que no se etiqueta de clausura es necesario, como cuando el cuerpo est vaco y que est emitiendo ya sea XHTML o el elemento es uno de los especiales Empty elementos). Independientemente de si en realidad se emitir una etiqueta de clausura, es necesario emitir una nueva lnea final de bloque y elementos de prrafo.
(Defun emiten-cerca-tag (etiqueta de procesador de cuerpo-p) (A menos que (y (o * xhtml * (elemento vaco, p etiqueta)) (no el cuerpo-p)) (Prima de cadena de procesador (nula en formato "</ ~ (~ a ~)>" etiqueta))) (Cuando (o (prrafo-elemento-p etiqueta) (bloque-elemento-p etiqueta)) (Procesador de freshline)))

La funcin de proceso de base es el intrprete FOO. Para hacerlo un poco ms fcil de usar, se puede definir una funcin, emiten-html , que invoca el proceso , pasndole una impresora html-bonito- y una forma de evaluar. Puede definir y utilizar una funcin auxiliar, get-bonito-impresora , para obtener la impresora bonita, que devuelve el valor actual de * html-muy-de la impresora * si se trata de la envolvente, de lo contrario, tiene una nueva instancia dehtml-muy- la impresora con * html * La salida como su flujo de salida.
(Defun emiten-html (sexp) (proceso de (get-bonita-de la impresora) sexp)) (Defun get-bonito-impresora () (O * html-bonito-impresora * (Marca de instancia 'Html-bonito-impresora : La impresora (marca de instancia 'sangra-de la impresora: a * html de salida *))))

Con esta funcin, usted puede emitir HTML * html de salida * . En lugar de exponer a la variable * HTML * La salida como parte de la API pblica de FOO, debe definir una macro, con-html-producto , que se encarga de obligar a la corriente para usted. Tambin le permite especificar si desea que la salida muy HTML, por defecto el valor de la variable * muy * .
(Defmacro con-html-salida ((corriente y la llave (* muy bonita *)) y el cuerpo del cuerpo) `(Let * ((* html * La salida, la corriente) (* Muy *, bastante)) , El cuerpo de @))

Por lo tanto, si desea utilizar emiten-html para generar HTML en un archivo, podra escribir lo siguiente:
(Con-de archivos abiertos (out "foo.html": La salida de la direccin) (Con-html-output (salida: muy t) (Emiten-html * cierta-foo-expresin *)))

Qu sigue? En el siguiente captulo, veremos cmo implementar una macro que compila expresiones FOO en Common Lisp para que pueda integrar la generacin de cdigo HTML directamente en sus programas Lisp. Tambin extender el lenguaje FOO para que sea un poco ms expresiva, aadiendo su propio sabor de los operadores especiales y macros.

1 De hecho, es probable que sea demasiado expresivo, ya que tambin puede generar todo tipo de

salida que no es ni siquiera vagamente HTML legal. Por supuesto, eso podra ser una opcin si usted necesita para generar HTML que no es estrictamente correcto para compensar los navegadores web con errores. Adems, es comn que los procesadores de lenguaje para aceptar programas que son sintcticamente correctos y por lo dems bien formada, que sin embargo va a provocar un comportamiento indefinido cuando se ejecuta.
2 Bueno, casi todas las etiquetas. Algunas etiquetas, como IMG y el BR no lo hacen. Vas a hacer frente

a los de la seccin "La regla de clculo bsico."


3 En el lenguaje estricto de la Norma comn Lisp, smbolos de palabras clave no son auto-

evaluacin , a pesar de que, de hecho, evalan a s mismos. Vase la seccin 3.1.2.1.3 de la norma de lenguaje o hiperespec para una breve discusin.
4 El requisito de utilizar los objetos que el lector Lisp sabe leer no es un duro y rpido. Dado que el

lector Lisp en s es personalizable, tambin se podra definir un nuevo lector de nivel de sintaxis para un nuevo tipo de objeto. Pero eso tiende a ser ms problemas de lo que vale la pena.
5 Otra, ms puramente orientado a objetos, el enfoque sera definir dos clases, tal vez en html

bastante-la impresora y HTML-prima de la impresora- , y luego definir no-op mtodos especializados en html-prima de la impresora para los mtodos que deben hacer rellenar slo cuando * muy * es cierto. Sin embargo, en este caso, despus de definir todos los mtodos no-op, que acabara con ms cdigo, y entonces tendra la molestia de asegurarse de que ha creado una instancia de la clase a la derecha en el momento adecuado. Pero, en general, utilizando el polimorfismo para reemplazar condicionales es una buena estrategia.
6 No es necesario un predicado de * en lnea *-elementos , ya que slo alguna vez la prueba de

bloque y elementos de prrafo. Que incluya el parmetro para completar.


7 Si bien los atributos booleanos XHTML requiere que se indican con su nombre como el valor para

indicar un valor verdadero, en HTML tambin es legal incluir slo el nombre del atributo sin un valor, por ejemplo, selected> <option en lugar de la opcin <seleccionados = 'seleccionado'> . Todos HTML 4.0 compatibles con los navegadores deben entender las dos formas, pero algunos navegadores buggy slo entienden la forma no-valor de ciertos atributos. Si usted necesita para generar cdigo HTML para los navegadores, tendrs que cortar emiten los atributos para emitir esos atributos un poco diferente.

31. Prctico: una biblioteca de la generacin de HTML, el compilador


Ahora ya ests listo para ver cmo funciona el compilador FOO. La principal diferencia entre un compilador y un intrprete es que un intrprete procesa un programa y genera directamente un comportamiento - la generacin de HTML en el caso de un intrprete FOO - pero un compilador procesa el mismo programa y genera el cdigo en algn otro idioma que se presentan el mismo comportamiento. En FOO, el compilador es un macro Lisp Comn que se traduce en FOO Common Lisp para que pueda ser incorporado en un programa comn de Lisp. Los compiladores, en general, tienen la ventaja sobre los intrpretes que, debido a la compilacin pasa por anticipado, puede pasar un poco de tiempo optimizando el cdigo que generan para que sea ms eficiente. El compilador FOO hace eso, la fusin de texto literal tanto como sea posible con el fin de emitir el mismo HTML con un menor nmero de escrituras que el intrprete utiliza. Cuando el compilador es un macro Lisp Comn, usted tambin tiene la ventaja de que es fcil que el lenguaje entendido por el compilador para contener embebido Common Lisp - el compilador slo tiene que reconocer e incrustarlo en el lugar correcto en el cdigo generado. El compilador de FOO se aproveche de esta capacidad. El compilador de La arquitectura bsica del compilador se compone de tres capas. Primero usted tiene que implementar una clase html compilador que tiene una ranura que contiene un vector de ajuste que se utiliza para acumular operaciones que representan a las llamadas realizadas a las funciones genricas de la interfaz de gestin durante la ejecucin del proceso . A continuacin, poner en prctica los mtodos de las funciones genricas en el interface de usuario que almacenar la secuencia de acciones en el vector. Cada op est representada por una lista que consiste en nombrar una palabra clave de la operacin y los argumentos pasados a la funcin que ha generado el op. La funcin de sexp-> ops implementa la primera fase del compilador, la compilacin de una lista de formularios FOO llamando al proceso en cada formulario con una instancia de html compilador .

Este vector de operaciones almacenadas por el compilador se pasa a una funcin que optimiza, la fusin consecutivos primas cadena operaciones en un nico programa operativo que emite la cadena combinada de una sola vez. La funcin de optimizacin tambin puede, opcionalmente, despojar a cabo operaciones que son necesarias slo para la impresin bonita, que es sobre todo importante porque le permite combinar ms prima de cuerda op. Por ltimo, el vector optimizado operaciones se pasa a una tercera funcin, generar cdigo de , que devuelve una lista de expresiones comunes de Lisp que en realidad es la salida del HTML. Cuando * muy * es cierto, generar cdigo de genera el cdigo que utiliza los mtodos especializados en html-muy-de la impresora a la salida muy HTML. Cuando * muy * es NIL , se genera el cdigo que escribe directamente en la corriente de salida * html * . La macro html realmente genera un cuerpo que contiene dos expansiones, uno generado con * bastante * obenlazado a T y una con * bastante * obenlazado a NIL . Qu expansin se utiliza est determinada por el valor de tiempo de ejecucin de * bastante * . Por lo tanto, todas las funciones que contiene una llamada a HTML contiene cdigo para generar la salida tanto bonito y compacto. La otra diferencia significativa entre el compilador y el intrprete es que el compilador puede incrustar formas de Lisp en el cdigo que genera. Para tomar ventaja de eso, es necesario modificar el proceso de funcin, as que llama a la incrustacin de cdigo y de insercin de valor cuando se le pregunt funciones para procesar una expresin que no es una forma FOO. Dado que todos los objetos de la evaluacin de s mismo son vlidas las formas FOO, las nicas formas que no se pasarn alproceso sexp-html son listas que no coincidan con la sintaxis de los contras FOO formas y smbolos que no son palabras clave, los tomos nicos que aren 't auto-evaluacin.Se puede asumir que los contras no FOO es la ejecucin de cdigo en lnea y todos los smbolos son variables cuyo valor debe incorporar.
(Defun proceso (la forma del procesador) (Cond ((Sexp-html-forma p) (proceso-sexp-html forma de procesador)) ((Forma consp) (embed-procesador de cdigo del formulario)) (T (embed-valor del formulario de procesador))))

Ahora vamos a mirar el cdigo del compilador. En primer lugar se deben definir dos funciones que un poco abstracta del vector que se utilizar para salvar a operaciones en las dos primeras fases de la compilacin.

(Defun crea-op-tampn () (make-array 10: regulable t: relleno triple 0)) (Defun push-op (op ops-buffer) (vector de empuje se extienden operaciones op-buffer))

A continuacin, puede definir el html compilador de clase y los mtodos especializados que en implementar la interfaz de backend.
(Defclass html compilador () ((OPS: acceso ops: initForm (make-op-buffer)))) (Defmethod prima cuerdas ((compilador html compilador) y cadena de saltos de lnea opcional-p) (Push-op `(: prima de cadena, cuerda, saltos de lnea-p) (operaciones del compilador))) (Defmethod nueva lnea ((compilador html compilador)) (Push-op '(: nueva lnea) (OPS compilador))) (Defmethod freshline ((compilador de HTML del compilador)) (Push-op '(: freshline) (OPS compilador))) (Defmethod guin ((compilador de HTML del compilador)) (Push-op `(: guin) (OPS compilador))) (Defmethod Quitar sangra ((compilador de HTML del compilador)) (Push-op `(: Quitar sangra) (OPS compilador))) (Defmethod alternar sangra ((compilador de HTML del compilador)) (Push-op `(: alternar sangrado) (OPS compilador))) (Defmethod embed-valor ((compilador de HTML del compilador) valor) (Push-op `(: Insertar valor, valor, * escapa *) (OPS compilador))) (Defmethod embed del cdigo ((compilador de HTML del compilador) de cdigo) (Push-op `(: embed-, el cdigo) (OPS compilador)))

Con estos mtodos se define, se puede implementar la primera fase del compilador, sexp-> ops .
(Defun sexp-> ops (el cuerpo) (bucle con el compilador = (make-instance 'html del compilador) por la forma en el cuerpo de hacerlo (forma el proceso del compilador) finalmente, (ida y vuelta (OPS compilador))))

Durante esta fase no es necesario preocuparse por el valor de * muy * : acaba de grabar todas las funciones llamadas por el proceso . Esto es lo que sexp-> ops hace de una forma simple FOO:
HTML> (sexp-> ops '((: p "Foo"))) # ((: Freshline) (: RAW-string "<p" NIL) (:RAW-STRING ">" NIL) (: RAW-cadena "foo" T) (: RAW-cadena "</ p>" NIL) (: Freshline))

La siguiente fase, optimizar la produccin, la electricidad esttica , tiene un vector de operaciones y devuelve un nuevo vector que contiene la versin optimizada. El algoritmo es simple - para cada uno : crudo cuerdas op, escribe la cadena a un bfer de cadena temporal. As, consecutiva : prima cadena operaciones crear una sola

cadena que contiene la concatenacin de las cadenas que deben ser emitidos. Cada vez que encuentro un artculo que no sea : prima cuerdas op, es convertir la cadena construida en una secuencia de la alternancia : prima de cadena y : nueva lnea operaciones con la funcin auxiliar de compilacin-buffer y luego agregue el siguiente op. Esta funcin es tambin el lugar donde dejamos de lado las operaciones de impresin bonitos si * muy * es NIL .
(Optimizar la produccin-static-defun (OPS) (Let ((nuevo-ops (make-op-buffer))) (Con salida a cadena (buf) (FLET ((add-op (op) (Compilacin de bfer buf nueva-ops) (Push-op op nueva-ops))) (Bucle para hacer operaciones a travs de op (Ecase (primer op) (: Prima cuerdas (escritura de la secuencia (op segundo) buf)) ((: Nueva lnea: Insertar valor: de insercin de cdigo) (add-op op)) ((: Guin: Quitar sangra: freshline: alternar sangra) (* Muy * cuando (add-op op))))) (Compilacin de bfer buf nueva-ops))) nueva-ops)) (Defun compilacin de amortiguacin (buffer ops) (Bucle con cadena = (get-salida de la corriente de cadena buf) para la puesta en = 0, entonces (1 + pos) para la pos = (posicin # \ str nueva lnea: comienzo de inicio) cuando (<inicio (str longitud)) hacer (push-op `(: prima-string, (subseq str pos de inicio) nil) ops) cuando las organizaciones lo hacen (push-op '(: nueva lnea) ops) mientras POS))

El ltimo paso consiste en traducir las operaciones en el cdigo correspondiente de Common Lisp. Esta fase tambin presta atencin al valor de * muy * . Cuando * muy * es cierto, se genera el cdigo que invoca las funciones de back-end de carcter genrico sobre * html-muy-de la impresora * , que sern unidos a una instancia dehtml-bonito-impresora . Cuando * muy * es NIL , se genera el cdigo que escribe directamente en * html * La salida , la corriente a la que la impresora bonita que enviara a su salida. La funcin real, generar cdigo de , es trivial.
(Defun genera-cdigo (OPS) (Bucle para op travs de operaciones recoger (se aplican # 'op-> cdigo de operacin)))

Todo el trabajo se realiza mediante los mtodos de la funcin genrica op-> Cdigo de la especialidad op discusin con un EQL specializer en el nombre de la op.
(Defgeneric op-code> (op y dems operandos))

(Defmethod op-> (cdigo (op (EQL: prima-string)) y dems operandos) (Desestructuracin-bind (cadena controles para los saltos de lnea) operandos (Si es * muy * `(Prima de cuerda * html-bonito-impresora * y de cuerda, controles para los saltos de lnea) `(Escritura de la secuencia, la cadena * html de salida *)))) (Defmethod op-> (cdigo (op (EQL: salto de lnea)) y dems operandos) (Si es * muy * '(Nueva lnea * html-bonito-impresora *) `(Write-char # \ * Nueva lnea de salida en HTML *))) (Defmethod op-> (cdigo (op (EQL: freshline)) y dems operandos) (Si es * muy * `(Freshline * html-muy-de la impresora *) (Error de "Bad op cuando no bastante de impresin: ~ un" op))) (Defmethod op-> (cdigo (op (EQL: guin)) y dems operandos) (Si es * muy * `(Guin * html-muy-de la impresora *) (Error de "Bad op cuando no bastante de impresin: ~ un" op))) (Defmethod op-> (cdigo (op (EQL: Quitar sangra)) y dems operandos) (Si es * muy * `(Unindent * html-muy-de la impresora *) (Error de "Bad op cuando no bastante de impresin: ~ un" op))) (Defmethod op-> (cdigo (op (EQL: alternar sangra)) y dems operandos) (Si es * muy * `(Alternar sangra * html-muy-de la impresora *) (Error de "Bad op cuando no bastante de impresin: ~ un" op)))

Los dos ms interesantes op-> cdigo mtodos son los que generan cdigo para la : Insertar valor y : embed-cdigo operaciones. En: Insertar valor de mtodo, se puede generar cdigo ligeramente diferente en funcin del valor de los escapes operando ya que si se escapa es NIL , que no es necesario para generar una llamada a escapar . Y cuando ambos * muy * y escapes son NIL , puede generar el cdigo que utiliza Princ para emitir el valor directamente en la secuencia.
(Defmethod op-> (cdigo (op (EQL: embed-valor)) y dems operandos) (Desestructuracin-bind (valor escapa) operandos (Si es * muy * (Si se escapa `(Prima-string * html-muy-de la impresora * (escape (princ a cadena, valor), se escapa) t) `(Prima-string * html-muy-de la impresora * (princ a cadena, valor) t)) (Si se escapa `(Escritura de la secuencia (de escape (princ a cadena, valor), se escapa) * html de salida *) `(Princ, el valor de salida * html *)))))

Por lo tanto, algo como esto:


HTML> (let ((x 10)) (html (: px))) <p> 10 </ p> NIL

funciona porque html traduce (: px) en algo como esto:

(Progn (Escritura de la secuencia "<p>" * html de salida *) (Escritura de la secuencia (de escape (princ a cadena x) "<> &") * html de salida *) (Escritura de la secuencia "</ p>" * html de salida *))

Cuando ese cdigo sustituye a la llamada a html en el contexto de la LET , se obtiene lo siguiente:
(Let ((x 10)) (Progn (Escritura de la secuencia "<p>" * html de salida *) (Escritura de la secuencia (de escape (princ a cadena x) "<> &") * html de salida *) (Escritura de la secuencia "</ p>" * html de salida *)))

y la referencia a x en el cdigo generado se convierte en una referencia a la variable lxica a partir de la LET que rodea el html formulario. El : Insertar cdigo de mtodo, por otro lado, es interesante porque es tan trivial. Debido a que el proceso pasa a la forma de insercin de cdigo , que se escondi en el : Insertar cdigo de op, todo lo que tienes que hacer es sacarlo y devolverlo.
(Defmethod op-> (cdigo (op (EQL: embed-code)) y dems operandos) (Operandos primeros))

Esto permite que un cdigo como este para trabajar:


> HTML <ul> <li> <li> <li> </ Ul> NIL (html (: ul (dolist (x '(foo bar baz)) (html (Li x))))) FOO </ li> BAR </ li> BAZ </ li>

La llamada externa a HTML se expande en el cdigo que hace algo como esto:
(Progn (Escritura de la secuencia "<ul>" * html de salida *) (Dolist (x '(foo bar baz)) (html (: x li))) (Escritura de la secuencia "</ ul>" * html de salida *))))

Entonces, si se expande el llamado a html en el cuerpo de la dolist , obtendr algo como esto:
(Progn (Escritura de la secuencia "<ul>" * html de salida *) (Dolist (x '(foo bar baz)) (Progn (Escritura de la secuencia "<li>" * html de salida *) (Escritura de la secuencia (de escape (princ a cadena x) "<> &") * html de salida *) (Escritura de la secuencia "</ li>" * html de salida *))) (Escritura de la secuencia "</ ul>" * html de salida *))

Este cdigo ser, de hecho, generar la salida que lo vio. FOO Operadores Especiales Usted puede parar all, y desde luego el lenguaje es expresivo FOO suficiente para generar casi todo el cdigo HTML que te interesa. Sin embargo, puede agregar dos funciones a la lengua, con el cdigo un poco ms, que har que sea un poco ms potente: los operadores especiales y macros. Operadores especiales en FOO son anlogos a los operadores especiales en Common Lisp. Operadores especiales proporcionan maneras de expresar las cosas en el lenguaje que no puede expresarse en el idioma con el apoyo de la regla de clculo bsico. O bien, otra manera de verlo es que los operadores especiales proporcionan acceso a los mecanismos primitivos utilizados por el evaluador del lenguaje. 1 Para tomar un ejemplo simple, en el compilador de foo, el evaluador de lenguaje utiliza el embed de valor la funcin de generar cdigo que incrustar el valor de una variable en el cdigo HTML de salida. Sin embargo, porque los smbolos slo se pasan a integrar la relacin calidad-precio , no hay manera, en el idioma que he descrito hasta ahora, para incorporar el valor de una expresin arbitraria Common Lisp, el proceso de la funcin pasa a las clulas de los contras para integrar de cdigo en lugar de embed relacin calidad-precio , por lo que los valores devueltos son ignorados. Por lo general esto es lo que te gustara, ya que la principal razn para incrustar cdigo Lisp en un programa de FOO es el uso de las construcciones de control de Lisp. Sin embargo, a veces le gustara integrar valores calculados en el cdigo HTML generado. Por ejemplo, usted puede ser que gusta de este programa FOO para generar una etiqueta de prrafo que contiene un nmero al azar:
(: P (al azar 10))

Pero eso no funciona debido a que el cdigo se ejecuta y se desecha su valor.


> HTML (html (: p (al azar 10))) </ p> NIL

En el lenguaje, como lo ha aplicado hasta ahora, se puede evitar esta limitacin mediante el clculo del valor fuera de la llamada a html y luego integrndola a travs de una variable.
HTML> (let ((x (random 10))) (html (: px))) <p> 1 </ p> NIL

Pero eso es una especie de molesto, sobre todo si tenemos en cuenta que si se pudiera organizar la forma (al azar 10) que se pasa a integrar valor en lugar delcdigo embed- , que hara exactamente lo que quieres. Por lo tanto, se puede definir un operador especial, : impresin , que se procesa por el procesador de lenguaje FOO acuerdo a una regla diferente a la normal expresin foo. Es decir, en lugar de generar un <PRINT> elemento, que pasa a la forma de su cuerpo a un valor de insercin .Por lo tanto, puede generar un prrafo que contiene un nmero al azar de esta manera:
> HTML (html (: P (: print (random 10)))) <p> 9 </ p> NIL

Obviamente, este operador especial es til slo en el cdigo compilado FOO ya embed valor no funciona en el intrprete. Otro operador especial que puede ser utilizado tanto en interpretacin y el cdigo compilado FOO es : formato , que le permite generar la salida utilizando el FORMATO funcin. Los argumentos de la : formato de operador especial son una cadena que se utiliza como una cadena de control de formato y los argumentos a interpolar. Cuando todos los argumentos a : Formato de auto-evaluacin son los objetos, una cadena se genera haciendo pasar a FORMATO , y que cadena se emite al igual que cualquier otra cadena. Esto permite por ejemplo : formato de los formularios que se utilizarn en FOO pas a emitirhtml . En compilado FOO, los argumentos : el formato puede ser cualquier expresin Lisp. Otros operadores especiales proporcionan control sobre qu caracteres son automticamente escapados y emitir de forma explcita caracteres de nueva lnea: la : noescape operador especial provoca que todas las formas de su cuerpo para ser evaluados como regulares, pero con formas FOO * Los escapes * Los obenlazados a NIL , mientras que,atributo: evala las formas de su cuerpo con * Los escapes * Los obenlazados a * Los atributos se escapa * . Y : nueva lnea se traduce en el cdigo para emitir una nueva lnea explcita. As que, cmo se define operadores especiales? Hay dos aspectos de procesamiento de los operadores especiales: cmo el procesador de lenguaje de reconocer las formas que utilizan los operadores especiales, y cmo sabe lo que la ejecucin de cdigo para procesar cada operador especial?

Se puede hackear proceso sexp-html para reconocer cada operador especial y manejarlo de la manera adecuada - operadores especiales son, lgicamente, parte de la aplicacin de la lengua, y no va a ser que muchos de ellos. Sin embargo, sera bueno tener una forma un poco ms modular de aadir nuevos operadores especiales - no porque los usuarios de los FOO podr, pero slo para su propia cordura. Definir una forma especial como cualquier lista cuyo CAR es un smbolo que es el nombre de un operador especial. Puede marcar los nombres de los operadores especiales mediante la adicin de un no- NIL de valor a la lista de la propiedad del smbolo en la clave html-especial-operador . Por lo tanto, se puede definir una funcin que comprueba si una determinada forma es una forma especial de esta manera:
(Defun especial-form-p (formulario) (Y (forma consp) (symbolp (forma de coche)) (obtener el formulario (en coche) 'html-especial-operador)))

El cdigo que implementa cada operador especial se encarga de desmontar el resto de la lista sin embargo, ve en forma y hacer lo que la semntica del operador especial requieren.Suponiendo que usted tambin definir una funcin de procesos especiales de forma , que se llevar el procesador de lenguaje y una forma especial y ejecutar el cdigo apropiado para generar una secuencia de llamadas en el objeto del procesador, puede aumentar el nivel superior el proceso de la funcin manejar las formas tan especiales como esta:
(Defun proceso (la forma del procesador) (Cond ((Especial-form-forma p) (proceso especial forma de la forma del procesador)) ((Sexp-html-forma p) (proceso-sexp-html forma de procesador)) ((Forma consp) (embed-procesador de cdigo del formulario)) (T (embed-valor del formulario de procesador))))

Debe agregar la especial forma de p- primera clusula porque las formas especiales puede mirar, sintcticamente, como expresiones regulares FOO slo la forma en formas especiales de Common Lisp pueden parecerse a las llamadas a funciones regulares. Ahora slo tiene que poner en prctica los procesos de forma especial . En lugar de definir una funcin nica, monoltica que implementa todos los operadores especiales, debe definir una macro que le permite definir operadores especiales al igual que las funciones regulares y que tambin se encarga de aadir elhtml-

especial-operador de la entrada a la lista de propiedades de la operador especial su nombre. De hecho, el valor se almacena en la lista de la propiedad puede ser una funcin que implementa el operador especial. Aqu est la macro:
(Defmacro define-html-especial-operador (nombre (procesador y resto de otros parmetros) y el cuerpo del cuerpo) `(Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get ', el nombre de' html-especial-operador) (Lambda (, procesador, otro de los parmetros @), @ cuerpo))))

Este es un tipo bastante avanzada de la macro, pero si se toma una lnea a la vez, no hay nada complicado todo lo que sobre ello. Para ver cmo funciona, tener un uso simple de la macro, la definicin del operador especial : noescape , y mirar la expansin de la macro. Si usted escribe lo siguiente:
(Define-html-especial-operador: noescape (procesador y resto del cuerpo) (Let ((* escapa * nil)) (Bucle de exp en el cuerpo de hacer (exp proceso de procesador))))

es como si hubiera escrito esto:


(Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get ': noescape' html-especial-operador) (Lambda (procesador y resto del cuerpo) (Let ((* escapa * nil)) (Bucle de exp en el cuerpo de hacer (exp proceso de procesador))))))

El IC-CUANDO operador especial, como ya coment en el captulo 20, se asegura de que los efectos del cdigo en su cuerpo se hizo visible durante la compilacin al compilar concompile-file . Esto es importante si usted desea utilizar definir-htmlespecial-operador en un archivo y luego usar el operador especial slo definida en ese mismo archivo. A continuacin, el SETF expresin establece la propiedad html especiales del operador en el smbolo : noescape a una funcin annima con la misma lista de parmetros, como se especific en definir-html-especial-operador . Mediante la definicin de definir-html-especial-operador de dividir la lista de parmetros en dos partes, procesador y todo lo dems, se asegura de que todos los operadores especiales acepte al menos un argumento. El cuerpo de la funcin annima es entonces el cuerpo siempre a definir-htmlespecial-operador . El trabajo de la funcin annima es la aplicacin del operador especial al hacer las llamadas apropiadas en la interfaz de backend para generar el cdigo HTML correcto o el cdigo que va a generar. Tambin puede usar el proceso para evaluar una expresin como una forma FOO.

El : noescape operador especial es particularmente simple - todo lo que hace es pasar a las formas de su cuerpo para el proceso con * Los escapes * Los obenlazados a NIL . En otras palabras, este operador especial desactiva el carcter normal escapar realiza por medio de procesos sexp-html . Con los operadores especiales definida de esta manera, todo proceso especial de forma tiene que hacer es buscar la funcin annima en la lista de propiedades del nombre del operador especial y APLICABLE para el procesador y el resto del formulario.
(Procesador de forma defun proceso especial de forma () (Se aplican (get (forma de coche) 'html-especial-operador) del procesador (la forma resto)))

Ahora est listo para definir los cinco restantes operadores FOO especiales. Similar a : noescape es : atributo , que evala las formas de su cuerpo con* Los escapes * obenlazado a * Los atributos se escapa * . Este operador especial es til si desea escribir funciones de ayuda que los valores de los atributos de salida. Si escribe una funcin como esta:
(Algo defun foo-value () (Html (: print (FROB algo))))

el html macro se va a generar cdigo que escapa a los personajes de los elementos * escapa * . Pero si usted est planeando utilizar el valor foo- de esta manera:
(Html (: P: estilo (foo-valor de 42) "Foo"))

entonces usted quiere para generar cdigo que utiliza * Los atributos se escapa * . As que, en cambio, se puede escribir as: 2
(Algo defun foo-value () (Html (atributo: (: print (FROB algo)))))

La definicin de atributo: se parece a esto:


(Define-html-especial-operador: el atributo (procesador y resto del cuerpo) (Let ((* escapa ** atributos escapa *)) (Bucle de exp en el cuerpo de hacer (exp proceso de procesador))))

Los prximos dos operadores especiales, impresin: y : formato , se utilizan para valores de salida. El : impresin operador especial, como ya he comentado anteriormente, se utiliza en programas compilados FOO para incorporar el valor de una expresin arbitraria Lisp. El : El formato de operador especial es ms o menos equivalente a la generacin de una cadena con (formato nula ...) y luego

incrustarlo. La razn principal para definir : el formato como un operador especial para mayor comodidad. Este:
(: Formato de "Foo: ~ d" x)

es mejor que esto:


(: Imprimir (formato nulo "Foo: ~ d" x))

Tambin tiene la pequea ventaja de que si se utiliza : el formato con argumentos que son todos auto-evaluacin, se puede evaluar la FOO : formato en tiempo de compilacin en lugar de esperar hasta el tiempo de ejecucin. Las definiciones de : impresin y : formato son los siguientes:
(Define-html-especial-operador: de impresin (forma del procesador) (Cond ((Auto-evaluacin-P) (Advertir "redundante: impresin de la auto-evaluacin de la forma ~ s" forma) (Proceso-sexp-html forma de procesador)) (T (Embed-valor del formulario de procesador)))) (Define-html-especial-operador: formato (procesadores y dems argumentos) (En caso de (auto-evaluacin de cada #-p 'args) (Proceso-sexp-html del procesador (se aplican argumentos de formato nil # ')) (Embed valor procesador `(cero formato, @ args))))

El : nueva lnea operador especial fuerza a una salida de una nueva lnea literal, que en ocasiones es til.
(Define-html-especial-operador: nueva lnea (procesador) (Procesador de nueva lnea))

Por ltimo, el : progn operador especial es anloga a la progn operador especial en Common Lisp. Es simplemente procesa los formularios en su cuerpo en secuencia.
(Define-html-especial-operador: progn (procesador y resto del cuerpo) (Bucle de exp en el cuerpo de hacer (exp proceso de procesador)))

En otras palabras, el texto siguiente:


(Html (: P (: progn "Foo" (: i "bar") "baz")))

va a generar el mismo cdigo como este:


(Html (: p "Foo" (: i "bar") "baz"))

Esto puede parecer una cosa extraa a la necesidad ya las expresiones normales FOO puede tener cualquier nmero de formas en su cuerpo. Sin embargo, este operador especial vendr en muy til en una situacin - la hora de escribir las

macros foo, que te lleva a la ltima caracterstica del lenguaje es necesario implementar. FOO macros Macros FOO son similares en espritu a las macros de Common Lisp. Una macro FOO es un poco de cdigo que acepta una expresin FOO como argumento y devuelve una nueva expresin foo como resultado, que es evaluado de acuerdo a las reglas normales de evaluacin FOO. La implementacin real es bastante similar a la aplicacin de operadores especiales. Al igual que con los operadores especiales, se puede definir una funcin de predicado para probar si una determinada forma es una forma macro.
(Defun forma macro-form-p () (Cons-form-forma p # '(lambda (x) (y (symbolp x) (x llegar "html-macro)))))

Se utiliza la funcin definida previamente cons-form-p , porque desea permitir que las macros que se utilizan en cualquiera de las sintaxis de las formas nonmacro contras FOO.Sin embargo, usted necesita para pasar una funcin de predicado diferente, que comprueba si el nombre del formulario es un smbolo con un no- NIL -html macro propiedad.Tambin, como en la puesta en prctica de los operadores especiales, va a definir una macro para definir macros foo, que es responsable de almacenar una funcin en la lista de propiedades del nombre de la macro, en la clave html-macro . Sin embargo, la definicin de una macro es un poco ms complicado porque FOO soporta dos tipos de macro.Algunas macros va a definir se comportan como elementos normales de HTML y puede querer tener un fcil acceso a una lista de atributos. Otras macros simplemente desea tener acceso en bruto a los elementos de su cuerpo. Usted puede hacer la distincin entre los dos tipos de macros implcitas: cuando se define una macro FOO, la lista de parmetros pueden incluir un atributos y parmetros. Si lo hace, la forma macro se analiza como una forma cons regular, y la funcin de macro se pasar dos valores, un plist de atributos y una lista de expresiones que conforman el cuerpo de la forma. Una forma de macro sin atributos y parmetros que no se va a analizar los atributos y la funcin de macro se invoca con un solo argumento, una lista que contiene las expresiones corporales. El primero es til para lo que en esencia son las plantillas HTML. Por ejemplo:
(Define-html-macro: myTag (attrs y atributos del cuerpo y del cuerpo)

`((: Div: clase" myTag ", @ attrs), @ cuerpo)) > HTML (html (: myTag "Foo")) <div class='mytag'> Foo </ div> NIL > HTML (html (: myTag: Identificacin del "bar", "Foo")) <div class='mytag' id='bar'> Foo </ div> NIL > HTML (html ((: myTag: Identificacin del "bar") "Foo")) <div class='mytag' id='bar'> Foo </ div> NIL

Este ltimo tipo de macro es ms til para la escritura de macros que manipulan las formas de su cuerpo. Este tipo de macro puede funcionar como una especie de HTML control de construir. Como un ejemplo trivial, considere la siguiente macro que implementa una : si la construccin de:
(Define-html-macro: si (prueba a continuacin, otra cosa) `(Si, la prueba (html, entonces) (html, de lo contrario)))

Esta macro le permite escribir lo siguiente:


(: P (: si (zerop (al azar 2)) "Jefes" "colas"))

en lugar de esta versin un poco ms detallado:


(: P (si (zerop (al azar 2)) (html "Jefes") (html "colas")))

Para determinar qu tipo de macro que debe generar, se necesita una funcin que pueda analizar la lista de parmetros dado a definir-html-macro . Esta funcin devuelve dos valores, el nombre de la y los atributos de parmetro o NIL si no haba ninguno, y una lista que contiene todos los elementos de argumentos despus de retirar ely los atributos de marca y el elemento de la lista siguiente. 3
(Defun anlisis sintctico-html-macro-lambda-lista (args) (Let ((attr-cons (miembro 'y atributos args))) (Valores (CADR attr-cons) (Nconc (args ldiff attr-cons) (cddr attr-cons))))) HTML> (anlisis sintctico-html-macro-lambda-list '(abc)) NIL (ABC) HTML> (anlisis sintctico-html-macro-lambda-lista '(y atribuye attrs abc)) ATTRS (ABC) HTML> (anlisis sintctico-html-macro-lambda-list '(abc y attrs atributos)) ATTRS (ABC)

El siguiente elemento y atributos en la lista de parmetros puede ser tambin una lista de parmetros desestructuracin.
HTML> (anlisis sintctico-html-macro-lambda-lista '(& (& atributos clave de x, y) abc))

(CLAVE y XY) (ABC)

Ahora ests listo para escribir definir-html-macro . Dependiendo de si haba una y atribuye parmetro especificado, se necesita generar una u otra forma de macro HTML para la macro principal simplemente determina qu tipo de HTML macro que se define a continuacin, llama a una funcin auxiliar para generar el tipo de cdigo .
(Defmacro define-html-macro (nombre (y dems argumentos) y el cuerpo del cuerpo) (Multiple-value-bind (var atributo args) (Anlisis sintctico-html-macro-lambda-lista de argumentos) (Si el atributo-var (Genera-macro-con-los atributos de atributos var nombre del organismo de args) (Genera-macro-no-atributos nombre del organismo de args))))

Las funciones que realmente generan la apariencia de expansin de esta manera:


(Defun genera-macro-con-los atributos (nombre del atributo args args-cuerpo) (Con los atributos del cuerpo gensyms (forma) (If (symbolp atributo args) (setf atributo args `(y el descanso, el atributoargs))) `(Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get ', el nombre de' html-macro-quiere-atributos) t) (Setf (get ', el nombre de' html-macro) (Lambda (, atributos, la forma del cuerpo) (Desestructuracin-bind (, @ atributo args), atribuye (Se unen desestructuracin-(, @ args), la forma del cuerpo , @ Cuerpo))))))) (Defun genera-macro-no-atributos (nombre del cuerpo args) (Con-gensyms (forma del cuerpo) `(Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Setf (get ', el nombre de' html macro-quiere-atributos) nil) (Setf (get ', el nombre de' html-macro) (Lambda (, la forma del cuerpo) (Se unen desestructuracin-(, @ args), soy de la forma-, el cuerpo @)))))

Las funciones de macro te definen aceptan uno o dos argumentos y luego usar BIND desestructurada- de desarmarlas y se unen a los parmetros definidos en la llamada adefinir-html-macro . En los dos expansiones que necesita para salvar a la funcin de macro en la lista de la propiedad del nombre en html-macro y un booleano que indica si la macro toma una y atributos de parmetro en la propiedad html-macro-quiere-atributos . Puede utilizar esa propiedad en la siguiente funcin,ampliar la macro-forma , para determinar cmo la funcin de macro debe ser invocada:
(Defun expandir-macro-forma (formulario) (Si (o (consp (primera forma)) (Obtener el formulario (la primera) de html-macro-quiere-atributos))

(Multiple-value-bind (cuerpo de la etiqueta atributos) (anlisis sintctico-consforma de la forma) (Funcall (obtener la etiqueta 'html-macro) cuerpo de los atributos)) (Desestructuracin-bind (etiqueta y el cuerpo del cuerpo) forma (Funcall (obtener la etiqueta 'html-macro) del cuerpo))))

El ltimo paso es la integracin de las macros mediante la adicin de una clusula para el envo COND en el nivel superior proceso de la funcin.
(Defun proceso (la forma del procesador) (Cond ((Especial-form-forma p) (proceso especial forma de la forma del procesador)) ((Macro-forma-forma p) (procesador de proceso (ampliar-macro-forma de la forma))) ((Sexp-html-forma p) (proceso-sexp-html forma de procesador)) ((Forma consp) (embed-procesador de cdigo del formulario)) (T (embed-valor del formulario de procesador))))

Esta es la versin final del proceso . La API pblica Ahora, por fin, ya est listo para implementar el html macro, el principal punto de entrada para el compilador FOO. Las otras partes de la API pblica FOO son emitenhtml ycon-html de salida , lo que he comentado en el captulo anterior, y definir la macro-html- , que he comentado en el apartado anterior. Ladefine-html-macro macro tiene que ser parte de la API pblica porque los usuarios de FOO se desea escribir sus propias macros de HTML. Por otro lado,definir-html-especial-del operador no es parte de la API pblica, ya que requiere mucho conocimiento del funcionamiento interno de FOO para definir un operador especial nueva. Y no debe haber muy poco que no se puede hacer usando el lenguaje existente y operadores especiales. 4 Un ltimo elemento de la API pblica, antes de llegar a html , es otra macro, en estilo HTML . Esta macro controla si FOO genera HTML XHTML o regular mediante el establecimiento de la * xhtml * variables. La razn de esto tiene que ser una macro se debe a que usted querr para envolver el cdigo que establece * xhtml * en unEVAL-CUANDO lo que se puede configurar en un archivo y lo han afectar a los usos del HTML macro ms tarde en ese mismo archivo .
(Defmacro en html-estilo (sintaxis) (Eval-cuando (: compilacin nivel superior: nivel superior de carga: ejecutar) (Sintaxis caso (: Html (setf * xhtml * nil)) (: XHTML (setf * xhtml * t)))))

Por ltimo vamos a ver html en s. La nica parte difcil sobre la implementacin de HTML viene de la necesidad de generar cdigo que puede ser utilizado para generar

la salida tanto bonito y compacto, dependiendo del valor de tiempo de ejecucin de la variable * muy * . Por lo tanto, html necesario para generar una expansin que contiene un SI expresin y de dos versiones del cdigo, uno compilado con * muy * obenlazado a verdadero y un compilado con lo enlazado a NIL . Para complicar ms las cosas, es comn que un html llamado a contener las llamadas incorporados en html , de esta manera:
(Html (: ul (dolist (cosas de tema)) (html (: elemento li))))

Si el exterior de html se expande en un SI expresin con dos versiones del cdigo, uno para cuando * muy * es verdadera y otro para cuando es falso, que es una tontera para anidadas html formas de ampliar en dos versiones tambin. De hecho, va a llevar a una explosin exponencial de cdigo desde el anidado html ya va a ser ampliado en dos ocasiones - una vez en el * muy * rama-es cierto-y una vez en el * muy * -es-falsa rama. Si cada expansin genera dos versiones, entonces usted tendr cuatro versiones en total.Y si el anidado html formulario que figura otra anidada html forma, que acabara con ocho versiones de ese cdigo. Si el compilador es inteligente, con el tiempo se dar cuenta de que la mayor parte de ese cdigo generado est muerto y lo eliminar, pero aun pensando que puede llevar a cabo un poco de tiempo, lo que frena la compilacin de cualquier funcin que utiliza las llamadas anidadas a html . Afortunadamente, usted puede fcilmente evitar esta explosin de cdigo muerto, generando una expansin que a nivel local redefine el html macro, utilizando MACROLET , para generar slo el tipo de cdigo. En primer lugar se define una funcin auxiliar que tiene el vector de operaciones devueltas por sexp-> ops y se ejecuta a travs deoptimizar la produccin, la electricidad esttica y la generacin de cdigo - las dos fases que se ven afectados por el valor de * muy * -con* * muy enlazado a un valor especificado y que interpola el cdigo resultante en un progn . (El progn devuelve NIL slo para mantener las cosas ordenadas.).
(Defun codegen-html (OPS bonita) (Let ((* muy * muy)) `(Progn, @ (generate-cdigo (optimizar la salida-static-ops)) nil)))

Con esa funcin, a continuacin, puede definir html como este:


(Defmacro html (y todo su conjunto y el cuerpo del cuerpo) (Declare (ignorar el cuerpo)) `(Si es * muy * (Macrolet ((html (y el cuerpo del cuerpo) (codegen-html (sexp-> cuerpo de operaciones) t)))

(Let ((* html-muy-de la impresora * (get-bonita-de la impresora))), todo)) (Macrolet ((html (y el cuerpo del cuerpo) (codegen-html (sexp-> cuerpo de operaciones) nil))) , Entero)))

El y toda parmetro representa el original html forma, y porque se interpola en la expansin en los cuerpos de los dos MACROLET s, que ser procesada con cada una de las nuevas definiciones de html , el que genera casi de impresin de cdigo y el otro que genera no muy de impresin de cdigo. Tenga en cuenta que la variable * muy * se utiliza tanto durante la expansin de la macro y cuando el cdigo resultante se ejecute. Se utiliza en el momento de expansin macro -codegen html para causargenerar cdigo de generar una especie de cdigo u otro. Y se usa en tiempo de ejecucin, en el SI generada por el alto nivel de html macro, para determinar si la impresin bonita-o el cdigo no-muy-impresin de realidad se debe ejecutar. El final de la lnea Como de costumbre, usted puede seguir trabajando con este cdigo para mejorar de varias maneras. Un camino interesante a seguir es utilizar el marco de la generacin de la produccin de base para emitir otro tipo de salida. En la versin de FOO se puede descargar desde el sitio Web del libro, usted encontrar un cdigo que implementa la salida de CSS que puede ser integrado en HTML de salida, tanto en el intrprete y el compilador. Eso es un caso interesante porque la sintaxis de CSS no se puede asignar a s-expresiones de tal manera trivial como HTML puede. Sin embargo, si nos fijamos en el cdigo, ver que todava es posible definir una sintaxis s-expresin para representar las estructuras disponibles en el CSS. Una empresa ms ambiciosa sera la de aadir soporte para la generacin de JavaScript incrustado. Si se hace correctamente, aadiendo soporte JavaScript para FOO podra producir dos grandes victorias. Una es que despus de definir una sintaxis s-expresin que se puede asignar a la sintaxis de JavaScript, a continuacin, puede empezar a escribir las macros, en Common Lisp, para agregar nuevas construcciones a la lengua que se utiliza para escribir cdigo de cliente, que luego ser compilado a JavaScript. La otra es que, como parte de la FOO s-expresin JavaScript para regular de traduccin de JavaScript, podr hacer frente a las diferencias sutiles pero molestos entre las implementaciones de JavaScript en diferentes navegadores. Es decir, el cdigo JavaScript que genera FOO bien podra contener el cdigo condicional apropiado para hacer una cosa en un navegador y

otra en un navegador diferente o podra generar cdigo diferente dependiendo del navegador que quera apoyar. Entonces, si usted utiliza FOO en las pginas generadas dinmicamente, se podra utilizar la informacin sobre el User-Agent que hace la solicitud para generar el sabor derecho de JavaScript para ese navegador. Pero si que le interesa, usted tendr que implementarla uno mismo ya que este es el final del ltimo captulo prctica de este libro. En el prximo captulo voy a envolver las cosas, discutir brevemente algunos temas que no he tocado en otras partes en el libro como la manera de encontrar las bibliotecas, cmo optimizar el cdigo Lisp Comn, y la forma de ofrecer aplicaciones de Lisp.

1 La analoga entre los agentes especiales de FOO y macros, que voy a discutir en la prxima seccin,

y Lisp propia es bastante buena. De hecho, la comprensin de cmo los operadores especiales FOO y el trabajo de las macros pueden darle una idea de por qu Common Lisp se junta la forma en que lo es.
2 El : noescape y : los atributos especiales de los operadores debe ser definido como operadores

especiales porque FOO determina lo que escapa a utilizar en tiempo de compilacin, no en tiempo de ejecucin. Esto permite que FOO para escapar de valores literales en tiempo de compilacin, que es mucho ms eficiente que tener que escanear toda la salida en tiempo de ejecucin.
3 Tenga en cuenta que los atributos y es slo un smbolo, no hay nada intrnsecamente especial

acerca de los nombres que comienzan con y .


4 El nico elemento subyacente de la infraestructura de procesamiento del lenguaje que no est

actualmente expuesta a travs de operadores especiales es la sangra. Si quera hacer FOO ms flexible, aunque a costa de hacer su API mucho ms compleja, habra que agregar los operadores especiales para la manipulacin de la impresora sangrado subyacente. Pero parece que el costo de tener que explicar a los operadores especiales adicionales sea mayor que la ganancia ms bien pequeo en la expresividad.

32. Conclusin: Qu sigue?


Espero que ahora que est convencido de que el ttulo de este libro no es un oxmoron. Sin embargo, es bastante probable que haya algn rea de la programacin que es de gran importancia prctica para usted que no he hablado en absoluto. Por ejemplo, yo no he dicho nada acerca de cmo desarrollar interfaces grficas de usuario (GUI), la forma de conectarse a bases de datos relacionales, la forma de analizar XML, o cmo escribir programas que actan como clientes de varios protocolos de red. Del mismo modo, no he hablado de dos temas que sern importantes al escribir aplicaciones reales en Common Lisp: optimizacin del cdigo de Lisp y empaquetar la aplicacin para la entrega. Estoy, obviamente, no va a cubrir todos estos temas en profundidad en este captulo final. En su lugar, te voy a dar algunos consejos que puede utilizar para perseguir a cualquier aspecto de los intereses de programacin Lisp la mayora de ustedes. Encontrando bibliotecas Lisp Mientras que la biblioteca estndar de funciones, tipos de datos y macros que viene con Common Lisp es bastante grande, proporciona slo para fines generales construcciones de programacin. Tareas especializadas, tales como escribir interfaces grficas de usuario, hablando con bases de datos, y analizar XML requieren las bibliotecas ms all de lo que son proporcionados por el lenguaje normalizado ANSI. La forma ms sencilla de obtener una biblioteca para hacer algo que usted necesita puede ser simplemente echa un vistazo a la implementacin de Lisp. La mayora de las implementaciones de proporcionar por lo menos en algunas instalaciones no especificadas en el estndar del lenguaje. Los proveedores comerciales Common Lisp tienden a trabajar especialmente duro a proporcionar bibliotecas adicionales para su ejecucin con el fin de justificar sus precios. Allegro Common Lisp de Franz, Enterprise Edition, por ejemplo, viene con las bibliotecas para analizar XML, hablando de SOAP, la generacin de HTML, la conexin a bases de datos relacionales, y la creacin de interfaces grficas de diversas maneras, entre otras. LispWorks, otro prominente Lisp comercial, ofrece varias bibliotecas similares, incluyendo un juego de herramientas porttil, bien considerado interfaz grfica de

usuario, CAPI, que se puede utilizar para desarrollar aplicaciones GUI que se ejecutan en cualquier sistema operativo se ejecuta en LispWorks. Las implementaciones comunes libres y de cdigo abierto Lisp no suelen incluir bibliotecas de paquetes tan muchos, confiando en cambio en las bibliotecas porttiles libres y de cdigo abierto. Pero incluso estas implementaciones suelen llenar algunas de las zonas ms importantes no contempladas en el estndar del lenguaje, tales como la creacin de redes y el multithreading. La nica desventaja del uso de bibliotecas especficas de cada implementacin es que te aten a la aplicacin que les ofrece. Si usted entrega al usuario final o aplicaciones a implementar una aplicacin de servidor basada en un servidor que permite controlar, que puede no importar mucho. Pero si usted quiere escribir cdigo para compartir con otros Lispers o si simplemente no quiere estar atado a una aplicacin particular, es un poco ms molesto. Para las bibliotecas porttiles, porttiles, ya sea porque est escrito enteramente en Common Lisp estndar o porque contienen adecuada lectura en tiempo condicionalizacin para trabajar en mltiples implementaciones 1 - la mejor opcin es ir a la Web. Con las advertencias usuales acerca de las direcciones URL que van rancio tan pronto como est impreso en papel, estos son tres de los mejores puntos de partida actuales: Comn-Lisp.net ( http://www.common-lisp.net/ ) es un sitio que aloja los proyectos comunes libres y de cdigo abierto-Lisp, proporcionando el control de versiones, listas de correo y web hosting de pginas del proyecto. En el primer ao y medio despus de que el sitio se puso en marcha, casi un centenar de proyectos fueron registrados. El Common Lisp Collection Cdigo Abierto (CLOCC) ( http://clocc.sourceforge.net/ ) es una coleccin un poco mayor de las bibliotecas de software libre, que estn destinados a ser portable entre Comunes implementaciones de Lisp y autnomo, no depende de ninguna las bibliotecas no se incluyen en CLOCC s mismo. Cliki ( http://www.cliki.net/ ) es un wiki dedicado al software libre en Common Lisp. Si bien, como cualquier wiki, puede cambiar en cualquier momento, por lo general tiene un buen nmero de enlaces a bibliotecas pocos, as que varios de cdigo abierto ms comunes implementaciones de Lisp. El software del mismo nombre que se ejecuta en tambin est escrito en Common Lisp. Los usuarios de Linux se ejecutan las distribuciones Debian o Gentoo tambin se puede instalar fcilmente un nmero cada vez mayor de bibliotecas Lisp que se han

empaquetado con herramientas de las distribuciones de embalaje, apt-get de Debian y emerge en Gentoo. No voy a recomendar las bibliotecas especficas aqu ya que la situacin de la biblioteca est cambiando todos los das - despus de aos de envidiar a las colecciones de las bibliotecas de Perl, Python, y Java, han Lispers comunes, en el ltimo par de aos, comenz a tomar el reto de dar Common Lisp el conjunto de las bibliotecas - tanto de cdigo abierto y comercial - que se merece. Una de las reas donde ha habido mucha actividad recientemente, est en el frente interfaz grfica de usuario. A diferencia de Java y C #, pero como Perl, Python y C, no hay una nica manera de desarrollar interfaces grficas de usuario en Common Lisp. Por el contrario, depende tanto de lo comn Lisp que est usando y qu sistema operativo o de sistemas que desea apoyar. Las implementaciones comerciales de Common Lisp usualmente proveen alguna forma de crear interfaces grficas de usuario para las plataformas que se ejecutan. Adems, ofrece LispWorks CAPI, el ya mencionado, porttil API de interfaz grfica de usuario. En el lado de fuente abierta, tiene una serie de opciones. En Unix, puede escribir bajo nivel de X Windows GUI con CLX, una implementacin pura de Common Lisp del protocolo X-Window, ms o menos similar a la Xlib en C. O bien, puede utilizar los enlaces diferentes a un mayor nivel de APIs y kits de herramientas tales como GTK y Tk, tanto la forma en que podra en Perl o Python. O, si usted est buscando algo completamente diferente, usted puede comprobar fuera de Common Lisp Interface Manager (CLIM). Un descendiente del marco de Symbolics Lisp Machines interfaz grfica de usuario, CLIM es potente y complejo. A pesar de muchas implementaciones comerciales Common Lisp realmente apoyar, no parece haber visto una gran cantidad de uso. Pero en los ltimos dos aos, una implementacin de cdigo abierto de CLIM, McCLIM - ahora alojado en ComnLisp.net-, ha sido a todo vapor ltimamente, as que podemos estar al borde de un renacimiento CLIM. Interconexin con otros idiomas Si bien muchas bibliotecas tiles se pueden escribir en "puro" Common Lisp usando slo las caractersticas especificadas en el estndar del lenguaje, y muchos ms se

puede escribir en Lisp utilizacin de las instalaciones no estndar proporcionados por una aplicacin determinada, en ocasiones es ms sencillo de usar una biblioteca existente por escrito en otro idioma, como por ejemplo C. La lengua estndar no especifica un mecanismo para el cdigo Lisp para llamar a cdigo escrito en otro idioma o incluso exigir que las implementaciones proporcionan dicho mecanismo. Pero en estos das, casi todas las implementaciones de Common Lisp apoyo lo que se llama una interfaz para funciones de Relaciones Exteriores , o FFI, para abreviar. 2 El trabajo bsico de un FFI es que le permite dar Lisp suficiente informacin para ser capaz de enlazar en el cdigo extranjero. Por lo tanto, si usted va a llamar a una funcin de una biblioteca de C, es necesario indicar a Lisp sobre cmo traducir los objetos de Lisp pasados a la funcin en los tipos C y el valor devuelto por la funcin de nuevo en un objeto Lisp.Sin embargo, cada aplicacin proporciona su propia FFI, cada uno con capacidades que varan ligeramente y la sintaxis. Algunos FFI permiten devoluciones de llamada de C a Lisp, y otros no. La interfaz universal de funciones extranjeras (UFFI) proyecto proporciona una capa de portabilidad a travs de los IFF de ms de media docena de implementaciones diferentes Common Lisp. Su accin consiste en la definicin de sus propias macros que se expanden en el correspondiente cdigo de FFI para la aplicacin se est ejecutando pulg El UFFI toma un enfoque ms bajo denominador comn, lo que significa que no se puede tomar ventaja de todas las caractersticas de las FFI implementaciones de diferentes, pero lo hace proporcionar una buena manera de construir un simple contenedor de Lisp en torno a una base de la API C. 3 Hacer que funcione, Make It Right, que sea rpido Como se ha dicho muchas veces, y atribuido indistintamente a Donald Knuth, CAR Hoare y Dijkstra Edsger, la optimizacin prematura es la raz de todo mal. 4 Common Lisp es un lenguaje excelente programa si quieres prestar atencin a esta sabidura y an as necesita de alto rendimiento. Esto puede venir como una sorpresa si has odo hablar de la sabidura convencional de que Lisp es lento. En los primeros das de Lisp, cuando las computadoras se programaban con tarjetas perforadas, de alto nivel Lisp caractersticas pueden haber condenado a ser ms lento que la competencia, es decir, el montaje y FORTRAN. Pero eso fue hace mucho tiempo. Mientras tanto, Lisp ha sido utilizado para todo, desde la creacin de complejos sistemas de inteligencia artificial a la escritura de los sistemas operativos,

y un montn de trabajo ha ido en encontrar la manera de compilar el cdigo Lisp en la eficiencia.En esta seccin voy a hablar sobre algunas de las razones por las que Common Lisp es un lenguaje excelente para escribir cdigo de alto rendimiento y algunas de las tcnicas para hacerlo. La primera razn que Lisp es un lenguaje excelente para escribir cdigo de alto rendimiento es, irnicamente, la naturaleza dinmica de la programacin Lisp - la misma cosa que en un principio haca difcil llevar el rendimiento de Lisp hasta los niveles alcanzados por los compiladores de Fortran. La razn de las caractersticas dinmicas de Common Lisp hacen que sea ms fcil escribir cdigo de alto rendimiento es que el primer paso para escribir cdigo eficiente es encontrar los algoritmos y estructuras de datos adecuadas. Caractersticas dinmicas de Common Lisp de mantener el cdigo flexible, lo que hace que sea ms fcil de probar diferentes enfoques. Teniendo en cuenta una cantidad finita de tiempo para escribir un programa, es mucho ms probable que terminar con una versin de alto rendimiento si no pasan mucho tiempo de entrar y salir de los callejones sin salida. En Common Lisp, puede probar una idea, ver que va a ninguna parte, y seguir adelante sin haber pasado un montn de tiempo convencer al compilador de cdigo es digno de ser atropellado y luego esperar a que termine de compilar. Usted puede escribir una versin sencilla, pero eficiente de una funcin - un esbozo de cdigo - para determinar si su enfoque bsico es el sonido y luego reemplazar esa funcin con una implementacin ms compleja pero ms eficiente si se determina que es. Y si el enfoque general resulta ser errnea, entonces usted no ha perdido un montn de tiempo ajustando una funcin que ya no se necesita, lo que significa que tienen ms tiempo para encontrar un mejor enfoque. La siguiente razn Common Lisp es un buen lenguaje para el desarrollo de software de alto rendimiento es que las implementaciones de Lisp ms comunes vienen con los compiladores maduros que generan cdigo de mquina muy eficiente. Voy a hablar en un momento sobre cmo ayudar a estos compiladores generan cdigo que ser competitivo con el cdigo generado por los compiladores de C, pero estas implementaciones que ya son un poco ms rpido que los de las lenguas cuya implementacin son menos maduros y usar simples compiladores o intrpretes . Adems, como el compilador de Lisp est disponible en tiempo de ejecucin, el programador de Lisp tiene algunas posibilidades de que sera muy difcil de emular

en otros idiomas - los programas pueden generar cdigo Lisp en tiempo de ejecucin que se compilan en cdigo de mquina y de ejecucin. Si el cdigo generado se va a ejecutar suficientes veces, esto puede ser una gran victoria. O, incluso sin necesidad de utilizar el compilador en tiempo de ejecucin, el clausura de darle otra forma de cdigo de mquina se funden con los datos de tiempo de ejecucin. Por ejemplo, la biblioteca CL-PPCRE expresin regular, se ejecuta en CMUCL, es ms rpido que el motor de expresiones regulares de Perl en algunos puntos de referencia, a pesar de que el motor de Perl est escrito en altamente sintonizado C. Esto es probablemente porque en Perl con una expresin regular se traduce en lo que son esencialmente bytecodes que luego son interpretados por el motor de expresiones regulares, mientras que CL-PPCRE traduce una expresin regular en un rbol de los clausuras de compilados que invocan unos a otros a travs de la funcin normal de los insultos de maquinaria. 5 Sin embargo, incluso con el algoritmo de la derecha y un compilador de alta calidad, usted no puede obtener la velocidad en bruto que necesita. Entonces es hora de pensar en perfiles y puesta a punto. La clave, en Lisp, como en cualquier idioma, es el perfil de los primeros en encontrar los puntos donde el programa est gastando su tiempo y luego preocuparse por la aceleracin de las partes. 6 Usted tiene un nmero de maneras diferentes de acercarse a los perfiles. El lenguaje estndar proporciona algunas herramientas rudimentarias para medir cunto tiempo toman ciertas formas de ejecutar. En particular, el TIEMPO macro se puede envolver alrededor de cualquier forma y volver lo valora el rendimiento de forma despus de imprimir un mensaje al* TRACE-SALIDA * acerca de cunto tiempo se tard en correr y la cantidad de memoria que utiliza. La forma exacta del mensaje de la implementacin. Usted puede utilizar TIEMPO por un poco de rpida y sucia de perfiles para limitar la bsqueda de cuellos de botella. Por ejemplo, suponga que tiene una funcin que est tomando mucho tiempo para correr y que llama a otras dos funciones, algo como esto:
(Defun foo () (Bar) (BAZ))

Si quieres ver si bar o baz est llevando ms tiempo, puede cambiar la definicin de foo a esto:

(Defun foo () (Hora (barra)) (Hora (baz)))

Ahora puede llamar a foo , y Lisp va a imprimir dos informes, uno para bar y otra para baz . La forma depende de la implementacin, esto es lo que se ve en Allegro Common Lisp:
CL-USER> (foo) , Tiempo de CPU (no-CG) 60 ms de usuario, 0 ms sistema de , Tiempo de CPU (GC) 0 ms usuario, 0 mseg sistema de , El tiempo de CPU (total) 60 ms de usuario, 0 ms sistema de , En tiempo real de 105 ms , El espacio de la asignacin: ; 24,172 clulas contras, 1,696 bytes, 0 bytes otros estticos , Tiempo de CPU (no GC) 540 ms de usuario, sistema de 10 ms , El tiempo de CPU (GC) 170 ms usuario, 0 mseg sistema de , Tiempo de CPU (total) 710 ms de usuario, sistema de 10 ms , En tiempo real de 1.046 ms , El espacio de la asignacin: ; 270,172 clulas contras, 1,696 bytes, 0 bytes otros estticos

Por supuesto, eso sera un poco ms fcil de leer si la salida incluye una etiqueta. Si se utiliza esta tcnica mucho, puede ser que valga la definicin de su propia macro como esta:
(Defmacro marcado en tiempo (la forma) `(Progn (Formato * traza de salida * "~ ~ 2 y un" ', la forma) (Tiempo, forma)))

Si reemplaza TIEMPO con la etiqueta de tiempo en la foo , obtendr la siguiente salida:


CL-USER> (foo) (BAR) , Tiempo de CPU (no-CG) 60 ms de usuario, 0 ms sistema de , Tiempo de CPU (GC) 0 ms usuario, 0 mseg sistema de , El tiempo de CPU (total) 60 ms de usuario, 0 ms sistema de , En tiempo real de 131 ms , El espacio de la asignacin: ; 24,172 clulas contras, 1,696 bytes, 0 bytes otros estticos (BAZ) , Tiempo de CPU (no GC) 490 ms usuario, 0 mseg sistema de , El tiempo de CPU (GC) 190 ms de usuario, sistema de 10 ms , Tiempo de CPU (total) 680 ms de usuario, sistema de 10 ms , En tiempo real de 1.088 ms , El espacio de la asignacin: ; 270,172 clulas contras, 1,696 bytes, 0 bytes otros estticos

A partir de este resultado, est claro que la mayor parte del tiempo en foo se gasta en baz . Por supuesto, la salida de TIEMPO se hace un poco difcil de manejar si la forma que desee el perfil se llama varias veces. Usted puede construir sus propios

instrumentos de medicin que utilizan las funciones GET-INTERIOR-EN TIEMPO REAL y el GET-INTERIOR-en tiempo de ejecucin , que devuelven un nmero que se incrementa por el valor de la constante INTERNO-unidades de tiempo por segundo cada segundo. GET-INTERIOR-EN TIEMPO REAL medidas de tiempo de la pared , la cantidad real de tiempo transcurrido, mientras que GET-INTERIOR tiempo de ejecucin de medidas, algunas de valor definido por la implantacin, como la cantidad de tiempo que Lisp era en realidad el tiempo de ejecucin o Lisp estaba ejecutando el cdigo de usuario y no la contabilidad interna, como el recolector de basura. Aqu hay una herramienta de perfiles de trivial, pero tiles construidos con unas pocas macros y GET-INTERIOR-en tiempo de ejecucin :
(* Defparameter sincronizacin de datos * ()) (Defmacro-con el tiempo (etiqueta y el cuerpo del cuerpo) (Con-gensyms (inicio) `(Let ((, inicio (get-interna-en tiempo de ejecucin))) (Desconectar de proteccin (progn, el cuerpo de @) (Push (lista ', etiqueta, comience (get-interna-en tiempo de ejecucin)) * sincronizacin de datos *))))) (Defun claro el momento de datos () (Setf * sincronizacin de datos * ())) (Defun show-momento-de datos () (Bucle de (nmero de etiqueta de tiempo de tiempo por%-de-total) en (de tiempo de compilacin de datos) se (Formato t "~% 3d ~ a. ~ D ~ d las garrapatas en las convocatorias de ~ ~ d por%" % De total de la etiqueta-el tiempo-por cuenta del tiempo))) (Defun tiempo de compilacin de datos () (Bucle con el tiempo = mesa (make-hash-table) con el recuento de mesa = (make-hash-table) de (extremo de la etiqueta de inicio) en la sincronizacin de los datos * * para el tiempo = (- extremo inicial) sumando el tiempo en total de hacer (Incf (etiqueta GetHash calendario de mesa 0) el tiempo) (Incf (etiqueta GetHash recuento de la mesa 0)) finalmente (Retorno (Ms o menos (bucle para la etiqueta de ser las claves de hash en el tiempo de recoger la mesa (Let ((el tiempo (etiqueta GetHash calendario de mesa)) (Count (conteo de la etiqueta GetHash de mesa))) (Etiqueta lista de recuento de tiempo (round (/ tiempo de cuenta)) (round (* 100 (/ tiempo total)))))) # ">: Tecla '#' quinta))))

Este analizador permite envolver un con-tiempo alrededor de cualquier forma, cada vez que el formulario ha sido firmado, el tiempo que se inicia y se termina el tiempo que se registran, asocindose con una etiqueta que usted proporcione. La

funcin de presentacin de datos de tiempo- los vertederos fuera una tabla que muestra cunto tiempo transcurri en diferentes secciones etiquetadas de cdigo como este:
CL-> USER (se presenta el momento-datos) 84% de BAR: 650 garrapatas ms de 2 convocatorias de 325 por. 16% FOO: 120 garrapatas ms de 5 llamadas de un 24 por. NIL

Usted, evidentemente, podra hacer que el cdigo de perfiles ms sofisticados de muchas maneras. Por otra parte, la implementacin de Lisp ms probable es que ofrece a sus herramientas de creacin de perfiles propios, que, ya que tienen acceso a la zona interna de la aplicacin, puede obtener en la informacin no necesariamente disponible para el cdigo de nivel de usuario. Una vez que hayas encontrado el cuello de botella en el cdigo, puede comenzar la sintonizacin. La primera cosa que usted debe intentar, por supuesto, es encontrar un algoritmo bsico ms eficiente - que es donde estn las grandes ganancias que se tena. Pero suponiendo que usted ya est usando un algoritmo adecuado, entonces es hasta codificar vagando - a nivel local la optimizacin del cdigo por lo que no hace absolutamente ningn trabajo ms de lo necesario. Las principales herramientas para el cdigo vagando en Common Lisp son sus declaraciones facultativas. La idea bsica detrs de las declaraciones en Common Lisp es que ellos estn acostumbrados a dar la informacin compilador lo puede utilizar en una variedad de formas de generar un mejor cdigo. Para un ejemplo simple, considere esta funcin Lisp Comn:
(Defun suma (xy) (+ xy))

He mencionado en el captulo 10 que si se compara el rendimiento de esta funcin Lisp a la funcin C, aparentemente equivalentes:
int suma (int x, int y) {return x + y;}

lo ms probable es encontrar el comn de la versin de Lisp a ser un poco ms lento, aunque su puesta en prctica comn Lisp cuenta con un compilador nativo de alta calidad. Eso es porque la versin de Common Lisp est haciendo mucho ms - el compilador de Lisp Comn ni siquiera sabe que los valores de uno y b son nmeros y por lo tanto tiene que generar el cdigo para comprobar en tiempo de ejecucin. Y una vez

que se determina que son los nmeros, se tiene que determinar qu tipos de nmeros: enteros, racionales, de punto flotante, o complejo - y el envo a la rutina adems apropiado para los tipos reales. Y aunque una y b son nmeros enteros: el caso de que se preocupan por - adems de la rutina debe dar cuenta de la posibilidad de que el resultado puede ser demasiado grande para representarlo como un fixnum , un nmero que puede representarse en una sola mquina palabra, y por lo tanto, puede tener que asignar un nmero grande de objetos. En C, por el contrario, debido a que el tipo de todas las variables se declaran, el compilador sabe exactamente qu tipo de valores de uno y b llevar a cabo. Y debido a que la aritmtica de C simplemente se desborda cuando el resultado de una suma es demasiado grande para representarlo en cualquier tipo se devuelve, no hay ninguna comprobacin de desbordamiento y de asignacin de un objeto nmero grande para representar el resultado cuando la suma matemtica es demasiado grande para caber en la una palabra mquina. As, mientras que el comportamiento del comn cdigo Lisp es mucho ms probable que sea matemticamente correcto, la versin C probablemente puede ser compilado hacia abajo para uno o dos instrucciones de mquina. Pero si usted est dispuesto a dar el compilador de Lisp Comn de la misma informacin que el compilador de C tiene sobre los tipos de argumentos y valores de retorno y aceptar ciertos compromisos como la C-en trminos de generalidad y de la comprobacin de errores, la funcin Lisp Comn tambin puede ser compilado hacia abajo para una instruccin o dos. Eso es lo que las declaraciones son para. El uso principal de las declaraciones es para indicar al compilador acerca de los tipos de variables y otras expresiones. Por ejemplo, puede indicar al compilador que los argumentos para agregar son dos fixnums de escribir la funcin como esta:
(Defun complemento (xy) (Declare (fixnum xy)) (+ Xy))

El DECLARE expresin no es una forma de Lisp, sino que es parte de la sintaxis de la DEFUN y deben aparecer antes de cualquier otro cdigo en el cuerpo de la funcin. 7 Esta declaracin afirma que los argumentos pasados de los parmetros x , y y siempre ser fixnums . En otras palabras, es una promesa para el compilador, y

el compilador se le permite generar el cdigo en el supuesto de que todo lo que te digo es cierto. Para declarar el tipo del valor devuelto, se puede envolver la forma (+ xy) en el LA operador especial. Este operador tiene un especificador de tipo, como fixnum , y un formulario y le dice al compilador de la forma se evaluar el tipo dado. Por lo tanto, para dar el compilador de Lisp Comn toda la informacin acerca de agregar que el compilador de C se pone, se puede escribir as:
(Defun complemento (xy) (Declare (fixnum xy)) (La fixnum (+ xy)))

Sin embargo, incluso esta versin necesita una declaracin ms para dar el comn de compilador de Lisp la misma licencia que el compilador de C para generar el cdigo rpido, pero peligroso. El OPTIMIZE declaracin se utiliza para indicar al compilador cmo equilibrar cinco cualidades: la velocidad del cdigo generado, la cantidad de la comprobacin de errores en tiempo de ejecucin, el uso de la memoria del cdigo, tanto en trminos de tamao del cdigo y el uso de memoria en tiempo de ejecucin, la cantidad de mantiene la informacin de depuracin con el cdigo, y la velocidad del proceso de compilacin. Un OPTIMIZE declaracin se compone de una o ms listas, cada una de ellas contiene los smbolos de la VELOCIDAD , LA SEGURIDAD , EL ESPACIO , DEBUG , y RECOPILACIN DE VELOCIDAD- , y un nmero de cero a tres, ambos inclusive. El nmero indica el peso relativo que el compilador debe dar a la calidad correspondiente, con el 3 es el ms importante y 0 significa nada importante. Por lo tanto, poner en comn compilacin Lisp aadir ms o menos como un compilador de C que, se puede escribir as:
(Defun complemento (xy) (Declarar optimizar ((velocidad 3) (de seguridad 0))) (Declare (fixnum xy)) (La fixnum (+ xy)))

Por supuesto, ahora la versin de Lisp sufre de muchas de las mismas responsabilidades que la versin C - si los argumentos pasados no son fixnums o si la adicin se desborda, el resultado ser matemticamente incorrecta o algo peor. Adems, si alguien llama agrega con un nmero incorrecto de argumentos, puede no ser bastante. Por lo tanto, usted debe utilizar este tipo de declaraciones slo despus de que su programa est funcionando correctamente. Y usted debe agregar slo los perfiles que muestra que van a marcar la diferencia.Si usted est

consiguiendo un rendimiento razonable, sin ellos, dejarlos fuera. Sin embargo, cuando el perfil que muestra un punto caliente de verdad en el cdigo y que hay que poner a punto, seguir adelante. Debido a que usted puede utilizar las declaraciones de esta manera, rara vez es necesario volver a escribir el cdigo en C slo por razones de rendimiento, las FFI se utilizan para acceder a la existente de cdigo en C, pero las declaraciones se utilizan cuando el C-al igual que el rendimiento que se necesita. Por supuesto, lo cerca que se puede obtener el rendimiento de una determinada pieza de cdigo Lisp Comn a C y C + + depende en gran medida de la cantidad como la C que est dispuesto a hacerlo. Otra herramienta de cdigo de ajuste incorporado en Lisp es la funcin DESMONTE . El comportamiento exacto de esta funcin depende de la implementacin, ya que depende de cmo la aplicacin compila el cdigo - ya sea en cdigo mquina, los bytecodes, o alguna otra forma. Pero la idea bsica es que te muestra el cdigo generado por el compilador cuando se compila una funcin especfica. Por lo tanto, puede utilizar DESMONTE para ver si sus declaraciones estn teniendo ningn efecto sobre el cdigo generado. Y si la implementacin de Lisp utiliza un compilador nativo y usted sabe el idioma de su plataforma de montaje, puede tener una idea bastante buena de lo que realmente sucede cuando se llama a una de sus funciones. Por ejemplo, usted podra utilizar DESMONTE para tener una idea de la diferencia entre la primera versin del complemento , sin declaraciones, y la versin final. En primer lugar, definir y elaborar la versin original.
(Defun suma (xy) (+ xy))

Entonces, en el REPL, llame DESMONTE con el nombre de la funcin. En Allegro, muestra el siguiente lenguaje ensamblador-como descarga del cdigo generado por el compilador:
CL-> USER (desmonte "aadir) ;; El desmontaje de # ADD> <Funciones ;; Formales: XY ;; Inicio de cdigo: # x737496f4: 0: 55 pushl ebp 1: 8b ec movl ebp, esp 3: 56 esi pushl 4: 83 CE 24 subl esp, $ 36 7: 83 F9 02 CMPL ecx, $ 2 10: 74 02 14 jz 12: cd 61 int $ 97; SYS :: TRAP-ARGERR 14: 80 7F 00 ter CMPB [edi-53], $ 0; SYS :: C_INTERRUPT pendiente 18: 74 02 22 jz 20: cd 64 int $ 100; SYS :: TRAP-SEAL-HIT

22: 8b d8 movl ebx, eax 24: 0b da orl ebx, edx 26: f6 03 c3 TestB bl, $ 3 29: 75 0e jnz 45 31: 8b d8 movl ebx, eax 33: 03 pers da ebx, edx 35: 70 08 45 jo 37: 8b c3 movl EAX, EBX 39: clc f8 40: c9 licencia 41: 75 8b fc esi movl, [ebp-4] 44: c3 ret 45: 8b 8f 5f movl ebx, [edi-113]; EXCL :: + _2OP 48: FF 57 27 llamada * [edicin 39]; SYS :: VAGABUNDO Y DOS 51: EB 40 jmp f3 53: 90 nop ; No hay ningn valor

Claramente, hay un montn de cosas pasando aqu. Si usted est familiarizado con el lenguaje ensamblador x86, usted puede decir probablemente lo que pase. Ahora compila esta versin de aadir con todas las declaraciones.
(Defun complemento (xy) (Declarar optimizar ((velocidad 3) (de seguridad 0))) (Declare (fixnum xy)) (La fixnum (+ xy)))

Ahora desmonte agregar de nuevo, y ver si las declaraciones haban tenido ningn efecto.
CL-> USER (desmonte "aadir) ;; El desmontaje de # ADD> <Funciones ;; Formales: XY ;; Inicio de cdigo: # x7374dc34: 0: 03 C2 pers eax, edx 2: f8 clc 3: 75 8b fc esi movl, [ebp-4] 6: c3 ret 7: 90 nop ; No hay ningn valor

Parece que lo hicieron. Entrega de Aplicaciones Otro tema de importancia prctica, que yo no hablaba de otra parte del libro, es la forma de entrega de software escrito en Lisp. La razn principal por la que descuidado este tema se debe a que hay muchas maneras diferentes de hacerlo, y cul es el mejor para usted depende de qu tipo de software que necesita para ofrecer a qu tipo de usuario con lo comn de Lisp. En esta seccin voy a dar una visin general de algunas de las opciones diferentes.

Si usted ha escrito cdigo que desea compartir con los compaeros de los programadores de Lisp, la forma ms sencilla para su distribucin es como el cdigo fuente. 8 Puede distribuir una biblioteca simple como un solo archivo fuente, que los programadores pueden CARGA a su imagen Lisp, posiblemente despus de compilar con compile-file . Bibliotecas ms complejas o aplicaciones, dividido en varios archivos fuente, representan un reto adicional - con el fin de cargar y compilar el cdigo, los archivos deben ser cargados y compilados en el orden correcto. Por ejemplo, un archivo que contiene definiciones de macros se debe cargar antes de poder compilar los archivos que utilizan las macros. Y un archivo que contiene DEFPACKAGE formas se debe cargar antes de que los archivos que utilizan los paquetes, incluso se puede LEER . Lispers llaman a esto el sistema de definicin deproblemas y por lo general lo maneja con las herramientas llamadas instalaciones del sistema de definicin o de definicin del sistema de servicios pblicos , que son algo anlogo a la construccin de herramientas, tales como realizar o de la hormiga . Al igual que hacen y las hormigas , las herramientas de definicin del sistema le permiten especificar las dependencias entre los diferentes archivos y luego tomar el cuidado de la carga y la compilacin de los archivos en el orden correcto al tratar de hacer un trabajo nico que es necesario - volver a compilar slo los archivos que han cambiado, por ejemplo. Hoy en da la herramienta de definicin del sistema ms utilizado es la ASDF, siglas de otro centro de definicin del sistema . 9 La idea bsica detrs ASDF es que se define en los archivos de los sistemas de ASD, y ASDF ofrece una serie de operaciones en sistemas como el de cargarlas o compilar ellos. Un sistema tambin se puede definir a depender de otros sistemas, los cuales sern cargados segn sea necesario. Por ejemplo, la siguiente se muestra el contenido de html.asd , el archivo ASD para la biblioteca FOO de los Captulos 31 y 32:
(Defpackage: com.gigamonkeys.html sistema (: uso: asdf: cl)) (En el paquete: com.gigamonkeys.html-sistema) (Defsystem html : Nombre de "html" : Autor de "Peter Seibel <peter@gigamonkeys.com>" : La versin "0.1" : Mantenedor de "Peter Seibel <peter@gigamonkeys.com>" : Licencia "BSD" : Descripcin "de HTML y CSS a partir de la generacin de sexps". : Larga descripcin "" : Componentes

((: Archivo "paquetes") (: Archivo "html": depende-en ("paquetes")) (: Archivo "css": depende-en ("paquetes", "html"))) : Depende-en (: macro-empresas de servicios pblicos))

Si se agrega un enlace simblico a este archivo en un directorio que aparece en asdf: * Registro central * , 10 entonces usted puede escribir lo siguiente:
(Asdf: operar "asdf: la carga de la operacin: html)

para compilar y cargar los archivos packages.lisp , html.lisp y html macros.lisp- en el orden correcto despus de asegurarse de que el primero: macro-empresas de servicios pblicos del sistema ha sido compilado y cargado. Para otros ejemplos de archivos ASD, usted puede mirar el cdigo fuente de este libro - el cdigo de cada captulo prctica se define como un sistema con dependencias entre sistemas apropiados expresados en los archivos ASD. La mayora de las bibliotecas comunes libres y de cdigo abierto-Lisp que encontrars vendr con un archivo de TEA. Algunos utilizan otras herramientas de definicin del sistema, como el MK ligeramente mayores: los servicios pblicos DEFSYSTEM o incluso ideados por el autor de la biblioteca, pero la marea parece estar cambiando en la direccin de la ASDF. 11 Por supuesto, mientras ASDF hace que sea fcil de instalar bibliotecas Lispers Lisp, no es de mucha ayuda si desea empaquetar una aplicacin para un usuario final que no sabe o no se preocupan por Lisp. Si usted entrega una pura aplicacin del usuario final, es de suponer que usted quiere dar algo que el usuario puede descargar, instalar y ejecutar sin tener que saber nada acerca de Lisp. No puedes esperar a descargar e instalar por separado una implementacin de Lisp. Y usted quiere que ellos sean capaces de ejecutar la aplicacin como cualquier otra aplicacin - haciendo doble clic en un icono en Windows o OS X o escribiendo el nombre del programa en la lnea de comandos en Unix. Sin embargo, a diferencia de los programas en C, que normalmente se puede confiar en determinadas bibliotecas compartidas (DLL en Windows) que conforman la C "de tiempo de ejecucin" estar presente como parte del sistema operativo, programas Lisp debe incluir un tiempo de ejecucin de Lisp, es decir, el mismo programa se ejecuta cuando se inicia Lisp aunque tal vez con cierta funcionalidad no es necesaria para ejecutar la aplicacin extirpado.

Para complicar ms las cosas, el programa no est muy bien definido en Lisp. Como hemos visto en este libro, el proceso de desarrollo de software en Lisp es un proceso gradual que implica realizar cambios en el conjunto de definiciones y los datos que viven en su imagen Lisp. El "programa" es slo un estado particular de la imagen llega por la carga de loslisp. o . FasL archivos que contienen cdigo que crea las definiciones y datos apropiados. Se podra, entonces, distribuir una aplicacin de Lisp como un tiempo de ejecucin de Lisp, ms un montn de archivos FasL y un archivo ejecutable que se inicia el tiempo de ejecucin, carga los FASLs, y de alguna manera invoca la funcin de partida adecuado. Sin embargo, ya que en realidad la carga de los FASLs puede llevar algn tiempo, especialmente si se tiene que hacer ningn clculo para establecer el estado del mundo, las implementaciones de Lisp ms comunes proporcionan una manera de deshacerse de una imagen - para guardar el estado de ejecucin de Lisp en un archivo llamado un archivo de imagen o, a veces un ncleo . Cuando se inicia un tiempo de ejecucin de Lisp, lo primero que hace es cargar un archivo de imagen, que se puede hacer en mucho menos tiempo de lo que tomara para volver a crear el estado de carga de archivos msnm. Normalmente, el archivo de imagen es una imagen predeterminada que contiene slo los paquetes estndar definidos por el lenguaje y algunos extras proporcionados por la aplicacin.Pero con la mayora de las implementaciones, usted tiene una manera de especificar un archivo de imagen diferente. As, en lugar de empaquetar una aplicacin como un tiempo de ejecucin de Lisp, ms un montn de FASLs, se pueden agrupar como un tiempo de ejecucin de Lisp, ms un solo archivo de imagen que contiene todas las definiciones de las y los datos que componen la aplicacin. Entonces todo lo que necesitas es un programa que se inicia el tiempo de ejecucin Lisp con la imagen apropiada y llama a cualquier funcin sirve como punto de entrada a la solicitud. Aqu es donde las cosas se ponen la aplicacin y dependiendo del sistema operativo. Algunas implementaciones de Lisp comunes, en particular las comerciales, como Allegro y LispWorks, proporcionar herramientas para la construccin de un ejecutable. Por ejemplo, Enterprise Edition de Allegro proporciona una funcin devalor agregado Signo: generate-aplicacin que crea un directorio que contiene el tiempo de ejecucin de Lisp, como una biblioteca compartida, un archivo de imagen y un archivo ejecutable que se inicia el tiempo de ejecucin con la imagen dada. Del mismo modo, el LispWorks Professional Edition "entrega" mecanismo le permite

crear un nico archivo ejecutable de sus programas. En Unix, con las diversas implementaciones libres y de cdigo abierto, que puede hacer bsicamente lo mismo excepto que es ms fcil usar un script de shell para empezar todo. Y en OS X son las cosas an mejor - ya que todas las aplicaciones de OS X se empaquetan como . app paquetes, que son, esencialmente, directorios con una cierta estructura, no es tan difcil de empaquetar todas las partes de una aplicacin de Lisp como un doble hacer clic . aplicacin paquete. Herramienta de Evins Mikel Bosco hace que sea fcil de crear. app paquetes de aplicaciones que se ejecutan en OpenMCL. Por supuesto, otra forma muy conocido para aplicaciones en estos das es que las aplicaciones del lado del servidor. Se trata de un nicho en el Common Lisp puede sobresalir - ahora puede recoger una combinacin de sistema operativo y la aplicacin comn de Lisp que funciona bien para usted, y usted no tiene que preocuparse sobre cmo empaquetar la aplicacin para ser instalado por un usuario final. Y depuracin interactiva Common Lisp y las caractersticas de desarrollo que sea posible depurar y actualizar un servidor en vivo de manera que, o bien simplemente no son posibles en un lenguaje menos dinmico o que requieren volver a construir una gran cantidad de infraestructuras especficas. Dnde ir a continuacin Por lo tanto, eso es todo. Bienvenido al maravilloso mundo de Lisp. Lo mejor que podemos hacer ahora - si usted no lo ha hecho - es comenzar a escribir su propio cdigo Lisp. Escoja un proyecto que le interesa, y lo hacen en Common Lisp. Despus haga otra. Espuma, enjuague, repita. Sin embargo, si usted necesita algunas ideas nuevas, esta seccin ofrece algunos lugares a donde ir. Para empezar, echa un vistazo a la prctica de Common Lisp sitio Web enhttp://www.gigamonkeys.com/book/ , donde se puede encontrar el cdigo fuente de los captulos prcticos, fe de erratas, y enlaces a otros recursos de Lisp en la Web. Adems de los sitios que he mencionado en la "Bsqueda de Lisp Libraries" la seccin, es posible que desee explorar la hiperespec Lisp Comn (tambin conocido como el hiperespec o CLHS), una versin HTML de la lengua estndar ANSI preparado por Kent Pitman y puestos a disposicin por LispWorks enhttp://www.lispworks.com/documentation/HyperSpec/index.html . El hiperespec

no es un tutorial, pero es como una gua autorizada para la lengua que se puede obtener sin necesidad de comprar una copia impresa de la norma de ANSI y mucho ms conveniente para el uso del da a da. 12 Si desea ponerse en contacto con otros Lispers, comp.lang.lisp en Usenet y el # lisp canal de IRC o de la red Freenode ( http://www.freenode.net ) son dos de los principales colgar en lnea de espera. Hay tambin un nmero de Lisp relacionados con los blogs, la mayora de los cuales se agrupan en el planeta Lisp enhttp://planet.lisp.org/~~V . Y mantener los ojos bien abiertos en todos los foros de anuncios de los usuarios locales de Lisp tertulias en su rea, en los ltimos aos, las reenlacees Lispnik han surgido en ciudades de todo el mundo, desde Nueva York a Oakland, de Colonia a Mnich , y vuelta a Ginebra y Helsinki. Si usted desea pegarse a los libros, he aqu algunas sugerencias. Para un buen libro de referencia de grosor para pegar en su escritorio, agarra el Libro Comn de Referencia ANSI Lispeditado por David Margolies (Apress, 2005). 13 Para ms informacin sobre Common Lisp Object System, usted puede comenzar con programacin orientada a objetos en Common Lisp: Una Gua del programador para CLOSpor Sonya E. Keene (Addison-Wesley, 1989). Entonces, si usted realmente quiere llegar a ser un asistente de objetos o simplemente para estirar tu mente de manera interesante, leer El arte del Protocolo Metaobject por Gregor Kiczales, Jim des Rivires, y Daniel G. Bobrow (MIT Press, 1991). Este libro, tambin conocido como OP, es a la vez una explicacin de lo que es un protocolo de metaobject es y por qu quiere uno y el estndar de facto para el protocolo metaobject con el apoyo de muchas implementaciones de Common Lisp. Dos libros que cubren tcnica general de Common Lisp son paradigmas de la programacin de la Inteligencia Artificial: Estudios de caso en Common Lisp de Peter Norvig (Morgan Kaufmann, 1992) y On Lisp: Tcnicas avanzadas para Common Lisp de Paul Graham (Prentice Hall, 1994). El primero ofrece una slida introduccin a las tcnicas de inteligencia artificial, mientras que la enseanza de un poco acerca de cmo escribir un buen cdigo de Common Lisp, y el ltimo es especialmente bueno en el tratamiento de las macros. Si usted es el tipo de persona que le gusta saber cmo funcionan las cosas hasta los bits, Lisp en pequeos trozos de Christian Queinnec (Cambridge University Press,

1996) ofrece una buena mezcla de la teora del lenguaje de programacin y la prctica de tcnicas de implementacin de Lisp. A pesar de que est centrado principalmente en rgimen en lugar de Common Lisp, aplican los mismos principios. Para aquellos que quieran un look un poco ms terica en las cosas - o que simplemente quieren saber qu se siente ser un estudiante de primer ao estudiante de un borrador de ciencia en el MIT - Estructura e Interpretacin de Programas de Ordenador , segunda edicin, de Harold Abelson, Gerald Jay Sussman y Julie Sussman (MIT Press, 1996) es un texto de ciencias de la computacin clsica que emplea el sistema para ensear importantes de programacin C ONCEPTOS. Cualquier programador puede aprender mucho de este libro - slo recuerde que existen diferencias importantes entre el rgimen y Common Lisp. Una vez que ha cumplido con su mente alrededor de Lisp, es posible que desee colocarlo en un poco de contexto. Puesto que nadie puede reclamar para entender realmente la orientacin a objetos que no sabe algo acerca de Smalltalk, es posible que desee comenzar con Smalltalk-80: El lenguaje por Adele Goldberg y Robson David (Addison Wesley, 1989), la introduccin estndar para el ncleo de Smalltalk. Despus de eso, los mejores patrones de prctica de Smalltalk por Kent Beck (Prentice Hall, 1997) est llena de buenos consejos dirigido a Smalltalkers, muchos de los cuales es aplicable a cualquier lenguaje orientado a objetos. Y en el otro extremo del espectro, la Construccin de Software Orientado a Objetos de Bertrand Meyer (Prentice Hall, 1997) es una exposicin excelente de la lengua esttica mentalidad del inventor de Eiffel, un descendiente frecuentemente pasada por alto de Simula y Algol. Contiene mucho que pensar, incluso para los programadores que trabajan con lenguajes dinmicos como Common Lisp. En particular, Meyer ideas sobre el diseo por contrato puede arrojar mucha luz sobre cmo se debe utilizar el sistema de Common Lisp de condicin. Aunque no se trata de ordenadores per se, la sabidura de las multitudes: Por qu los muchos son ms inteligentes que los pocos y cmo la sabidura colectiva de Empresas Formas, economas, sociedades y naciones de James Surowiecki (Doubleday, 2004) contiene una excelente respuesta a la pregunta " Si Lisp es tan bueno cmo es que no todo el mundo lo est usando? " Vea la seccin de "FiebrePlank Road" comienza en la pgina 53.

Y, por ltimo, un poco de diversin, y para aprender acerca de la influencia Lisp y Lispers han tenido en la cultura hacker, echar mano (o leer de cabo a rabo) El Diccionario del Hacker Nuevo , Tercera Edicin, compilado por Eric S. Raymond (MIT Press, 1996) y basado en el original El Diccionario del Hacker editada por Guy Steele (Harper & Row, 1983). Pero no dejes que todas estas sugerencias interferir con su programacin - la nica manera de aprender realmente un idioma es utilizarlo. Si has llegado hasta aqu, eres sin duda dispuesto a hacer eso. Feliz hacking!

1 La combinacin de Common Lisp de lectura en tiempo condicionalizacin y macros hace que sea

muy factible desarrollar bibliotecas de portabilidad que no hacen sino ofrecer una API comn en capas sobre lo diferentes implementaciones de la API de proporcionar servicios no especificados en el estndar del lenguaje. La biblioteca porttil de ruta del Captulo 15 es un ejemplo de este tipo de bibliotecas, aunque para suavizar las diferencias en la interpretacin de la norma en lugar de las API depende de la implementacin.
2 Una interfaz para funciones de Relaciones Exteriores es bsicamente equivalente a la JNI en Java,

XS en Perl, o el mdulo de ampliacin de API en Python.


3 A partir de este escrito, los dos principales inconvenientes de UFFI son la falta de apoyo a las

devoluciones de llamada de C en Lisp, que muchos, pero no todo el apoyo de las implementaciones de las FFI, y la falta de apoyo a CLISP, cuyo FFI es bastante bueno, pero lo suficientemente diferentes como de los dems para no encajan fcilmente en el modelo de UFFI.
4 Knuth ha utilizado la "programacin de computadoras como un arte", dice varias veces en las

publicaciones, incluyendo en su estudio de 1974 el Premio Turing ACM, y en su trabajo "Programas estructurados con instrucciones goto." En su artculo "Los errores de TeX", que atribuye el dicho de CAR Hoare. Y Hoare, en 2004 un correo electrnico a Hans Genwitz de phobia.com, dijo que no recordaba el origen de la frase, pero que podra haber atribuido a Dijkstra.
5 CL-PPCRE tambin se aprovecha de otra caracterstica comn Lisp no he hablado, las macros de

compilacin . Una macro compilador es un tipo especial de macro que le da la oportunidad de optimizar las llamadas a una funcin especfica mediante la transformacin de las llamadas a esa funcin en el cdigo ms eficiente. CL-PPCRE define macros de compilacin de sus funciones que toman argumentos con expresiones regulares. El compilador de macros de optimizar las llamadas a las funciones en las que la expresin regular es un valor constante al analizar la expresin regular en tiempo de compilacin en lugar de dejar que se haga en tiempo de ejecucin. Mire para arriba DEFINE compilador-MACRO en su referencia favorita Common Lisp para obtener ms informacin acerca de las macros del compilador.
6 La palabra prematura en "la optimizacin prematura" puede ser ms o menos definido como

"antes de perfiles". Recuerde que incluso si se puede acelerar un fragmento de cdigo hasta el punto

en que se necesita, literalmente, no hay tiempo para correr, an va a acelerar su programa slo por lo que el porcentaje de tiempo que pas en ese pedazo de cdigo.
7 Las declaraciones pueden aparecer en la mayora de las formas que introducen nuevas variables,

tales como LET , LET * , y la DO de la familia de las macros de bucle. LOOP tiene su propia sintaxis para declarar los tipos de variables de bucle. El operador especial LOCAL , que se menciona en el captulo 20, no hace ms que crear un mbito en el que usted puede hacer declaraciones.
8 Los archivos producidos por FasL compile-file dependen de la implementacin y pueden o pueden

no ser compatibles entre las diferentes versiones de la misma implementacin de Common Lisp. Por lo tanto, no son una muy buena manera de distribuir el cdigo Lisp. El tiempo de una que puede ser prctico es como una forma de proporcionar parches que deben aplicarse a una aplicacin que se ejecuta en una versin conocida de una aplicacin en particular. Aplicando el parche simplemente implica CARGA ING en el FASL, y porque un FASL puede contener cdigo arbitrario, que puede ser utilizado para actualizar los datos existentes, as como para proporcionar nuevas definiciones de cdigo.
9 ASDF fue originalmente escrito por Daniel Barlow, uno de los desarrolladores SBCL, y se ha

incluido como parte de SBCL durante mucho tiempo, y tambin se distribuye como una biblioteca independiente.Recientemente se ha aprobado e incluido en otras implementaciones como OpenMCL y Allegro.
10 En Windows, donde no hay enlaces simblicos, funciona un poco diferente, pero ms o menos lo

mismo.
11 Otra de las herramientas, ASDF a la instalacin, se basa en la parte superior de la ASDF y MK:

DEFSYSTEM, proporcionando una manera fcil de descargar e instalar automticamente las bibliotecas de la red. El mejor punto de partida para aprender sobre ASDF-instalacin es Edi Weitz "Un tutorial para ASDF-Install" ( http:// www.weitz.de/asdf-install/ ).
12 BABA incorpora una biblioteca de elisp que le permite ir directamente a la entrada hiperespec

para cualquier nombre definido en la norma. Tambin puede descargar una copia completa de la hiperespec para mantener a nivel local para la navegacin fuera de lnea.
13 Otra referencia clsica es Common Lisp: El lenguaje por Guy Steele (Digital Press, 1984 y 1990). La

primera edicin, tambin conocido como CLtL1, era el estndar de facto para el idioma de una serie de aos. A la espera de la norma ANSI oficial que se haya terminado, Guy Steele - que estaba en el comit ANSI - decidi lanzar una segunda edicin para cerrar la brecha entre los CLtL1 y la norma final. La segunda edicin, ahora conocido como CLtL2, es esencialmente un resumen de la labor del comit de normalizacin tomada en un momento determinado en el tiempo cerca, pero no es menos, el final del proceso de normalizacin.En consecuencia, CLtL2 difiere de la norma en maneras que hacen que no se un muy buen da-a-da de referencia. Es, sin embargo, un documento histrico til, sobre todo porque incluye la documentacin de algunas de las caractersticas que se cay de la norma antes de que fuera terminado, as como un comentario que no es parte de la norma acerca de por qu ciertas caractersticas son como son.

También podría gustarte