Perlexamples
Perlexamples
1
Casiano R. León
31 de marzo de 2012
1
DEIOC Universidad de La Laguna
Índice general
1. Introducción 14
1.1. Primeros Pasos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.2. Escalares . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.2.1. Números . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
1.2.2. Cadenas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
1.2.3. Contexos Numérico y de Cadena: Conversiones . . . . . . . . . . . . . . . . . . 29
1.2.4. Variables Mágicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
1.3. Variables privadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.3.1. Lectura de Datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.4. La Lógica de Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
1.4.1. Operadores Lógicos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
1.4.2. Operadores de Comparación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
1.5. Algunas Sentencias de Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
1.6. Depuración de errores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
1.7. Una Brevı́sima Introducción a las Expresiones Regulares . . . . . . . . . . . . . . . . . 42
1.8. Un Programa Simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
1.9. Breve Introducción al Manejo de Excepciones . . . . . . . . . . . . . . . . . . . . . . . 48
1.10. Autentificación Automática . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
1.11. Uso de Subversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
1.12. Práctica: Area de un Cı́rculo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
1.13. Arrays y Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
1.13.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
1.13.2. Operadores de Listas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
1.13.3. Ejercicios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
1.13.4. Práctica: Fichero en Orden Inverso . . . . . . . . . . . . . . . . . . . . . . . . . 69
1.13.5. Práctica: En Orden ASCIIbético . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.13.6. Práctica: Sin Distinguir Case . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
1.13.7. Práctica: Indexación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
1.13.8. Práctica: Postfijo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
1.13.9. Práctica: Ordenación Internacional . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.14. Hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.14.1. Acceso a los elementos de un hash . . . . . . . . . . . . . . . . . . . . . . . . . 74
1.14.2. El operador flecha grande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.14.3. Asignación de Hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
1.14.4. Troceado de un hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.14.5. Inversión de un Hash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.14.6. Las funciones keys y values . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
1.14.7. La función each . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
1.14.8. Las funciones delete y exists . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.14.9. Interpolación de hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
1.14.10.Obtener el Conjunto de Elementos de una Lista . . . . . . . . . . . . . . . . . . 79
1.14.11.Bloqueo de las Claves de un Hash . . . . . . . . . . . . . . . . . . . . . . . . . 79
1.14.12.Práctica: Ordenar por Calificaciones . . . . . . . . . . . . . . . . . . . . . . . . 80
1
1.15. Subrutinas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.15.1. Definición de subrutinas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.15.2. Argumentos y valores de retorno . . . . . . . . . . . . . . . . . . . . . . . . . . 82
1.15.3. Otros modos de llamar a una subrutina . . . . . . . . . . . . . . . . . . . . . . 85
1.15.4. Tipo de objeto y ámbito . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
1.15.5. La declaración our . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
1.15.6. El uso de local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
1.15.7. Argumentos con Nombre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
1.15.8. Aliasing de los parámetros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
1.15.9. Contexto de la llamada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
1.15.10.¿Quién llamó a esta rutina? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
1.15.11.Calculando el Máximo de Forma Genérica . . . . . . . . . . . . . . . . . . . . . 94
1.15.12.Ejercicio: Prioridad de Operaciones . . . . . . . . . . . . . . . . . . . . . . . . . 94
1.15.13.Ejercicio: Significados de la Coma . . . . . . . . . . . . . . . . . . . . . . . . . 95
1.15.14.Práctica: Polares a Cartesianas . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
1.15.15.Práctica: Postfijo y Subrutina . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
2. Entrada /Salida 97
2.1. El Operador Diamante y el Manejador de Ficheros ARGV . . . . . . . . . . . . . . . . . 97
2.2. El manejador ARGVOUT . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
2.3. Uso de Perl desde la Lı́nea de Comandos: Modificación en Múltiples Ficheros . . . . . 99
2.4. El Manejador de Ficheros DATA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
2.5. Operaciones sobre Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
2.6. Práctica: Ficheros Grandes y Viejos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
2.7. Ficheros Binarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
2.8. La función localtime . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
2.9. Directorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
2.10. Operaciones con ficheros, links y directorios . . . . . . . . . . . . . . . . . . . . . . . . 114
2.11. Renombrar Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116
2.12. Práctica: Descenso Recursivo en Subdirectorios . . . . . . . . . . . . . . . . . . . . . . 119
2
3.19. El ancla \ G . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
3.20. Palabras Repetidas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
3.21. Análisis de cadenas con datos separados por comas . . . . . . . . . . . . . . . . . . . . 134
3.22. Número de substituciones realizadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
3.23. Evaluación del remplazo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
3.24. Anidamiento de /e . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
3.25. Expandiendo y comprimiendo tabs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
3.26. Modificación en múltiples ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
3.27. tr y split . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
3.28. Pack y Unpack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
4. Referencias 142
4.1. Referencias a variables ya existentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
4.1.1. Referencias y referentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
4.1.2. Referencias a constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
4.1.3. Contextos y Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
4.1.4. Ambiguedad en el De-referenciado . . . . . . . . . . . . . . . . . . . . . . . . . 144
4.1.5. La Notación Flecha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
4.2. Identificando un referente ref . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
4.3. Paso de Listas y Hashes a Subrutinas . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
4.4. Referencias a almacenamiento anónimo . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
4.5. Práctica: Conjuntos a través de Hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
4.6. Estructuras anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
4.7. Asignación Implı́cita de Memoria y Autovivificación . . . . . . . . . . . . . . . . . . . 151
4.8. Impresión de Estructuras Anidadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
4.9. Ejemplo: El Producto de Matrices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
4.10. Ejercicio: Identificadores entre LLaves . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
4.11. Gestión de la memoria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156
4.12. Referencias Simbólicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
4.12.1. Práctica: Referenciado Simbólico . . . . . . . . . . . . . . . . . . . . . . . . . . 168
4.13. Referencias a subrutinas anónimas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
4.14. Funciones de orden superior . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
4.14.1. Práctica: Emulación de un Switch . . . . . . . . . . . . . . . . . . . . . . . . . 170
4.15. Typeglobs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.15.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.15.2. Asignación de typeglobs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.15.3. Variables léxicas y typeglobs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
4.15.4. local y typeglobs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
4.15.5. Paso de parámetros a una subrutina por medio de typeglobs . . . . . . . . . . 172
4.15.6. Typeglobs y Eficiencia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
4.15.7. Typeglobs Selectivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
4.15.8. Typeglobs vistos como Hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
4.15.9. Referencias Simbólicas y typeglobs . . . . . . . . . . . . . . . . . . . . . . . . . 177
4.15.10.Práctica: Construcción de un wrapper . . . . . . . . . . . . . . . . . . . . . . . 178
4.15.11.Suprimiendo Subrutinas con Typeglobs y Referenciado Simbólico . . . . . . . . 179
4.15.12.El Módulo Symbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
4.15.13.Práctica: Inserción de una Subrutina . . . . . . . . . . . . . . . . . . . . . . . . 181
4.16. Prototipos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
4.16.1. Práctica: Suma de Prefijos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
4.17. Las Cadenas como Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
4.18. Clausuras . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
4.18.1. Clausuras y Generación de Funciones Similares . . . . . . . . . . . . . . . . . . 188
4.18.2. Anidamiento de subrutinas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
3
4.18.3. Clausuras e Iteradores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
4.18.4. Memoizing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195
4.18.5. Currying . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198
4.18.6. Listas Perezosas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
5. Módulos 203
5.1. Los packages: Repaso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
5.2. Tablas de Sı́mbolos y Packages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
5.3. Subrutinas Privadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205
5.4. Paquetes y Ficheros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206
5.5. Búsqueda de Librerı́as y Módulos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208
5.6. Control de Versiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.7. Importación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
5.8. Acceso a la tabla de sı́mbolos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
5.8.1. Práctica: Stash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
5.9. AUTOLOAD: Captura de LLamadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
5.10. Práctica: AUTOLOAD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217
5.11. El Pragma use subs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
5.12. Los Paquetes CORE y CORE::GLOBAL . . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
5.13. Uso del Módulo de Exportación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
5.14. CPAN: The Comprehensive Perl Archive Network . . . . . . . . . . . . . . . . . . . . 225
5.14.1. Instalación a mano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
5.14.2. Práctica: Instalar un Módulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
5.14.3. Saber que Módulos están Instalados . . . . . . . . . . . . . . . . . . . . . . . . 227
5.14.4. Suprimir un Módulo Instalado . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
5.14.5. Usando el módulo CPAN.pm como Administrador . . . . . . . . . . . . . . . . . 230
5.14.6. Opciones de Configuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236
5.14.7. Bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246
5.14.8. CPAN: Si no tenemos los privilegios de administrador . . . . . . . . . . . . . . 247
5.14.9. Construyendo un Mirror de CPAN . . . . . . . . . . . . . . . . . . . . . . . . . 249
5.14.10.Práctica: CPAN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
5.15. PAR: The Perl Archive Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
5.16. Instalación de Ejecutables con pp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
5.17. Construcción de un Módulo con h2xs . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
5.18. La Documentación en Perl . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
5.19. Bancos de Pruebas y Extreme Programming . . . . . . . . . . . . . . . . . . . . . . . . 264
5.19.1. Versiones anteriores a la 5.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 264
5.19.2. Versiones posteriores a la 5.8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 265
5.20. Práctica: Construcción de una Distribución . . . . . . . . . . . . . . . . . . . . . . . . 269
5.21. Pruebas en la Construcción de una Distribución . . . . . . . . . . . . . . . . . . . . . . 269
5.21.1. El Problema de la Mochila 0-1 . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
5.21.2. El Módulo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
5.21.3. La Documentación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
5.21.4. MANIFEST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
5.21.5. El fichero pm to blib . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
5.21.6. El fichero META.yml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
5.21.7. Las Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
5.21.8. Formas de Ejecutar las Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
5.21.9. Ejecutables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 283
5.21.10.Profundizando en Makefile.PL . . . . . . . . . . . . . . . . . . . . . . . . . . . 284
5.21.11.Comprobando la Distribución con Test::Kwalitee . . . . . . . . . . . . . . . . 286
5.21.12.Comprobando la Portabilidad del Código . . . . . . . . . . . . . . . . . . . . . 287
5.21.13.Práctica: Pruebas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
4
5.21.14.El módulo Test::LectroTest . . . . . . . . . . . . . . . . . . . . . . . . . . . 292
5.21.15.Práctica: Generación de Pruebas con Test::LectroTest . . . . . . . . . . . . 300
5.21.16.A Veces las Pruebas Tienen Fallos . . . . . . . . . . . . . . . . . . . . . . . . . 300
5.22. Software de Rastreo de Errores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
5.22.1. Request Tracker . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
5.23. Patches o Parches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
5.23.1. Creación de un Parche/Patch . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
5.23.2. Aplicar el Patch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
5.24. Escribir Módulos para CPAN . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
7. Templates 363
7.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
7.2. Depuración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
7.3. Bucles FOR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 364
7.4. Bucles sobre Hashes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
7.5. Generando HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365
7.6. Filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 366
7.7. CGI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
7.7.1. ttcgi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
7.7.2. Mas sobre CGIs con Templates . . . . . . . . . . . . . . . . . . . . . . . . . . . 369
5
8. SQLite 371
8.1. Introducción . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371
8.2. Triggers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 372
8.3. Logging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 373
9. DBI 376
6
13.33.Dificultades a la Hora de Usar Gestión de Configuraciones . . . . . . . . . . . . . . . . 418
13.34.Conjuntos de Cambios en Subversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
13.35.Mezclas en svnbook . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
13.36.Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
13.37.Enviando Mails via Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
13.38.Controlando los Permisos via Hooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . 423
13.39.Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
13.40.Búsqueda Binaria . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 426
13.41.Replicación de Repositorios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
13.42.Referencias . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427
7
Índice de figuras
6.1. Al bendecir un objeto este es marcado con la clase a la que pertenece . . . . . . . . . 309
6.2. Formas de bendición: esquema de herencia del programa . . . . . . . . . . . . . . . . . 323
6.3. Esquema de herencia/delegación del programa . . . . . . . . . . . . . . . . . . . . . . 324
6.4. Esquema del programa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
8
Índice de cuadros
9
A Juana
10
Agradecimientos/Acknowledgments
I’d like to thank Tom Christiansen, Damian Conway, Simon Cozens, Francois
Desarmenien, Richard Foley, Jeffrey E.F. Friedl, Joseph N. Hall, Tim Jennes, Andy
Lester, Allison Randall, Randal L. Schwartz, Michael Schwern, Peter Scott, Sriram
Srinivasan, Linconl Stein, Dan Sugalski, Leopold Tötsch, Nathan Torkington and
Larry Wall for their books and/or their modules. To the Perl Community.
11
Erratas
Por favor, ayudanos a mejorar. Haznos saber cualquier errata que encuentres relativa a los docu-
mentos que componen este material didáctico ([email protected]).
12
Como Obtener Estos Apuntes
http://nereida.deioc.ull.es/~lhp/perlexamples/perlexamples.ps
http://nereida.deioc.ull.es/~lhp/perlexamples/
13
Prólogo
Perl (Practical Extraction and Report Language) es un lenguaje interpretado que permite la mani-
pulación de ficheros de texto y procesos. Perl propociona funciones muy potentes para la manipulación
de textos, y en principio se consideró solo para esta tarea. Sin embargo, ha evolucionado tanto que
hoy en dı́a se le considera como una herramienta de programación de internet y de administración de
sistemas y redes Unix.
Algunas caracterı́stas que hacen a Perl popular son:
Reduce el ciclo de programación. Puesto que es interpretado, no es necesario compilar las apli-
caciones y se pueden ejecutar en plataformas diferentes.
Es portable. Existen intérpretes Perl para una gran variedad de sistemas operativos.
La sintaxis es muy similar a la de otros lenguajes como Sed, AWK o C. Existen herramientas
para traducir código Sed y AWK a Perl de manera automática.
Por ser interpretado y no compilado, su velocidad puede ser sustancialmente inferior a la versión
C en algunos casos.
14
Capı́tulo 1
Introducción
http://www.perl.com/pub/language/info/software.html.
4. Cygwin: http://www.cygwin.com/
$perl -v
This is perl, version 5.005_03 built for i386-linux
$ which perl
/usr/bin/perl
El comando whereis puede ser usado para encontrar los binarios, fuentes y páginas del manual de
Perl:
15
Ejecución de un Programa Perl
Edita y guarda el fichero hola.pl. Cambia los permisos de ejecución:
>vi hola.pl
> ... salimos y salvamos ...
>cat hola.pl
#!/usr/bin/perl -w
print "hola!\n";
>
>chmod a+x hola.pl
La primera lı́nea, comenzando por #! indica al intérprete de comandos en que lugar se encuentra el
interprete para este guión, esto es, donde se encuentra perl. La opción -w (la w viene de warnings) le
indica al intérprete perl que debe advertirnos de cualquier potencial error que detecte. La conducta
de Perl es, en general, permisiva con el usuario. Es conveniente hacer uso siempre de esta opción.
Ahora ya puedes ejecutar tu primer programa Perl:
>./hola.pl
$ perl -w hola.pl
hola!
1 $ perl -d hola.pl
2
3 Loading DB routines from perl5db.pl version 1.25
4 Editor support available.
5
6 Enter h or ‘h h’ for help, or ‘man perldebug’ for more help.
7
8 main::(hola.pl:2): print "hola!\n";
9 DB<1> h
10 List/search source lines: Control script execution:
11 l [ln|sub] List source code T Stack trace
12 - or . List previous/current line s [expr] Single step [in expr]
13 v [line] View around line n [expr] Next, steps over subs
14 f filename View source in file <CR/Enter> Repeat last n or s
15 /pattern/ ?patt? Search forw/backw r Return from subroutine
16 M Show module versions c [ln|sub] Continue until position
17 Debugger controls: L List break/watch/actions
18 o [...] Set debugger options t [expr] Toggle trace [trace expr]
19 <[<]|{[{]|>[>] [cmd] Do pre/post-prompt b [ln|event|sub] [cnd] Set breakpoint
20 ! [N|pat] Redo a previous command B ln|* Delete a/all breakpoints
21 H [-num] Display last num commands a [ln] cmd Do cmd before line
22 = [a val] Define/list an alias A ln|* Delete a/all actions
23 h [db_cmd] Get help on command w expr Add a watch expression
24 h h Complete help page W expr|* Delete a/all watch exprs
25 |[|]db_cmd Send output to pager ![!] syscmd Run cmd in a subprocess
26 q or ^D Quit R Attempt a restart
16
27 Data Examination: expr Execute perl code, also see: s,n,t expr
28 x|m expr Evals expr in list context, dumps the result or lists methods.
29 p expr Print expression (uses script’s current package).
30 S [[!]pat] List subroutine names [not] matching pattern
31 V [Pk [Vars]] List Variables in Package. Vars can be ~pattern or !pattern.
32 X [Vars] Same as "V current_package [Vars]". i class inheritance tree.
33 y [n [Vars]] List lexicals in higher scope <n>. Vars same as V.
34 For more help, type h cmd_letter, or run man perldebug for all docs.
35 DB<1> l
36 2==> print "hola!\n";
37 DB<1> n
38 hola!
39 Debugged program terminated. Use q to quit or R to restart,
40 use O inhibit_exit to avoid stopping after program termination,
41 h q, h R or h O to get additional info.
42 DB<1> q
43 $
La orden n (lı́nea 37) nos permite ejecutar paso a paso. A diferencia de la orden s, en el caso de
que la instrucción sea una llamada a subrutina, no se entra en la subrutina.
Consulte el libro de Foley y Lester [4] para saber mas sobre el depurador. También podemos consul-
tar http://debugger.perl.org/. En http://refcards.com/docs/forda/perl-debugger/perl-debugger-refcard-a4.pdf
se puede encontrar una página de referencia (PDF).
export MANPATH=$MANPATH:/soft/perl5lib/man/
export PERL5LIB=/soft/perl5lib/lib/perl5:/soft/perl5lib/lib/perl/5.10.0/:/soft/perl5lib/share/
export PATH=.:/home/casiano/bin:$PATH:/soft/perl5lib/bin
Visite esta página de vez en cuando. Es posible que añada algún nuevo camino de búsqueda de
librerı́as y/o ejecutables.
Obsérvese como se han utilizado comillas simples para proteger el programa Perl de un posible
preprocesado por el intérprete de comandos del sistema ( de la shell).
En la jerga Perl a los programas ejecutados con la opción -e se les llama one-liners.
17
Carga de Módulos en One-liners
La opción -M seguida del nombre de un módulo (sin espacios) permite cargar un módulo para
que las funciones proveı́das por el mismo estén disponibles en el one-liner. Por ejemplo, el siguiente
programa:
cpan Bundle::CPAN
que carga el módulo CPAN y llama a la función install con argumento Bundle::CPAN.
Alternativamente, si se dispone de una distribución de Linux Ubuntu se puede instalar uno de los
paquetes libterm-readkey-perl o libterm-readline-gnu-perl.
lhp@nereida:~/Lperl/doc$ perl -d -e 0
Loading DB routines from perl5db.pl version 1.28
Editor support available.
Enter h or ‘h h’ for help, or ‘man perldebug’ for more help.
main::(-e:1): 0
DB<1> p sqrt(16)
4
DB<2> p "hola"x4
holaholaholahola
DB<3> q
Personalización del Depurador El fichero .perldb puede ser utilizado para personalizar el
comportamiento del depurador:
Usando esta definición hacemos que el comando vi ejecute el editor vi. Véase la siguiente sesión:
18
main::(-e:1): 0
DB<1> vi prueba
DB<2> !!cat prueba
use 5.010;
my $alice = ’Bob’;
say "Catastrophic crypto fail!" if $alice == ’Bob’;
DB<3> do prueba
Argument "Bob" isn’t numeric in numeric eq (==) at prueba line 3.
...
Catastrophic crypto fail!
DB<4> vi prueba
DB<5> !!cat prueba
use 5.010;
my $alice = ’Bob’;
say "Catastrophic crypto fail!" if $alice eq ’Bob’;
DB<6> do prueba
Catastrophic crypto fail!
h h
|h h
perldoc print
La Documentación en Perl
Para obtener información sobre un tópico o un módulo Perl utilize perldoc . Si tiene instalado el
módulo Tk::Pod entonces puede utilizar el visualizador gráfico tkpod . Otra posibilidad es arrancar
el programa podwebserver disponible en la distribución del módulo Pod::Webserver. Este programa
arranca un servidor HTTP que sirve la documentación de los módulos instalados en la máquina:
$ podwebserver &
[2] 4069
$ I am process 4069 = perl Pod::Webserver v3.05
Indexing all of @INC -- this might take a minute.
@INC = [ /soft/perl5lib/share/perl/5.8.8/ /soft/perl5lib/lib/perl/5.8.8 /soft/perl5lib/lib/per
Done scanning @INC
You can now open your browser to http://localhost:8020/
19
La fuente de información sobre el lenguaje Perl mas fiable es la que se encuentra en la documen-
tación .pod adjunta con su distribución. Veamos algunos de los parámetros que admite perldoc:
lhp@nereida:~$ perldoc -h
perldoc [options] PageName|ModuleName|ProgramName...
perldoc [options] -f BuiltinFunction
perldoc [options] -q FAQRegex
Options:
-h Display this help message
-V report version
-r Recursive search (slow)
-i Ignore case
-t Display pod using pod2text instead of pod2man and nroff
(-t is the default on win32 unless -n is specified)
-u Display unformatted pod text
-m Display module’s file in its entirety
-n Specify replacement for nroff
-l Display the module’s file name
-F Arguments are file names, not modules
-v Verbosely describe what’s going on
-T Send output to STDOUT without any pager
-d output_filename_to_send_to
-o output_format_name
-M FormatterModuleNameToUse
-w formatter_option:option_value
-X use index if present (looks for pod.idx at /usr/lib/perl/5.8)
-q Search the text of questions (not answers) in perlfaq[1-9]
PageName|ModuleName...
is the name of a piece of documentation that you want to look at. You
may either give a descriptive name of the page (as in the case of
‘perlfunc’) the name of a module, either like ‘Term::Info’ or like
‘Term/Info’, or the name of a program, like ‘perldoc’.
BuiltinFunction
is the name of a perl function. Will extract documentation from
‘perlfunc’.
FAQRegex
is a regex. Will search perlfaq[1-9] for and extract any
questions that match.
Any switches in the PERLDOC environment variable will be used before the
command line arguments. The optional pod index file contains a list of
filenames, one per line.
[Perldoc v3.14]
Hay una serie de documentos que pueden consultarse con perldoc o tkpod. Los mas básicos son:
20
Tutorials
perlfaq
21
perllexwarn Perl warnings and their control
Por último - solo para gurus - estan los documentos que describen las partes internas de Perl:
22
perlxs Perl XS application programming interface
$ perldoc -l perlintro
/usr/share/perl/5.8/pod/perlintro.pod
Si se desea convertir desde pod a algún formato especı́fico, por ejemplo a LATEX basta con usar el
conversor adecuado:
En este caso hemos usado pod2latex. Para poder ejecutar la conversión de .pod a LATEX asegúrese
de tener instalado el módulo Pod::LaTeX (puede obtenerlo desde CPAN).
-o output-formatname
especifica el formato de salida. -oman. Usar -oformatname hace que se carge un módulo para
ese formato.
-M module-name
Especifica el módulo que queremos usar para formatear el pod . La clase debe proporcionar un
método parse from file . Por ejemplo perldoc -MPod::Perldoc::ToChecker.
Se pueden especificar varias clases separándolas por comas o punto y coma: -MTk::SuperPod;Tk::Pod.
-w option:value o -w option
Especifica opciones en la llamada al formateador utilizado. Por ejemplo -w textsize:15 lla-
mará al objeto formateador $formatter->textsize(15) antes de que se use para formatear. La
forma -w optionname es una abreviación para -w optionname:TRUE.
23
$ perldoc -oLaTeX -wAddPreamble:1 -f require > /tmp/require.tex
La opción AddPreamble indica al objeto formateador que se desea generar un documento completo y
no una sección de un documento completo. Después de generar el fichero LaTeX podemos compilarlo
con alguno de los compiladores de latex para producir un dvi o un pdf. Como LATEXno está instalado
en las máquinas de las salas copiamos el fichero a uno de los servidores y compilamos con pdflatex :
casiano@tonga:/tmp$
casiano@tonga:/tmp$ kpdf require.pdf &
Puede que encuentre útil usar la hoja de referencia rápida sobre Perl que se encuentra en http://johnbokma.com/
1.2. Escalares
En español se puede distinguir entre singular y plural. En Perl el singular esta representado por las
expresiones escalares y el plural por las expresiones de tipo lista. Una variable escalar puede almacenar
un número, una cadena de caracteres o una referencia. El valor de una variable escalar puede accederse
a través de su nombre precedido de un $. Las listas, por el contrario, se prefijan de @. Esto contrasta
con el español, que usa el sufijo s para indicar pluralidad. Ejemplos de escalares son:
$days = 5;
$unit = "meters";
$height = 1.50;
Es posible usar el depurador para ver la conducta de Perl con estas sentencias:
$ perl -de 0
Loading DB routines from perl5db.pl version 1.25
Editor support available.
Enter h or ‘h h’ for help, or ‘man perldebug’ for more help.
main::(-e:1): 0
DB<1> $days = 5;
DB<2> x $days
0 5
Los comandos x y p muestran los contenidos de una variable. Perl es un lenguaje en el que el significado
de las frases depende del contexto. El comando x evalúa la expresión en un contexto de lista. Es por
eso que aparece el 0 junto al 5 en la respuesta. El resultado es una lista cuyo primer elemento, el
elemento 0, es 5.
1.2.1. Números
Los números se representan en punto flotante (doble precisión). Asi pues, en Perl no se distingue
entre enteros y flotantes. Perl además, admite el uso de subrayados en las constantes numericas, para
hacerlas mas legibles:
24
$b = 123_456_000_000_000_001;
Aunque en otros lenguajes de programación números y cadenas son cosas bien distintas, en Perl
ambos objetos son escalares.
Perl permite trabajar con números en bases octal (comenzando por 0), hexadecimal (0x) y binario
(0b). Por ejemplo:
main::(-e:1): 0
DB<1> p 0377
255
DB<2> p 0xff
255
DB<3> p 0b1111_1111
255
25
Ejercicio 1.2.1. ¿Que significa la respuesta nan que da el depurador al cálculo de la raı́z cuadrada?
Si se quiere precisión infinita existe un buen número de librerı́as disponibles. Para cálculo con
enteros se puede, ejemplo, usar Math::Pari:
1.2.2. Cadenas
Las cadenas son secuencias de caracteres y pueden contener cualquier combinación de caracteres.
Perl proporciona múltiples formas de entrecomillar cadenas. Las mas comunes implican el uso de
las cadenas de comillas simples y las cadenas de comillas dobles. Por ejemplo:
26
print ’El \’Pescailla\’ y la "Faraona"’; # El ’Pescailla’ y la "Faraona"
Comillas Simples
Mientras que en las comillas dobles ciertas secuencias como \n son interpretadas, en el caso de
las cadenas de comillas simples, cualquier carácter entre las comillas que no sea la comilla simple o
el carácter de escape \ (lo que incluye al retorno de carro si la cadena esta hecha de varias lı́neas)
no recibe interpretación de ningún tipo. Para obtener una comilla simple dentro de la cadena, la
precedemos de un \. Para obtener una \ hay que poner dos sucesivas \\. Por ejemplo:
> cat singlequote.pl
#!/usr/bin/perl -w
$a = ’hola,
chicos’;
print "$a\n";
$a = ’Le llaman \’speedy\’ por que es muy rápido’;
print "$a\n";
$a = ’El último carácter en esta cadena es un escape \\’;
print "$a\n";
Cuando se ejecuta da como resultado:
> singlequote.pl
hola,
chicos
Le llaman ’speedy’ por que es muy rápido
El último carácter en esta cadena es un escape \
>
Ejercicio 1.2.2. ¿Que salida resulta de ejecutar el siguiente programa?
$ cat comillassimples.pl
#!/usr/bin/perl -w
print ’hola\n’;
print "\n";
print ’hola\\n’;
print "\n";
print ’hola\\\’’;
print "\n";
print ’hola\\’’;
Comillas Dobles
En las comillas dobles el carácter de escape permite especificar caracteres de control.
Existe un buen número de secuencias de escape. He aqui algunas:
$a = 1;
print "$a"; # imprime un 1
print ’$a’; # imprime $a
El nombre de la variable es el identificador mas largo que sigue al $. Esto puede ser un problema si lo
que quieres es imprimir el valor de la variable seguido de letras. La solución es envolver el identificador
de la variable entre llaves. Vea el siguiente ejemplo:
27
Código Significado
\a Alarma (beep!)
\b Retroceso
\n Nueva lı́nea
\r Retorno de carro
\t Tabulador
\f Formfeed
\e Escape
\007 Cualquier carácter ASCII en octal
\x7f Cualquier carácter ASCII en hexadecimal
\cC Cualquier carácter de control. Aqui CTRL-C
$p = "pescailla"; $f = "faraona";
print "El \U$p\E y la \u$f\n"; # El PESCAILLA y la Faraona
28
lhp@nereida:~/Lperl/doc$ perl -de 0
main::(-e:1): 0
DB<1> p "Hola\cL\cMJuan\cL\cM"
Hola
Juan
Los operadores q y qq
En Perl las diversas formas de entrecomillados son operadores (de la misma manera que la conca-
tenación, la suma o el producto).
Es posible usar los operadores q y qq para entrecomillar cadenas. El operador q convierte su
argumento en una cadena de comillas simples. El operador qq convierte su argumento en una cadena
de comillas dobles.
Ası́:
Es posible usar delimitadores “pareja” como {}, [], (), etc. En tal caso, Perl lleva la cuenta del
anidamiento de la pareja:
$a = 4;
print qq<No me <<cites>> mas de $a veces!>; #No me <<cites>> mas de 4 veces!
Para sabe mas sobre este tipo de operadores consulte la sección Quote and Quote-like Operators
de perlop (escriba perldoc perlop en la consola).
Operadores de Cadena
El punto concatena y el operador x permite repetir una cadena:
El operador de asignación .= permite asignar a una variable conteniendo una cadena el resultado de
concatenarla con la cadena en el lado derecho.
La función index retorna la posición de una subcadena dentro de una cadena. La función rindex
es similar pero busca desde el final:
29
DB<1> $x = ’hola mundo’
DB<2> p index $x, ’mundo’
5
DB<3> p index $x, ’o’
1
DB<4> p index $x, ’o’, 3
9
DB<5> p rindex $x, ’o’ # busca desde el final
9
DB<6> p rindex $x, ’o’, 4
1
La función reverse invierte los elementos de una cadena:
DB<1> $x = ’hola mundo’
DB<2> $y = reverse $x
DB<3> p $y
odnum aloh
La función length retorna la longitud de una cadena.
DB<1> $x = ’hola mundo’
DB<2> p length $x
10
La función chop elimina el último elemento de una cadena. La función chomp sólo elimina el último
elemento si es un retorno de carro (mas concretamente si es el separador de lectura):
DB<3> chop $x
DB<4> p $x
hola mund
DB<5> chomp $x
DB<6> p $x
hola mund
DB<7> $x .= "\n"
DB<8> p $x
hola mund
DB<9> chomp $x
DB<10> p $x
hola mund
30
El proceso de conversión no reconoce números octales ni hexadecimales. Para convertirlos de-
berá usarse explı́citamente el operador oct:
$n = 0 + "x123"; # no hay número al comienzo $n == 0
$n = 0 + oct("x123"); # $n == 291
La llamada oct expresión devuelve el valor decimal de expresion, interpretado como una cadena
octal. Si la expresion comienza con 0x, la interpreta como una cadena hexadecimal.
Los operadores de bit (como &, |, etc.) son sensibles al contexto. Si es un contexto de cadena,
utilizan como unidad el carácter, mientras que si es un contexto numérico, la unidad es el bit. Veamos
un ejemplo. Para ello usaremos el depurador de Perl que se activa usando la opción -d:
~/perl/src> perl -de 0
Es necesario facilitarle al depurador un programa. Para evitar la molestia de escribir uno, le indicamos
que le pasamos el programa en la lı́nea de comandos. Esto se hace con la opción -e, que debe ir seguida
del programa Perl. En el ejemplo que sigue hemos elegido pasarle como programa uno bastante trivial:
una expresión que es la constante cero:
Forzando Contextos
31
en la mayorı́a de los casos, se actualizan como consecuencia de algún efecto lateral. En un contexto
numérico, la variable escalar $! retorna el valor numérico de la variable del sistema errno, la cual
contiene el último error encontrado durante una llamada al sistema o a ciertas funciones de la biblioteca
estándar C. En un contexto de cadena devuelve la cadena producida por la función perror():
La función open intenta abrir para lectura el fichero con nombre fichero para dejar una descrip-
ción del mismo en la variable $FILE. En el supuesto de que tal fichero no exista, se producirán los
correspondientes mensajes de error almacenados en $!.
Mas potente aún es Contextual::Return de Damian Conway, el cual permite crear variables con-
textuales multitipo con mas de dos tipos:
Si se usa la etiqueta ACTIVE el código de definición será ejecutado cada vez que la variable multi-
estado es evaluada. Por ejemplo:
32
1 #!/usr/local/bin/perl -w
2 use strict;
3 use Contextual::Return;
4
5 my $now = ACTIVE NUM { time } STR { localtime };
6
7 print "Now: $now (".($now+0).")\n";
8 sleep(1);
9 print "Now: $now (".($now+0).")\n";
La Variable $
Probablemente la mas importante de estas variables mágicas es $ . Es la variable de la que se esta
hablando.
Cuando en un constructo que requiere una variable no se especifica de que variable se habla, es
que se está hablando de $_.
Es el equivalente de ”lo que estamos hablando”(it) y por ello establecer su valor se conoce con el
nombre de .establecer el tema”(topicalize).
La variable especial $_ es el argumento por defecto para un gran número de funciones, operadores
y estructuras de control. Asi, por ejemplo:
print;
es lo mismo que
print $_;
Otro ejemplo: La función length devuelve la longitud en caracteres de la expresión que se le pasa
como argumento. Si se omite la expresión usará la variable por defecto $ :
$ perldoc perlvar
Como se señala en dicha documentación el módulo English permite usar un seudónimo para
cada variable mágica. Por ejemplo, el alias de $_ es $ARG :
33
1.3. Variables privadas
Una variable privada o variable léxica se declara usando el operador my:
1 my $a = 4;
2 {
3 my $a = <STDIN>;
4 my $b = <STDIN>;
5 $a = ($a < $b)? $a : $b
6 print "$a\n";
7 }
8 print "$a\n"; # 4
Estas variables tienen como ámbito el bloque que las rodea. Asi la declaración de la variable léxica $a
en la lı́nea 3 oculta la variable léxica declarada en la lı́nea 1. Las modificaciones de $a en el bloque de
las lı́neas 2-7 no afecta a la variable $a declarada en la lı́nea 1.
Si no esta dentro de ningún bloque, el ámbito de la variable será el fichero actual.
$line = <STDIN>;
La variable $line contiene el retorno de carro leı́do. Es por eso que se usa el operador chomp el
cual elimina el retorno de carro final:
Si la lı́nea termina en varios retornos de carro, chomp sólo elimina uno, si no hay no hace nada.
Cuando se alcanza el final del fichero, la lectura devuelve el valor especial undef.
En general un fichero se abre mediante la función open :
34
El operador defined provee el medio para distinguir undef de 0 y de la cadena vacı́a ’’. El operador
defined trabaja con cualquier valor. En versiones previas de Perl se requerı́a que el argumento fuera
un lvalue, una expresión que se pueda interpretar como la parte izquierda de una asignación.
The conditional directives —if, unless, and the ternary conditional operator— all evaluate
an expression in boolean context.
As comparison operators such as eq, ==, ne, and != all produce boolean results when
evaluated.
Empty hashes and arrays evaluate to false.
Perl 5 has no single true value, nor a single false value.
Any number that evaluates to 0 is false. This includes 0, 0.0, 0e0, 0x0, and so on.
The empty string (’’) and ’0’ evaluate to false, but the strings ’0.0’, ’0e0’, and so on do
not.
The idiom ’0 but true’ evaluates to 0 in numeric context but evaluates to true in boolean
context, thanks to its string contents.
Both the empty list and undef evaluate to false.
Empty arrays and hashes return the number 0 in scalar context, so they evaluate to false
in boolean context.
An array which contains a single element — even undef — evaluates to true in boolean
context.
A hash which contains any elements — even a key and a value of undef — evaluates to
true in boolean context.
35
DB<11> p $a or $b
4
Toda la evaluación se hace en circuito corto. Ası́ $a and $b es $a si $a es falso, si no da como resultado
$b. Lo mismo es cierto para $a && $b.
Ejercicio 1.4.1. ¿Porqué p $a and $b produce como salida 4? ¿Es ese realmente el resultado de la
evaluación de la expresión? Antes de contestar estudie este experimento:
DB<1> $a = 4; $b = "hola";
DB<2> $d = (print $a and $b)
4
DB<3> p $d
hola
DB<4> $e = print $a and $b
4
DB<5> p $e
1
DB<6> $d = (print $a && $b)
hola
DB<7> p $d
1
Para saber en que forma esta parentizando Perl un programa puede usarse el módulo B::Deparse
con el siguiente formato:
36
Comparación Numerico Cadena
Igual == eq
distinto != ne
Menor que < lt
mayor que > gt
Menor o igual que <= le
Mayor o igual que >= ge
Comparación <=> cmp
Los operadores de comparación de cadenas no deberı́an usarse para números, salvo que sea eso preci-
samente lo que se quiere (esto es, 10 va antes de 2). Analogamente, los operadores de comparación de
números no deberı́an usarse para cadenas:
if
if ( condition ) {
...
} elsif ( other condition ) {
...
} else {
...
}
que es equivalente a:
37
Forma Sufija
Si no se quieren escribir las llaves se puede usar la notación sufija de la sentecia:
# la forma tradicional
if ($zippy) {
print "Yow!";
}
# al estilo Perl
print "Yow!" if $zippy;
Una expresión puede ir seguida de una sentencia de control que determina su evaluación. Por
ejemplo:
while
while ( condition ) {
...
}
for y foreach
Se puede usar como en C:
O en estilo Perl:
foreach my $i (1..5) {
print "$i ";
}
print "\n";
print "\n";
for my $i (1..5) {
print "$i ";
}
print "\n";
38
Al ejecutarlo produce:
lhp@europa:~/projects/perl/src/perltesting$ ./for.pl
1 2 3 4 5
1 2 3 4 5
1 2 3 4 5
next
El comando next comienza la siguiente iteración del bucle:
last
El comando last produce la salida del bucle:
given
Del libro Modern Perl:
The given construct is a feature new to Perl 5.10. It assigns the value of an expression
to the topic variable and introduces a block:
given ($name) {
...
}
Unlike for, it does not iterate over an aggregate. It evaluates its value in scalar context,
and always assigns to the topic variable:
given (my $username = find_user())
given also makes the topic variable lexical to prevent accidental modification:
39
11 $_ = shift;
12 s/mouse/man/;
13 say "Inside mouse_to_man: $_";
14 }
lhp@nereida:~/Lperl/src/perltesting$ ./given.pl
mouse
Inside mouse_to_man: man
mouse
given is most useful when combined with when. given topicalizes a value within a block so
that multiple when statements can match the topic against expressions using smart-match
semantics.
given . . . when
The when construct can match against many types of expressions including scalars, aggre-
gates, references, arbitrary comparison expressions, and even code references.
40
36 when (/^\s*papel\s*$/) { say "Yo gano!"; break; }
37 when (/^\s*piedra\s*$/) { say "Tu ganas!"; break; }
38 when (/^\s*tijeras\s*$/) { say "Empatados!"; break; }
39 default { say "No te entiendo"; exit(0); }
40 }
41 }
lhp@nereida:~/Lperltesting$ ./given2.pl
¡Piedra, Papel, Tijeras!. Elije:
piedra papel
Elijo tijeras. No te entiendo
lhp@nereida:~/Lperltesting$ ./given2.pl
¡Piedra, Papel, Tijeras!. Elije:
piedra papel
Elijo piedra. No te entiendo
lhp@nereida:~/Lperltesting$ ./given2.pl
¡Piedra, Papel, Tijeras!. Elije:
piedra papel
Elijo piedra. No te entiendo
lhp@nereida:~/Lperltesting$ ./given2.pl
¡Piedra, Papel, Tijeras!. Elije:
piedra papel
Elijo piedra. No te entiendo
lhp@nereida:~/Lperltesting$ ./given2.pl
¡Piedra, Papel, Tijeras!. Elije:
piedra papel
Elijo papel. Yo gano!
piedra papel
¡Piedra, Papel, Tijeras!. Elije:
Elijo piedra. No te entiendo
Switch
Use el módulo Switch (no es necesario en las versiones posteriores a la 5.10):
my $val = <>;
chomp($val);
switch ($val) {
41
case 1 { say "number 1" }
case "a" { say "string a" }
case [1..10,42] { say "number in list" }
case /^\w+$/ { say "pattern" }
else { say "previous case not true" }
}
lhp@europa:~/projects/perl/src/perltesting$ ./switch.pl
5
number in list
lhp@europa:~/projects/perl/src/perltesting$ ./switch.pl
12bc
pattern
lhp@europa:~/projects/perl/src/perltesting$ ./switch.pl
.2.
previous case not true
Para detectar errores sintácticos puede usar la opción -c, la cual le indica a Perl que debe
compilar pero no ejecutar el programa.
Añada:
use strict;
use diagnostics;
al comienzo de su programa. Esto ya activa el modo -w. Vea un ejemplo. Dado el programa:
42
lhp@europa:~/projects/perl/src/perltesting$ perl diag.pl
Illegal division by zero at diag.pl line 6.
Mientras que la ejecución con diagnostics produce una descripción mas profusa:
Si tiene errores en tiempo de ejecución utilice el depurador, para ello use la opción -d de Perl.
Consulte el libro de Foley y Lester [4] para saber mas sobre el depurador. También podemos con-
sultar http://debugger.perl.org/. En http://refcards.com/docs/forda/perl-debugger/perl-debugger-refcard-a4
se puede encontrar una página de referencia (PDF).
Puede instalar y usar perlcritic. Le informará sobre las malas prácticas que aprecia en su pro-
grama. Vea un ejemplo:
La referencia PBP se refiere al libro Perl Best Practices [11] de D. Conway. En la dirección
http://refcards.com/docs/vromansj/perl-best-practices/refguide.pdf Puede encontrar una tar-
jeta de referencia resumen con las 256 PBP del libro.
43
La tabla 1.3 muestra algunos de los metası́mbolos usados en las expresiones regulares. En http://refcards.com/do
puede encontrar un PDF con una hoja de referencia que resume el uso de las expresiones regulares en
Perl.
* El elemento precedente debe aparecer 0 o más veces.
+ El elemento precedente debe aparecer 1 o más veces.
. Un carácter cualquiera excepto salto de linea.
? Operador unario. El elemento precedente es opcional
{n} que coincida exactamente n veces
{n,} que coincida al menos n veces
{n,m} que coincida al menos n veces y no mas de m
| O uno u otro.
^ Comienzo de linea
$ Fin de linea
. Cualquier carácter que no sea el retorno de carro
[...] Conjunto de caracteres admitidos.
[^...] Conjunto de caracteres no admitidos.
- Operador de rango
(...) Agrupación.
\ Escape
\n Representación del carácter fin de linea.
\t Representación del carácter de tabulación.
Para saber más sobre expresiones regulares, estudie el capı́tulo 3. Por ahora nos basta saber que
una expresión regular define un lenguaje. Asi la expresión regular /__END__/ define el lenguaje cuya
única palabra es __END__ y la expresión regular /LHP/ define el lenguaje cuya única palabra es LHP.
El Operador de Binding
Normalmente, con las expresiones regulares se usa El operador de binding =~ , el cuál nos permite
”asociar” la variable con la operación de casamiento/sustitución sobre la expresión regular:
if ($d =~ /esto/) { print "la palabra ’esto’ aparece en: $d\n" }
Si se omiten el operador =~ y la variable entonces se usa la variable por defecto $_:
Clases de Caracteres
A menudo resulta necesario comprobar si la variable contiene una cifra, una vocal, o caracteres de
control particulares. Una clase de caracteres se define mediante el operador [ ]. He aquı́ algunas
posibles construcciones:
44
[0123456789] # Igual [0-9]
[0-9a-z] # Cualquier letra o cualquier numéro
[\~\@;:\^_] # Cualquiera de los caracteres(~,@,;,:^,_)
Se puede definir una clase de caracteres valiéndose de la complementaria mediante el uso del
circumflejo "^":
Perl introduce algunas abreviaturas usadas para algunas de las clases mas comunes:
Código Significado
\d [0-9] dı́gitos del 0 al 9
\D [^0-9] carácter que no sea un dı́gito
\w [a-zA-Z0-9_] carácter alfanumérico
\W [^a-zA-Z0-9_] carácter no alfanumérico
\s [ \t\n\r\f] espacio en blanco
\S [^ \t\n\r\f] carácter que no es un espacio en blanco
45
15 =head1 DESCRIPTION
16
17 Reads file C<file> and prints all the lines containing
18 the string C<LHP> up to a line containing
19 the string C<__END__>. For example given the input file:
20
21 lhp@nereida:~/Lperl/src$ cat -n example1.input
22 1 Esta linea no se imprime
23 2 Esta linea si se imprime LHP
24 3 Esta no
25 4 Esta LHP si
26 5 __END__
27 6 y se acabo LHP
28 7 esta linea tampoco sale
29
30 the program produces the following output:
31
32 lhp@nereida:~/Lperl/src$ example1.pl < example1.input
33 Esta linea si se imprime LHP
34 Esta LHP si
La Documentación
Las lı́neas de la 9 a la 34 documentan el uso del programa mediante un lenguaje de marcas
denominado pod . Podemos ver la documentación con perldoc:
46
EXAMPLE1(1) User Contributed Perl Documentation EXAMPLE1(1)
NAME example1.pl
SYNOPSIS
example1.pl < file
DESCRIPTION
Reads file "file" and prints all the lines containing the string "LHP"
up to a line containing the string
"__END__". For example given the input file:
Para saber mas sobre el sistema de documentación Perl lea la sección 5.18.
Expresiones Regulares
Una expresión entre barras como /__END__/ (lı́nea 2) o /LHP/ (lı́nea 3) es una expresión regular
en Perl.
La condición if (/__END__/) . . . en la lı́nea 2 es cierta si la variable por defecto $_ “casa” con
la expresión regular /__END__/ o, lo que es lo mismo, pertenece al lenguaje descrito por la expresión
regular /__END__/.
Definición 1.8.1. Casar significa que la cadena a la que se le hace el binding, en este caso $_,
contiene en algúna posición una subcadena que pertenece al lenguaje descrito por la expresión regular.
Si se quisiera que solo hubiera casamiento cuando $_ sea exactamente __END__ deberı́amos usar
anclas.
47
Un ancla es un metası́mbolo que casa con una posición. Por ejemplo, el circunflejo ^ es un me-
tası́mbolo que casa con el comienzo de la cadena y el dolar $ casa con el final. Ası́ pues, si la expresión
regular fuera /^__END__$/ estarı́amos forzando a que casar sea equivalente a que la cadena sea exac-
tamente igual a __END__.
El constructo elsif
El constructo elsif nos permite abreviar un else seguido de un if. Asi escribimos:
en vez de:
if (/__END__/) { last; }
else {
if (/LHP/) { print; }
}
Ejecución y Pruebas
Sigue un ejemplo de ejecución. consideremos el fichero de entrada:
Ejecución de la prueba:
48
lhp@nereida:~/src/perl/src$ perl example1.t
1..1
ok 1 - smoke test with example1.input
Antes de seguir, responda a las siguientes preguntas:
Ejercicio 1.8.1. Consideremos el siguiente programa:
lhp@nereida:~/Lperl/src$ cat -n muerte_prematura4.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 print while <STDIN>;
Véase un ejemplo de ejecución:
Aun cuando es adelantar mucho, la expresión $SIG{INT} denota la entrada INT del hash/diccionario
%SIG. Un hash puede verse como una tabla con dos columnas, donde la de la izquierda almacena las
claves y la de la derecha los valores. Una variable hash se prefija con el sı́mbolo %.
49
%a = ( ’x’, 5, ’y’, 3); # llena 2 elementos del hash
Los elementos individuales de un hash se acceden prefijando el nombre del hash de un $ seguido
de la clave entre llaves:
lhp@europa:~/projects/perl/src/perltesting$ ./io.pl
Tu nombre: Juan
Hola Juan
lhp@europa:~/projects/perl/src/perltesting$ ./io.pl
Tu nombre: ¡Que forma tan violenta de terminar!
1. ssh,
2. ssh-key-gen,
3. ssh config,
4. scp,
5. ssh-agent,
6. ssh-add,
7. sshd
50
1.11. Uso de Subversion
Use Subversion: Creación de un Repositorio Parece que en banot esta instalado subversion.
Para crear un repositorio emita el comando svnadmin create:
-bash-3.1$ uname -a
Linux banot.etsii.ull.es 2.6.24.2 #3 SMP Fri Feb 15 10:39:28 WET 2008 i686 i686 i386 GNU/Linux
-bash-3.1$ svnadmin create /home/loginname/repository/
-bash-3.1$ ls -l repository/
total 28
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 conf
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 dav
drwxr-sr-x 5 loginname apache 4096 feb 28 12:09 db
-r--r--r-- 1 loginname apache 2 feb 28 11:58 format
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 hooks
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 locks
-rw-r--r-- 1 loginname apache 229 feb 28 11:58 README.txt
Añadiendo Proyectos
Ahora esta en condiciones de añadir proyectos al repositorio creado usando svn import:
[loginname@tonga]~/src/perl/> uname -a
Linux tonga 2.6.24.2 #1 SMP Thu Feb 14 15:37:31 WET 2008 i686 i686 i386 GNU/Linux
[loginname@tonga]~/src/perl/> pwd
/home/loginname/src/perl
[loginname@tonga]~/src/perl/> ls -ld /home/loginname/src/perl/Grammar-0.02
drwxr-xr-x 5 loginname Profesor 4096 feb 28 2008 /home/loginname/src/perl/Grammar-0.02
[loginname@tonga]~/src/perl/> svn import -m ’Grammar Extended Module’ \
Grammar-0.02/ \
svn+ssh://banot/home/loginname/repository/Grammar
A~
nadiendo Grammar-0.02/t
A~
nadiendo Grammar-0.02/t/Grammar.t
A~
nadiendo Grammar-0.02/lib
A~
nadiendo Grammar-0.02/lib/Grammar.pm
A~
nadiendo Grammar-0.02/MANIFEST
A~
nadiendo Grammar-0.02/META.yml
A~
nadiendo Grammar-0.02/Makefile.PL
A~
nadiendo Grammar-0.02/scripts
A~
nadiendo Grammar-0.02/scripts/grammar.pl
A~
nadiendo Grammar-0.02/scripts/Precedencia.yp
A~
nadiendo Grammar-0.02/scripts/Calc.yp
A~
nadiendo Grammar-0.02/scripts/aSb.yp
A~
nadiendo Grammar-0.02/scripts/g1.yp
A~
nadiendo Grammar-0.02/Changes
A~
nadiendo Grammar-0.02/README
Commit de la revisión 2.
* mkdir /tmp/nombreProyecto
* mkdir /tmp/nombreProyecto/branches
51
* mkdir /tmp/nombreProyecto/tags
* mkdir /tmp/nombreProyecto/trunk
* svn mkdir file:///var/svn/nombreRepositorio/nombreProyecto -m ’Crear el proyecto nombreProye
* svn import /tmp/nombreProyecto \
file:///var/svn/nombreRepositorio/nombreProyecto \
-m "Primera versión del proyecto nombreProyecto"
3 directories, 12 files
[loginname@tonga]~/src/perl/>
[loginname@tonga]~/src/perl/> cd Grammar
[loginname@tonga]~/src/perl/Grammar/> ls -la
52
total 44
drwxr-xr-x 6 loginname Profesor 4096 feb 28 2008 .
drwxr-xr-x 5 loginname Profesor 4096 feb 28 2008 ..
-rw-r--r-- 1 loginname Profesor 150 feb 28 2008 Changes
drwxr-xr-x 3 loginname Profesor 4096 feb 28 2008 lib
-rw-r--r-- 1 loginname Profesor 614 feb 28 2008 Makefile.PL
-rw-r--r-- 1 loginname Profesor 229 feb 28 2008 MANIFEST
-rw-r--r-- 1 loginname Profesor 335 feb 28 2008 META.yml
-rw-r--r-- 1 loginname Profesor 1196 feb 28 2008 README
drwxr-xr-x 3 loginname Profesor 4096 feb 28 2008 scripts
drwxr-xr-x 6 loginname Profesor 4096 feb 28 2008 .svn
drwxr-xr-x 3 loginname Profesor 4096 feb 28 2008 t
Observe la presencia de los subdirectorios de control .svn.
Observe que ya no es necesario especificar el lugar en el que se encuentra el repositorio: esa información
esta guardada en los subdirectorios de administración de subversion .svn
El servicio de subversion parece funcionar desde fuera de la red del centro. Véase la conexión desde
una maquina exterior:
53
A Grammar/scripts/Calc.yp
A Grammar/scripts/Precedencia.yp
A Grammar/scripts/aSb.yp
A Grammar/scripts/g1.yp
A Grammar/Changes
A Grammar/README
Revisión obtenida: 3
Comandos Básicos
Actualizar el proyecto
svn update
Referencias
Consulte
3. Vea los capı́tulos disponibles del libro Subversion in Action de la editorial Manning
En KDE puede instalar el cliente gráfico KDEsvn. Puede encontrar algunas capturas de pantalla
del mismo en http://kdesvn.alwins-world.de/screenshots.
practicas_lhp
|
|-trunk
| |
54
| ‘-area_de_un_circulo
| |
| |- area_de_un_circulo.pl
| |- MANIFEST
| |- Makefile
| |- t- 01area_de_un_circulo.t
| ‘- test.expected
|-branches
|
‘-tags
Cree una copia de trabajo del tronco en un directorio practicas_lhp
MANIFEST es la lista de ficheros que forman parte de la distribución
El programa Makefile debe tener al menos tres objetivos:
• make dist El objetivo dist creará un fichero tar.gz area_de_un_circulo.tar.gz con-
teniendo todos los ficheros de la distribución
• make test El objetivo test ejecutará las pruebas
• make man El objetivo man deberá producir un fichero de manual a partir de la docu-
mentación (véase pod2man). Un ejemplo:
$ pod2man area_de_un_circulo.pl > ./docs/man1/area_de_un_circulo.1
Ahora podemos ver el manual con el comando:
$ man -Mdocs area_de_un_circulo
my $a =4;
{
my ($b, $c) = ("a", 9.2);
$a = $b + 2*$c;
}
print $a;
Una variable declarada con my tiene por ámbito el del bloque en que fué declarada o el fichero
si esta fuera de bloques.
5. Constantes: Para definir la constante π use el módulo Math::Trig
6. Compruebe el comportamiento de su programa contra entradas
a) Valores normales: 1, 4.5, -3.2
b) Diferentes formatos de flotante: 1e2, 1.0e-1, etc.
c) Cero
d) Negativas
e) Cadenas de caracteres: la cadena vacı́a, cadenas de caracteres que contienen números, p.
ej. "one 1.0 two 2.0", etc.
55
El operador ..
El operador .. toma dos numeros x1 y x2 y devuelve una lista con los enteros entre esos dos
números:
DB<1> @b = 4..8
DB<2> p @b # Los elementos son mostrados sin separación
45678
DB<3> p "@b" # Interpolación: los elementos son separados
4 5 6 7 8
DB<4> x @b
0 4
1 5
2 6
3 7
4 8
DB<5> @a = 2.1 .. 4.1
DB<6> x @a
0 2
1 3
2 4
DB<8> x @c
empty array
foreach my $i (0..5) {
print $days[$i]," es un dı́a laboral/n";
}
foreach (0..5) {
print $days[$_]," es un dı́a laboral/n";
}
Troceado de arrays
Un slice o trozo de un array resulta de indexar el array en un subconjunto de ı́ndices:
56
La función reverse
El operador reverse toma los elementos de una lista y devuelve la lista en orden inverso:
DB<1> @a = 1..10
DB<2> @a = reverse @a
DB<3> p "@a"
10 9 8 7 6 5 4 3 2 1
En un contexto escalar, cuando reverse es aplicado a una lista devuelve la cadena resultante de
concatenar los elementos de la lista en orden inverso:
DB<6> @a = 1..10
DB<7> $b = reverse @a
DB<8> p $b
01987654321
DB<9> @a = qw{one two three}
DB<10> $x = reverse @a
DB<11> p $x
eerhtowteno
DB<29> q
lhp@nereida:~/public_html/cgi-bin$ perl -wde 0
main::(-e:1): 0
DB<1> $a = "dabale arroz a la zorra el abad"
DB<2> p scalar(reverse($a))
daba le arroz al a zorra elabad
DB<3> p reverse($a)
dabale arroz a la zorra el abad
Tenga cuidado si esta trabajando en un entorno que usa UTF-8. En ese caso reverse puede
producir un resultado erróneo:
Para solucionarlo es necesario usar el módulo utf8 y poner el fichero de salida en modo :utf8:
lhp@nereida:~/Lperl/src$ reverse.pl
daba le arroz al a zorra elabád
57
Dinamicidad de los Arrays
Una asignación a un elemento que no existe lo crea:
$days[9] = 10;
Los elementos extra $days[6], $days[7] and $days[8] son asi mismo creados y se inicializan al
valor indefinido undef .
Indices Negativos
En Perl, los ı́ndices negativos cuentan desde el final del array (-1 es el último elemento).
DB<1> @a = 1..10
DB<2> $a[-1] = 1
DB<3> p "@a"
1 2 3 4 5 6 7 8 9 1
DB<4> $a[-20] = 0
Modification of non-creatable array value attempted,
subscript -20 at (eval 18)
[/usr/share/perl/5.6.1/perl5db.pl:1521] line 2.
DB<5> p $#a
9
my $i = 0;
while ($i < @days) {
print $days[$i++],"\n";
}
La función scalar
La función scalar fuerza un contexto escalar:
DB<1> @a = 0..9
DB<2> p "@a\n"
0 1 2 3 4 5 6 7 8 9
58
Asignación a Listas y Asignación a Arrays
Se puede asignar listas de valores a listas de variables:
Sin embargo si la parte izquierda es un array, la variable se hace igual a la lista que esta en la parte
derecha:
DB<9> @b = reverse @a
DB<10> x @b
0 3
1 2
2 1
El Operador qw
Una abreviación muy cómoda para crear listas de cadenas la proporciona el operador qw. Una
cadena formada por palabras separadas por espacios en blanco dentro de qw se descompone en la lista
de sus palabras.
Observa que no se ponen comas entre las palabras. Si por error escribieras:
59
Un Array No Inicializado es Vacı́o
Los escalares no inicializados tienen el valor undef. Sin embargo, las variables array no inicializadas
tienen como valor la lista vacı́a (). Si se asigna undef a una variable de tipo array lo que se obtiene
es una lista cuyo único elemento es undef:
El modo mas simple de evitar este tipo de problemas cuando se quiere limpiar una variable de tipo
array es asignar explı́citamente la lista vacı́a ():
@a = ();
if (@a) { ...}; # scalar(@a) == 0 es FALSE
undef @a;
if (defined(@a)) { ... }; # FALSE
$a[3] = undef;
O bien usando la lista vacı́a, como se hace en la siguiente sesión con el depurador:
DB<1> @a = 0..9
DB<2> @a[1,5,7] = ()
DB<3> p "@a"
0 2 3 4 6 8 9
DB<4> p $#a
9
DB<5> p scalar(@a)
10
DB<6> $" = "," # separador de output de arrays
DB<7> p "@a"
0,,2,3,4,,6,,8,9
DB<8> @a[1..7] = ()
DB<9> p "@a"
0,,,,,,,,8,9
Interpolación de arrays en cadenas Al igual que los escalares, los valores de un array son
interpolados en las cadenas de comillas dobles. Los elementos son automáticamente separados mediante
el separador de elementos de un array que es el valor guardado en la variable especial $". Esta variable
contiene un espacio por defecto.
DB<1> @a = 0..9
DB<2> print "@a 10\n"
0 1 2 3 4 5 6 7 8 9 10
DB<3> $email = "[email protected]"
DB<4> print $email
casiano.es
DB<5> $email = ’[email protected]’
DB<6> print $email
[email protected]
DB<7> $email = "casiano\@ull.es"
DB<8> print $email
[email protected]
60
Un único elemento de un array es reemplazado por su valor. La expresión que aparece como ı́ndice
es evaluada como una expresión normal, como si estuviera fuera de una cadena:
DB<1> @a = 0..9
DB<2> $i = 2
DB<3> $x = "$a[1]"
DB<4> p $x
1
DB<5> $x = "$a[$i-1]"
DB<6> p $x
1
DB<7> $i = "2*4"
DB<8> $x = "$a[$i-1]"
DB<9> p $x
1
Ejercicio 1.13.1. ¿Cuál es la explicación para la salida de p $x en el último ejemplo?
Ejercicio 1.13.2. Dado la siguiente sesión con el depurador
nereida:~/perl/src> perl -de 0
main::(-e:1): 0
DB<1> @a = 1..5
DB<2> ($a[0], $a[1]) = undef
DB<3> p "@a"
¿Cuál es la salida de la impresión que aparece al final de la secuencia de comandos?. ¿Se produce
alguna dvertencia? ¿Que ocurre si se cambia la última lı́nea por esta otra?
Bucles sobre arrays: el Indice como alias Una diferencia del bucle for de Perl con el de C es
que el ı́ndice del bucle constituye un ”alias” del correspondiente elemento del array, de modo que su
modificación conlleva la modificación del elemento del array. Consideremos el siguiente código:
lhp@nereida:~/Lperl/src$ cat -n foreach1.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 my @list = 1..10;
5 foreach my $n (@list) {
6 $n *= 2;
7 }
8 print "@list\n";
su ejecución produce la siguiente salida:
lhp@nereida:~/Lperl/src$ foreach1.pl
2 4 6 8 10 12 14 16 18 20
61
grep recibe como primer argumento una expresión regular y como segundo una lista. En Perl las
expresiones regulares se escriben entre barras de división. La notación \d casa con cualquier dı́gito
decimal. Ası́ pues el cierre positivo casará con cualquier número entero sin signo. Para saber más sobre
expresiones regulares, estudie el capı́tulo 3.
El siguiente código corresponde a la segunda forma de uso: guarda en $n_undef el número de items
undef en el array v
Obsérvese que en esta segunda forma de uso no hay coma de separación entre el bloque y el array.
En general, el primer argumento de grep es un bloque en el cual se utiliza $_ como referente a cada
elemento de la lista, y se devuelve un valor lógico. Los restantes argumentos constituyen la lista de
items sobre la que hay que buscar. El operador grep evalúa la expresión una vez para cada item en
la lista, como si se tratara de un bucle foreach. Cuando el bloque esta constituido por una única
expresión se puede escribir simplemente la expresión, separándola mediante una coma de la lista.
Ejercicio 1.13.3. Explique la interpretación que hace Perl de los siguientes fragmentos de código.
Consulte 1.13.2 y el manual de Perl para entender el uso de la función grep:
@b = grep {not $_ % 5} @s;
El operador map
Si lo que se quiere es construir un array transformado del array inicial, se debe usar map:
El operador -s retorna el tamaño de un fichero. Este código genera un array conteniendo los tamaños
de los ficheros especificados en @files. La forma de uso anterior utiliza la sintáxis map bloque array.
Observe la ausencia de coma.
El operador glob produce una lista con los ficheros descritos por la expresión shell que se le
pasa como argumento (vea perldoc -f glob). La expresión regular /(\d+)/ casa con números
enteros sin signo. Al estar paréntizada la cadena que ha casado queda automáticamente guardada
en la variable especial $1. El valor devuelto por un bloque es la última sentencia evaluada, en
este caso es el valor de $1.
Cuando un casamiento con una expresión regular tiene lugar en un contexto de lista se devuelve
una lista con las cadenas que han casado con los paréntesis:
62
DB<0> x glob(’/tmp/*.pl’)
0 ’/tmp/cgisearch.pl’
1 ’/tmp/ficha.pl’
2 ’/tmp/uploadpractica.config.pl’
3 ’/tmp/uploadpractica.pl’
DB<1> x map { m{/([^u/]+)$}; $1 } glob(’/tmp/*.pl’)
0 ’cgisearch.pl’
1 ’ficha.pl’
2 undef
3 undef
DB<2> x grep { defined} map { m{/([^u/]+)$}; $1 } glob(’/tmp/*.pl’)
0 ’cgisearch.pl’
1 ’ficha.pl’
2. La expresión
produce el mismo resultado que la anterior. ¿Porqué? ¿Que retorna cada una de las evaluaciones
individuales del bloque? ¿En que contexto lista o escalar se evalúa /(\d+)/?
Observe este comando en el depurador:
push(ARRAY,LIST)
empuja el valor de LIST en el ARRAY. La longitud del ARRAY se incrementa en la longitud de LIST.
Es lo mismo que hacer:
pop(ARRAY)
pop ARRAY
63
Si no hay elementos en ARRAY, devuelve el valor undef.
Las funciones shift y unshift actuán de manera similar a push y pop pero utilizando el comienzo
de la lista en vez del final de la misma.
Las funciones push, pop, shift y unshift son un caso particular de la función splice , la cual
cambia los elementos de un ARRAY. La función splice toma 4 argumentos: el ARRAY a modificar,
el ı́ndice OFFSET en el cual es modificado, el número de elementos a suprimir LENGTH y la lista de
elementos extra a insertar.
splice(ARRAY,OFFSET,LENGTH,LIST)
splice(ARRAY,OFFSET,LENGTH)
splice(ARRAY,OFFSET)
La función splice devuelve los elementos suprimidos del ARRAY. Si se omite LENGTH se suprime
todo desde OFFSET hacia adelante. Se cumplen las siguientes equivalencias
push(@a,$x) splice(@a,$#a+1,0,$x)
pop(@a) splice(@a,-1)
shift(@a) splice(@a,0,1)
unshift(@a,$x) splice(@a,0,0,$x)
$a[$x] = $y splice(@a,$x,1,$y);
No se puede acortar un array asignando undef a sus elementos del final. Para acortarlo se debe
asignar $#a o utilizar un operador como pop ó splice.
@a = 1 .. 10;
$a[9] = undef; # @a = (1 ..9, undef)
$x = pop @a; # @a = (1 ..9)
splice @a, -2; # @a = (1 ..7) OFFSET = -2. Como se ha suprimido
# LENGTH, se suprime desde el penúltimo elemento
# hacia adelante
$#a = 4; # @a = (1 ..5)
La Función join
La función join convierte un arreglo en un escalar La llamada a join EXPR, LIST concatena las
cadenas en LIST separadas por EXPR:
@a = (’a’..’e’);
$a = join ":", @a # $a queda con "a:b:c:d:e";
La Función split
La función split es la inversa de join : convierte un escalar en un arreglo. split /PATTERN/,EXPR,LIMIT
retorna el arreglo resultante de partir la cadena EXPR. Si se especifica LIMIT indica el número máximo
de campos en los que se divide.
64
DB<5> x @a
0 ’a’
1 ’b’
2 ’c:d:e’
DB<6> $a = ’a:b:c:d::’
DB<7> @a = split /:/, $a
DB<8> x @a # Los nulos finales se suprimen
0 ’a’
1 ’b’
2 ’c’
3 ’d’
DB<9> @a = split /:/, $a, -1
DB<10> x @a # No si LIMIT es negativo
0 ’a’
1 ’b’
2 ’c’
3 ’d’
4 ’’
5 ’’
Observe que en split el primer parámetro es una expresión regular mientras que en join es un
carácter.
La función sort
El operador sort toma una lista de valores y los ordena según el alfabeto ASCII. Por ejemplo:
El Bloque de Comparación
La función sort admite como primer argumento un bloque que determina la función de compara-
ción. Dicho bloque depende de dos variables ”especiales” a y b .
El valor retornado por un bloque es el valor asociado con la última sentencia del bloque. En
este caso sort espera que el bloque devuelva -1, 0 ó 1. Dicho valor es utilizado como elemento de
comparación. Por ejemplo:
65
DB<5> @a = sort { $a <=> $b } @a;
DB<6> p "@a"
-1 4 7 9 12
DB<7> @a = sort { $b <=> $a } @a;
DB<8> p "@a"
12 9 7 4 -1
Ejemplo de Ordenación
El siguiente ejemplo ordena los usuarios de un sistema unix atendiendo a su uid:
En la lı́nea 3 se obtiene en @user la lı́sta de lı́neas no comentadas en /etc/passwd. en las lı́neas 6-8
se inicializan los arrays @name y @uid a los nombres (login) y uid de los usuarios (campos primero y
tercero de /etc/passwd). Las lı́neas 10-12 ordenan primero la lista 0..$#name de acuerdo con el valor
de uid. El nuevo conjunto ordenado de ı́ndices es utilizado para reindexar el array name.
El operador sort en un contexto escalar devuelve undef.
DB<1> use List::Util qw(first max maxstr min minstr reduce shuffle sum)
DB<2> @a = map { int(rand 20) } 1..5
DB<3> x @a
0 8
1 9
2 4
3 16
4 5
DB<4> x min @a
0 4
DB<5> x max @a
0 16
DB<6>x first { $_ > 8 } @a
0 9
DB<7> x sum @a
0 42
DB<8> x shuffle @a
66
0 8
1 5
2 16
3 4
4 9
DB<9> x reduce { $a." $b" } @a
0 ’8 9 4 16 5’
Existe también un módulo List::MoreUtils que provee mas funciones para el manejo de listas. He
aqui un fragmento de la sección SYNOPSIS de la documentación:
use List::MoreUtils qw(any all none notall true false firstidx first_index
lastidx last_index insert_after insert_after_string
apply after after_incl before before_incl indexes
firstval first_value lastval last_value each_array
each_arrayref pairwise natatime mesh zip uniq minmax);
Veamos algunos ejemplos de uso de List::MoreUtils :
lhp@nereida:~/Lperl/src/XSUB/h2xsexample/Coord/script$ perl -de 0
main::(-e:1): 0
DB<1> use List::MoreUtils qw(:all) # Importar todas las funciones
DB<2> @a = (1..8,-2,-3)
DB<4> print "@a" if any { $_ > 0 } @a
1 2 3 4 5 6 7 8 -2 -3
DB<5> print "@a" if all { $_ > 0 } @a
DB<6> print (false {$_ > 0} @a),"\n" # Número de elementos para los que es falsa la condició
2
DB<7> print (firstidx {$_ < 0} @a),"\n"
8
DB<8> @a = (1..3,2..5,3..6)
DB<9> x uniq(@a) # Array con los elementos distintos
0 1
1 2
2 3
3 4
4 5
5 6
DB<8> @a = 1..5; @b = ’a’..’e’; @c = 10..14
DB<9> x mesh @a, @b, @c # Mezcla los 3 arrays
0 1
1 ’a’
2 10
3 2
4 ’b’
5 11
6 3
7 ’c’
8 12
9 4
10 ’d’
11 13
12 5
13 ’e’
14 14
67
El Array Especial @ARGV
El array especial @ARGV contiene la lista de argumentos del programa.
El array @ARGV es usado como array por defecto cuando se realiza una operación de arrays dentro
del programa principal:
La variable especial $^
X contiene el nombre del ejecutable Perl que esta siendo utilizado.
La variable $0 contiene el nombre del ejecutable. Si el programa fué ejecutado con la opción
-e command su nombre es -e.
no produce una jerarquı́a de listas, sino que la lista es aplanada y es lo mismo que si nunca se
hubieran puesto los paréntesis. Esto es, es equivalente a:
1.13.3. Ejercicios
Contextos
¿En que contexto (escalar o lista) se evalúa algo en las siguientes expresiones?
$f = algo;
@p = algo;
($w, $b) = algo;
($d) = algo;
$f[3] = algo;
123 + algo;
push @f, algo;
algo + 456;
foreach $f (algo) { ... }
if (algo) { ... }
sort algo
68
while (algo) { ... }
reverse algo;
$f[algo] = algo;
@w = undef;
$a = @w;
print "$a\n";
Elemento o Trozo
¿Que es @a[1]? ¿Un elemento de un array o un trozo (slice) de un array?
¿En que contexto se interpreta el array @text que aparece dentro del corchete? ¿Escalar o lista?
Respuesta:
El array text entre corchetes es interpretado en un contexto de lista, pues no dice $text[@text],
dice @text[@text]. El resultado es un trozo con 5 elementos. Por tanto dice: @text[(1, 2, 3, 4, 5)] = 0.
Asi pues, text[0] permanece no modificado y es 1, text[1] es igualado a 0 y los restantes miembros
quedan undef ya que la parte derecha es una lista que contiene un sólo elemento.
69
La Lectura en un Contexto de Lista
En un contexto de lista el operador <STDIN> lee todas las lı́neas restantes en la entrada y las
almacena en una lista. Por ejemplo:
Leemos y almacenamos las lı́neas en el array @x. La orden chomp Elimina todos los separadores de
registro de todas las lı́neas en la lista.
grep
Explique la interpretación que hace Perl del siguiente fragmento de código. Consulte 1.13.2 y el
manual de Perl para entender el uso de la función grep. Lea el capı́tulo 3 para saber mas sobre
expresiones regulares:
@a = grep /\bjose\b/, <>;
En la expresión regular \b es un ancla: casa con una frontera (boundary) de palabra. Asi pues,
josefa y quejose no casan con /\bjose\b/.
70
DB<1> open $F, "prueba.txt" # Abrimos fichero
DB<2> @a = <$F> # Leemos el fichero en un contexto de lista
DB<3> x @a # Que hay en @a?
0 1
1 2
2 3
3 4
4 5
5 6
6 7
71
5 ’B’
6 ’b’
DB<7> x sort { uc($a) cmp uc($b) } @a # perldoc -f uc
0 ’A’
1 ’a’
2 ’B’
3 ’B’
4 ’b’
5 ’C ’
6 ’d’
@a = ’A’..’Z’ # A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
La siguiente sesión con el depurador muestra algunos de los problemas que pueden surgir asi como
algunas de las posibles soluciones:
main::(-e:1): 0
DB<1> print (undef, ’A’..’Z’)[@ARGV]
72
syntax error at (eval 5)[/opt/local/lib/perl5/5.8.9/perl5db.pl:638] line 2, near ")["
DB<2> print ((undef, ’A’..’Z’)[@ARGV])
DCBA
4 5 2 + *
Lea la cadena. Una lı́nea como esta la obtendrá de la lı́nea de comandos o bien desde STDIN:
recuerde proteger con comillas simples el argumento si lo provee desde la lı́nea de comandos:
Convierta la cadena en una lista de tokens usando split. Recuerde que la expresion regular \s
casa con cualquier blanco Puede consultar la tabla en la sección 3.6.
El ancla \b casa con una frontera de palabra. ¿Es suficiente con este arreglo?.
73
Observe el comportamiento del split cuando la entrada contiene errores:
Los operadores binarios extraen dos elementos y empujan el resultado. Compruebe que el ope-
rador binario es uno de los esperado: ([-+*/]).
when (m{^[-+/*]$}) {
my $b = pop @stack or die("Missed arg for ’$_’!\n");
...
next;
}
Tenga cuidado de reconocer los números con la expresión regular apropiada. Use el operador =~
si es necesario.
Recuerde que las expresiones lógicas se evalúan en circuito corto (véase la sección 1.4.1). La fun-
ción die puede utilizarse para abortar la ejecución de un programa. Observe estos dos ejemplos
de uso:
Utilice la función eval. Esta función recibe una cadena y evalúa la expresión Perl que representa.
Vea un ejemplo de uso con el depurador:
DB<1> $x = ’4 + 2’
DB<2> p $x
4 + 2
DB<3> $y = eval($x)
DB<4> p $y
6
DB<5> p eval(’3+$y’)
9
DB<6> $a = 4
DB<7> $b = 5
DB<8> $op = ’*’
DB<9> print "$a $op $b"
4 * 5
DB<10> print eval "$a $op $b"
20
74
Para saber mas sobre la función eval consulte la documentación escribiendo perldoc -f eval.
Recuerde que * y + son metası́mbolos dentro de una expresión regular pero no dentro de una
clase.
La variable especial $& almacena la cadena que casó con la última expresión regular usada, $1,
$2, etc. contienen las cadenas que casaron con los paréntesis que aparecen en la expresión regular.
Véase también
1.14. Hashes
Un hash puede verse como una tabla con dos columnas, donde la de la izquierda almacena las
claves y la de la derecha los valores. Una variable hash se prefija con el sı́mbolo %.
Aqui x e y son las claves del hash y 5 y 3 los respectivos valores. El elemento indexado en x de %a
es 5 y el elemento indexado en y de %a es 3.
Las claves deben ser únicas, aunque los valores pueden estar duplicados. Los valores de un hash
deben ser escalares, pero las claves han de ser cadenas. Si no lo fueran se producirá la conversión
pertinente.
75
1.14.2. El operador flecha grande
Cuando se tiene una lista con un número grande de pares clave/valor, es fácil perder la pista de
que elementos son claves y cuales valores. Perl proporciona el operador => que puede ser utilizado
en lugar de la coma. Además trata el identificador a su izquierda como si estuviera entrecomillado.
Asi pues, es posible escribir:
La diferencia entre el operador coma y el operador => es que el argumento en el lado izquierdo de
=> se trata siempre como una cadena. Por ejemplo:
%n = %a;
En un contexto de lista, el valor de un hash es la lista formada por sus pares clave-valor. En este
ejemplo en la expresión (%a, %c) los hashes son automáticamente convertidos en listas:
%a = ();
if (keys %a) { ... } # FALSE
76
1.14.4. Troceado de un hash
Es posible seleccionar un sub-hash utilizando la notación que aparece en este ejemplo:
hp@nereida:~/public_html/cgi-bin/tt$ perl -wde 0
main::(-e:1): 0
DB<1> %a = (x => 5, y => 3, z => "woof", t => undef)
DB<2> print @a{"y", "z"}
3woof
DB<3> print @a{qw(y z) }
3woof
DB<4> print @a{y, z}
Transliteration pattern not terminated at (eval 8)
Dos observaciones:
1. Puesto que la lista de claves no es un único identificador se hace conveniente el uso de comillas
2. El prefijo del trozo de un hash es @. El sı́mbolo % se reserva para el hash completo. Un trozo de un
hash es la lista de valores para las claves especificadas. No es un escalar (hay varios elementos)
ni un hash (no ha claves)
Observe también el sı́mbolo al comienzo de la variable (dolar o arroba) determina el contexto
(escalar o lista) en el que se evalúa la expresión que indexa.
Al usar un trozo de un hash en el lado izquierdo se obtiene el efecto esperado:
La asignación @a{"y", "z"} = ("andale, andale!", 7.2) es equivalente a la asignación ($a{y}, $a{z}) =
Algunos otros ejemplos:
@char_num{’A’..’Z’} = 1..26; # crea un hash con claves ’A’ .. ’Z’
@old{keys %new} = values %new; # Escribe/extiende los contenidos de %new sobre %old
delete @name{keys %invalid}; # Suprime los elementos en %invalid de %name
Reflexione: ¿Porqué @name en el último ejemplo?
%r = reverse %h;
En el hash r las claves de h son los valores y viceversa. Si los valores de h no fueran únicos, en r
”triunfarı́a” como clave algun valor de h, si bien cuál, depende de la implementación particular que la
versión de Perl haga del hash.
77
Si sólo quisieramos los valores escribiremos:
3. Si each ha iterado sobre todos los elementos del mismo, alcanzando el final del hash.
Sin embargo, almacenar una nueva pareja clave-valor durante la iteración no necesariamente reinicializa
el iterador. Incluso si se inicializa un trozo:
78
lhp@nereida:~/public_html/cgi-bin/tt$ perl -wde 0
main::(-e:1): 0
DB<1> %a = (juan=>5, pedro=>0, marta=>10)
DB<2> $n = each %a; print $n
pedro
DB<3> @a{juan,marta} = (2,3)
DB<4> $n = each %a; print $n
marta
DB<5> x %a
0 ’pedro’
1 0
2 ’marta’
3 3
4 ’juan’
5 2
La Función exists
La función exists devuelve TRUE si el elemento especificado (de un hash o un array) ha sido
inicializado, incluso si el valor correspondiente es undef.
Una diferenciación interesante, especialmente cuando se trata de hashes es entre existencia y
definición:
79
1.14.10. Obtener el Conjunto de Elementos de una Lista
Ya vimos que la función uniq de List::MoreUtils devuelve el conjunto de elementos de una
lista:
my @x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # 1 2 3 5 4
my $x = uniq 1, 1, 2, 2, 3, 5, 3, 4; # 5
Una alternativa para obtener un array @out con los elementos no repetidos de un array @in es utilizar
grep y un hash auxiliar %saw para los elementos ya vistos:
my %saw = ();
@out = grep(!$saw{$_}++, @in);
otra forma de hacerlo (Uno de los lemas de Perl es TIMTOWTDI : There is more than one way to do
it! )
undef %saw;
@saw{@in} = (); # Creamos un hash con claves en @in
@out = keys %saw;
La llamada a lock keys bloque las claves. Sin embargo es posible suprimir claves existentes:
DB<6> delete($h{foo})
DB<7> x %h
0 ’bar’
1 23
DB<8> $h{foo} = 5
80
5 1
6 ’b’
7 2
8 ’d’
9 2
DB<13> $t{x} = 4
Attempt to access disallowed key ’x’ in a restricted hash at (eval 17)[/usr/share/perl/5.8/per
DB<14> unlock_keys(%t)
DB<15> $t{x} = 4
DB<16> x %t
0 ’e’
1 3
2 ’c’
3 1
4 ’a’
5 1
6 ’b’
7 2
8 ’x’
9 4
10 ’d’
11 2
Es posible bloquear los valores de un hash cuyas claves hayan sido bloqueadas:
81
Rodrı́guez González, Andrés & 10’0\\
Rodrı́guez Rodrı́guez, Jorge & 9’2\\
Lea el fichero (para saber como leer un fichero cuyo nombre ha sido pasado en la lı́nea de argumentos léa
la sección 2.1 ) de manera que los elementos queden almacenados en un hash indexado en los nombres
de los alumnos. Ordene el hash en orden decreciente de calificaciones. Observe que las números se han
escrito según la convención española de usar apóstrofes en vez del punto decimal. Una posibilidad es
que utilice el operador de sustitución para convertirlo a punto. Por ejemplo, $a =~ s/’/./ sustituirá el
primer apóstrofe en $a por un punto. También le puede ayudar el uso de paréntesis con memoria en
expresiones regulares. Veamos un ejemplo:
El uso de la expresión regular en la lı́nea 3 en un contexto de lista hace que map construya una lista
de pares (nombre, nota) que es ”enrollada” en el hash %h. Los pares (nombre, nota) son retornados
por la expresión regular al ser el resultado del matching con el primer y segundo paréntesis.
Puede que le convenga leer las secciones 3.1, 3.2 y 3.4 y en particular el uso de paréntesis y el
operador de sustitución en las expresiones regulares.
Para la ordenación repase la sección 1.13.2 y en particular el ejemplo en el que los usuarios del
sistema se ordenan según su uid.
82
1.15. Subrutinas
1.15.1. Definición de subrutinas
Las subrutinas se definen mediante la palabra clave sub seguidas del cuerpo entre llaves. Las
definiciones de subrutina se pueden poner en cualquier lugar del texto pero son siempre definiciones
globales. Para invocar a una subrutina se escribe su nombre, opcionalmente precedido de un &. Como
ocurre en C las subrutinas son objetos globales, visibles desde cualquier punto del programa. Sin
embargo se permite ubicarlas en el interior de otra subrutina:
Observe como - a diferencia de lo que ocurre en Pascal - la subrutina submarine es accesible desde
el programa principal:
nereida:~/perl/src> nestedsubs.pl
sub marine 1!
sub submarine 2!
83
Retorno de Valores
El valor retornado puede hacerse explı́cito utilizando el operador return <expresion> el cual
termina la subrutina. Si no se usa, el valor retornado por la subrutina es el valor de la última expresión
computada.
return EXPR Returns from a subroutine, eval, or do FILE with the value given in
<expresion>. Evaluation of <expresion> may be in list, scalar, or void context, depending
on how the return value will be used, and the context may vary from one execution to the
next
Ejemplo
Veamos un ejemplo (el hash %ENV contiene las variables de entorno de la ejecución del programa):
El formato %-${max}s hace que el argumento se alinee a izquierda y se expanda con blancos hasta
max espacios.
Observe como los argumentos en la llamada se han puesto entre paréntesis. Al ejecutar el programa
anterior obtenemos una salida similar a esta:
lhp@nereida:~/Lperl/src$ return.pl
CVSROOT => /var/cvs
HOME => /home/lhp
LANG => es_US.UTF-8
LANGUAGE => es_ES:es:en_GB:en
LESSCLOSE => /usr/bin/lesspipe %s %s
....
84
Uso del prefijo &
Al igual que las variables, las subrutinas tiene un sı́mbolo de prefijo que indica que se trata de
funciones. El nombre ”formal” de una subrutina tiene el prefijo &, el cual puede usarse en la llamada:
Ejercicio 1.15.1. ¿En que contexto se evalúa la llamada al operador diamante en la lı́nea 1 en el
programa anterior?
85
lhp@nereida:~/Lperl/src/testing$ perl readonly.pl
readonly(4)=0
readonly(-8)=8388608
Modification of a read-only value attempted at readonly.pl line 8.
readonly(lonely)=8388608
Modification of a read-only value attempted at readonly.pl line 8.
sub fun1 {
my $a = shift; # shift asume el arreglo @_
my $y = 2 * $a;
return ( $y );
}
# mas tarde ...
$x = fun1 4;
sub checked_inverse {
die "can’t invert 0" if $_[0] == 0;
my $inv = &inverse; # llama a &inverse con los argumentos actuales
die "inversion failed" unless $inv*$_[0] == 1;
return $inv;
}
Observe que esto significa que hay una importante diferencia entre:
goto &inverse;
Que llama a la función y le pasa los argumentos en @_. Pero no se vuelve a la siguiente sentencia
después de la llamada. En vez de eso, goto &inverse reemplaza la llamada a la subrutina actual por
una llamada a inverse. Esta forma especial de llamada se usa principalmente en las subrutinas de
carga automática.
86
nombre. Asi pues una modificación de la variable escalar $a no afecta a la variable array con el mismo
nombre @a.
No siempre puede considerarse que es mal estilo de programación el utilizar esta independencia
en el espacio de nombres para dar a dos variables diferentes el mismo nombre. Obsérvese el siguiente
código que muestra los usuarios de un sistema Unix que casan con la expresión regular dada como
argumento en la lı́nea de comandos:
La llamada a shift de la lı́nea 2 actúa sobre el array especial @ARGV. Este array contiene la lista de
argumentos pasados en la lı́nea de comandos.
El operador -r devuelve TRUE si el fichero con ese nombre es de lectura (lı́nea 6).
package D110;
# ahora estamos en el espacio de nombres D110
# ...salimos del paquete C110
print $C110::a;
# imprime 5
# note como podemos acceder el espacio de nombres C110...
87
# note el $ y los ::
Nombre Completo
Asi pues, para acceder a un identificador situado en un espacio de nombres diferente del actual
debemos prefijar el identificador con el nombre del paquete; esto se denomina especificación completa
del nombre o fully qualifying the name. Si un identificador no está completamente especificado, Perl
lo busca en el package actual.
Al compilar obtenemos errores que indican que $x no ha sido declarada antes del uso:
Para convertirlo en un programa correcto debemos declarar $x antes de su uso, por ejemplo en la lı́nea
3. Vamos, sin embargo, a hacer algo mas extraño:
Sólo hay una variable $x en el paquete actual. La declaración de la lı́nea 5 ”hace visible” $x en las
lı́neas 5-7, pero es la misma variable que se declara en la lı́nea 9. El programa produce la salida:
nereida:/home/lhp/projects/perl/src# our.pl
5
5
88
Los Accesos con Nombre Completo son Aceptados por el Compilador
Otra forma de reconvertir el programa en sintácticamente correcto es hacer uso del nombre com-
pleto de la variable:
Semántica de local
Una aproximación a lo que ocurre cuando se declara una variable local es la equivalencia que se
expresa en la siguiente tabla:
{ {
local($SomeVar); my $TempCopy = $SomeVar;
$SomeVar = ’My Value’; $SomeVar = undef;
... $SomeVar = ’My Value’;
} ...
$SomeVar = $TempCopy;
}
Ejemplo
La diferencia entre variables dinámicas y léxicas deberı́a quedar mas clara observando el siguiente
ejemplo . . .
89
2 use strict;
3
4 our $x;
5
6 sub pr { print "$x\n"; }
7 sub titi { my $x = "titi"; pr(); }
8 sub toto { local $x = "toto"; &pr(); &titi(); }
9
10 $x = "global";
11 &pr();
12 &toto();
13 &titi();
. . . y su ejecución:
> local.pl
global
toto
toto
global
Localización Automática
Ciertas variables son automáticamente declaradas como dinámicas en la entrada a cada bloque,
salvándose ası́ su valor. Es como si, el compilador pusiera una declaración local automáticamente.
Por ejemplo, esto se hace con la variable ı́ndice de un bucle. Esto es lo que permite el anidamiento de
bucles como muestra el siguiente ejemplo:
90
> cat nestedfors.pl
#!/usr/bin/perl -w
for (1..3) {
for (0..4) {
print "$_ ";
}
print ": $_\n";
}
print "-----\n";
for $i (1..3) {
for $i (0..4) {
print "$i ";
}
print ": $i\n";
}
> nestedfors.pl
0 1 2 3 4 : 1
0 1 2 3 4 : 2
0 1 2 3 4 : 3
-----
0 1 2 3 4 : 1
0 1 2 3 4 : 2
0 1 2 3 4 : 3
Dentro de la subrutina, simplemente inicializamos un hash con los contenidos del array @_ resul-
tante. De este modo podemos acceder a los argumentos a través de su nombre, utilizando cada nombre
como clave de entrada en el hash:
sub listdir {
%arg = @_; # convierte la lista de argumentos en un hash
# Utilizamos valores por defecto para los argumentos desaparecidos
$arg{match} = "*" unless exists $arg{match};
$arg{cols} = 1 unless exists $arg{cols};
# etc.
91
Otra ventaja es que, si tenemos varias llamadas que requieren el mismo conjunto de argumentos,
podemos almacenarlos en un hash separado y reutilizarlo:
Esta idea de un conjunto de argumento estándar, sobreescrito por argumentos especificados explı́ci-
tamente puede ser utilizando dentro de una subrutina para simplificar el manejo de los valores por
defecto. Por ejemplo:
sub listdir {
%defaults = (match =>"*", cols=>1, sort_by=>"name");
%arg = (%defaults, @_);
#etc.
}
Una posible optimización es mover el hash defaults fuera de la subrutina listdir, de manera
que su inicialización se haga una sola vez:
sub listdir {
%arg = (%defaults, @_);
#etc.
}
sub fun1 {
$_[0]=7;
# altera el 1er parámetro en el llamador
}
$a = 5;
&fun1($a);
print $a; # imprime: 7
Esta información puede obtenerse mediante la función wantarray . Esta función devuelve:
92
undef si no se esperaba valor,
Podemos utilizar esta información para seleccionar la información apropiada a utilizar en la sen-
tencia return. El siguiente programa suprime los espacios en blanco al comienzo y al final de la
variable:
1 #!/usr/bin/perl -w
2 my @many = (" one ", " two ", " three ");
3 my $string = "\n\nstring\n\n";
4 print "string = $string\n";
5 print "many = (@many)\n";
6 $string = trim($string);
7 @many = trim(@many);
8 print "string = $string\n";
9 print "many = (@many)\n";
10
11 sub trim {
12 my @out = @_;
13
14 for (@out) {
15 s/^\s+//;
16 s/\s+$//;
17 }
18 return wantarray? @out : $out[0];
19 }
20
Ejemplo de ejecución:
> trim.pl
string =
string
El Módulo Contextual::Return
Se puede obtener una información mas detallada sobre el contexto de la llamada usando el módulo
Contextual::Return :
sub sensible {
return STR { "one" }
NUM { 1 }
;
93
}
pl@nereida:~/src/perl/testing$ contextual.pl
Result = one
Result = 1
Contextual::Return tambien permite crear variables contextuales multitipo con mas de dos tipos
(a diferencia de dualvar que está restringida a los tipos cadena y número):
Si se usa la etiqueta ACTIVE el código de definición será ejecutado cada vez que la variable multi-
estado es evaluada. Por ejemplo:
lhp@nereida:~/Lperl/src/testing$ context2.pl
Now: Sat Mar 15 11:45:58 2008 (1205581558)
Now: Sat Mar 15 11:45:59 2008 (1205581559)
94
Cuando caller se llama en un contexto escalar sólo devuelve el nombre del paquete.
Se le puede pasar un argumento (caller expr) en cuyo caso expr indica el número de contextos
de pila que se retroceden a partir de este. En ese caso la información devuelta es aún mas rica:
$this_function = (caller(0))[3];
95
> perl -de0
Default die handler restored.
Loading DB routines from perl5db.pl version 1.07
Editor support available.
Enter h or ‘h h’ for help, or ‘man perldebug’ for more help.
main::(-e:1): 0
DB<1> print (2+3)*5
5
DB<2>print 5*(2+3)
25
DB<3>
¿Podrı́a explicar los resultados?. La función print devuelve 1 o 0 dependiendo de si pudo realizar la
impresión o no. Observe esta otra prueba:
Moraleja: ponga paréntesis en todas las llamadas a función en las que pueda aparecer alguna ambi-
guedad.
Como separador de listas es su significado habitual en una llamada a subrutina, aunque podrı́a
producirse alguna ambiguedad.
96
1.15.14. Práctica: Polares a Cartesianas
Escriba una función p2c que permita pasar de coordenadas Polares a Cartesianas y de Cartesianas
a Polares. Las fórmulas de conversión de Polares a Cartesianas son:
$x = $r*cos($angle); $y = $r*sin($angle)
p2c(x=>1, r=>1)
p2c(x=>1, y=>0)
p2c(r=>1, angle => 0.2)
97
Capı́tulo 2
Entrada /Salida
El operador diamante, cuando sea llamado desde miprog tomara la entrada desde datos.dat,
file.txt y x.log. Si el programa se llama sin argumentos leerá desde la entrada estándar.
Se puede usar el guión corto como sinónimo de leer desde la entrada estándar. El siguiente programa
muestra los ficheros proveidos en la lı́nea de comandos. Enumera las lı́neas y justifica los números de
lı́nea a la derecha según el valor de $width:
La variable $ARGV contiene el nombre del fichero actual. Veamos varias ejecuciones del programa:
98
1 Welcome ...
FILE which.pl
1 #!/usr/bin/perl -w
2
3 $pattern = shift || ’’;
4 $pattern =~ s{::}{/}g;
5 #$pattern =~ s{$}{.pm};
6 @dirs = @INC;
7 #push @dirs, qw(
8 # /etc/perl
9 # /usr/local/lib/perl/5.8.4
10 # /usr/local/share/perl/5.8.4
11 # /usr/lib/perl5
12 # /usr/share/perl5
13 # /usr/lib/perl/5.8
14 # /usr/share/perl/5.8
15 # /usr/local/lib/site_perl
16 #);
17
18 for (@dirs) {
19 my $file = $_."/".$pattern.’.pm’;
20 print "$file\n" if (-e $file);
21 $file = $_."/".$pattern.’.pod’;
22 print "$file\n" if (-e $file);
23 $file = $_."/pod/".$pattern.’.pod’;
24 print "$file\n" if (-e $file);
25 }
El operador diamante utiliza el array @ARGV el cual contiene la lista de argumentos pasados al
programa en la lı́nea de comandos. Cuandose usa el operador shift fuera de una subrutina, actúa
por defecto sobre este array. Si al comienzo de nuestro programa modificamos el array @ARGV podemos
alterar la conducta del operador diamante. El ejemplo que vemos a continuación modifica @ARGV de
manera que el programa hará caso omiso de los argumentos pasados por el usuario.
99
0 ABCDEF
1 ABCDEF
2 ABCDEF
3 ABCDEF
4 ABCDEF
5 ABCDEF
El manejador de ficheros especial usado por el operador diamante es conocido como ARGV . La expresión
<> es un sinónimo de <ARGV>.
100
perl -e ’s/nereida\.deioc\.ull\.es/miranda.deioc.ull.es/gi’ -p -i.bak *.html
Este programa sustituye la palabra original (g)lobalmente e i)gnorando el “case”) en todos los
ficheros *.html y para cada uno de ellos crea una copia de seguridad *.html.bak.
Otro ejemplo: la sustitución que sigue ocurre en todos los ficheros info.txt en todos los subdi-
rectorios de los subdirectorios que comiencen por alu:
-e puede usarse para definir el script en la lı́nea de comandos. Multiples -e te permiten escribir un
multi-script. Cuando se usa -e, perl no busca por un fichero de script entre la lista de argumentos.
-p La opción -p hace que perl incluya un bucle alrededor de tu “script” al estilo sed:
while (<>) {
... # your script goes here
} continue {
print;
}
#!/usr/bin/perl -pi.orig
s/foo/bar/;
which is equivalent to
#!/usr/bin/perl
$extension = ’.orig’;
LINE: while (<>) {
if ($ARGV ne $oldargv) {
if ($extension !~ /\*/) {
$backup = $ARGV . $extension;
}
else {
($backup = $extension) =~ s/\*/$ARGV/g;
}
rename($ARGV, $backup);
open(ARGVOUT, ">$ARGV");
select(ARGVOUT);
$oldargv = $ARGV;
}
s/foo/bar/;
}
continue {
print; # this prints to original filename
}
101
select(STDOUT);
except that the -i form doesn’t need to compare $ARGV to $oldargv to know when
the filename has changed. It does, however, use ARGVOUT for the selected
filehandle. Note that STDOUT is restored as the default output filehandle
after the loop.
-n Nótese que las lı́neas se imprimen automáticamente. Para suprimir la impresión usa la opción
-n
-i[ext ] La opción -i Expresa que los ficheros procesados serán modificados. Se renombra el fichero
de entrada file.in a file.in.ext, abriendo el de salida con el mismo nombre del fichero de
entrada file.in.
Se selecciona dicho fichero como de salida por defecto para las sentencias print. Si se proporciona
una extensión se hace una copia de seguridad. Si no, no se hace copia de seguridad.
En general las opciones pueden ponerse en la primera lı́nea del “script”, donde se indica el intérpre-
te. Asi pues, decir
perl -p -i.bak -e "s/foo/bar/;"
es equivalente a usar el “script”:
#!/usr/bin/perl -pi.bak
s/foo/bar/;
$x = <>;
lee una lı́nea y almacena su contenido (incluyendo el retorno de carro final) en $x. Este operador es
también sensible al contexto. Ası́, en un contexto de lista
@x = <>;
Se lee todo el fichero (en entrada estadar unix, se leerán todas las lı́neas hasta que se pulse CTRL-D)
y las diferentes lı́neas constituyen los elementos del array x.
¿En que contexto interpreta Perl el siguiente fragmento de código?
102
2.5. Operaciones sobre Ficheros
En Perl un filehandle es el nombre una conexión de entrada/salida que conecta nuestro proceso
Perl con el mundo exterior. Esto es, se trata del nombre de una conexión, no necesariamente del
nombre de un fichero. Existen seis filehandles que son especiales: STDIN, STDOUT, STDERR, DATA,
ARGV y ARGVOUT.
Abrir un fichero
Para abrir una conexión se utiliza el operador open. Por ejemplo:
El operador open devuelve verdadero o falso, dependiendo de si la operación pudo realizarse con
éxito o no.
Se puede usar una expresión escalar en lugar del especificador de fichero. Por ejemplo:
my $outfile = "alu.txt";
open FILE, "> $outfile";
Observe el espacio después del “mayor que”. Asi se evitan extrañas conductas, si por ejemplo
$outfile es algo asi como >alu.txt, se podriá producir un append (esto es >>) en vez de una
escritura (>).
Cerrar un fichero
Para cerrar el fichero use el operador close:
close FILE;
Perl cierra automáticamente un fichero cuando se reabre o bien cuando termina la ejecución del
programa.
Esta expresión aprovecha la evaluación en circuito corto. La función die imprime el mensaje a STDERR
y hace que nuestro programa termine con un estatus distinto de cero. Además añade al mensaje el
nombre del programa y el número de lı́nea en caso de que el mensaje de error no termine en un retorno
de carro (\n). La variable especial $! contiene el último mensaje de error del sistema operativo.
Lectura desde un fichero Una vez que un fichero esta abierto para lectura, podemos leer las
lı́neas de la misma forma que lo hacemos desde STDIN usando el operador de lectura <FILEHANDLE>.
Veamos un ejemplo:
my $user = shift;
open PASSWD, "/etc/passwd" or die "Se esperaba un sistema Unix. $!";
while (<PASSWD>) {
chomp;
if (/^$user:/) { print "$_\n"; }
}
103
El separador de lectura
La variable especial $/ contiene el separador de lectura, que por defecto es un \n. Asi, si le
asignamos $/ = ’.’; en la siguiente lectura se leerá hasta el próximo punto o hasta el final del fichero
si no lo hubiera. Asignarle a $/ la cadena vacı́a "" hace que se lea hasta el siguiente párrafo (esto es,
hasta la siguiente aparición de dos o más lı́neas en blanco). Cuando tiene el valor undef se leerá todo
el resto del fichero.
undef $/;
$x = <FILE>; # Ahora $x contiene todo el fichero
¿Existe el riesgo de “muerte” prematura debido a que una lı́nea contenga sólamente "0"? Pruebe con
varios posibles ficheros de entrada. Observe que el operador de lectura <FILEHANDLE> incluye el retorno
de carro \n en \$_.
El operador select
El operador select modifica la salida por defecto. En vez de STDOUT el fichero especificado será uti-
lizado por defecto. La variable especial $|, cuando vale 1, hace que los buffers de salida se vacı́en
inmediatamente a la salida por defecto.
my $user = shift;
open LOG, ">/tmp/log.file" or die "Se esperaba un sistema Unix";
select LOG;
$| = 1;
print "Esto es una prueba\n";
select STDOUT;
print "Esto es otra prueba\n"
nereida:~/perl/src> select.pl
Esto es otra prueba
nereida:~/perl/src> cat /tmp/log.file
Esto es una prueba
104
El separador de registros de salida
Perl normalmente no separa dos salidas realizadas en dos llamadas consecutivas a la sentencia
print. Para ello se puede usar la variable $\. Vea el ejemplo:
$\ = "\n***\n";
print "uno"; print "dos";
La salida será:
uno
***
dos
***
$name = "index.html";
if (-e $name) {
print "Ya existe un fichero denominado $name\n";
} else {
print "No existe un fichero denominado $name\n";
}
Existen otros operadores. Por ejemplo, -r $filevar es cierto si el fichero cuyo nombre se guarda
en $filevar existe y es de lectura. Análogamente, -w $filevar comprueba si es de escritura.
if (-x SOMEFILE) {
# SOMEFILE es ejecutable
}
105
Fichero Significado
-r El fichero o directorio es legible
-w El fichero o directorio es de escritura
-e El fichero o directorio existe
-x El fichero es ejecutable
-z El fichero existe y tiene tamaño cero (Los directorios nunca tiene tamaño cero)
-s El fichero o directorio existe y tiene tamaño no nulo (el tamaño en bytes
-f La entrada es un fichero
-d La entrada es un directorio
-t isatty sobre el fichero es cierto (esto es, es un dispositivo de caracteres)
-T Fichero de texto
-B Fichero binario
-M Edad de modificación en dı́as
-A Tiempo de acceso en dı́as
-C Edad de modificación del Inode en dı́as
Si no se especifica el nombre del fichero, el operador por defecto es la variable $_. Por ejemplo:
foreach (@some_list_of_filenames) {
print "$_ es de lectura\n" if -r; # Lo mismo que -r $_
}
La función stat
Si se quiere obtener información más detallada como el número de enlaces a un fichero o el uid del
propietario de un fichero es necesario recurrir a la función stat. El operando de stat es un fichero o
un nombre de fichero. La función stat devuelve bien una lista vacı́a (en caso de error) o una lista de
13 elementos (véase la tabla 2.2).
0 dev device
1 ino inode
2 mode permisos
3 nlink numero de hard links
4 uid user ID del propietario
5 gid group ID del propietario
6 rdev tipo de dispositivo (ficheros especiales)
7 size tamaño total, en bytes
8 atime Tiempo del último acceso
9 mtime Tiempo de la última modificación
10 ctime Tiempo del último cambio del inodo
11 blksize blocksize para filesystem I/O
12 blocks número de bloques asignados
Los argumentos atime, mtime y ctime indican cuantos segundos han pasado desde el comienzo de
1970 MUT (Midnight Universal Time).
En el siguiente ejemplo, en la lı́nea 1 el uso del operador glob nos permite la expansión de los
comodines tipo shell:
DB<1> @f = glob(’xml*’)
DB<2> p "@f"
106
xml xmlparser.pl xmlparserinput.xml
DB<3> @a = stat "xmlparser.pl"
DB<4> x @a
0 2051
1 2171300
2 33261
3 1
4 1007
5 1007
6 0
7 191
8 1112287076
9 1087853581
10 1099385099
11 4096
12 8
La función localtime permite convertir números en formato MUT en una cadena tipo fecha:
Cuando se llama a stat sobre un enlace simbólico, stat devuelve información sobre el fichero
apuntado, no sobre el enlace. Si se trata de un enlace en vez de un fichero, use la función lstat. Si
el operando no es un enlace simbólico lstat devuelve la misma información que stat. Cuando no se
indica operando, ambos stat y lstat usan la variable por defecto $_.
La Función openhandle
La función openhandle en Scalar::Util permite saber si una expresión es un manejador de fichero
(o un atado mediante tie de un manejador).
openhandle FH
Retorna FH si FH puede ser usado como manejador y esta abierto. En otro caso retorna undef:
107
2.7. Ficheros Binarios
La función ⁀read La función read FILEHANDLE,SCALAR,LENGTH,OFFSET lee desde el fichero
FILEHANDLE un número de LENGTH caracteres en la variable SCALAR. Devuelve el número de caracteres
leı́dos, 0 si es el final de fichero y undef si hubo un error. Es posible indicar que el fichero usado es
binario mediante la función binmode (lı́nea 7 en el ejemplo que sigue):
La salida de la ejecución nos permite comprobar que la copia de un ejecutable a.out preserva el
formato binario:
La función syswrite
La función syswrite (lı́nea 13) tiene el siguiente formato:
syswrite FILEHANDLE,SCALAR,LENGTH,OFFSET
syswrite FILEHANDLE,SCALAR,LENGTH
syswrite FILEHANDLE,SCALAR
Intenta escribir LENGTH bytes de SCALAR en FILEHANDLE saltándose los buffers de E/S. Si no se
especifica LENGTH se escribe todo SCALAR.
108
2.8. La función localtime
localtime en contexto de Lista
Cuando localtime es llamada en un contexto de lista devuelve una lista como sigue:
# 0 1 2 3 4 5 6 7 8
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time);
DB<1> @f = ‘ls‘
DB<2> chomp(@f) # eliminamos retornos de carro finales
DB<3> x @f[0..5] # veamos los nombres de los 6 primeros ficheros
0 ’a2pdf’
1 ’Abstract.pm’
2 ’adressbook.pl’
3 ’advanced_perl_programming’
4 ’amatch.pl’
5 ’A.pl’
DB<4> @s = stat $f[0]
DB<5> x @s
0 2051
1 2171079
2 33261
3 1
4 1007
5 1007
6 0
7 339
8 1111133369
9 1092043518
10 1096562651
11 4096
12 8
DB<6> use constant mtime => 9 # definimos la constante mtime como 9
DB<7> p $s[mtime] # formato Midnight Universal Time
1092043518
DB<8> @t = localtime $s[mtime] # contexto de lista
DB<9> x @t
0 18 # segundos
1 25 # minutos
2 10 # horas
3 9 # dı́a del mes
4 7 # mes: Agosto. Enero es el 0.
5 104 # a~ no desde 1900: 1900+104 = 2004
6 1 # dia de la semana: Lunes. Domingo es el 0.
7 221 # dia del a~ no
8 1 # daylight savings time (cambio de horario) es cierto
DB<10> $t = localtime $s[mtime] # contexto escalar
DB<11> x $t
0 ’Mon Aug 9 10:25:18 2004’
109
lhp@nereida:~$ perl -wde 0
main::(-e:1): 0
DB<1> p localtime
45307331084931
DB<2> p scalar(localtime)
Thu Apr 3 07:30:54 2008
La función gmtime
La función gmtime es similar a localtime sólo que devuelve el tiempo Greenwich:
DB<3> $g = gmtime
DB<4> p $g
Thu Mar 31 17:47:13 2005
DB<5> $c = time
DB<6> p $c
1112291278
DB<7> p localtime $c
5847183121054891
DB<8> p "".localtime $c
Thu Mar 31 18:47:58 2005
DB<9> p scalar(localtime $c)
Thu Mar 31 18:47:58 2005
DB<10> p scalar localtime(time-(24*60*60)), "\n";
Wed Mar 30 19:14:33 2005
Ejercicio 2.8.1. Explique los resultados en los pasos 7, 8, 9 y 10 del ejemplo anterior.
Ejercicio 2.8.2. Usando el depurador de Perl calcule que dı́a del calendario fué hace 500 dı́as.
El Módulo Time::HiRes
Si se quiere tener mayor precisión en la medida del tiempo, use el módulo Time::HiRes. Sigue un
ejemplo de uso:
Puede verse como se usa un número flotante incluyendo no sólo los segundos sino los microsegundos
MUT. Si queremos sustituir la función time por la que provee Time::HiRes deberemos importarla
nombrándola explı́citamente en el pragma use:
2.9. Directorios
El Operador chdir
El operador chdir nos permite cambiar de directorio dentro de la jerarquı́a de directorios. Estudie
el siguiente ejemplo:
110
lhp@nereida:~/Lperl/src/dir$ cat -n chdir.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 my $directory = shift;
5 my $expreg = (shift or ’.*’);
6 $expreg = qr/$expreg/o;
7 chdir $directory or die "no encuentro $directory: $!";
8 my @files = ‘ls -a‘;
9
10 for (@files) {
11 next unless /$expreg/;
12 chop;
13 printf "$_\n";
14 }
use Cwd;
my $dir = getcwd;
111
lhp@nereida:~/Lperl/src/dir$ cat -n dirhandles.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 my $directory = shift;
5 my $regexp = (shift or ’.*’);
6 opendir DH, $directory or die "No encuentro $directory: $!";
7
8 foreach my $file (readdir DH) {
9 printf "$file\n" if $file =~ m/$regexp/o;
10 }
11 closedir DH;
Observe como en la salida producida los ficheros no parecen seguir un orden especial.
112
En un sistema windows $x contendrı́a ’a\b\c’. Para saber mas, lea con detalle la documentación de
los módulos File::Basename y File::Spec . Usa para ello el comando man File::Basename o bien
perldoc File::Basename.
Sigue un ejemplo de uso de la función fileparse :
la variable all_files guarda ordenados alfabéticamente la lista de los nombres de los ficheros en el
directorio actual.
Es posible, como indica el siguiente ejemplo utilizar varios patrones separados por espacios.
Usar glob es mas eficiente y mas portable que enviar el requerimiento a la shell del sistema usando
backticks (como por ejemplo en ‘ls .*‘).
my $dir = "/home/casiano/";
my $dir_files = <$dir/* $dir/.*>;
DB<1> $f = ’yapp’
DB<2> @f = <$f/*.yp>
DB<3> p "@f"
yapp/aSb.yp yapp/Autoaction1.yp yapp/Calc.yp yapp/Calc2.yp
DB<4> open F, ’logic.pl’
DB<5> $x = F
DB<6> @x = <$x>
DB<7> p "@x"
113
#!/usr/bin/perl -w
$a = 4; $b = "hola"; $c = 0; $d = "";
print $a && $b,"\n";
print $a and $b,"\n";
print ($a and $b),"\n";
En este código, la lectura <alu*> se produce en un contexto de lista. Por ello el directorio completo es
leı́do y produce una lista con todos los nombres de ficheros y directorios alu* existentes en el directorio
actual. El subsecuente map produce los tamaños y la llamada a sum devuelve el tamaño total.
En este código, en cada pasada del bucle el operador <*> produce un nuevo nombre de fichero del
directorio actual. El nombre es asignado a $file. ¿Que ocurre si el nombre del fichero es "0"?. En tal
caso el bucle deberı́a tener una ”muerte” prematura. La codificación correcta serı́a:
while (defined($file = <*>) {
do_something($file);
}
Siguiendo la filosofı́a de ”ayudar” al programador, las últimas versiones de Perl tiene el compor-
tamiento ”esperado”. Esto es, detectan el uso en un while del glob e interpretan que "0" como
nombre de fichero no es falso. Sin embargo en otro contexto, "0" si se interpreta como falso. Observe
el siguiente ejemplo:
$ cat muerte_prematura2.pl
#!/usr/bin/perl -w
my $file;
my @a = ("uno","0","dos");
sub do_something {
print "$file\n";
}
Al ejecutar obtenemos:
$ ./muerte_prematura2.pl
uno
114
Esta modificación de la evaluación ”normal” de una expresión en un contexto lógico/booleano no
es (del todo) privilegio exclusivo de Perl. El programador puede conseguir un efecto similar usando
el módulo Contextual::Return de Damian Conway, el cual permite crear variables contextuales
multitipo con mas de dos tipos:
La función symlink
La función symlink crea un link simbólico:
cat ln.pl
#!/usr/bin/perl -w
La función mkdir
Para crear un directorio use mkdir :
115
rmdir /tmp/chum/cham
rmdir /tmp/chum/chim
DB<4> x $err_list
0 ARRAY(0x84b6178)
empty array
DB<5> !! ls -ld /tmp/chum /tmp/chum/cham/ /tmp/chum/chim
ls: /tmp/chum/cham/: No existe el fichero o el directorio
ls: /tmp/chum/chim: No existe el fichero o el directorio
drwxr-xr-x 2 pp2 pp2 4096 2008-04-23 11:42 /tmp/chum
(Command exited 2)
DB<6> mkpath( ’/tmp/chum/cham/’, ’/tmp/chum/chim’, {verbose => 1} )
mkdir /tmp/chum/cham/
mkdir /tmp/chum/chim
La función rename
La función rename nos permite cambiar el nombre de un fichero o moverlo de ubicación:
La función rmdir
Utilice la función rmdir para suprimir directorios:
rmdir "tutu/";
La función chmod
Utilice la función chmod para cambiar los permisos de un fichero o directorio:
La función chown Utilice la función chown para cambiar el propietario y el grupo de un fichero
o directorio:
Utilice la función getpwnam para traducir de nombre a número y la función getgrnam para
traducir de un nombre de grupo a su número:
116
3 1033
4 ’’
5 ’’
6 ’Elvira,,,’
7 ’/home/elvira’
8 ’/bin/bash’
DB<3> $a = getpwnam(’elvira’)
DB<4> x $a
0 1033
DB<5> x getgrnam "users"
0 ’users’
1 ’x’
2 100
3 ’’
DB<6> x scalar(getgrnam "users")
0 100
117
$ rename -n ’s/1628/chuchu/’ node-1628.html
node-1628.html renamed as node-chuchu.html
$ ls -l *chuchu*
ls: *chuchu*: No existe el fichero o el directorio
46 $verbose++ if $no_act;
48 if (!@ARGV) {
49 print "reading filenames from STDIN\n" if $verbose;
50 @ARGV = <STDIN>;
51 chop(@ARGV);
52 }
54 for (@ARGV) {
55 my $was = $_; # viejo nombre del fichero
56 eval $op; # sustitución efectuada
57 die $@ if $@; # alto si la expresión es inválida
A menos que se especificara --force no se renombra el fichero si existe ya uno con ese nombre:
118
63 elsif ($no_act or rename($was, $_)) # Se renombran mediante "rename"
64 {
65 print "$was renamed as $_\n" if $verbose;
66 }
67 else
68 {
69 warn "Can’t rename $was $_: $!\n";
70 }
71 }
Si $no_act es verdadero nunca se evalúa rename($was, $_) y se pasa a ejecutar la lı́nea 65.
El Código
Sigue el código completo:
119
39 }
40 }
File::Find
El módulo File::Find provee la función find. La sintáxis es:
find(\&wanted, @directories);
find() hace un recorrido primero profundo de los directorios especificados en @directories. Para
cada fichero o directorio encontrado se llama a la subrutina &wanted. Además, para cada directorio
que se encuentre se cambia chdir() al directorio y se continúa la búsqueda, invocando a la función
&wanted.
La función wanted es lo que se denomina un callback (Callback (computer science) En ella el
programador escribe lo que quiere hacer con el fichero o directorio: imprimirlo, renombrarlo, borrarlo,
etc. La función será llamada sin argumentos y su valor de retorno será ignorado.
Cuando se la llama:
5 directories, 7 files
DB<2> use File::Find
DB<3> sub callback { return unless -f $_; push @large, $File::Find::name if -s $_ > 30000 }
DB<4> find(\&callback, ’.’)
DB<5> x @large
0 ’./_Inline/lib/auto/example_pl_7eb7/example_pl_7eb7.so’
1 ’./_Inline/lib/auto/example_pl_7eb7/.svn/text-base/example_pl_7eb7.so.svn-base’
120
El ejemplo encuentra un fichero adicional al mostrado por tree que se encuentra en un directorio
escondido (.svn).
Referencias
La función find espera como primer argumento una referencia a una subrutina. El operador Perl
\ actúa de manera similar al ampersand en C: nos devuelve una referencia al objeto al que se le aplica.
Por ejemplo:
Getopt::Long
Utilice Getopt::Long para leer las opciones en la lı́nea de comandos. Deberá admitir las llamadas:
Pod::Usage
Use Pod::Usage para escribir la ayuda.
Por ejemplo, la llamada:
hace que se muestren las secciones SYNOPSIS ası́ como cualesquiera secciones (esto es, definidas con
=head1) tituladas OPTIONS, ARGUMENTS, o OPTIONS AND ARGUMENTS.
Véase también
121
DB<6> x $progs[0]
0 ’kpad.pl’
DB<7> !!ls -l kpad.pl
-rwxr-xr-x 1 lhp lhp 12881 2006-06-15 12:52 kpad.pl
122
Capı́tulo 3
Expresiones Regulares
1 #!/usr/bin/perl -w
2 print "Enter a temperature (i.e. 32F, 100C):\n";
3 $input = <STDIN>;
4 chop($input);
5
6 if ($input !~ m/^([-+]?[0-9]+(\.[0-9]*)?)\s*([CF])$/i) {
7 print "Expecting a temperature, so don’t understand \"$input\".\n";
8 }
9 else {
10 $InputNum = $1;
11 $type = $3;
12 if ($type eq "C" or $type eq "c") {
13 $celsius = $InputNum;
14 $fahrenheit = ($celsius * 9/5)+32;
15 }
16 else {
17 $fahrenheit = $InputNum;
18 $celsius = ($fahrenheit -32)*5/9;
19 }
20 printf "%.2f C = %.2f F\n", $celsius, $fahrenheit;
21 }
22
123
3.3. Variables especiales después de un emparejamiento
Despues de un emparejamiento con éxito, las siguientes variables especiales quedan definidas:
Ejemplo:
1 #!/usr/bin/perl -w
2 if ("Hello there, neighbor" =~ /\s(\w+),/) {
3 print "That was: ($‘)($&)($’).\n",
4 }
> matchvariables.pl
That was: (Hello)( there,)( neighbor).
La variable $+ contiene el texto que casó con el último paréntesis en el patrón. Esto es útil en
situaciones en las cuáles una de un conjunto de alternativas casa, pero no sabemos cuál:
El vector @- contiene los offsets o desplazamientos de los casamientos en la última expresión regular.
La entrada $-[0] es el desplazamiento del último casamiento con éxito y $-[n] es el desplazamiento
de la subcadena que casa con el n-ésimo paréntesis (o undef si el párentesis no casó). Por ejemplo:
DB<1> $z = "hola13.47"
DB<2> if ($z =~ m{a(\d+)(\.(\d+))?}) { print "@-\n"; }
3 4 6 7
El array @+ contiene los desplazamientos de los finales de los emparejamientos. La entrada $+[0]
contiene el desplazamiento del final de la cadena del emparejamiento completo. Siguiendo con el
ejemplo anterior:
Se puede usar $#+ para determinar cuantos subgrupos habı́a en el último emparejamiento que tuvo
éxito.
La variable $#- contiene el ı́ndice del último paréntesis que casó. Observe la siguiente ejecución
con el depurador:
124
DB<1> $x = ’13.47’; $y = ’125’
DB<2> if ($y =~ m{(\d+)(\.(\d+))?}) { print "last par = $#-, content = $+\n"; }
last par = 1, content = 125
DB<3> if ($x =~ m{(\d+)(\.(\d+))?}) { print "last par = $#-, content = $+\n"; }
last par = 3, content = 47
Para saber más sobre las variables especiales disponibles consulte perldoc perlvar.
$a = "hola juanito";
$b = "adios anita";
$a =~ /(ani)/;
$b =~ s/(adios) *$1/\1 boni$1/;
print "$b\n";
> dollar1slash1.pl
adios boniadiosta
Observe como el $1 que aparece en la cadena de reemplazo se refiere a la cadena que caso en este
último casamiento (adios).
if (m/(...)/) {
&do_something();
print "the matched variable was $1.\n";
}
Puesto que $1 es automáticamente declarada local a la entrada de cada bloque, no importa lo que se
haya hecho en la función &do_something(), el valor de $1 en la sentencia print es el correspondiente
al “matching” realizado en el if.
125
3.7. Listas y ExpReg
Si se utiliza en un contexto que requiere una lista, el “pattern match” retorna una lista consis-
tente en las subexpresiones casadas mediante los paréntesis, esto es $1, $2, $3, . . . . Si no hubiera
emparejamiento se retorna la lista vacı́a. Si lo hubiera pero no hubieran paréntesis se retorna la lista
(1).
1 #!/usr/bin/perl -w
2 $foo = "one two three four five";
3 ($F1, $F2, $Etc) = ($foo =~ /^\s*(\S+)\s+(\S+)\s*(.*)/);
4 print "List Context: F1 = $F1, F2 = $F2, Etc = $Etc\n";
5 # This is equivalent to:
6 ($F1, $F2, $Etc) = split(’ ’,$foo,3);
7 print "Split: F1 = $F1, F2 = $F2, Etc = $Etc\n";
> escapes.pl
List Context: F1 = one, F2 = two, Etc = three four five
Split: F1 = one, F2 = two, Etc = three four five
#!/usr/bin/perl -w
open MAILFILE, shift;
@from = map /^From:\s+(.*)$/, <MAILFILE>;
map { print "$_\n" } @from;
Observése el uso de map aplicando la expresión regular /^From:\s+(.*)$/ a cada una de las lı́neas del
fichero de entrada. Las cadenas que se emparejan con el primer paréntesis son devueltas para formar
la lista que se almacena en @from. El segundo uso de map imprime cada uno de los elementos de la
lista @from. Al ejecutarlo se produce una salida como esta:
3.9. Opciones
Modificador Significado
e evaluar: evaluar el lado derecho de una sustitución como una expresión
g global: Encontrar todas las ocurrencias
i ignorar: no distinguir entre mayúsculas y minúsculas
m multilı́nea (^ y $ casan con \n internos)
o optimizar: compilar una sola vez
s ^ y $ ignoran \n pero el punto . “casa” con \n
x extendida: permitir comentarios
126
3.10. La opción /m
Em el modo m o multilı́nea (^ y $ casan con los \n internos) pero el punto no casa con los retornos
de carro. Véase el ejemplo:
3.11. La opción /s
La opción /s hace que . se empareje con un \n. Esto es, casa con cualquier carácter.
Veamos otro ejemplo, que imprime los nombres de los ficheros que contienen cadenas que casan
con un patrón dado, incluso si este aparece disperso en varias lı́neas:
1 #!/usr/bin/perl -w
2 #use:
3 #smodifier.pl ’expr’ files
4 #prints the names of the files that match with the give expr
5 undef $/; # input record separator
6 my $what = shift @ARGV;
7 while(my $file = shift @ARGV) {
8 open(FILE, "<$file");
9 $line = <FILE>;
10 if ($line =~ /$what/s) {
11 print "$file\n";
12 }
13 }
Ejemplo de uso:
Vea la sección 3.20 para ver los contenidos del fichero double.in. En dicho fichero, el patrón
three.*three aparece repartido entre varias lı́neas.
127
3.12. El Modificador /g
La conducta de este modificador depende del contexto. En un contexto de listas devuelve una
lista con todas las subcadenas casadas por todos los paréntesis en la expresión regular. Si no hubieran
paréntesis devuelve una lista con todas las cadenas casadas (como si hubiera paréntesis alrededor del
patrón global).
1 #!/usr/bin/perl -w
2 ($one, $five, $fifteen) = (‘uptime‘ =~ /(\d+\.\d+)/g);
3 print "$one, $five, $fifteen\n";
Observe la salida:
> uptime
1:35pm up 19:22, 0 users, load average: 0.01, 0.03, 0.00
> glist.pl
0.01, 0.03, 0.00
En un contexto escalar m//g itera sobre la cadena, devolviendo cierto cada vez que casa, y falso
cuando deja de casar. En otras palabras, recuerda donde se quedo la última vez y se recomienza la
búsqueda desde ese punto. Se puede averiguar la posicion del emparejamiento utilizando la función
pos. Si por alguna razón modificas la cadena en cuestión, la posición de emparejamiento se reestablece
al comienzo de la cadena.
1 #!/usr/bin/perl -w
2 # count sentences in a document
3 #defined as ending in [.!?] perhaps with
4 # quotes or parens on either side.
5 $/ = ""; # paragraph mode
6 while ($paragraph = <>) {
7 print $paragraph;
8 while ($paragraph =~ /[a-z][’")]*[.!?]+[’")]*\s/g) {
9 $sentences++;
10 }
11 }
12 print "$sentences\n";
Observe el uso de la variable especial $/. Esta variable contiene el separador de registros en el
fichero de entrada. Si se iguala a la cadena vacı́a usará las lı́neas en blanco como separadores. Se le
puede dar el valor de una cadena multicarácter para usarla como delimitador. Nótese que establecerla
a \n\n es diferente de asignarla a "". Si se deja undef, la siguiente lectura leerá todo el fichero.
Sigue un ejemplo de ejecución. El programa se llama gscalar.pl. Introducimos el texto desde
STDIN. El programa escribe el número de párrafos:
> gscalar.pl
este primer parrafo. Sera seguido de un
segundo parrafo.
"Cita de Seneca".
3.13. La opción /x
La opción /x permite utilizar comentarios y espacios dentro de la expresión regular. Los espacios
dentro de la expresión regular dejan de ser significativos. Si quieres conseguir un espacio que sea
significativo, usa \s o bien escápalo.
128
El siguiente ejemplo elimina los comentarios de un programa C.
1 #!/usr/bin/perl -w
2 $progname = shift @ARGV;
3 open(PROGRAM,"<$progname") || die "can’t find $progname";
4 undef($/);
5 $program = <PROGRAM>;
6 $program =~ s{
7 /\* # Match the opening delimiter
8 .*? # Match a minimal number of characters
9 \*/ # Match the closing delimiter
10 }[]gsx;
11 print $program;
main() {
printf("hello world!\n");
}
1 #!/usr/bin/perl -w
2 my $what = shift @ARGV;
3 while (<>) {
4 if (/$what/o) { # compile only once
5 print ;
6 }
7 }
129
3.15. RegExp no “Greedy”
Las expresiones no greedy hacen que el NFA se detenga en la cadena mas corta que casa con la
expresión. Se denotan como sus análogas greedy añadiéndole el postfijo ?:
{n,m}?
{n,}?
{n}?
*?
+?
??
El siguiente ejemplo muestra las lı́neas de los ficheros *.html que contienen dos “anclas” en la
misma lı́nea. Observe como el uso de los operadores no “Greedy” nos permite, por ejemplo, evitar el
uso de la negación de una clase:
1 > perl -e ’print "$ARGV\n\t$_" if (/<a href=\"(.*?)\">(.*?)<\/a>.*?<a href/i)’ \
2 -n *.html
3 plindex.html
4 <li><a href="http://osr5doc.sco.com:1996/tools/CONTENTS.html">
5 Programming Tools Guide</a> (<a href="http://osr5doc.sco.com:19
6 96/tools/Lex.html">Chapter 12: LEX</a>) (<a href="http://osr5do
7 c.sco.com:1996/tools/Yacc.html">Chapter 13: yacc </a>)
8 plpracticas.html
9 <li><a href="http://nereida.deioc.ull.es/html/java.html#html">
10 Varios links sobre HTML</a> (<a href="http://www.lcc.uma.es/~
11 eat/services/html-js/manu.html" target="_blank">Tutorial</a>)
12 (<a href="http://nereida.deioc.ull.es/html/barebone.html" tar
13 get="_blank">Guı́a de Referencia</a>)
> negynogreedy.pl
Ella dijo -"Ana" y yo contesté: "Jamás!"-. Eso fué todo.
Ella dijo "Ana" y yo contesté: -"Jamás!"-. Eso fué todo.
130
3.17.2. Paréntesis de agrupamiento
(?: ...) Permite agrupar las expresiones tal y como lo hacen los paréntesis ordinarios. La dife-
rencia es que no “memorizan” esto es no guardan nada en $1, $2, etc. Se logra ası́ una compilación
mas eficiente. Veamos un ejemplo:
> cat groupingpar.pl
#!/usr/bin/perl
my $a = shift;
$a =~ m/(?:hola )*(juan)/;
print "$1\n";
nereida:~/perl/src> groupingpar.pl ’hola juan’
juan
131
> cat lookaheadneg.pl
#!/usr/bin/perl
$a = "452";
if ($a =~ m{\d+(?=[^.])}i) { print "$a casa clase negada. \$& = $&\n"; }
else { print "$a no casa\n"; }
if ($a =~ m{\d+(?!\.)}i) { print "$a casa predicción negativa. \$& = $&\n"; }
else { print "$b no casa\n"; }
nereida:~/perl/src> lookaheadneg.pl
452 casa clase negada. $& = 45
452 casa predicción negativa. $& = 452
Una clase negada casa un único carácter. Un ‘mirar-adelante” negativo puede tener longitud
arbitraria.
^(?![A-Z]*$)[a-zA-Z]*$ casa con lı́neas formadas por secuencias de letras tales que no todas
son mayúsculas.
^(?=.*?esto)(?=.*?eso) casan con cualquier lı́nea en la que aparezcan esto y eso. Ejemplo:
my $a = shift;
(?!000)(\d\d\d) casa con cualquier cadena de tres dı́gitos que no sea la cadena 000.
Nótese que el “mirar-adelante” negativo es diferente del “mirar-atrás”. No puede usar este operador
para “mirar-atrás”: (?!foo)bar/ no casa con una aparición de bar que no ha sido precedida de foo.
Lo que dice (?!foo) es que los tres caracteres que siguen no puede ser foo. Ası́, foo no pertenece a
(?!foo)bar/, pero foobar pertenece a (?!foo)bar/ porque bar es una cadena cuyos tres siguientes
caracteres son bar y no son foo.
my $a = shift;
132
if ($a =~ m{(?!foo)bar}i) { print "$a casa la primera. \$& = $&\n"; }
else { print "$a no casa la primera\n"; }
Si quisieramos conseguir algo parecido tendrı́amos que escribir algo asi como /(?!foo)...bar/ que
casa con una cadena de tres caracteres que no sea foo seguida de bar. En realidad, es mucho mas fácil
escribir:
1 #!/usr/bin/perl -w
2 $s = "foobar";
3 if ($s =~ /(?!foo)bar/) {
4 print "$s matches (?!foo)bar\n";
5 }
6 if ($s !~ /(?!foo)...bar/) {
7 print "$s does not match (?!foo)...bar\n";
8 }
9 if ($s =~ /bar/ and $‘ !~ /foo$/) {
10 print "$s matches /bar/ and \$‘ !~ /foo\$/\n";
11 }
12 else {
13 print "$s does not match /bar/ and \$‘ !~ /foo\$/\n";
14 }
> lookbehind.pl
foobar matches (?!foo)bar
foobar does not match (?!foo)...bar
foobar does not match /bar/ and $‘ !~ /foo$/
012345678901123334234567890123125934890123345126
El problema es encontrar los códigos que comienzan por 12. En negrita se han resaltado las so-
luciones. Son soluciones sólo aquellas que, comienzan por 12 en una posición múltiplo de seis. Una
solución es:
133
@nums = grep {m/^12/} m/\d{6}/g;
que genera una lista con los números y luego selecciona los que comienzan por 12. Otra solución
es:
que aprovecha que la expresión regular devolverá una lista vacı́a cuando el número no empieza por
12. Obsérvese que se esta utilizando también que el operador | no es greedy.
¿Se puede resolver el problema usando sólamente una expresión regular? Obsérvese que esta solu-
ción “casi funciona”:
@nums = m/(?:\d{6})*?(12\d{4})/g;
recoge la secuencia mas corta de grupos de seis dı́gitos que no casan, seguida de una secuencia
que casa. El problema que tiene esta solución es al final, cuando se han casado todas las soluciones,
entonces la búsqueda exhaustiva hará que nos muestre soluciones que no comienzan en posiciones
múltiplo de seis. Encontrará ası́ números como el último resaltado:
012345678901123334234567890123125934890123345126
@nums = m/(?:\d{6})*?(12\d{4})(?:(?!12)\d{6})*/g;
Se asume que existe al menos un éxito en la entrada inicial. Que es un extraordinario ejemplo
de como el uso de paréntesis de agrupamiento simplifica y mejora la legibilidad de la solución. Es
fantástico también el uso del operador de predicción negativo.
3.19. El ancla \ G
El ancla \G ha sido concebida para su uso con la opción /g. Casa con el punto en la cadena en
el que terminó el último emparejamiento. Cuando se trata del primer intento o no se está usando /g,
usar \G es lo mismo que usar \A.
Mediante el uso de este ancla es posible formular la siguiente solución al problema planteado en la
sección 3.18:
@nums = m/\G(?:\d{6})*?(12\d{4}))*/g;
Aceptar cualquier número de ficheros. Resaltar las apariciones de duplicaciones. Cada lı́nea del
informe debe estar precedida del nombre del fichero.
Funcionar independientemente del case y de los blancos usados en medio de ambas palabras.
Esta es la solución:
134
1 #!/usr/bin/perl -w
2 # one <a>one</a>
3 # is two three
4 # three
5 $/ = ".\n";
6 while (<>) {
7 next if !s{
8 \b # start word ...
9 ([a-z]+) # grab word in $1 and \1
10 ( # save the tags and spaces in $2
11 (\s|<[^>]+>)+ # spaces or HTML tags
12 )
13 (\1\b) # repeated word in $4
14 }
15 "\e[7m$1\e[m$2\e[7m$4\e[m"igx;
16 s/^([^\e]*\n)+//mg; # remove lines that don’t contain escapes
17 s/^/$ARGV: /mg; # insert filename at the beginning of the lines
18 print;
19 }
Normalmente el carácter ^ casa solamente con el comienzo de la cadena y el carácter $ con el final.
Los \n empotrados no casan con ^ ni $. El modificador /m modifica esta conducta. De este modo ^ y
$ casan con cualquier frontera de lı́nea interna. Las anclas \A y \Z se utilizan entonces para casar con
el comienzo y final de la cadena.
Sigue un ejemplo de uso:
three
.
xxxx
>
> doublee.pl double.in
double.in: one <a><b>one</b></a>
double.in: is two three
double.in: three
"earth",1,"moon",9.374
Esta lı́nea representa cinco campos. Es razonable querer guardar esta información en un array,
digamos @field, de manera que $field[0] == ’earth’, $field[1] == ’1’, etc. Esto no sólo implica
descomponer la cadena en campos sino también quitar las comillas de los campos entrecomillados. La
primera solución que se nos ocurre es hacer uso de la función split:
@fields = split(/,/,$text);
135
Pero esta solución deja las comillas dobles en los campos entrecomillados. Peor aún, los cam-
pos entrecomillados pueden contener comas, en cuyo caso la división proporcionada por split serı́a
errónea.
1 #!/usr/bin/perl -w
2 use Text::ParseWords;
3
4 sub parse_csv {
5 my $text = shift;
6 my @fields = (); # initialize @fields to be empty
7
8 while ($text =~
9 m/"(([^"\\]|\\.)*)",? # quoted fields
10 |
11 ([^,]+),? # $3 = non quoted fields
12 |
13 , # allows empty fields
14 /gx
15 )
16 {
17 push(@fields, defined($1)? $1:$3); # add the just matched field
18 }
19 push(@fields, undef) if $text =~ m/,$/; #account for an empty last field
20 return @fields;
21 }
22
23 $test = ’"earth",1,"a1, a2","moon",9.374’;
24 print "string = \’$test\’\n";
25 print "Using parse_csv\n:";
26 @fields = parse_csv($test);
27 foreach $i (@fields) {
28 print "$i\n";
29 }
30
31 print "Using Text::ParseWords\n:";
32 # @words = "ewords($delim, $keep, @lines);
33 #The $keep argument is a boolean flag. If true, then the
34 #tokens are split on the specified delimiter, but all other
35 #characters (quotes, backslashes, etc.) are kept in the
36 #tokens. If $keep is false then the &*quotewords()
37 #functions remove all quotes and backslashes that are not
38 #themselves backslash-escaped or inside of single quotes
39 #(i.e., "ewords() tries to interpret these characters
40 #just like the Bourne shell).
41
42 @fields = quotewords(’,’,0,$test);
43 foreach $i (@fields) {
44 print "$i\n";
45 }
Las subrutinas en Perl reciben sus argumentos en el array @_. Si la lista de argumentos contiene
listas, estas son “aplanadas” en una única lista. Si, como es el caso, la subrutina ha sido declarada
antes de la llamada, los argumentos pueden escribirse sin paréntesis que les rodeen:
@fields = parse_csv $test;
136
Otro modo de llamar una subrutina es usando el prefijo &, pero sin proporcionar lista de argumen-
tos.
@fields = &parse_csv;
push(@fields, $+);
Puesto que la variable $+ contiene la cadena que ha casado con el último paréntesis que haya casado
en el ultimo “matching”.
La segunda parte del código muestra que existe un módulo en Perl, el módulo Text::Parsewords
que proporciona la rutina quotewords que hace la misma función que nuestra subrutina.
Sigue un ejemplo de ejecución:
> csv.pl
string = ’"earth",1,"a1, a2","moon",9.374’
Using parse_csv
:earth
1
a1, a2
moon
9.374
Using Text::ParseWords
:earth
1
a1, a2
moon
9.374
1 #!/usr/bin/perl -w
2 undef($/);
3 $paragraph = <STDIN>;
4 $count = 0;
5 $count = ($paragraph =~ s/Mister\b/Mr./ig);
6 print "$paragraph";
7 print "\n$count\n";
> numsust.pl
Dear Mister Bean,
Is a pleasure for me and Mister Pluto
to invite you to the Opening Session
Official dinner that will be chaired by
Mister Goofy.
137
Yours sincerely
Mister Mickey Mouse
Dear Mr. Bean,
Is a pleasure for me and Mr. Pluto
to invite you to the Opening Session
Official dinner that will be chaired by
Mr. Goofy.
Yours sincerely
Mr. Mickey Mouse
1 #!/usr/bin/perl -w
2 $_ = "abc123xyz\n";
3 s/\d+/$&*2/e;
4 print;
5 s/\d+/sprintf("%5d",$&)/e;
6 print;
7 s/\w/$& x 2/eg;
8 print;
> replacement.pl
abc246xyz
abc 246xyz
aabbcc 224466xxyyzz
3.24. Anidamiento de /e
1 #!/usr/bin/perl
2 $a ="one";
3 $b = "two";
4 $_ = ’$a $b’;
5 print "_ = $_\n\n";
6 s/(\$\w+)/$1/ge;
7 print "After ’s/(\$\w+)/$1/ge’ _ = $_\n\n";
8 s/(\$\w+)/$1/gee;
9 print "After ’s/(\$\w+)/$1/gee’ _ = $_\n\n";
> enested.pl
_ = $a $b
After ’s/($w+)/$b/ge’ _ = $a $b
138
3.25. Expandiendo y comprimiendo tabs
Este programa convierte los tabs en el número apropiado de blancos.
1 #!/usr/bin/perl -w
2 undef($/);
3 $string = <>;
4 $width=2;
5 while ($string =~ s/\t+/’ ’x(length($&)*$width-length($‘)%$width)/e) {
6 }
7 print "$string";
Supuesto que los tabs se paran cada tpw = 8 posiciones se convierte a espacios calculando el corres-
pondiente múltiplo de $tpw a partir de la posición actual. La función length devuelve la longitud en
caracteres de la expresión que se le pasa como argumento. Si se omite la expresión usará la variable
$_.
Sigue un ejemplo de ejecución:
-e puede usarse para definir el script en la lı́nea de comandos. Multiples -e te permiten escribir un
multi-script. Cuando se usa -e, perl no busca por un fichero de script entre la lista de argumentos.
while (<>) {
... # your script goes here
} continue {
print;
}
-n Nótese que las lı́neas se imprimen automáticamente. Para suprimir la impresión usa la opción -n
-i[ext ] Expresa que los ficheros procesados serán modificados. Se renombra el fichero de entrada
file.in a file.in.ext, abriendo el de salida con el mismo nombre del fichero de entrada
file.in. Se selecciona dicho fichero como de salida por defecto para las sentencias print. Si se
proporciona una extensión se hace una copia de seguridad. Si no, no se hace copia de seguridad.
139
En general las opciones pueden ponerse en la primera lı́nea del “script”, donde se indica el intérpre-
te. Asi pues, decir
perl -p -i.bak -e "s/foo/bar/;"
es equivalente a usar el “script”:
#!/usr/bin/perl -pi.bak
s/foo/bar/;
3.27. tr y split
El operador de traducción permite la conversión de unos caracteres por otros. Tiene la sintáxis:
tr/SEARCHLIST/REPLACEMENTLIST/cds
y/SEARCHLIST/REPLACEMENTLIST/cds
El operador permite el reemplazo carácter a carácter, por ejemplo:
$ perl -de 0
DB<1> $a = ’fiboncacci’
DB<2> $a =~ tr/aeiou/AEIOU/
DB<3> print $a
fIbOncAccI
DB<4> $a =~ y/fbnc/FBNC/
DB<5> print $a
FIBONCACCI
El operador devuelve el número de carácteres reeemplazados o suprimidos.
$cnt = $sky =~ tr/*/*/; # count the stars in $sky
Si se especifica el modificador /d, cualquier carácter en SEARCHLIST que no figure en REPLACEMENTLIST
es eliminado.
DB<6> print $a
FIBONCACCI
DB<7> $a =~ y/OA//d
DB<8> print $a
FIBNCCCI
Si se especifica el modificador /s, las secuencias de carácteres consecutivos que serı́an traducidas
al mismo carácter son comprimidas a una sola:
DB<1> $b = ’aaghhh!’
DB<2> $b =~ tr/ah//s
DB<3> p $b
agh!
Cuando se dan múltiples traducciones para un mismo carácter, solo la primera es utilizada:
tr/AAA/XYZ/
traducirá A por X.
El siguiente script busca una expresión regular en el fichero de passwords e imprime los login de
los usuarios que casan con dicha cadena. Para evitar posibles confusiones con las vocales acentuadas
se usa el operador tr.
140
1 #!/usr/bin/perl -w
2 $search = shift(@ARGV) or die("you must provide a regexpr\n");
3 $search =~ y/ÁÉÍÓÚáéı́óú/AEIOUaeiou/;
4 open(FILE,"/etc/passwd");
5 while ($line = <FILE>) {
6 $line =~ y/ÁÉÍÓÚáéı́óú/AEIOUaeiou/;
7 if ($line =~ /$search/io) {
8 @fields = split(":",$line);
9 $login = $fields[0];
10 if ($line !~ /^#/) {
11 print "$login\n";
12 }
13 else {
14 print "#$login\n";
15 }
16 }
17 }
18
1 #!/usr/bin/perl -w
2 $searchlist = shift @ARGV;
3 $replacelist = shift @ARGV;
4 $option = "";
5 $option = shift @ARGV if @ARGV;
6
7 while (<>) {
8 $num = eval "tr/$searchlist/$replacelist/$option";
9 die $@ if $@;
10 print "$num: $_";
11 }
141
> tr.pl ’a-z’ ’A-Z’ s
jose hernandez
13: JOSE HERNANDEZ
joosee hernnandez
16: JOSE HERNANDEZ
unpack("CCC", "ABCD")
La cadena de formato es una lista de especificadores que indican el tipo del dato que se va a empaque-
tar/desempaquetar. Cada especificador puede opcionalmente seguirse de un contador de repetición que
indica el número de elementos a formatear. Si se pone un asterisco (*) se indica que la especificación
se aplica a todos los elementos restantes de la lista.
Formato Descripción
A Una cadena completada con blancos
a Una cadena completada con ceros
B Una cadena binaria en orden descendente
b Una cadena binaria en orden ascendente
H Una cadena hexadecimal, los nibble altos primero
h Una cadena hexadecimal, los nibble bajos primero
Ejemplo de uso del formato A:
La variable @b tiene ahora dos cadenas. Una es Pe la otra es rl. Veamos un ejemplo con el formato B:
p ord(’A’)
65
DB<22> $x = pack "B8", "01000001"
DB<23> p $x
A
DB<24> @y = unpack "B8", "A"
DB<25> p "@y"
01000001
DB<26> $x = pack "b8", "10000010"
DB<27> p $x
DB<28> ’A’
142
Capı́tulo 4
Referencias
$rc = \10;
$rs = \"hello";
main::(-e:1): 0
DB<1> $ra = \10
DB<2> p $$ra
10
DB<3> p $ra
SCALAR(0xc0b840)
143
DB<4> x $ra
0 SCALAR(0xc0b840)
-> 10
DB<5> $$ra = 20
Modification of a read-only value attempted at (eval 18)[/usr/share/perl/5.8/perl5db.pl:628] l
DB<6> use Scalar::Util qw{readonly}
DB<7> x readonly($$ra)
0 8388608
DB<8> x readonly($ra)
0 0
DB<9> @b = 5..10
DB<10> $rb = \@b
DB<11> print "rb = $rb; rb-> = @{$rb}"
rb = ARRAY(0xc5dfc0); rb-> = 5 6 7 8 9 10
No se trata por tanto que produzca referencias a cada uno de los vectores. En realidad @a contiene un
vector de referencias a los elementos individuales:
DB<3> x @a
0 SCALAR(0x8450d74) -> 1
1 SCALAR(0x81046f4) -> 0
2 SCALAR(0x8450dc8) -> 0
144
3 SCALAR(0x8461ccc) -> 0
4 SCALAR(0x846cd28) -> 1
5 SCALAR(0x8461c9c) -> 0
6 SCALAR(0x8461a68) -> 0
7 SCALAR(0x84619d8) -> 0
8 SCALAR(0x84619cc) -> 1
DB<1> $a = 4; $b = 5; $c = 6
DB<2> @a = \($a, $b, $c)
DB<3> $a = \@a
DB<4> p $$a[1]
SCALAR(0x81046f4)
DB<5> p @a
SCALAR(0x81046d0)SCALAR(0x81046f4)SCALAR(0x81046ac)
DB<6> p ${$a[1]}
5
DB<7> p ${$a}[1] # $$a[1] es ${$a}[1]
SCALAR(0x81046f4)
145
sub t {
return \$a;
}
$a = 10;
$b = ${t()};
print $b; # 10
$arr_ref = \@a;
$hsh_ref = \%h;
$sub_ref = \&s;
$a[0] = $hsh_ref->{"first"} # $a[0] = $h{"first"}
# o bien ...
$arr_ref->[0] = $h{"first"} # $a[0] = $h{"first"}
El operador flecha -> toma una referencia a su izquierda y un ı́ndice o clave a su derecha, localiza
el array o hash correspondiente y accede al elemento apropiado.
Analize el siguiente ejemplo:
podamos escribir:
146
4.2. Identificando un referente ref
La función ref devuelve un string que indica el tipo del referente:
print $hsh_ref,"\n";
HASH(0X10027588)
La Función reftype
La función reftype de Scalar::Util funciona de manera parecida a ref EXPR, con la diferencia
de que sobre los objetos no retorna la clase del objeto:
147
4.3. Paso de Listas y Hashes a Subrutinas
El aplanado al que somete Perl los arrays y hashes cuando son pasados a una subrutina hace que
sean indistinguibles desde la perspectiva de la subrutina llamada. Una forma de evitar esta limitación
consiste en pasar como argumentos las referencias a los arrays y los hashes implicados.
El siguiente ejemplo muestra una subrutina que permite realizar la suma, el producto, etc. de un
número n de vectores. Para ello recibe una referencia a una subrutina implantando una operación
binaria conmutativa y una lista de referencias a listas (una matriz en la que las filas pueden ser de
distinta longitud). Por ejemplo, las siguientes llamadas son legales:
lhp@nereida:~/Lperl/src$ vect3.pl
a = (3 2 1 9)
b = (5 6 7)
c = (1 2)
a+b:
8 8 8 9
a*b*c:
15 24 7 9
max(a, b, c):
5 6 7 9
La rutina operate devuelve un vector conteniendo el resultado de operar vectorialmente los dife-
rentes vectores.
Si un vector es mas corto que otro se supone que es extendido a la longitud del mas largo con
elementos iguales al elemento neutro de la operación usada. La operación se supone conmutativa:
148
20 }
21
22 sub plus { $_[0]+$_[1] }
23 sub times { $_[0]*$_[1] }
24
25 my @a = (3, 2, 1, 9); print "a = (@a)\n";
26 my @b = (5, 6, 7); print "b = (@b)\n";
27 my @c = (1, 2); print "c = (@c)\n";
28 my @m = operate( \&plus, \@b, \@a);
29 print "a+b:\n@m\n";
30 my @m2 = operate( \×, \@a, \@b, \@c);
31 print "a*b*c:\n@m2\n";
32 my @m3 = operate( \&max, \@a, \@b, \@c);
33 print "max(a, b, c):\n@m3\n";
La función reduce aplica el bloque de código a la lista. Inicialmente las variables $a y $b contienen
los dos primeros elementos. El bloque es llamado y el resultado devuelto por el bloque queda en $a. A
continuación $b recorre los elementos de la lista siendo operado con $a y dejando siempre el resultado
en $a. El valor final de $a es el valor devuelto por reduce.
se denomina array anónimo. La expresión devuelve una referencia a un array con esos elementos.
Ası́ es posible asignarlo a una variable escalar:
Observe el uso de corchetes en vez de paréntesis. Aqui el array no tiene nombre, quien tiene nombre
es la referencia. Por supuesto, @$rb denota al array referido.
Hashes Anónimos
Lo mismo puede hacerse con los hashes. en vez de corchetes se usan llaves:
Subrutinas Anónimas
También puede hacerse con las subrutinas. Para tener una subrutina anónima basta con suprimir
el nombre de la subrutina:
149
$association = ( cat =>"nap", dog=>"gone", mouse=>"ball");
print "When I say ’cat’, you say ...",$association->{cat};
Explique el significado de las siguientes sentencias y que queda en los diferentes arrays después
de ejecutarlas:
La operación de unión:
La operación de intersección
Diferencia de conjuntos
Diferencia simétrica
La siguiente sesión con el depurador le ayudará a encontrar las respuestas a algunas de las preguntas
que se le plantearán durante la resolución de la práctica:
150
4.6. Estructuras anidadas
Listas Anidadas
Es posible crear estructuras de datos multidimensionales usando listas anónimas.
my $table = [ [1,2,3],[2,4,6],[3,6,9]];
print $table->[$x]->[$y];
print $table->[$x][$y];
Hashes Anidados
Es posible crear hashes multinivel anidando referencias a hashes anónimos:
$behaviour = {
cat => { nap => "lap", eat=>"meat"},
dog => { prowl => "growl", pool=>"drool"},
mouse => { nibble=>"kibble"}
};
Al igual que para los arrays multidimensionales, las flechas después de la primera pueden ser
suprimidas:
En general las secuencias ]->{, }->[, etc. pueden ser abreviadas omitiendo la flecha: ]{, }[, etc.
En caso de ambiguedad es necesario prefijar la llave o el corchete de un sı́mbolo + o de un return:
151
DB<12> sub hashem { {; @_ } }
DB<13> @a = hashem(a => 1, b => 2)
DB<14> x @a
0 ’a’
1 1
2 ’b’
3 2
152
Autovivificación
Se denomina Autovivificación o Autovivification a la creación automática de una referencia cuando
una valor indefinido es de-referenciado. El siguiente ejemplo ilustra una autovivificación:
Es en cierto modo razonable, ya que si está en el lado izquierdo es que se le quiere definir. Esto no
ocurre si se usa como rvalue:
Distinguir cuando una expresión esta siendo usada como rvalue o lvalue puede ser sutı́l. Por ejemplo:
DB<1> x use strict; my $r = undef; my @x = map{ $_ } @{ $r }
empty array
require ’dumpvar.pl’;
153
0 1
1 HASH(0x8106af0)
’A’ => ARRAY(0x8103068)
0 ’AB’
1 ’empty’
’B’ => ARRAY(0x8106b68)
0 ’bB’
1 ’empty’
El Módulo Data::Dumper
El módulo Data::Dumper convierte la lista de escalares pasada como parámetro a su función
Dumper en una cadena conteniendo código Perl que describe la estructura de datos.
154
#1
{
’A’ => [
#0
’AB’,
#1
’empty’
],
’B’ => [
#0
’bB’,
#1
’empty’
]
}
];
Matriz B
1 2
2 4
3 6
Matriz C
14 28
28 56
42 84
155
3 use List::Util qw(sum);
4 use List::MoreUtils qw(any);
5 use Scalar::Util qw{reftype};
6
7 sub matrixProd {
8 my ($A, $B) = @_;
9 die "Error. Se esperaban dos matrices" if any { !defined($_) or reftype($_) ne ’ARRAY’ }
10
11 my $nrA = @$A;
12 my $nrB = @$B;
13 my $ncB = @{$B->[0]};
14 my @C;
15
16 die ("Las matrices no son multiplicables\n") if any { @$_ != $nrB } @$A;
17
18 for(my $i = 0; $i < $nrA; $i++) {
19 for(my $j = 0; $j < $ncB; $j++) {
20 my $k = 0;
21 $C[$i][$j] = sum(map { $A->[$i][$k++] * $_ } map { $_->[$k] } @$B);
22 }
23 }
24
25 return \@C;
26 }
El Módulo PDL
Todo queda mucho mas simple usando el módulo PDL (PDL significa Perl Data Language) el cual
da soporte al cálculo cientı́fico en Perl:
1 #!/usr/bin/perl -w
2 use PDL;
3
4 $a = pdl [[1,2,3],[2,4,6],[3,6,9]];
5 $b = pdl [[1,2],[2,4],[3,6]];
6 $c = $a x $b; # x esta sobrecargado
7
8 print "a = $a\nb = $b\nc = $c\n";
156
8 print "@a\n";
9 }
lhp@nereida:~/Lperl/src/testing$ perl -c shift.pl
Global symbol "@shift" requires explicit package name at shift.pl line 7.
shift.pl had compilation errors.
"hola" "mundo"
En todo momento el contador de referencia del valor asociado con una variable $a mantiene el
número de referencias existentes a dicho valor.
Véase la figura 4.2. Puesto que hay una ligadura entre la variable $a y su valor, este contador es
al menos uno. Si este contador desciende hasta cero, Perl elimina la memoria asignada a ese valor.
Los contadores descienden por diversas razones. Por ejemplo, cada vez que termina un bloque, el
contador de los valores asociados con las variables declaradas en ese bloque desciende una unidad.
En el caso habitual de que el contador valiera 1, pasará a valer 0 y la memoria asociada con el valor
será liberada.
Ejemplo
El siguiente ejemplo, debido a Ikegami (PerlMonks), está tomado de una respuesta en PerlMonks.
Suponga el siguiente código:
1 my $array_ref = [];
2 for(1 .. 2) {
3 my $hash_ref = { foo => foo, bar => bar };
157
Figura 4.2: Contadores de referencia y asignaciones
4 push(@{$array_ref}, $hash_ref);
5 }
6
7 my $hash_ref = $array_ref->[0];
8 $array_ref = [];
$array_ref $hash_ref
+--------------+ +--------------+
| array ref | | hash ref |
+--------------+ +--------------+
| refcount = 1 | | refcount = 1 |
+--------------+ +--------------------+ +--------------+
| o=====> | array | | o |
+--------------+ +--------------------+ +---------- : -+
| refcount = 1 | :
+--------------------+ :
| element 0 | :
| +--------------+ | :
| | hash ref | | :
| +--------------+ | :
| | refcount = 1 | | V
| +--------------+ | +--------------+
| | o========> | hash |
| +--------------+ | +--------------+
+--------------------+ | refcount = 2 |
| element 1 | +--------------+
| +--------------+ | | keys & vals |
| | hash ref | | +--------------+
| +--------------+ |
| | refcount = 1 | |
| +--------------+ | +--------------+
| | o========> | hash |
158
| +--------------+ | +--------------+
+--------------------+ | refcount = 1 |
+--------------+
| keys & vals |
+--------------+
Despúes que se sobreescriba la referencia al array (lı́nea 8):
+--------------+ +--------------+
| array ref | | hash ref |
+--------------+ +--------------+
| refcount = 1 | | refcount = 1 |
+--------------+ +--------------------+ +--------------+
| o | | array | | o |
+---------- : -+ +--------------------+ +---------- : -+
: | refcount = 0 | :
: +--------------------+ :
V | element 0 | :
+--------------+ | +--------------+ | :
| array | | | hash ref | | :
+--------------+ | +--------------+ | :
| refcount = 1 | | | refcount = 1 | | V
+--------------+ | +--------------+ | +--------------+
| no elements | | | o========> | hash |
+--------------+ | +--------------+ | +--------------+
+--------------------+ | refcount = 2 |
| element 1 | +--------------+
| +--------------+ | | keys & vals |
| | hash ref | | +--------------+
| +--------------+ |
| | refcount = 1 | |
| +--------------+ | +--------------+
| | o========> | hash |
| +--------------+ | +--------------+
+--------------------+ | refcount = 1 |
+--------------+
| keys & vals |
+--------------+
El array original es liberado automáticamente puesto que su contador de referencias alcanza cero.
Las estructuras de los contadores de los elementos alcanzan cero:
+--------------+ +--------------+
| array ref | | hash ref |
+--------------+ +--------------+
| refcount = 1 | | refcount = 1 |
+--------------+ +--------------+
| o | | o |
+---------- : -+ +---------- : -+
: :
: :
V :
+--------------+ +--------------+ :
| array | | hash ref | :
+--------------+ +--------------+ :
159
| refcount = 1 | | refcount = 0 | V
+--------------+ +--------------+ +--------------+
| no elements | | o========> | hash |
+--------------+ +--------------+ +--------------+
| refcount = 2 |
+--------------+
+--------------+ | keys & vals |
| hash ref | +--------------+
+--------------+
| refcount = 0 |
+--------------+ +--------------+
| o========> | hash |
+--------------+ +--------------+
| refcount = 1 |
+--------------+
| keys & vals |
+--------------+
Los contadores de referencias de los hashes alcanzan cero y son liberados. Ahora el contador del
antiguo segundo elemento alcanza cero:
+--------------+ +--------------+
| array ref | | hash ref |
+--------------+ +--------------+
| refcount = 1 | | refcount = 1 |
+--------------+ +--------------+
| o | | o |
+---------- : -+ +---------- : -+
: :
: :
V :
+--------------+ :
| array | :
+--------------+ :
| refcount = 1 | V
+--------------+ +--------------+
| no elements | | hash |
+--------------+ +--------------+
| refcount = 1 |
+--------------+
| keys & vals |
+--------------+
+--------------+
| hash |
+--------------+
| refcount = 0 |
+--------------+
| keys & vals |
+--------------+
y es liberado:
+--------------+ +--------------+
160
| array ref | | hash ref |
+--------------+ +--------------+
| refcount = 1 | | refcount = 1 |
+--------------+ +--------------+
| o | | o |
+---------- : -+ +---------- : -+
: :
: :
V :
+--------------+ :
| array | :
+--------------+ :
| refcount = 1 | V
+--------------+ +--------------+
| no elements | | hash |
+--------------+ +--------------+
| refcount = 1 |
+--------------+
| keys & vals |
+--------------+
La función weaken
La función weaken recibe como argumento una referencia. Hace que no se incremente el contador
de referencias del valor asociado con dicha referencia. Vea el siguiente ejemplo:
161
hp@nereida:~/Lperl/src$ cat -n noweaken.pl
lhp@nereida:~/Lperl/src$ cat -n weaken.pl
1 use strict; 1 use strict;
2 use Scalar::Util qw(weaken); 2 use Scalar::Util qw(weaken);
3 my $ref; 3 my $ref;
4 { 4 {
5 my $var = "hola\n"; 5 my $var = "hola\n";
6 $ref = \$var; 6 $ref = \$var;
7 } 7 weaken($ref);
8 print $$ref; 8 }
9 print $$ref;
Una referencia se dice débil (weak) si no cuenta en el objeto al que referencia. Cuando el contador
del objeto al que referencia alcanza cero la referencia contiene undef.
Tenga en cuenta que la copia de una referencia débil (creada con weaken) es una referencia fuerte:
La función isweak
La función isweak retorna TRUE si la expresión pasada como parámetro es una referencia débil.
162
12 find_cycle($test);
13 exit 0;
lhp@nereida:~/Lperl/src/t
lhp@nereida:~/Lperl/src/testing$ ./ciclos.pl
Cycle (1):
$A->{’fred’} => \@B
$B->[3] => \%C
$C->{’mary’} => \@B
Cycle (2):
$A->{’fred’} => \@B
$B->[3] => \%C
$C->{’phyllis’} => \%A
Cycle (3):
$A->{’george’} => \%C
$C->{’mary’} => \@B
$B->[3] => \%C
Cycle (4):
$A->{’george’} => \%C
$C->{’phyllis’} => \%A
El módulo permite comprobar la existencia de ciclos con referencias débiles mediante la función
find weakened cycle . El programa:
Produce la salida:
lhp@nereida:~/Lperl/src/testing$ ciclosweaken.pl
Cycle (1):
$A->{’fred’} => \@B
$B->[3] => \%C
$C->{’mary’} => \@B
Cycle (2):
163
$A->{’fred’} => \@B
$B->[3] => \%C
w-> $C->{’phyllis’} => \%A
Cycle (3):
$A->{’george’} => \%C
$C->{’mary’} => \@B
$B->[3] => \%C
Cycle (4):
$A->{’george’} => \%C
w-> $C->{’phyllis’} => \%A
El Módulo Devel::Peek
Estudie la siguiente ejecución con el depurador. El módulo Devel::Peek nos permite observar la
estructura interna de las variables en el intérprete:
164
REFCNT = 1
FLAGS = (PADBUSY,PADMY,RMG,POK,pPOK)
IV = 0
NV = 0
PV = 0x8238fa8 "hola\n"\0
CUR = 5
LEN = 8
MAGIC = 0x83e3368
.............................
DB<1> @a = 1..5
DB<2> $ra = \$a[4]
DB<3> pop @a
DB<4> p @a
1234
DB<5> p $$ra
5
la orden pop @a rompe la ligadura entre $a[4] y el valor, esto es, la ligadura-variable $a[4] es
eliminada del array. Sin embargo el valor asociado no es eliminado de memoria ya que su contador de
referencias todavı́a no es cero. La variable $ra aún esta referenciándolo.
165
lhp@nereida:~/Lperl/src/testing$ symbolicref.pl
4
1 2 3 4 1 2 3 4
Inside data
First list: 1 2 3 4 1 2 3 4
Second list: 5 6 7 8
$ cat symbol_ref.pl
#!/usr/bin/perl -w
use strict;
our $x = 4;
my $a = "x";
$$a = 10;
print $x;
$ ./symbol_ref.pl
Can’t use string ("x") as a SCALAR ref while "strict refs" in use at ./symbol_ref.pl line 7.
$ cat symbol_ref2.pl
#!/usr/bin/perl -w
$x = 4; # no se ha usado our
$a = "x"; # $a tampoco es declarada
$$a = 10;
print $x;
$ ./symbol_ref2.pl
Can’t use string ("x") as a SCALAR ref while "strict refs" in use at ./symbol_ref2.pl line 7.
no strict ’refs’
Si, por el contrario, lo que se quiere es permitir el uso de referenciado simbólico en un segmento
del programa sin renunciar al control que nos da use strict, debemos usar la cláusula no:
166
11 }
12
13 print "x=$x, y=$y, z=$z\n";
lhp@nereida:~/Lperl/src$ ./symbol_ref3.pl
z
x=4, y=5, z=10
Una referencia simbólica es simplemente una cadena de caracteres que contiene el nombre de
una variable o subrutina de la tabla de sı́mbolos de un determinado paquete.
Cuando Perl encuentra una cadena alli donde esperaba encontrar una referencia, busca por una
entrada en la tabla de sı́mbolos con ese nombre y reemplaza la referencia de manera apropiada.
Toda referencia simbólica se refiere a una variable de paquete. Las variables léxicas no pueden
ser referenciadas a través de referencias simbólicas. Las referencias simbólicas acceden a la tabla
de sı́mbolos y las variables léxicas no se almacenan en las tablas de sı́mbolos - solo las declaradas
con our lo son. Las referencias simbólicas no pueden ser usadas para acceder a las variables
léxicas. Por ejemplo:
#!/usr/bin/perl
{
my $grain = "headache";
${"grain"} = "rye";
print "$grain\n";
print "$grain\n";
Imprime la primera vez headache y no rye. Es asi porque la variable léxica $grain oculta a la
variable de paquete $main::headache en el ámbito. La salida del programa es:
$ symbolex.pl
headache
rye
Si la cadena sigue las reglas de un nombre completo, Perl utilizará la tabla de sı́mbolos adecuada:
$name = "General::Specific::data";
print ${$name}; # Lo mismo que: print $General::Specific::data;
$symref = "set";
$symref->{type} = "discrete"; # Lo mismo que: $set->{type} = "discrete";
El pragma strict no protesta del uso de referenciados simbólicos cuando este se hace mediante el
operador ->.
167
Un Ejemplo de uso de Referenciado Simbólico: La función can
El ejemplo que sigue muestra un pequeño intérprete. El usuario llama al programa con un comando
y un glob que especifica un conjunto de ficheros.
lhp@nereida:~/Lperl/src/perl_testing_adn_examples/chapter_09$ symbolic.pl dirs /p*
/pendriver
/proc
lhp@nereida:~/Lperl/src/perl_testing_adn_examples/chapter_09$ symbolic.pl latest *
symbolic.pl
lhp@nereida:~/Lperl/src/perl_testing_adn_examples/chapter_09$ symbolic.pl sort_by_time *.pl
symbolic.pl
simplecalc.pl
make_test_files.pl
filefilter.pl
lhp@nereida:~/Lperl/src/perl_testing_adn_examples/chapter_09$ symbolic.pl chuchu *.pl
Unknown command ’chuchu’
Para cada comando el programa implanta una función de filtro e imprime el resultado del filtro.
lhp@nereida:~/Lperl/src/perl_testing_adn_examples/chapter_09$ cat -n symbolic.pl
1 #!/usr/local/bin/perl
2 use strict;
3 use warnings;
4
5 main( @ARGV );
6
7 sub main {
8 die "Usage:\n$0 <command> [file_pattern]\n" unless @_;
9 my $command = shift;
10
11 die "Unknown command ’$command’\n" unless main->can( $command );
12
13 no strict ’refs’;
14 my @r = $command->( @_ );
15 local $" = "\n";
16 print "@r\n";
17 }
18
19 sub sort_by_time {
20 map { $_->[0] }
21 sort { $a->[1] <=> $b->[1] }
22 map { [ $_, -M $_ ] } @_
23 }
24
25 sub latest {
26 (sort_by_time( @_ ) )[0];
27 }
28
29 sub dirs {
30 grep { -d $_ } @_;
31 }
La principal novedad en este código se refiere al control (lı́nea 11) de que comandos están disponibles.
La función can puede ser llamada de la forma:
paquete->can(’nombre_de_subrutina’)
168
devuelve cierto si existe una subrutina con ese paquete y falso en caso contrario. De hecho devuelve
una referencia (dura/no simbólica) a la subrutina si esta existe.
Recuerde
En un programa Perl pueden existir varias tablas de sı́mbolos. Una declaración package cambia
el espacio de nombres hasta que encontremos una nueva declaración package, o hasta el final
del bloque actual.
De hecho, las variables en Perl se clasifican como package variables y lexical variables.
Cuando sea necesario hacer explı́cito a que package pertenece la variable, puede hacerse prefi-
jando su nombre con el del package, separado por ::.
1 sub tutu {
2 print "En tutu\n";
3 }
4
5 $refsub = \&tutu;
Note que en la lı́nea 5 no se esta llamando a la función &tutu de la misma forma que cuando
tomamos una referencia a una variable escalar no evaluamos el escalar. La cosa cambia totalmente si
la asignación de la lı́nea 5 se cambia por $refsub = \&tutu(). ¿Qué ocurre en este caso?
Para crear una referencia a una subrutina anónima, simplemente, omitimos el nombre de la subru-
tina. He aqui un ejemplo de referencia a subrutina anónima:
y un ejemplo de llamada:
$sub_ref->("Ana");
o bien:
&$sub_ref("Ana");
169
4.14. Funciones de orden superior
Una de las ventajas de disponer de referencias a funciones es que podemos escribir meta-funciones:
funciones que reciben funciones como parámetros. Se dice de tales funciones que son funciones de
orden superior.
El siguiente ejemplo muestra una función sum que recibe como parámetros de entrada dos referen-
cias a funciones de una variable y devuelve una referencia a la función suma de aquellas.
lhp@nereida:~/Lperl/src$ cat -n readfunction.pl
1 #!/usr/local/bin/perl5.8.0 -w
2 use strict;
3
4 sub f {
5 my $x = shift;
6 2*$x;
7 }
8
9 sub g {
10 my $x = shift;
11 $x*$x;
12 }
13
14 sub sum {
15 my $rf1 = shift;
16 my $rf2 = shift;
17
18 return (sub { my $x = shift; &$rf1($x)+&$rf2($x); });
19 }
20
21 my $rf = \&f;
22 my $rg = \&g;
23
24 my $s = sum($rf, $rg);
25
26
27 print "Dame un número: ";
28 my $x;
29 chomp($x = <>);
30 print "f($x)+g($x) = ",&$s($x),"\n";
31
32 print "Dame una subrutina anónima ";
33 my $r;
34 chomp($r = <>);
35 $r = eval($r);
36 my $t = sum($r, $s);
37 print "Tu función($x)+f($x)+g($x) = ",&$t($x),"\n";
170
4.14.1. Práctica: Emulación de un Switch
Vamos a realizar una función de orden superior: Emule la sentencia switch de C usando un hash
de punteros a subrutinas. Escriba una función switch que recibe como segundo parámetro un hash con
claves las constantes del case y como valores las referencias a las subrutinas. Cómo primer parámetro
recibe un valor escalar. Según sea el valor deberá ejecutar la subrutina con clave correspondiente. Si
no hay ninguna clave con ese valor deberá de ejecutar la subrutina que corresponde a la clave default,
si tal clave existe en el hash. Aqui tiene una indicación de como hacerlo:
sub switch {
my $exp = shift or die "switch error: expecting an expression\n";
die "switch error. Empty cases\n" unless @_;
my %cases = @_;
if (exists($cases{$exp})) {
die "switch error. Expected code for <$exp>\n" unless ref($cases{$exp}) eq ’CODE’;
........................
}
..........................
}
my $x = 4;
switch $x,
2 => sub { print "two\n" },
3 => sub { print "three\n" },
4 => sub { print "four\n" },
default => sub { print "Other value\n" }
;
En el ejemplo que haga para la llamada a la función switch use subrutinas anónimas cuando el
código en cuestión lleve menos de una lı́nea.
El módulo Switch de Damian Conway proporciona una sentencia switch mas completa. Sigue un
ejemplo de uso:
$ cat -n useswitch.pl
1 #!/usr/local/bin/perl5.8.0 -w
2
3 use Switch;
4
5 $val = shift;
6
7 switch ($val) {
8 case 1 { print "number 1\n" }
9 case "hello" { print "string hello\n" }
10 else { print " none of these two\n" }
11 }
171
4.15. Typeglobs
4.15.1. Introducción
Perl mantiene una tabla de sı́mbolos o espacios de nombres (que internamente es una tabla hash)
separados para cada paquete y cada tipo de objeto en un paquete. Asi pues, fijado un paquete tenemos
variables
que son distintas y que pueden ser usadas simultáneamente. Puesto que una tabla hash no permite
claves duplicadas, Perl interpone una estructura que se sitúa entre la entrada de la tabla de sı́mbolos y
la memoria para los diferentes tipos de variable para cada prefijo. Podemos decir que esta estructura
es, en cierto modo, un typeglob.
Tabla de Typeglob
símbolos *file Memoria
... $file
@file
file %file
&file
file (filehandle)
... file (format)
Figura 4.3: Un typeglob nombra a la estructura que se interpone entre la tabla de sı́mbolos y la
memoria
Perl provee una sintáxis especial, denominada ”typeglob” para referirse conjuntamente a los dife-
rentes tipos de variable: *file. (Piensa en * como en un comodı́n).
*file = *source;
hace que file se convierta en un sinónimo de source: La entrada en la tabla de sı́mbolos para file
es una copia de la entrada en la tabla de sı́mbolos de source. Ası́ $file referencia a la misma variable
que $source, @file a la misma que @source, etc.
Un ”typeglob” puede ser visto como un hash o vector de referencias con un elemento por tipo de
variable (escalar, lista, ”’ash”, etc.). Cuando se asigna un ”typeglob” a otro se copia dicho vector de
referencias.
172
$ cat mytypeglob.pl
#!/usr/bin/perl -w
use strict;
my *a;
my $b;
*a = *b;
$ ./mytypeglob.pl
syntax error at ./mytypeglob.pl line 4, near "my *a"
Execution of ./mytypeglob.pl aborted due to compilation errors.
$a = 3.14159;
my $a = 5;
*b = *a;
print $b;
$b = 5;
{
local *b;
*b = *a;
$b = 20;
}
print $a; # 20
print $b; # 5
173
lhp@nereida:~/Lperl/src$ cat -n passbyref.pl
1 #!/usr/local/bin/perl
2 use strict;
3 use warnings;
4
5 sub sum {
6 our (@a, @b);
7 local (*a, *b) = @_;
8
9 $a[$_] += $b[$_] for 0..$#a;
10 }
11
12 our @c = 1..4;
13 our @d= 5..8;
14
15 sum(*c, *d);
16 print "@c\n";
lhp@nereida:~/Lperl/src$ ./passbyref.pl
6 8 10 12
En los años A.R. (Antes de las Referencias) la alternativa era hacer una asignación de los typeglobs:
*F = *G
Lo mismo ocurrı́a para el paso de ficheros como parámetros de una función. Vea el siguiente
ejemplo:
174
Paso de un Fichero sin Prefijo como Referencia a un Typeglob
Otra forma de pasar un manejador de fichero sin prefijo es usar una referencia a un typeglob:
#!/usr/bin/perl -w
sub welcome {
my $fh = shift;
open(FILE, ">test.txt");
$file = *FILE;
welcome($file);
close($file);
El Módulo IO::File
El modo recomendado de uso de ficheros consiste en tratarlos como objetos de la clase IO::File
$ cat -n ./fileparameter3.pl
1 #!/usr/bin/perl -w
2
3 use IO::File;
4
5 sub welcome {
6 my $fh = shift;
7
8 print $fh "Welcome ...\n";
9 }
10
11 $file = new IO::File "test.txt", "w";
12 die "No pude abrir el fichero: $!" unless $file;
13 welcome($file);
14 $file->close;
175
16 symbolic => sub { for ($$s = 0; $$s < 1e6; $$s++) { $$s; } },
17 });
lhp@nereida:~/Lperl/src$ benchtypeglobs3.pl
Benchmark: timing 4 iterations of reference, symbolic, typeglob...
reference: 1 wallclock secs ( 1.13 usr + 0.00 sys = 1.13 CPU) @ 3.54/s (n=4)
symbolic: 6 wallclock secs ( 6.33 usr + 0.00 sys = 6.33 CPU) @ 0.63/s (n=4)
typeglob: 1 wallclock secs ( 0.75 usr + 0.00 sys = 0.75 CPU) @ 5.33/s (n=4)
*SOURCE = \$SOURCE1;
*args = \@ARGV;
*do_it = sub { print "do it!\n" };
La primera asignación hace que $SOURCE sea un sinónimo de $SOURCE1, pero @SOURCE, %SOURCE y
&SOURCE continúan con sus anteriores valores.
Del mismo modo @args es un sinónimo de @ARGV pero no de $ARGV y do_it es el nombre de una
subrutina que inicialmente era anónima.
Referencias a Typeglobs
Es legal tomar una referencia a un ”typeglob”. Por ejemplo:
DB<1> $a = 4; @a = 1..5
DB<2> $b = \*a
DB<3> x $b
0 GLOB(0x8450df8)
-> *main::a
DB<4> x ${*$b}
0 4
DB<5> x @{*$b}
0 1
1 2
2 3
3 4
4 5
Ahora typeglob_ref contiene una referencia a la entrada en la tabla de sı́mbolos para variable.
Podemos acceder a los elementos individuales a través de la referencia:
DB<4> x %{*$typeglob_ref}
0 ’r’
1 ’rumania’
2 ’a’
3 ’a’
176
4 ’v’
5 ’ven’
DB<5> x &{*$typeglob_ref}()
0 1
DB<6> x *$typeglob_ref->()
0 1
DB<7> x *$typeglob_ref->{r}
0 ’rumania’
Ejercicio 4.15.3. ¿Por que no sale por pantalla el mensaje "esto es una variable" en las lı́neas
5,6 y 7? ¿Por que la respuesta es "1"?
#!/usr/local/bin/perl5.8.0 -w
sub mundo { "mundo\n" }
*hola = \&mundo;
$mundo = "hola";
$hola = "bienvenido";
print "$mundo ",&hola();
Podemos usar esta sintáxis para acceder a la entrada de fichero del ”typeglob”:
$handle_ref = *variable{IO}
177
Veamos un ejemplo con el depurador:
DB<1> $a = 4; @a = 1..5
DB<2> $rsa = *a{SCALAR}
DB<3> x $rsa
0 SCALAR(0x8450e04)
-> 4
DB<4> open a, "matrixP.pl"
DB<5> $rfa = *a{IO}
DB<6> x $rfa
0 IO::Handle=IO(0x847db4c)
DB<7> $b = <$rfa>
DB<8> p $b
#!/usr/bin/perl -w
$h = *$t{HASH};
$symbol_name = "data";
Entonces podemos acceder a traves del mismo al ”typeglob” como *{$symbol_name} en vez de
*{data}.
1 #!/usr/local/bin/perl -w
2 use strict;
3 use Scalar::Util qw(looks_like_number);
4 use List::MoreUtils qw(all);
5
6 sub wrap {
7 my ($subname, $wrapper) = @_;
8 die "Error in ’wrap’: Provide a sub name\n" unless $subname =~ /[a-zA-Z_]\w+/;
9 die "Error in ’wrap’: No wrapper provided\n" unless ref($wrapper) eq ’CODE’;
10
.. .....................................................................
18 }
19
20 sub square {
21 return map { $_ * $_ } @_;
22 }
23
178
24 wrap(’square’,
25 sub {
26 my $subname = shift;
27
28 die "Error en $subname: se esperaban números\n"
29 unless all { looks_like_number($_) } @_;
30
31 return @_;
32 }
33 );
34
35
36 my @a = square(5, 2);
37 print "square(5, 2) = (@a)\n";
38 @a = square(3, ’Juan’, 9);
39 print "square(3, ’Juan’, 9) = (@a)\n";
lhp@nereida:~/Lperl/src$ wrap.pl
square(5, 2) = (25 4)
Error en main::square: se esperaban números
La subrutina wrap en el código que sigue comienza comprobando los parámetros. A continuación
se construye el nombre completo de la función. La función caller devuelve el nombre del paquete
desde el que se llamó a la función actual (véase la sección 1.15.10).
wrap ’mirutina’,
179
pre => sub { print "Ejecutando ".shift()." con args <@_>\n"; @_ },
post => sub { print "Saliendo de mi_rutina\n"; @_ }
;
pl@nereida:~/LEyapp/examples$ localwithinsert.pl
inside tutu
Can’t do tutu
inside tutu
180
5 my @classes = @_;
6
7 @classes = scalar(caller) unless @classes;
8 no strict ’refs’;
9 for (@classes) {
10 croak "Error in delete_method: Illegal class name <$_>\n" unless /^[\w:]+$/;
11 unless ($_->can($name)) {
12 print STDERR "Warning in delete_method: No sub <$name> to delete in package <$_>\n";
13 next;
14 }
15 my $fullname = $_."::".$name;
16
17 # Temporarily save the other entries
18 my @refs = map { *{$fullname}{$_} } qw{HASH SCALAR ARRAY GLOB};
19
20 # Delete typeglob
21 *{$fullname} = do { local *{$fullname} };
22
23 # Restore HASH SCALAR ARRAY GLOB entries
24 for (@refs) {
25 next unless defined($_);
26 *{$fullname} = $_;
27 }
28 }
29 }
Se salvan las referencias a los viejos valores de los diversos tipos con un typeglob selectivo:
La expresión local *{$fullname} elimina temporalmente el typeglob (lo deja undef) y retorna el
typeglob. Como queremos hacer permanente la eliminación del typeglob. Véase el siguiente ejemplo
ilustrativo con el depurador:
181
4.15.12. El Módulo Symbol
El módulo Symbol provee un buen número de funciones para la manipulación de typeglobs y
sı́mbolos. Por ejemplo, es posible
Suprimir un paquete completo usando la función Symbol::delete package .
4.16. Prototipos
Los prototipos en Perl son un mecanismo que permite
La activación de ciertas comprobaciones de tipos
Escribir subrutinas cuyo comportamiento disfrute de privilegios similares a los de los operadores
internos de Perl.
Introducción: El Problema
Como ejemplo, supongamos que queremos escribir una función shift2 que retorna y elimina los
dos primeros elementos del principio de un array. Nos gustarı́a usarla como shift:
@a = 1..10;
($f, $s) = shift2 @a;
Si queremos escribirla, no vale hacer:
sub shift2 { splice @_, 0, 2 }
Observe el comportamiento del siguiente programa.
$ cat -n shift2.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 sub shift2 { splice @_, 0, 2 }
5
6 my @a = 1..5;
7 my ($f, $g) = shift2 @a;
8
9 local $" = ’,’;
10 print "f = $f, g = $g \@a = (@a)\n";
$ ./shift2.pl
f = 1, g = 2 @a = (1,2,3,4,5)
La llamada a splice en la lı́nea 4 retira Los elementos de @_ pero no del array @a.
La solución al problema es pasar el array por referencia:
sub shift2 { splice @{$_[0]}, 0, 2 }
pero entonces la llamada queda distinta, obligándonos a pasar la referencia:
@a = 1..10;
($f, $s) = shift2 \@a;
182
Control de la LLamada Mediante Prototipos
Si queremos que la interfaz de shift2 sea similar a la de shift debemos hacer uso de prototipos.
Podemos redeclarar la función como:
@x = shift2 \@a
Type of arg 1 to main::shift2 must be array
@x = shift2 @a, @b
Too many arguments for main::shift2
Como se ve en el ejemplo, se comprueba que el número y la clase de los parametros coinciden.
183
Prototipo Referencia a un Escalar
Un prototipo como \$ le indica a Perl que en la llamada se debe especificar una variable escalar
la cuál será convertida en la rutina en una referencia a la variable. Véase la sesión que sigue:
Cualquier prototipo backslash (\) representa a un argumento que debe comenzar con ese carácter.
De ahı́ las protestas del intérprete en las lı́neas 2, 3 y 6.
El Prototipo $
Un prototipo de la forma $ lo que hace es que fuerza un contexto escalar. Observe la conducta del
siguiente programa:
$ cat -n ./dollarproto.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 sub t ($@) { my $a = shift; my @b = @_; print "a = $a, b = (@b)\n"; }
5
6 my ($a, $b, $c) = qw/uno dos tres/;
7 t ’:’,$a, $b, $c;
8
9 my @r = 1..5;
10 t @r;
11
$ ./dollarproto.pl
a = :, b = (uno dos tres)
a = 5, b = ()
Los Prototipos @ y %
Un prototipo del tipo @ o del tipo % fuerza un contexto de lista y consume el resto de argumentos.
El Prototipo &
Un prototipo de la forma & fuerza una referencia a código. Si se trata del primer argumento,
entonces la palabra sub es opcional y se puede omitir en la llamada permitiendo un estilo similar al
uso de los operadores sort, eval o grep:
184
$ cat -n ampproto.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 sub t (&$) { my ($f, $x) = @_; print &$f($x),"\n" }
5
6 t { $_[0]**3 } 4;
7
$ ./ampproto.pl
64
Observe la ausencia de coma en la llamada de la lı́nea 6. Esta caracterı́stica nos permite escribir
funciones que siguen una sintáxis similar a la de sort.
El Prototipo *
El prototipo * fuerza una referencia a un typeglob.
Ejemplos
Veamos otros ejemplos de declaraciones de prototipos de operadores ya existentes en Perl:
185
Ejercicio 4.16.2. ¿Podrı́a explicar el resultado?
La función prototype
La función prototype retorna una cadena describiendo el prototipo de la referencia a la función
pasada como argumento:
Si el argumento es una cadena que comienza por CORE:: se interpreta como el nombre de un
operador Perl. Si el operador no se puede sobreescribir o sus argumentos no se pueden expresar como
un prototipo retorna undef.
186
El uso del prototipo nos permite omitir el uso de la palabra reservada sub y la coma intermedia. La
localización de las variables $a y $b nos permite evitar la posible contaminación de variables globales
con el mismo nombre.
Si se pone $/ a una referencia a un entero se intentará leer el número de registros referenciado por
dicho entero. Por ejemplo:
187
4.18. Clausuras
El concepto de clausura es una noción bien conocida en el mundo de la programación funcional.
Cuando se define una subrutina en un determinado contexto léxico, se ejecuta usando ese contexto
incluso si es llamada fuera de ese contexto. Las clausuras aparecieron como concepto en los años 60 y
fueron llevadas a la práctica por primera vez en un lenguaje que se llama Scheme.
lhp@nereida:~/Lperl/src$ closure2.pl
999
closure: 1001
closure: 1002
closure: 1003
lhp@nereida:~/Lperl/src$ closure3.pl 3 1 2 3 4
Multiplicar por 3: (3 6 9 12)
Multiplicar por -1: (-1 -2 -3 -4)
188
La subrutina creada dinámicamente recuerda el valor de la variable léxica $n en el momento de su
creación.
El bucle de la lı́nea 19 crea diferentes clausuras $t que acceden a diferentes instancias de la variable
léxica $n.
189
lhp@nereida:~/Lperl/src$ cat -n colors3.pl
1 #!/usr/bin/perl -w
2 use strict;
3
4 my @colors = qw(red blue green yellow orange purple violet);
5
6 for my $name (@colors) {
7 no strict ’refs’; # permitir la manipulación de la tabla de sı́mbolos
8 *$name = *{uc $name} = sub { "<FONT COLOR=’$name’>@_</FONT>" };
9 }
10
11 sub make_synonym {
12 my ($name, $sub) = @_;
13 die unless ($name =~ /[\w:]+/ && ref($sub) eq ’CODE’);
14
15 no warnings;
16 no strict ’refs’;
17 *$name = $sub;
18 }
19
20 sub tail {
21 return << ’COLA’;
22 </BODY>
23 </HTML>
24 COLA
25 }
26 make_synonym(TAIL => \&tail);
27
28 sub head {
29 my $title = shift;
30 return << "CABEZA";
31 <HTML>
32 <HEAD>
33 <TITLE>$title</TITLE>
34 </HEAD>
35 <BODY >
36 CABEZA
37 }
38 make_synonym(HEAD => \&head);
39
40 ########### main ##############
41 print HEAD("colores"),
42 "¡Tenga ", red("cuidado"), "con esa ", green("luz!"),"\n",
43 "¡Tenga ", RED("cuidado"), "con esa ", GREEN("luz!"),"\n",
44 tail;
La clausura se forma en la lı́nea 7 con la variable léxica $name declarada en la lı́nea 5. La subrutina
que se crea ”recuerda” el valor de $name. Se han usado typeglobs para instalar entradas en la tabla
de sı́mbolos a la función tanto en minúsculas como en mayúsculas.
Las funciones head y tail producen HTML para la cabecera y la cola usando documentos aqui
(conocidos como here-document o heredoc):
190
4.18.2. Anidamiento de subrutinas
Ya vimos en la sección 1.15.1 que Perl no anida subrutinas como ocurre en Pascal. Sin embargo,
el uso combinado de clausuras, typeglobs y local permite emular el efecto:
En este ejemplo se ha creado en la lı́nea 15 una subrutina denominada interior que ensombrece a la
subrutina interior global definida en la lı́nea 5 y que además tiene acceso al ámbito de exterior. Esta
subrutina interior de la lı́nea 15 es accesible solamente desde la subrutina exterior. El resultado
de la ejecución es:
lhp@nereida:~/Lperl/src$ ./nestedsub.pl
interior anidada: 5
exterior(2) = 9
sub marine: 3
interior(3) = 9
Obsérvese que hemos omitido la opción -w del intérprete y hemos usado el módulo warnings.
Ejercicio 4.18.1. ¿Cuál es el ámbito exacto de visibilidad de una subrutina contruida como la subru-
tina interior de la lı́nea 15? ¿Serı́a visible desde una subrutina que fuera llamada desde exterior
en un punto posterior a su creación?
Los ficheros (donde open crea el iterador y el operador de lectura <> nos da el siguiente),
191
Las cadenas cuando son sometidas a los operadores de emparejamiento y sustitución con la
opción g (la posición del siguiente lugar de búsqueda puede ser consultada mediante el operador
pos):
Cada llamada a iterators crea un conjunto nuevo de variables léxicas y una nueva subrutina
anónima. Es posible simultanear de este modo dos clausuras, cada una con su propio rango y paso:
192
Referencias
Para saber mas sobre iteradores consulte el libro Higher Order Perl [13].
Subconjuntos de un Conjunto
Introducción
El siguiente ejemplo muestra un iterador sobre los subconjuntos de un conjunto dado. El conjunto
viene definido por los argumentos en lı́nea de comando. Sigue un ejemplo de ejecución:
La opción -n de Perl
La opción -n de Perl inserta el siguiente bucle alrededor de nuestro programa:
LINE:
while (<>) {
193
... # nuestro programa va aqui
}
Asi pues el comando perl -ne ’printf "%2d:%4b %s",$k,$k,$_; $k++’ se convierte en:
LINE:
while (<>) {
printf "%2d:%4b %s",$k,$k,$_; $k++
}
LLamadas al Iterador
Los conjuntos son representados mediante listas. Dado que el conjunto vacı́o es un subconjunto de
cualquier conjunto y que el conjunto vacı́o es representado mediante la lista vacı́a no podemos usar
como criterio de finalización el retorno de la lista vacı́a.
Constructor
Sigue el código:
194
24 local $" = ’, ’;
25 print "(@S)\n";
26
27 last if (@S == @ARGV);
28
29 redo FOREVER;
30 }
Nota: para que el operador -d funcione es necesario que los nombres de fichero especifiquen también
el camino al archivo. No basta con el nombre de base. Sin embargo, lo que devuelve readdir es el
nombre base.
195
DB<3> x scalar(readdir $d)
0 ’..’
DB<4> x scalar(readdir $d)
0 ’script’
DB<5> x scalar(readdir $d)
0 ’t’
DB<6> x scalar(readdir $d)
0 ’META.yml’
DB<7> x -d ’t’
0 undef
DB<8> x -d ’/tmp/Parallel-Simple-Pipe-0.01/t’
0 1
Como se ve en las lı́neas 2 y 3 de la sesión readdir retorna las referencias al directorio actual y
al directorio padre. Tenga esto en cuenta apartando estos dos directorios para evitar la posibilidad de
una recursión infinita.
Si lo considera necesario, repase las secciones sobre el manejo de directorios y ficheros 2.5, 2.9 y
2.10.
4.18.4. Memoizing
Una función se dice pura si no tiene efectos laterales. Dicho de otro modo: el valor retornado
sólo depende de sus argumentos de entrada. Si una función pura es llamada muchas veces con los
mismos argumentos es posible acelerar la ejecución de la misma utilizando una estrategia denominada
memoizing: Se usa una cache para guardar la correspondencia entre los argumentos y los resultados.
Habitualmente esta cache es un hash, en el que las claves son los argumentos y los valores los resultados.
La variable usada como cache se sitúa en clausura con la subrutina. Para saber mas sobre memoizing
consulte el libro de Mark Jason Dominus Higher Order Perl [13]. El módulo Memoize , escrito por
Dominus, proporciona memoización automática de una función.
El siguiente ejemplo memoiza la función que computa los números de Fibonacci:
lhp@nereida:~/Lperl/src$ cat -n memoize2.pl
1 #!/usr/bin/perl -w
2 use Benchmark;
3 use Memoize;
4
5 sub fib {
6 my $n = shift;
7 if ($n < 2) { $n }
8 else { fib($n-1)+fib($n-2) }
9 }
10
11 {
12
13 my @fib = (0, 1);
14
15 sub fibm {
16 my $n = shift;
17
18 return $fib[$n] if defined $fib[$n];
19 $fib[$n] = fibm($n-1)+fibm($n-2);
20 }
21 }
22
23 sub fibM {
196
24 my $n = shift;
25 if ($n < 2) { $n }
26 else { fib($n-1)+fib($n-2) }
27 }
28
29 memoize ’fibM’;
30
31 my ($r, $m, $a);
32 timethese(2, {
33 recursivo => sub { $r = &fib(30) },
34 memoized => sub { $m = &fibm(30) },
35 automemoized => sub { $a = &fibM(30) }
36 }
37 );
38
39 print "recursivo = $r, memoized = $m, automemoized = $a\n";
Este es un ejemplo en el cuál es conveniente ocultar la cache e impedir posibles colisiones de la variable
@fib con cualesquiera otras variables que pudieran existir en el programa. La solución es hacer una
clausura (lı́neas 10-20).
La ejecución muestra como la técnica mejora claramente el rendimiento:
lhp@nereida:~/Lperl/src$ memoize2.pl
Benchmark: timing 2 iterations of automemoized, memoized, recursivo...
automemoized: 3 wallclock secs ( 2.98 usr + 0.00 sys = 2.98 CPU) @ 0.67/s (n=2)
(warning: too few iterations for a reliable count)
memoized: 0 wallclock secs ( 0.00 usr + 0.00 sys = 0.00 CPU)
(warning: too few iterations for a reliable count)
recursivo: 6 wallclock secs ( 5.97 usr + 0.00 sys = 5.97 CPU) @ 0.34/s (n=2)
(warning: too few iterations for a reliable count)
recursivo = 832040, memoized = 832040, automemoized = 832040
real 0m3.043s
user 0m3.050s
197
sys 0m0.010s
El programa usa la ecuación 4.1 para resolver el problema. El diseño recursivo de la subrutina da lugar
a un esquema divide-y-vencerás:
lhp@nereida:~/Lperl/src$ cat -n memoizeknap.pl
1 #!/usr/bin/perl -w
2 use strict;
3 use List::Util qw(max sum);
4
5 my $N = shift || 10;
6 my @w = map { 3 + int(rand($N-3)) } 1..$N;
7 my @p = @w;
8 my $C = int(sum(@w)/2);
9
10 sub knap {
11 my ($k, $c, $b) = @_;
12 my @s;
13
14 return $b if $k < 0;
15 $s[0] = knap($k-1, $c, $b);
16 if ($w[$k] <= $c) {
17 $s[1] = knap($k-1, $c-$w[$k], $b+$p[$k]);
18 }
19 return max(@s);
20 }
21
22 my $s = knap($N-1, $C, 0);
23
24 print "C = $C; w = @w\n";
25 print "$s\n";
Responda a las siguientes preguntas:
1. ¿Cual es el tamaño del árbol de búsqueda explorado por knap para un problema de la mochila
con N objetos?. La pregunta se refiere al peor caso posible.
3. Memoize usando el módulo Memoize la función knap. Use el módulo Benchmark para comparar
rendimientos. ¿Puede explicar las diferencias? ¿Como cambia el árbol de búsqueda explorado al
memoizar la función?
4. Memoize manualmente la función knap. Use el módulo Benchmark para comparar los rendimien-
tos de las tres versiones.
sub memoize {
my $func = shift;
my %cache;
my $stub = sub {
198
my $key = join ’,’, @_;
$cache{$key} = $func->(@_) unless exists $cache{$key};
return $cache{$key};
}
return $stub;
}
En las secciones 4.15.9 y 4.15.10 vimos como usando typeglobs selectivos podemos instalar en la
tabla de sı́mbolos un wrapper de una función dada. Nótese que la funcionalidad del módulo Memoize
cae dentro de la categorı́a del wrapping. Utilice la misma técnica para sustituir una función por su
memoizada.
4.18.5. Currying
Se denomina currying al proceso de representar una función de múltiples argumentos como una
función de un sólo argumento. Por ejemplo, la función suma $x+$y es una función de dos argumentos
($x, $y), pero puede verse también como una función de un argumento $x. ¿Cómo?. La clave esta
en pensar en la función suma como una función que cuando recibe el primer argumento $x devuelve
como resultado una función sub { $x+$_[0] } la cual suma a su argumento el primer argumento
$x. Para saber mas sobre currying consulte el libro Higher Order Perl [13]. El término currying hace
alusión al lógico americano Haskell Curry (1900-1982) que popularizó el concepto en 1930. Al parecer
Gottlob Frege ya habı́a introducido la idea en 1893. Moses Schönfinkel la redescubrió en 1924.
Ejercicio 4.18.2. Describa en palabras los dominios inicial y final de las funciones:
1. La función map.
2. La función splice
3. La función first: Consulte la documentación del módulo List::Util .
curry(f ) : D1 → F(D2 × . . . × Dn , E)
donde F(D2 × . . . × Dn , E) denota las funciones con argumentos en D2 × . . . × Dn y valores en E.
Cuando a la función curry(f ) se le pasa un argumento adicional x se obtiene una nueva función:
curry(f )(x) : D2 × . . . × Dn → E
Sigue un ejemplo de uso:
199
Veamos el código y una ejecución:
La técnica de currificación transforma una función ordinaria en una fábrica de funciones (function
factory). Al llamar a la fábrica con un parámetro nos devuelve una nueva función.
Currificar map
Veamos otro ejemplo de currificación. La función map es una función de dos argumentos: el primero
es código y el segundo una lista. La función retorna una lista. Matemáticamente:
200
11 print "Aleatorio = @r\n";
12 print "Suma de prefijos = @p\n";
201
5
6 sub node($$) {
7 my ($h, $t) = @_;
8 [$h, $t];
9 }
10
11 sub head {
12 my ($s) = @_;
13 $s->[0];
14 }
15
16 sub tail {
17 my ($s) = @_;
18 if (is_promise($s->[1])) {
19 $s->[1] = $s->[1]->();
20 }
21 $s->[1];
22 }
23
24 sub is_promise {
25 ref($_[0]) eq ’CODE’;
26 }
27
28 sub show_until_match {
29 my ($s, $regexp) = @_;
30 while ($s and head($s) !~ /$regexp/) {
31 print head($s);
32 $s = tail($s);
33 }
34 print head($s) if $s;
35 }
36
37 sub file2lazylist {
38 my $fn = shift;
39 my $fh = new IO::File;
40 $fh->open("< $fn") or carp "No se pudo abrir $fn: $!";
41
42 my $nl;
43 $nl = sub {
44 return unless defined($_ = <$fh>);
45 return node($_, $nl);
46 };
47 return ($fh, $nl)
48 }
49
50 my $file = shift;
51 my $regexp = shift;
52 my ($fh, $fl) = file2lazylist($file);
53 my $node = $fl->();
54 show_until_match($node, $regexp);
55 close($fh);
Al ejecutar el código anterior obtendremos una salida similar a esta:
lhp@nereida:~/Lperl/src/hop/Chap6$ useStreams1.pl useStreams.pl ’\(.*\)’
202
#!/usr/bin/perl -w
use strict;
use Carp;
use IO::File;
sub node($$) {
Para saber mas sobre listas perezosas consulte el libro Higher Order Perl [13].
203
Capı́tulo 5
Módulos
package C110;
# estamos en el espacio de nombres C110
print $C110::a;
# imprime 5
# note como accesamos el espacio de nombres C110...
# note el $ y los ::
Asi pues, para acceder a un identificador situado en un espacio de nombres diferente del actual
debemos prefijar el identificador con el nombre del paquete; esto se denomina especificación completa
del nombre o fully qualifying the name. Si un identificador no está completamente especificado, Perl
lo busca en el package actual.
Nótese que escribimos $C110::a y no C110::$a
204
5.2. Tablas de Sı́mbolos y Packages
Cada package tiene su propia tabla de sı́mbolos. Ası́ para acceder a una variable es preciso espe-
cificar de que tabla se habla (el package) y de que variable se habla (identificador dentro de la tabla).
Las variables en el package main pueden ser referidas prefijándolas con ::; ası́ el programa:
$ cat package_main.pl
#!/usr/local/bin/perl5.8.0 -w
$a = 4;
package toto;
$a = 8;
$::a = 7;
package main;
print $a, "\n";
print $toto::a, "\n";
$ ./package_main.pl
7
8
1 #!/usr/local/bin/perl5.8.0 -w
2
3 package toto;
4 $_ = 7;
5 package main;
6 print;
7 print "\n";
8 $main::_ = 4;
9 print;
10 print "\n";
Da como salida:
$ ./package_specialvars.pl
7
4
205
lhp@nereida:~/Lperl/src$ cat -n privacy.pl
1 #!/usr/bin/perl -w
2 use strict;
3 package tutu;
4 my $a = 10;
5
6 package titi;
7
8 if (defined($tutu::a)) { print "$tutu::a\n" }
9 else { print "No esta definida \$tutu::a\n"};
10 print "\$a = $a\n";
lhp@nereida:~/Lperl/src$ localsub.pl
En sub exterior. Llamo a la sub ’peer(5)’
En sub peer. Llamo a ’interior’
206
En sub interior anidada
En sub ’interior global’: 3
Tengo una subrutina en un package que no quiero que sea visible desde otros paquetes. ¿Que
puedo hacer para conseguirlo? (Recuerde que las subrutinas son siempre globales).
Explique cual será la salida del siguiente programa (la macro PACKAGE retorna el nombre
del paquete actual):
Sufijos
Como un paquete generalmente se hace para ser reutilizado muchas veces, se guarda en un archivo
libreria de extension .pl (por ejemplo cgilib.pl) o en un archivo módulo con el sufijo .pm.
207
/usr/local/lib/perl/5.8.8
/usr/local/share/perl/5.8.8
/usr/lib/perl5
/usr/share/perl5
/usr/lib/perl/5.8
/usr/share/perl/5.8
/usr/local/lib/site_perl
/usr/local/lib/perl/5.8.7
/usr/local/share/perl/5.8.7
/usr/local/lib/perl/5.8.4
/usr/local/share/perl/5.8.4.)
at (eval 17)[/usr/share/perl/5.8/perl5db.pl:628] line 2.
DB<4> x %INC
0 ’re.pm’
1 ’/usr/lib/perl/5.8/re.pm’
2 ’attributes.pm’
3 ’/usr/share/perl/5.8/attributes.pm’
.. ....................................................
42 ’GRID/Machine.pm’
43 ’/usr/local/share/perl/5.8.8/GRID/Machine.pm’
.. ....................................................
60 ’GRID/Machine/MakeAccessors.pm’
61 ’/usr/local/share/perl/5.8.8/GRID/Machine/MakeAccessors.pm’
62 ’Module/Which.pm’
63 ’/usr/local/share/perl/5.8.8/Module/Which.pm’
64 ’GRID/Machine/Result.pm’
65 ’/usr/local/share/perl/5.8.8/GRID/Machine/Result.pm’
.. ....................................................
110 ’Term/ReadLine/Gnu.pm’
111 ’/usr/lib/perl5/Term/ReadLine/Gnu.pm’
o bien:
DB<4> x module_is_loaded(’GRID::Machine’)
0 ’/soft/perl5lib/share/perl/5.8.8//GRID/Machine.pm’
El archivo cargado no tiene que necesariamente estar asociado con un package. Su evaluación por
require debe devolver verdadero. Por ello la última sentencia ejecutada deberá devolver una valor
cierto. De aquı́ que un paquete normalmente termine con:
return 1;
Si se omiten las comillas y el sufijo, se asume una extensión .pm:
208
lhp@nereida:/tmp$ perl -wde 0
main::(-e:1): 0
DB<1> require "CGI" # Error
Directory /usr/local/share/perl/5.8.8/CGI not allowed in require at (eval 5)[/usr/share/perl/5
DB<2> require CGI # OK
DB<3> require "CGI.pm" # OK
si tienes mas de una versión de Perl, puede que difieran en sus caminos de búsqueda:
$ perl5.8.0 -V
Summary of my perl5 (revision 5.0 version 8 subversion 0) configuration:
...
Characteristics of this binary (from libperl):
Compile-time options: USE_LARGE_FILES
Built under linux
Compiled at May 14 2003 16:02:03
@INC:
/usr/local/lib/perl5/5.8.0/i686-linux
/usr/local/lib/perl5/5.8.0
/usr/local/lib/perl5/site_perl/5.8.0/i686-linux
/usr/local/lib/perl5/site_perl/5.8.0
/usr/local/lib/perl5/site_perl
.
209
El Significado de ::
El compilador sustituye cada :: por el separador de caminos. Asi la orden: use Text::ParseWords;
se traduce por el fichero Text/ParseWords.pm. En cierta máquina el directorio exacto podrı́a ser algo
similar a /usr/lib/perl5/5.00503/Text/ParseWords.pm
2. Definir la variable PERL5LIB como secuencia de caminos de acceso separados por el sı́mbolo dos
puntos (:)
unshift(@INC, ’/home/casiano/perl/src/packages/’);
require ’mypackage.pl’;
4. Usar el módulo lib el cual añade los caminos especificados como argumentos en tiempo de
compilación:
#!/usr/local/bin/perl5.8.0 -w
use lib qw(/home/lhp/perl/src /home/lhp/public_html/cgi-bin);
print "@INC \n";
bash-2.05b$ ./use_lib.pl
/home/lhp/perl/src /home/lhp/public_html/cgi-bin
/usr/local/lib/perl5/5.8.0/i686-linux /usr/local/lib/perl5/5.8.0
/usr/local/lib/perl5/site_perl/5.8.0/i686-linux
/usr/local/lib/perl5/site_perl/5.8.0 /usr/local/lib/perl5/site_perl .
BEGIN {
unshift @INC, "home/lhp/perl/src";
}
Cuando Perl esta en la fase de compilación y encuentra un bloque con nombre BEGIN pasa a
ejecutarlo y continúa con la compilación. Puede existir mas de un bloque BEGIN en un programa, en
cuyo caso se van ejecutando durante la fase de compilacion según se van viendo.
210
Ejercicio 5.5.1. Explique la siguiente salida:
Observe que el primer require no produce ninguna queja, el segundo si. Consulte perldoc -f require
para entender este detalle.
Esto hace que en el momento de la carga del módulo Biblio::Doc se ejecute automáticamente
su subrutina VERSION (si existe) con argumento el número de versión. Existe una subrutina VERSION
por defecto, que es proveı́da por el módulo UNIVERSAL (véase la sección 6.6). La rutina por defecto
comprueba el valor en la variable $VERSION del paquete en cuestión.
Para conocer la versión de un módulo podemos escribir:
o bien:
5.7. Importación
Que es Importar
A menudo queremos importar ciertos sı́mbolos de un módulo en nuestro espacio de nombres, para
ahorrarnos pulsaciones: queremos escribir sqrt y no math::sqrt.
La Subrutina import
Una vez que un módulo ha sido localizado y compilado dentro de un programa Perl como conse-
cuencia de una declaración use, el siguiente paso es la ejecución de la subrutina import de ese módulo.
De hecho, la sentencia use module List es equivalente a:
La conducta por defecto de import es vacı́a, pero podemos cambiar dicha conducta creando en
nuestro módulo nuestra propia subrutina import. Es decir, el módulo en cuestión tiene que estar
preparado para exportar esos identificadores al código cliente que los utiliza. El uso de BEGIN implica
que require e import se ejecuten en el momento de la compilación.
211
Argumentos de import
Cuando es llamada import recibe como argumentos cualesquiera argumentos que aparezcan des-
pues de la sentencia use. Por ejemplo, si un programa incluye una lı́nea como:
use Technique::DandC::FFT ("sample");
entonces, una vez localizado y compilado el módulo Technique::DandC::FFT se procede a la llamada
de import:
Technique::DandC::FFT::import("Technique::DandC::FFT", "sample");
Aprenda a Decir no
Existe una declaración no que puede ser usada para desechar las importaciones realizadas mediante
use:
no integer;
no strict ’refs’;
La desactivación se mantendrá en el ámbito léxico de la declaración no.
Ejemplo
Supongamos que queremos que la subrutina import del módulo myimport.pm que se define mas
abajo exporte su función titi al espacio de nombres del package llamador, de manera que cuando se
ejecute el siguiente programa usemyimport.pl:
$ cat -n ./usemyimport.pl
1 #!/usr/bin/perl -w -I.
2 use strict;
3 use myimport;
4
5 my $titi = 4;
6 my @titi = (1,2,3);
7 &titi();
8
9 print"$titi\n";
10 print"@titi\n";
de lugar a la salida:
$ ./usemyimport.pl
Hola
4
1 2 3
2. Para instalar titi hay que averiguar el nombre del paquete llamador. La función caller devuelve
el ”package” desde el cuál fue llamada la subrutina (sección 1.15.10).
3. Para instalar la entrada titi en la tabla de sı́mbolos del paquete llamador hay que usar typeglobs
y referenciado simbólico.
4. Minimizamos el periodo de desactivación de control estricto del referenciado simbólico (no strict ’refs’).
5. Observe el uso de la opción -I. en la primera lı́nea del programa cliente usemyimport.pl:
garantiza que el intérprete perl encontrará el módulo ./myimport.pm.
212
lhp@nereida:~/Lperl/src$ cat -n myimport.pm
1 package myimport;
2 use strict;
3
4 sub titi {
5 print "Hola\n";
6 }
7
8 sub import {
9
10 my ($caller_package) = caller;
11 {
12 no strict ’refs’;
13 *{$caller_package."::titi"} = \&titi;
14 }
15 }
16
17 1;
Ejercicio 5.7.1. Explique que ocurre si cambiamos la lı́nea 13 por
*{"$caller_package::titi"} = \&titi;
¿Seguirá funcionando?
Ejemplo
Consideremos el siguiente código que contiene dos paquetes: main y toto:
$ cat -n package_main.pl
1 #!/usr/local/bin/perl5.8.0 -w
2
3 $a = 4;
4
5 package toto;
6 $a = 8;
7 $::a = 7;
8
9 package main;
10 print $a, "\n";
11 print $toto::a, "\n";
213
La ejecución con el depurador muestra la estructura de un stash:
$ perl -d package_main.pl
Loading DB routines from perl5db.pl version 1.25
main::(package_main.pl:3): $a = 4; # a de main
DB<1> n
toto::(package_main.pl:6): $a = 8; # a de toto
DB<1> n
toto::(package_main.pl:7): $::a = 7;
DB<1> n
main::(package_main.pl:10): print $a, "\n";
DB<1> x %toto::
0 ’a’ # clave
1 *toto::a # valor
DB<2> x $toto::{a} # indexamos
0 *toto::a
DB<3> x *{toto::a}{SCALAR} # typeglob usado como hash
0 SCALAR(0x8163978) # referencia a un escalar
-> 8
DB<4> x *{$toto::{a}}{SCALAR}
0 SCALAR(0x8163978)
-> 8
DB<5> *b = $toto::{a}
DB<6> p $b
8
la llamada:
48 DumpPackage::dumpackage("Test");
lhp@nereida:~/Lperl/src/advanced_perl_programming/Typeglob$ dumppackage.pl
y
Lista @y: [
1,
3,
4
]
=============================
214
__ANON__
=============================
x
Escalar $x: 10
=============================
f
Escalar $f: sub {
package Test;
use strict ’refs’;
1;
}
=============================
z
Escalar $z: 300
Hash %z: {
1 => 2,
3 => 4,
5 => 6
}
=============================
Para imprimir una estructura de datos compleja existen varias soluciones. Puede usar el módulo
Data::Dumper para volcar las estructuras de datos resultantes. Consulte la documentación del módulo.
Puede partir del esqueleto que sigue. Las lı́neas de puntos indican lugares en los que deberá insertar
el código apropiado.
215
31 if (defined (%var)) {
.. ..................................
35 }
36 print "============================= \n";
37 }
38 }
Jerarquı́a de Ficheros
La jerarquı́a de directorios de nuestra aplicación es:
lhp@nereida:~/Lperl/src/systemcommand$ pwd
/home/lhp/Lperl/src/systemcommand
lhp@nereida:~/Lperl/src/systemcommand$ tree
.
|-- lib
| ‘-- System
| ‘-- Commands.pm
‘-- script
‘-- usesystemcommand.pl
3 directories, 2 files
El Módulo
El módulo System::Commands proporciona una interfaz funcional a los comandos del sistema ope-
rativo:
216
17 return ‘$command @_‘;
18 };
19
20 goto &{$AUTOLOAD};
21
22 }
23
24 sub import {
25 my $mypackage = shift;
26 push @ALLOWED, @_;
27
28 my ($caller_package) = caller;
29 {
30 no strict ’refs’;
31 for my $command (@_) {
32 # Comprobar si existe el comando
33 die "Error! ’$command’ command does not exists!\n" unless which($command);
34 *{$caller_package."::".$command} = \&{$command};
35 }
36 }
37 }
38
39 1;
El Cliente
Veamos el programa cliente:
Ejecución
Sigue una ejecución:
lhp@nereida:~/Lperl/src/systemcommand$ cd script/
lhp@nereida:~/Lperl/src/systemcommand/script$ usesystemcommand.pl
******Contexto de lista*********
ls: pe*.pl: No existe el fichero o el directorio
217
******Contexto escalar*********
ls: pe*.pl: No existe el fichero o el directorio
******No existe*********
Undefined subroutine &main::chuchu called at ./usesystemcommand.pl line 16.
Clausura y Memoria
La siguiente sesión con el depurador muestra el interés de la lı́nea
y de tener @ALLOWED declarada como una variable léxica en el ámbito del fichero:
el módulo provee una función tag que cuando es llamada como sigue:
La llamada:
font("Alerta!", color=>red);
devolverá:
218
<font color="red">Alerta!</font>
etc.
Necesitará una función AUTOLOAD para realizar esta práctica.
Para la subrutina generada utilize la estrategia de paso de parámetros a través de un hash para
tener argumentos con nombres (véase la sección 1.15.7)
Las funciones creadas serán insertadas en el paquete/espacio de nombres especificado como argu-
mento de la directiva use HTML::Tags, como se muestra en el siguiente ejemplo:
{
package MyHTML;
my $t =
html(
head(title(’Hello World!),
body(
h1(’Hello World!’),
p,hr,p,
)
)
}
# de nuevo en package main
print "$t\n";
my $a = \&glob;
print $a->(’*’);
lhp@nereida:~/Lperl/src$ perl reftooperator.pl
Undefined subroutine &main::glob called at reftooperator.pl line 5.
Los nombres de los operadores no están en una tabla de sı́mbolos de un paquete particular.
219
3 use strict;
4 use subs qw(glob);
5
6 sub glob {
7 my $regexp = shift;
8
9 opendir my $DIR, "." or die "$!";
10 my @files = grep /$regexp/, readdir $DIR;
11 close($DIR);
12 return @files;
13 }
14
15 sub tutu {
16 my @a = glob(’^.[cd][ba].*\.pl’); # LLamada al nuevo glob
17 local $" = "\n";
18 my @b = <^.[cd][ba].*\.pl>; # El diamante es una llamada a glob
19 return <<"EOI";
20 ---glob(’^.[cd][ba].*\.pl’)---
21 @a
22 ---<^.[cd][ba].*\.pl>---
23 @b
24 EOI
25 }
26
27 package main;
28
29 sub tutu {
30 my @a = glob(’^.[cd][ba].*\.pl’);
31 local $" = "\n";
32 return "@a\n";
33 }
34
35 print Glob::Regexp::tutu;
36 print "---------main-----------\n";
37 print tutu;
Al ejecutar el programa vemos que la subrutina Glob::Regexp::tutu (llamada desde la lı́nea 35)
usa la nueva versión de glob mientras que los usos de glob dentro del paquete main usan la versión
original.
La ejecución también muestra que el uso de diamantes en ”modo directorio” como en <^.[cd][ba].*\.pl>
implica una llamada implı́cita a glob .
lhp@nereida:~/Lperl/src$ usesubs.pl
---glob(’^.[cd][ba].*.pl’)---
idbetcurly.pl
pcap.pl
scalar_sets.pl
scalar_sets2.pl
sdbm.pl
sdbm2.pl
---<^.[cd][ba].*.pl>---
idbetcurly.pl
pcap.pl
scalar_sets.pl
220
scalar_sets2.pl
sdbm.pl
sdbm2.pl
---------main-----------
El Paquete CORE::GLOBAL Como vimos en la sección 5.11 el pragma use subs permite la susti-
tución de un operador en el ámbito de un paquete. Si se quiere sustituir cualquier referencia (desde el
paquete o fuera de él) al operador por la nueva versión es necesario declarar la nueva versión dentro
del paquete CORE::GLOBAL :
$ perl -e ’BEGIN { *CORE::GLOBAL::system = sub { print "hello $_[0]\n" } } system "foo"’
hello foo
Sigue un ejemplo en el que reeemplazamos gobalmente el operador glob:
lhp@nereida:~/Lperl/src$ cat -n coreglob.pl
1 #!/usr/local/bin/perl -w
2 package Tutu;
3 use strict;
4
5 {
6 no warnings;
7 *CORE::GLOBAL::glob = sub {
8 my $regexp = shift;
9
10 opendir my $DIR, "." or die "$!";
11 my @files = grep /$regexp/, readdir $DIR;
12 close($DIR);
13 return @files;
14 };
15 }
16 1;
17
18 package main;
19 use strict;
20
21 sub tutu {
22 my @a = glob(’^.[cd][ba].*\.pl’);
23 local $" = "\n";
24 return "@a\n";
25 }
26
27 print "---------main-----------\n";
28 print tutu;
221
ahora la llamada en el paquete main a glob encuentra la nueva versión:
lhp@nereida:~/Lperl/src$ coreglob.pl
---------main-----------
idbetcurly.pl
pcap.pl
scalar_sets.pl
scalar_sets2.pl
sdbm.pl
sdbm2.pl
Heredando de Exporter
El módulo Exporter provee diferentes mecanismos para realizar la interfaz pública del módulo
que estamos desarrollando.
En el ejemplo que sigue, la inicialización del vector especial @ISA en la lı́nea 5 hace que (junto
que el use Exporter de la lı́nea 3) el módulo Modexample::HopsExport ”herede” de Exporter los
métodos que nos hacen falta como import.
Aún cuando no hemos visto objetos, puede dar una ojeada a la sección 6.6 que trata sobre la herencia.
La herencia indica que los métodos definidos y exportados por los paquetes en el array @ISA estan
disponibles en el módulo cliente.
EXPORT y EXPORT OK
222
El método import que proporciona Exporter examina la lista de cadenas en @EXPORT para
determinar que funciones y variables se exportan por defecto.
Si tenemos variables o rutinas que sólo deben ser exportadas bajo demanda del cliente (como foo
en use Tutu qw(foo)) debemos escribir sus nombres en la lista @EXPORT OK .
La lı́nea de asignación a la variable @EXPORT hace que se cree un alias para la función hop_along
en el programa cliente. De este modo no es necesario llamar a la función por su nombre completo
Modexample::HopsExport::hop_along sino simplemente hop_along.
Reglas de Exportación El módulo Exporter permite definir de manera precisa la interfaz externa
de nuestro módulo. Para ello deberemos escribir el siguiente código en NuestroModulo.pm
package NuestroModulo;
use strict;
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION);
use Exporter;
$VERSION = ’1.00’;
@ISA = qw(Exporter);
######################
Nuestro código va aqui
######################
1;
En los ficheros desde los que queremos usar nuestro módulo deberemos escribir una de estas lı́neas:
use NuestroModulo; # Importar los sı́mbolos por defecto
use NuestroModulo qw(...); # Importar los sı́mbolos listados
use NuestroModulo (); # No importar sı́mbolos
use NuestroModulo qw(:TAG1)# Importar el conjunto del tag
223
Cuando alguien escribe use NuestroModulo, ello implica un require "NuestroModulo.pm" se-
guido de una llamada a NuestroModulo->import() durante la compilación.
El método import, que es heredado del módulo EXPORTER usa un conjunto de variables en el
paquete que gobiernan la conducta de exportación del módulo. Estas variables son:
$VERSION
Se usa asi:
@EXPORT
Contiene la lista de funciones y variables que serán exportadas por defecto al espacio de nombres
del cliente.
EXPORT_OK
Este vector contiene los sı́mbolos que serán cargados únicamente si se pregunta especı́ficamente
por ellos. Si los vectores se cargan asi:
Usando use
Supongamos el módulo:
package Trivial::Tutu;
our @EXPORT = qw(uno dos);
our @EXPORT_OK = qw(tres cuatro cinco);
use Exporter;
our @ISA = qw(Exporter);
use Trivial::Tutu;
Esto nos exportarı́a uno dos.
224
use Trivial::Tutu();
No se importa ningún sı́mbolo.
use Trivial::Tutu(siete);
Es un error. Todo sı́mbolo importado debe estar bien en la lista @EXPORT bien en la lista
@EXPORT_OK.
La Etiqueta :DEFAULT
Si lo que se quiere es obtener todo lo que hay en @EXPORT además de los ”extras” se deberá usar
la etiqueta especial :DEFAULT. Por ejemplo:
%EXPORT_TAGS = (
Functions => [ qw(F1 F2 Op_Func) ],
Variables => [ qw(@List %Table) ],
);
Como se ha dicho, el módulo CGI.pm funciona con esta filosofı́a. Véase el siguiente ejemplo que
usa CGI en el que se carga el grupo :standard:
$ cat -n cgitaste.pl
1 #!/usr/bin/perl -w
2 use CGI qw(:standard);
3
4 print header;
5 print start_html(’Un ejemplo Sencillo’),
6 h1(’Un ejemplo Sencillo’),
7 start_form,
8 "¿Tu nombre? ",textfield(’nombre’),
9 p,
10 "¿Matriculado en?",
11 checkbox_group(-name=>’estudios’,
12 -values=>[’Sistemas’,’Gestión’,’Superior’],
13 -defaults=>[’sistemas’]),
14 p,
15 "¿Lenguaje favorito? ",
16 popup_menu(-name=>’len’,
17 -values=>[’C’,’C++’,’Pascal’,’Java’,’Lisp’,’Prolog’,’Python’,’Perl’]),
18 p,
19 submit(-name=>"Enviar"),
20 end_form,
225
21 hr;
22
23 if (param()) {
24 print h1(’Tus datos:’),
25 p,
26 "Nombre: ",em(param(’nombre’)),
27 p,
28 "Estudios: ",em(join(", ",param(’estudios’))),
29 p,
30 "Lenguaje favorito: ",em(param(’len’)),
31 hr;
32 }
33 print end_html;
Referencias
Recuerde consultar la documentación de Exporter con perldoc Exporter.
http://cpan.imasd.elmundo.es
ftp://ftp.rediris.es/mirror/CPAN/
ftp.ri.telefonica-data.net
ftp://ftp.etse.urv.es/pub/perl/
Las distribuciones son probadas en una variedad de plataformas por los CPAN testers: http://testers.cpan.org/
El módulo CPAN provee el software necesario para la instalación automática de módulos desde
CPAN
226
5.14.1. Instalación a mano
Si no ha sido instalado aún el módulo CPAN - o no es el administrador del sistema - los pasos para
una instalación son los siguientes:
Visite http://search.cpan.org/.
Una vez allı́, en el menu eliga la opción distributions o bien all y en la casilla de búsqueda
ponga el nombre del módulo o el tópico de búsqueda. Se nos mostrarán todos los módulos para
los que la búsqueda tiene éxito.
Elija el módulo concreto que le interesa. La documentación POD del módulo es automaticamente
convertida a HTML (figura 5.1).
El enlace de distribución esta formado a partir del nombre y el número de versión (Net-SSH-Perl-1.30
en el ejemplo).
227
> perl Makefile.PL
Si, por el contrario, quieres instalar los módulos en tu propio directorio, deberas escribir algo
parecido a esto:
o bien:
En ambos casos asegúrate que los directorios de búsqueda en los que las librerı́as quedan insta-
lados (~/perl5/...) serán encontrados por Perl.
> make
> make test
> make install
Referencias
Véase perldoc perlmodinstall. El documento describe como instalar módulos en otros Sistemas
Operativos (Windows, Macintosh, etc.). Consulte tambien http://www.cpan.org/misc/cpan-faq.html.
$ corelist Data::Dumper
Recuerde pasar la opción adecuada cuando ejecute perl Makefile.PL. No se olvide de usar la
opción -I en la llamada al intérprete perl indicando el camino en el que se debe buscar el módulo o
bien establecer la variable PERL5LIB al valor adecuado.
Module::Util
Si Module::Util está instalado, use pm which:
228
Use perldoc
Para saber si un módulo está instalado y donde se encuentra (supuesto que el módulo contiene
documentación en su interior) use perldoc -l:
229
/usr/share/perl/5.8/strict.pm
/usr/local/share/perl/5.8.8/IPC/PerlSSH.pm
/usr/share/perl/5.8/Symbol.pm
Módulos en el Núcleo de Perl En ocasiones no basta con saber que el módulo está instala-
do. Necesitamos saber si pertenece o no al núcleo de Perl. El método first release del módulo
Module::CoreList da respuesta a esta pregunta:
$ corelist Data::Dumper
Al ejecutarlo (con los privilegios adecuados) podemos eliminar los ficheros implicados:
230
removing /usr/local/share/perl/5.8.8/Parse/Flex.pdf
removing /usr/local/share/perl/5.8.8/Parse/Flex.pm
removing /usr/local/share/perl/5.8.8/Parse/Flex.tex
removing /usr/local/share/perl/5.8.8/Parse/Flex.toc
removing /usr/local/share/perl/5.8.8/Parse/Flex/Generate.pdf
removing /usr/local/share/perl/5.8.8/Parse/Flex/Generate.pm
removing /usr/local/lib/perl/5.8.8/auto/Parse/Flex/.packlist
nereida:/usr/sbin# ls -l /usr/local/share/perl/5.8.8/Parse/F*
total 0
Si conoces el nombre del módulo a instalar, por ejemplo Text::Balanced, todo lo que tienes que
hacer es escribir:
Configuración de CPAN
La primera vez que se ejecuta el módulo CPAN se dispara un proceso de configuración que establece
los valores por defecto que posibilitan la descarga, desempaquetado construcción y verificación de los
modulos cuya instalacion has requerido.
En el siguiente ejemplo arrancamos el módulo CPAN como administradores del sistema:
$ cpan
nereida:~/src/perl/Nmap-scanner# cpan
CPAN: File::HomeDir loaded ok (v0.69)
El Comando h
Pedimos ayuda . . .
231
cpan[1]> h
Upgrade
r WORDs or /REGEXP/ or NONE report updates for some/matching/all modules
upgrade WORDs or /REGEXP/ or NONE upgrade some/matching/all modules
Pragmas
force CMD try hard to do command fforce CMD try harder
notest CMD skip testing
Other
h,? display this menu ! perl-code eval a perl command
o conf [opt] set and query options q quit the cpan shell
reload cpan load CPAN.pm again reload index load newer indices
autobundle Snapshot recent latest CPAN uploads
cpan[2]>
El Comando a
El comando a nos permite obtener información sobre los autores:
cpan> a /.*wall/
Author CCWALLACE ("C. Chad Wallace" <[email protected]>)
Author LWALL ("Larry Wall. Author of Perl. Busy man." <[email protected]>)
Author PAWAL ("Patrik Wallstrom" <[email protected]>)
Author SABREN ("Michal Wallace" <[email protected]>)
Author SHAWNPW ("Shawn P. Wallace" <[email protected]>)
Author THW ("Thomas Walloschke" <[email protected]>)
Author ZAPHAR ("Jeremy Wall" <[email protected]>)
7 items found
El Comando b
El comando b nos permite obtener información sobre los bundles . Los bundles son grupos de
módulos relacionados. Los bundles son usados por CPAN para simplificar la instalación de un grupo
de módulos. Una sóla b listará los bundles disponibles. Si se especifica un argumento se obtiene
información sobre el bundle:
cpan> b /ssh/
Bundle id = Bundle::SSH
CPAN_USERID SZABGAB (Gabor Szabo <[email protected]>)
CPAN_VERSION 1.00
232
CPAN_FILE S/SZ/SZABGAB/Bundle-SSH-1.00.tar.gz
MANPAGE Bundle::SSH - A bundle to install modules to use SSH from Perl
CONTAINS Net::SSH Math::Pari Class::Loader Crypt::Random Digest::SHA1 \
Digest::HMAC Digest::BubbleBabble Digest::MD2 \
Convert::ASN1 Crypt::Rijndael Crypt::CBC Crypt::DES \
Crypt::DES_EDE3 Convert::PEM Data::Buffer Crypt::DSA \
Crypt::DH String::CRC32 Math::GMP Compress::Zlib \
Convert::ASCII::Armour Crypt::Blowfish Crypt::Primes \
Sort::Versions Tie::EncryptedHash Crypt::RSA \
Net::SSH::Perl \
INST_FILE /root/.cpan/Bundle/SSH.pm
INST_VERSION 1.00
Evaluación de Expresiones
El comando ! permite la evaluación de expresiones Perl:
El Comando r
El comando r nos permite conocer que módulos necesitan actualización:
cpan[2]> r /Net/
CPAN: Storable loaded ok (v2.15)
Going to read /root/.cpan/Metadata
Database was generated on Thu, 08 May 2008 14:29:50 GMT
CPAN: YAML loaded ok (v0.66)
Going to read /root/.cpan/build/
............................................................................DONE
Found 88 old builds, restored the state of 41
233
Net::Server 0.96 0.97 RHANDOM/Net-Server-0.97.tar.gz
300 installed modules have no parseable version number
El comando upgrade
Una vez hemos visto que un módulo necesita actualización podemos actualizarlo con upgrade .
234
cpan[4]>
El Comando ls
El comando ls nos permite listar los módulos desarrollados por un autor:
cpan> ls DCONWAY
Fetching with LWP:
ftp://archive.progeny.com/CPAN/authors/id/D/CHECKSUMS
Fetching with LWP:
ftp://archive.progeny.com/CPAN/authors/id/D/DC/CHECKSUMS
Fetching with LWP:
ftp://archive.progeny.com/CPAN/authors/id/D/DC/DCONWAY/CHECKSUMS
4299 2001-05-22 DCONWAY/Acme-Bleach-1.12.tar.gz
2410 2002-05-01 DCONWAY/Acme-Don-t-1.00.tar.gz
2659 2002-05-02 DCONWAY/Acme-Don-t-1.01.tar.gz
11942 2001-06-02 DCONWAY/Attribute-Handlers-0.70.tar.gz
12344 2001-09-02 DCONWAY/Attribute-Handlers-0.75.tar.gz
12858 2001-11-14 DCONWAY/Attribute-Handlers-0.76.tar.gz
...................................... etc.etc.tg.gz
347 2006-05-05 DCONWAY/Text-Balanced-1.98.meta
29040 2006-05-05 DCONWAY/Text-Balanced-1.98.tar.gz
18557 2003-04-02 DCONWAY/Text-Reform-1.08.tar.gz
20493 2003-04-09 DCONWAY/Text-Reform-1.10.tar.gz
20902 2003-05-07 DCONWAY/Text-Reform-1.11.tar.gz
7871 1999-05-14 DCONWAY/Tie-SecureHash-1.02.tar.gz
7994 1999-11-11 DCONWAY/Tie-SecureHash-1.03.tar.gz
447 2005-08-03 DCONWAY/Toolkit-0.0.2.meta
5491 2005-08-03 DCONWAY/Toolkit-0.0.2.tar.gz
cpan> ls DCON*
La Clase CPAN::Shell
Los comandos que están disponibles en la interfaz de la shell son métodos del paquete CPAN::Shell.
Cada vez que se introduce un comando shell la entrada es analizada por la rutina Text::ParseWords::shellwords
de Text::ParseWords, la cual analiza la entrada como lo hacen la mayorı́a de las shells:
235
El Comando look
El comando look abre una shell en el directorio en el que se ha descargado la distribución
especificada. Si no se ha descargado la distribución procederá a hacerlo:
nereida:~# cpan
CPAN: File::HomeDir loaded ok (v0.69)
cpan[6]> d /Expect-Simple/
Distribution id = D/DJ/DJERIUS/Expect-Simple-0.04.tar.gz
CPAN_USERID DJERIUS (Diab Jerius <[email protected]>)
CONTAINSMODS Expect::Simple
UPLOAD_DATE 2008-05-06
236
Expect-Simple-0.04/META.yml
Expect-Simple-0.04/Changes
Expect-Simple-0.04/MANIFEST
Expect-Simple-0.04/t/
Expect-Simple-0.04/t/testprog
Expect-Simple-0.04/t/Expect-Simple.t
Expect-Simple-0.04/LICENSE
Expect-Simple-0.04/lib/
Expect-Simple-0.04/lib/Expect/
Expect-Simple-0.04/lib/Expect/Simple.pm
Expect-Simple-0.04/ChangeLog
CPAN: File::Temp loaded ok (v0.18)
cpan[8]>
cpan> o conf
CPAN::Config options and /home/casiano/.cpan/CPAN/MyConfig.pm:
commit Commit changes to disk
defaults Reload defaults from disk
init Interactive setting of all options
build_cache 1000
build_dir /scratch/casiano/build
cache_metadata 1
237
cpan_home /scratch/casiano/.cpan
dontload_hash
ftp /usr/bin/ftp
ftp_proxy
getcwd cwd
gpg /usr/bin/gpg
gzip /bin/gzip
histfile /scratch/casiano/.cpan/histfile
histsize 100
http_proxy
inactivity_timeout 0
index_expire 1
inhibit_startup_message 0
keep_source_where /scratch/casiano/.cpan/sources
lynx /usr/bin/lynx
make /usr/bin/make
make_arg
make_install_arg
makepl_arg PREFIX=/soft/perl5lib/
ncftpget /usr/bin/ncftpget
no_proxy
pager /usr/bin/less
prerequisites_policy ask
scan_cache atstart
shell /bin/bash
tar /bin/tar
term_is_latin 1
unzip /usr/bin/unzip
urllist
ftp://cpan.ip.pt/pub/cpan/
ftp://ftp.rediris.es/mirror/CPAN/
ftp://ftp.etse.urv.es/pub/perl/
ftp://ftp.ri.telefonica-data.net/CPAN
http://cpan.imasd.elmundo.es/
wget /usr/bin/wget
Known options:
commit commit session changes to disk
defaults reload default config values from disk
help this help
init enter a dialog to set all or a set of parameters
Edit key values as in the following (the "o" is a literal letter o):
o conf build_cache 15
o conf build_dir "/foo/bar"
o conf urllist shift
o conf urllist unshift ftp://ftp.foo.bar/
o conf inhibit_startup_message 1
238
Modificación de la Lista de Servidores CPAN
Es posible consultar una opción especı́fica:
cpan> o conf urllist
urllist
ftp://archive.progeny.com/CPAN/
ftp://cpan-sj.viaverio.com/pub/CPAN/
ftp://cpan.calvin.edu/pub/CPAN
ftp://cpan.cs.utah.edu/pub/CPAN/
ftp://cpan.digisle.net/pub/CPAN
ftp://cpan.erlbaum.net/
ftp://cpan.llarian.net/pub/CPAN/
Y si nos conviene, modificarlas:
cpan> o conf urllist unshift ftp://ftp.rediris.es/mirror/CPAN/
cpan> o conf urllist
urllist
ftp://ftp.rediris.es/mirror/CPAN/
ftp://archive.progeny.com/CPAN/
ftp://cpan-sj.viaverio.com/pub/CPAN/
ftp://cpan.calvin.edu/pub/CPAN
ftp://cpan.cs.utah.edu/pub/CPAN/
ftp://cpan.digisle.net/pub/CPAN
ftp://cpan.erlbaum.net/
ftp://cpan.llarian.net/pub/CPAN/
Type ’o conf’ to view configuration edit options
Ahora al proceder a una instalación la URL insertada será la primera en ser consultada:
cpan> install Email::Find
Running install for module Email::Find
Running make for M/MI/MIYAGAWA/Email-Find-0.09.tar.gz
CPAN: LWP::UserAgent loaded ok
Fetching with LWP:
ftp://ftp.rediris.es/mirror/CPAN/authors/id/M/MI/MIYAGAWA/Email-Find-0.09.tar.gz
....
Para ver el estado de los mirrors puede consultar cualquiera de estas direcciones:
http://www.cs.uu.nl/stats/mirmon/cpan.html#es.
http://mirrors.cpan.org/. En esta página es posible buscar por paı́ses. Véase una posible salida
para ’España’:
239
Lea el nodo What Does CPAN Mirror ”Freshness Date”Mean? en PerlMonks
~/.cpan/sources# tree
.
|-- MIRRORED.BY
|-- authors
| |-- 01mailrc.txt.gz
| |-- 01mailrc.txt.gz.bak
| ‘-- id
| |-- A
| | |-- AF
| | | ‘-- AFERBER
| | | |-- CHECKSUMS
| | | ‘-- Thread-RWLock-1.02.tar.gz
| | |-- AM
| | | ‘-- AMS
| | | |-- CHECKSUMS
| | | ‘-- Storable-2.13.tar.gz
| | ‘-- AR
| | ‘-- AREIBENS
| | |-- CHECKSUMS
| | ‘-- PDF-API2-0.3r77.tar.gz
| |-- B
| | |-- BD
| | | ‘-- BDARRAH
| | | |-- CHECKSUMS
| | | ‘-- Proc-ParallelLoop-0.5.tgz
............ etc.
| |-- V
| | ‘-- VI
| | ‘-- VIPUL
| | |-- CHECKSUMS
240
| | |-- Class-Loader-2.02.tar.gz
| | |-- Convert-ASCII-Armour-1.4.tar.gz
| | |-- Crypt-Primes-0.50.tar.gz
| | |-- Crypt-RSA-1.50.tar.gz
| | |-- Crypt-Random-1.23.tar.gz
| | ‘-- Tie-EncryptedHash-1.21.tar.gz
| |-- f
| | ‘-- f
| | ‘-- f
| | ‘-- ft
| | ‘-- ftp:
| | ‘-- sunsite.rediris.es
| | ‘-- mirror
| | ‘-- CPAN
| | ‘-- modules
| | ‘-- by-module
| | ‘-- Class
| | ‘-- Class-MethodMaker-2.04-1.tar
| ‘-- t
| ‘-- t
| ‘-- t
| ‘-- tm
| ‘-- tmp
| ‘-- CPAN
‘-- modules
|-- 02packages.details.txt.gz
|-- 02packages.details.txt.gz.bak
|-- 03modlist.data.gz
‘-- 03modlist.data.gz.bak
/etc/perl/CPAN/Config.pm initialized.
If you do not want to enter a dialog now, you can answer ’no’ to this
question and I’ll try to autoconfigure. (Note: you can revisit this
dialog anytime later by typing ’o conf init’ at the cpan prompt.)
241
cpan[1]> o conf init makepl_arg mbuildpl_arg prefs_dir
242
cpan[2]> o conf commit
commit: wrote ’/home/pp2/.cpan/CPAN/MyConfig.pm’
INSTALLARCHLIB INSTALL_BASE/lib/perl5/$Config{archname}
INSTALLPRIVLIB INSTALL_BASE/lib/perl5
INSTALLBIN INSTALL_BASE/bin
INSTALLSCRIPT INSTALL_BASE/bin
INSTALLMAN1DIR INSTALL_BASE/man/man1
INSTALLMAN3DIR INSTALL_BASE/man/man3
243
Vemos que los módulos (p.ej. GRID::Machine) fueron instalados en /home/pp2/personalmodules/lib/perl5/,
los manuales en /home/pp2/personalmodules/man/ y los guiones en /home/pp2/personalmodules/bin/.
Para tener operativo nuestro repositorio tendremos que añadir estos caminos a las correspondientes
variables de entorno. Lo aconsejable es incluir estos comandos en .bashrc o en.cshrc, según sea
nuestra shell, para que se ejecute cada vez que creamos una nueva shell:
PATH
Establecer la variable de entorno PATH para encontrar los guiones/ejecutables. En una bash:
$ ps
PID TTY TIME CMD
12174 pts/21 00:00:00 bash
21386 pts/21 00:00:00 ps
$ export PATH=$PATH:/home/pp2/personalmodules/bin/
$ echo $PATH
/usr/local/bin:/usr/bin:/bin:/usr/bin/X11:/usr/games:/usr/local/mpich-1.2.5/ch_p4/bin:
/usr/local/j2sdk1.4.2_04/bin:.:/home/pp2/bin/:/usr/local/cc/bin:.:/home/pp2/bin/:
/home/pp2/personalmodules/bin/
$ which remotetest.pl
/home/pp2/personalmodules/bin//remotetest.pl
PERL5LIB Para que los módulos instalados y la documentación .pod puedan ser encontrados por
Perl añadimos:
$ export PERL5LIB=/home/pp2/personalmodules/lib/perl5:$PERL5LIB
$ perldoc -l GRID::Machine
MANPATH para que man encuentre las páginas de los manuales asociados con los módulos deberemos
establecer la variable de entorno MANPATH:
$ export MANPATH=/home/pp2/personalmodules/man/:$MANPATH
$ manpath
manpath: aviso: la variable $MANPATH está asignada, insertando /etc/manpath.config.
/home/pp2/personalmodules/man/:/usr/local/man:/usr/local/share/man:/usr/share/man:/usr/loc
$ man GRID::Machine
Dando formato a GRID::Machine(3pm); aguarde, por favor...
La opción install base juega un papel similar para el módulo alternativo a ExtUtils::MakeMaker
mas usado: Module::Build.
El Módulo CPAN/Config.pm
Los valores por defecto vienen definidos en el fichero CPAN/Config.pm. El lugar en el que se guarda
este módulo que contiene la configuración de CPAN viene dado por la opción cpan home :
244
nereida:~> cat -n /etc/perl/CPAN/Config.pm
1
2 # This is CPAN.pm’s systemwide configuration file. This file provides
3 # defaults for users, and the values can be changed in a per-user
4 # configuration file. The user-config file is being looked for as
5 # ~/.cpan/CPAN/MyConfig.pm.
6
7 $CPAN::Config = {
8 ’build_cache’ => q[10],
9 ’build_dir’ => q[/root/.cpan/build],
10 ’bzip2’ => q[/usr/bin/bzip2],
11 ’cache_metadata’ => q[1],
12 ’cpan_home’ => q[/root/.cpan],
13 ’cpan_version_check’ => q[1],
14 ’curl’ => q[/usr/bin/curl],
15 ’dontload_hash’ => { },
16 ’ftp’ => q[/usr/bin/ftp],
17 ’ftp_passive’ => q[1],
18 ’ftp_proxy’ => q[],
19 ’getcwd’ => q[cwd],
20 ’gpg’ => q[/usr/bin/gpg],
21 ’gzip’ => q[/bin/gzip],
22 ’histfile’ => q[/root/.cpan/histfile],
23 ’histsize’ => q[100],
24 ’http_proxy’ => q[],
25 ’inactivity_timeout’ => q[0],
26 ’index_expire’ => q[1],
27 ’inhibit_startup_message’ => q[0],
28 ’keep_source_where’ => q[/root/.cpan/sources],
29 ’lynx’ => q[/usr/bin/lynx],
30 ’make’ => q[/usr/bin/make],
31 ’make_arg’ => q[],
32 ’make_install_arg’ => q[],
33 ’make_install_make_command’ => q[/usr/bin/make],
34 ’makepl_arg’ => q[INSTALLDIRS=site],
35 ’mbuild_arg’ => q[],
36 ’mbuild_install_arg’ => q[],
37 ’mbuild_install_build_command’ => q[./Build],
38 ’mbuildpl_arg’ => q[],
39 ’ncftpget’ => q[/usr/bin/ncftpget],
40 ’no_proxy’ => q[],
41 ’pager’ => q[/usr/bin/less],
42 ’prefer_installer’ => q[EUMM],
43 ’prerequisites_policy’ => q[ask],
44 ’scan_cache’ => q[atstart],
45 ’shell’ => q[/bin/bash],
46 ’show_upload_date’ => q[1],
47 ’tar’ => q[/bin/tar],
48 ’term_is_latin’ => q[1],
49 ’unzip’ => q[/usr/bin/unzip],
50 ’urllist’ => [q[ftp://ftp.rediris.es/mirror/CPAN/], q[ftp://archive.progeny.com/CPAN/],
51 ’wait_list’ => [q[wait://ls6.informatik.uni-dortmund.de]],
52 ’wget’ => q[/usr/bin/wget],
245
53 };
54 1;
55 __END__
Un CPAN/Config.pm de Usuario
Estos valores por defecto pueden ser sobreescritos usando un fichero CPAN/Config.pm especı́fico
para el usuario. Lo mas conveniente es dejar ese fichero en $HOME/.cpan/CPAN/MyConfig.pm ya que
el directorio $HOME/.cpan es añadido al camino de búsqueda del módulo CPAN antes de la ejecución
de las sentencias use y/o require.
246
5.14.7. Bundles
Un bundle es un tipo de objeto CPAN que simplifica la instalación de un conjunto de módulos.
El modulo CPAN detecta que nos estamos refiriendo a un bundle porque siempre van prefijados por
Bundle::.
Ahora el fichero "Snapshot_2001_05_09_01.pm" puede ser usado en conjunción con CPAN.pm para
instalar la familia de módulos descrita en el bundle:
cpan> autobundle
Esto permite la creación automática de un bundle que congela la lista de módulos en nuestro sistema.
Si ahora queremos tener una instalación de Perl en otra máquina con los mismos módulos que esta,
sólo tendremos que instalar el bundle.
247
5.14.8. CPAN: Si no tenemos los privilegios de administrador
En las versiones mas recientes de Perl que hacen uso de las versiones mas modernas de CPAN.pm
esto no es un problema. Simplemente escriba perl -MCPAN -e shell y responda al interrogatorio
para determinar la configuración que necesita.
En el caso de que la versión con la que trabajemos sea antigua y no tengamos los privilegios de
administrador del sistema tendremos que trabajar un poco para usar el módulo CPAN:
1 home/casiano[23]> uname -a
2 Linux millo.etsii.ull.es 2.4.22-1.2188.nptl #1 \
Wed Apr 21 20:36:05 EDT 2004 i686 i686 i386 GNU/Linux
3 /home/casiano[5]> perl -MCPAN -e shell
4 Terminal does not support AddHistory.
5
6 Your configuration suggests "/root/.cpan" as your
7 CPAN.pm working directory. I could not create this directory due
8 to this error: mkdir /root/.cpan: Permiso denegado at \
/usr/local/lib/perl5/5.8.5/CPAN.pm line 553
9
10
11 Please make sure the directory exists and is writable.
12
En la lı́nea 15 ejecutamos un guión interactivo (-e) que carga el módulo CPAN::Config (opción -M) y
que hace uso del hash %INC .
La opción -n en la lı́nea 19 envuelve en un bucle de lectura el guión interactivo. Muestra las lı́neas
del fichero que casan con la expresión regular. Ahora editamos el fichero y cambiamos las lı́neas que
hacen alusión al root:
/home/casiano/.cpan/CPAN[13]> vi MyConfig.pm
un poco después . . .
El comando nos muestra que lı́neas fueron cambiadas en la edición. Ahora estamos en condiciones de
ejecutar CPAN, pero tenemos que hacer aún algunos cambios en la configuración:
248
28 /home/casiano/.cpan/CPAN[19]> perl -MCPAN -e shell
29 Terminal does not support AddHistory.
30
31 cpan shell -- CPAN exploration and modules installation (v1.7601)
32 ReadLine support available (try ’install Bundle::CPAN’)
33 cpan> o conf makepl_arg PREFIX=/scratch/casiano/perl
34 makepl_arg PREFIX=/scratch/casiano/perl
35 cpan> o conf commit
36 commit: wrote /home/casiano/.cpan/CPAN/MyConfig.pm
En las lı́neas 33 y 35 le hemos indicado a CPAN donde debe dejar los módulos descargados. Deberemos
asegurarnos que los programas que usen esos módulos tengan dicho directorio en su @INC. Ahora
podemos instalar un módulo:
$ tree /scratch/casiano/perl/
/scratch/casiano/perl/
|-- bin
| ‘-- yapp
|-- lib
| ‘-- perl5
| |-- 5.8.5
| | ‘-- i686-linux
| | ‘-- perllocal.pod
| ‘-- site_perl
| ‘-- 5.8.5
| |-- Parse
| | |-- Yapp
| | | |-- Driver.pm
| | | |-- Grammar.pm
| | | |-- Lalr.pm
| | | |-- Options.pm
| | | |-- Output.pm
| | | ‘-- Parse.pm
| | ‘-- Yapp.pm
| ‘-- i686-linux
| ‘-- auto
| ‘-- Parse
| ‘-- Yapp
‘-- share
‘-- man
|-- man1
| ‘-- yapp.1
‘-- man3
‘-- Parse::Yapp.3
17 directories, 11 files
249
Aún mas secillo: use el módulo CPAN::FirstTime:
$ perl -MCPAN::FirstTime -e ’CPAN::FirstTime::init()’
CPAN is the world-wide archive of perl resources. It consists of about
100 sites that all replicate the same contents all around the globe.
Many countries have at least one CPAN site already. The resources
found on CPAN are easily accessible with the CPAN.pm module. If you
want to use CPAN.pm, you have to configure it properly.
If you do not want to enter a dialog now, you can answer ’no’ to this
question and I’ll try to autoconfigure. (Note: you can revisit this
dialog anytime later by typing ’o conf init’ at the cpan prompt.)
250
5.14.10. Práctica: CPAN
Reconfigure CPAN para trabajar como usuario ordinario. Compruebe el buen funcionamiento des-
cargando un módulo.
Lea el nodo PerlMonks Yes, even you can use CPAN
251
Ejecutables en un .par
Es posible añadir ficheros ejecutables a un fichero .par. El módulo Parse::Eyapp viene con el
ejecutable eyapp. Añadámoslo al fichero .par:
casiano@orion:/usr/local/share/perl/5.8.8$ which eyapp
/usr/local/bin/eyapp
casiano@orion:/usr/local/share/perl/5.8.8$ cd /usr/local/bin/
casiano@orion:/usr/local/bin$ zip -r /tmp/orionparse.par eyapp
adding: eyapp (deflated 59%)
casiano@orion:/usr/local/bin$
Transferimos de nuevo el fichero .par a la máquina nereida:
lhp@nereida:~/Lperl/src$ scp orion:/tmp/orionparse.par .
orionparse.par 100% 108KB 108.0KB/s 00:00
Siempre es posible listar los ficheros que forman parte de una distribución usando unzip con la opción
-l:
lhp@nereida:~/Lperl/src$ unzip -l orionparse.par
Archive: orionparse.par
Length Date Time Name
-------- ---- ---- ----
0 11-02-07 12:40 Parse/
135815 11-01-07 13:14 Parse/Eyapp.pm
0 11-02-07 12:40 Parse/Eyapp/
29574 09-17-07 16:38 Parse/Eyapp/Lalr.pm
8178 09-17-07 16:17 Parse/Eyapp/YATW.pm
51402 11-01-07 13:17 Parse/Eyapp/Treeregexp.pm
52766 11-01-07 13:17 Parse/Eyapp/Parse.pm
11069 09-17-07 16:40 Parse/Eyapp/Scope.pm
6314 09-17-07 16:39 Parse/Eyapp/Options.pm
8826 09-17-07 17:53 Parse/Eyapp/Output.pm
23784 09-17-07 16:17 Parse/Eyapp/Node.pm
13832 09-17-07 16:38 Parse/Eyapp/Grammar.pm
17865 11-01-07 13:05 Parse/Eyapp/Driver.pm
3673 09-17-07 16:37 Parse/Eyapp/Base.pm
3296 09-17-07 16:40 Parse/Eyapp/_TreeregexpSupport.pm
7102 11-02-07 12:40 eyapp
0 07-10-08 10:22 Math/
0 08-08-08 12:08 Math/Prime/
5635 05-14-08 15:30 Math/Prime/XS.pm
-------- -------
379131 19 files
El programa par.pl permite ejecutar ficheros en un archivo .par:
lhp@nereida:~/Lperl/src$ par.pl orionparse.par eyapp -V
This is Parse::Eyapp version 1.082.
Por defecto par.pl busca por un ejecutable con nombre main.pl.
También puedo extraer el ejecutable:
lhp@nereida:~/Lperl/src/tmp$ unzip orionparse.par eyapp
Archive: orionparse.par
inflating: eyapp
y a continuación ejecutarlo usando el módulo PAR:
lhp@nereida:~/Lperl/src/tmp$ perl -MPAR=orionparse.par eyapp -V
This is Parse::Eyapp version 1.082.
252
Empaquetado de Modulos XS
PAR soporta la carga de módulos XS (véase perlxs). XS es un formato para la descripción de
interfases entre código C y código Perl. Veamos un ejemplo. La sesión se inicia como administrador
arrancando cpan para a continuación descargar el módulo Math::Prime::XS el cual, como su nombre
indica, tiene partes escritas en C:
root@orion:~# cpan
El comando
look Math::Prime::XS
que emitimos a continuación indica que queremos abrir una shell en el directorio de la distribución
de Math::Prime::XS. Si el módulo no está actualizado, cpan procederá a descargarse la última versión
antes de abrir una shell:
253
Math-Prime-XS-0.20/t/00-load.t
Math-Prime-XS-0.20/INSTALL
Math-Prime-XS-0.20/Build.PL
Math-Prime-XS-0.20/META.yml
Math-Prime-XS-0.20/Makefile.PL
Math-Prime-XS-0.20/README
Removing previously used /root/.cpan/build/Math-Prime-XS-0.20
Working directory is /root/.cpan/build/Math-Prime-XS-0.20
Aunque la sesión ha sido arrancada como root podrı́a haberla hecho como un usuario ordinario. El
único objetivo era descargar la distribución de Math::Prime::XS y posicionarse en el correspondiente
directorio.
root@orion:~/.cpan/build/Math-Prime-XS-0.20# ls -l
total 204
-r--r--r-- 1 csegura csegura 786 2008-05-14 15:30 Build.PL
-r--r--r-- 1 csegura csegura 1445 2008-05-14 15:30 Changes
-r--r--r-- 1 csegura csegura 290 2008-05-14 15:30 INSTALL
drwxr-xr-x 3 csegura csegura 4096 2008-05-14 15:30 lib
-r--r--r-- 1 csegura csegura 498 2008-05-14 15:30 Makefile.PL
-r--r--r-- 1 csegura csegura 172 2008-05-14 15:30 MANIFEST
-r--r--r-- 1 csegura csegura 560 2008-05-14 15:30 META.yml
-r--r--r-- 1 csegura csegura 154956 2008-05-14 15:30 ppport.h
-r--r--r-- 1 csegura csegura 5033 2008-05-14 15:30 README
drwxr-xr-x 2 csegura csegura 4096 2008-05-14 15:30 t
-r--r--r-- 1 csegura csegura 4843 2008-05-14 15:30 XS.xs
El aspecto mas notable de esta construcción es que se ha llamado al compilador de C. Las opciones
pasadas al compilador han sido las mismas que se usaron en la instalación de Perl en la plataforma
en uso. Ahora cambiamos al directorio de construcción blib:
root@orion:~/.cpan/build/Math-Prime-XS-0.20# cd blib
254
En el directorio arch está la librerı́a .so (shared object). En el directorio lib esta el módulo Perl .pm
que contiene la interfaz Perl a las funciones C:
root@orion:~/.cpan/build/Math-Prime-XS-0.20/blib# tree
.
|-- arch
| ‘-- auto
| ‘-- Math
| ‘-- Prime
| ‘-- XS
| |-- XS.bs
| ‘-- XS.so
|-- bin
|-- lib
| |-- Math
| | ‘-- Prime
| | ‘-- XS.pm
| ‘-- auto
| ‘-- Math
| ‘-- Prime
| ‘-- XS
|-- man1
|-- man3
| ‘-- Math::Prime::XS.3pm
‘-- script
Para construir el fichero .par usamos zip añadiendo los directorios arch y lib. los contenidos de
Es habitual que un fichero .par este fundamentalmente constituı́do por los contenidos del directorio
blib/ construido a partir de una distribución CPAN.
Ahora transferimos el fichero /tmp/primexs.par a una máquina que carece de los módulos en
dicho fichero, pero que es binario-compatible con la máquina en la que se realizó la construcción:
255
lhp@nereida:~/Lperl/src$ perldoc -l Math::Prime::XS
No documentation found for "Math::Prime::XS".
Después de la transferencia estamos en condiciones de usar el módulo Math::Prime::XS utilizando via
PAR la distribucion primexs.par:
lhp@nereida:~/Lperl/src$ scp orion:/tmp/primexs.par .
primexs.par 100% 11KB 11.4KB/s 00:00
lhp@nereida:~/Lperl/src$ vi prime3.pl
lhp@nereida:~/Lperl/src$ cat -n prime3.pl
1 #!/usr/bin/perl -I../lib -w
2 use PAR "primexs.par";
3 use Math::Prime::XS qw{:all};
4
5 @all_primes = primes(9);
6 print "@all_primes\n";
7
8 @range_primes = primes(4, 9);
9 print "@range_primes\n";
El programa se ejecuta sin errores produciendo la salida esperada:
lhp@nereida:~/Lperl/src$ prime3.pl
2 3 5 7
5 7
Por supuesto, el éxito de esta ejecución depende de la compatibilidad binaria de ambas plataformas
(orion y nereida)
256
El programa se ejecuta normalmente:
lhp@nereida:~/Lperl/src$ prime4.pl
2 3 5 7
5 7
require 5.002;
use English;
use Tk;
use Tk::DialogBox;
# use strict;
sub convert ;
Ahora podemos transferir el programa a una máquina en la que ni siquiera esta instalado el toolkit
Tk:
257
$ h2xs -XA -n Parse::Yard
Defaulting to backwards compatibility with perl 5.8.4
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.
Writing Parse-Yard/lib/Parse/Yard.pm
Writing Parse-Yard/Makefile.PL
Writing Parse-Yard/README
Writing Parse-Yard/t/Parse-Yard.t
Writing Parse-Yard/Changes
Writing Parse-Yard/MANIFEST
$ cd Parse-Yard/
Parse-Yard$ tree
.
|-- Changes
|-- MANIFEST
|-- Makefile.PL
|-- README
|-- lib
| ‘-- Parse
| ‘-- Yard.pm
‘-- t
‘-- Parse-Yard.t
El Programa Makefile.PL
El programa perl Makefile.PL generado por h2xs se encarga de crear el fichero Makefile:
$ cat -n Makefile.PL
1 use 5.008004;
2 use ExtUtils::MakeMaker;
3 # See lib/ExtUtils/MakeMaker.pm for details of how to influence
4 # the contents of the Makefile that is written.
5 WriteMakefile(
6 NAME => ’Parse::Yard’,
7 VERSION_FROM => ’lib/Parse/Yard.pm’, # finds $VERSION
8 PREREQ_PM => {}, # e.g., Module::Name => 1.1
9 ($] >= 5.005 ? ## Add these new keywords supported since 5.005
10 (ABSTRACT_FROM => ’lib/Parse/Yard.pm’, # retrieve abstract from module
11 AUTHOR => ’Lenguajes y Herramientas de Programacion <[email protected]>’) : ()),
12 );
Parámetros de WriteMakefile
El programa usa el módulo ExtUtils::MakeMaker que proporciona la subrutina WriteMakefile
, la cual se encarga de construir el Makefile de acuerdo a las especificaciones que se le pasan como
parámetros:
258
NAME es el nombre del módulo
La clave VERSION_FROM dice que fichero contiene la variable $VERSION que define la versión de
esta distribución.
La entrada PREREQ_PM es una referencia a un hash cuyas claves son los nombres de los módulos
de los que depende y los valores son los números de versión requeridos. Por ejemplo:
ABSTRACT_FROM indica el fichero que contiene la descripción, resumen o abstract del módulo.
ExtUtils::MakeMaker busca en la documentación POD de la distribución por una lı́nea que
case con la expresión regular /^($package\s-\s)(.*)/. La costumbre/convenio es que esta sea
la primera lı́nea en la sección =head1 NAME. El contenido de $2 se interpreta como abstract. Por
ejemplo en la distribución de Parse::Yapp podemos ver que contiene la lı́nea:
=head1 SYNOPSIS
ABSTRACT es una lı́nea describiendo el módulo. Se incluye en el fichero PPD (Perl Package
Descriptor ). Los ficheros PPD son ficheros XML.
Por tanto, para crear el Makefile basta ejecutar este programa escribiendo perl Makefile.PL
Parse-Yard$ perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for Parse::Yard
Parse-Yard$ ls -ltr
total 44
drwxr-xr-x 2 lhp lhp 4096 2004-12-23 09:47 t
-rw-r--r-- 1 lhp lhp 1202 2004-12-23 09:47 README
-rw-r--r-- 1 lhp lhp 69 2004-12-23 09:47 MANIFEST
-rw-r--r-- 1 lhp lhp 561 2004-12-23 09:47 Makefile.PL
drwxr-xr-x 3 lhp lhp 4096 2004-12-23 09:47 lib
-rw-r--r-- 1 lhp lhp 158 2004-12-23 09:47 Changes
-rw-r--r-- 1 lhp lhp 19549 2004-12-23 16:34 Makefile
El Fichero MANIFEST
En primer lugar se ha comprobado si nuestra aplicación está completa, para ello WriteMakefile
mira la lista de ficheros que figura en el fichero MANIFEST y comprueba que todos los ficheros en la
lista están presentes. Los contenidos de MANIFEST son:
$ cat MANIFEST
Changes
Makefile.PL
MANIFEST
README
t/Parse-Yard.t
lib/Parse/Yard.pm
259
Una de las opciones proporcionadas por MakeMaker es make dist el cual también usa el fichero
MANIFEST para determinar que ficheros forman parte de la distribución. Es importante mantener este
fichero actualizado.
make MANIFEST
MakeMaker también proporciona un objetivo MANIFEST. Tecleando make MANIFEST se crea un fi-
chero MANIFEST que contiene todos los directorios y subdirectorios del directorio actual. Esta conducta
puede modificarse creando un fichero MANIFEST.SKIP el cual especifica mediante expresiones regulares
que tipos de ficheros no deben ser incluidos en el MANIFEST. Por ejemplo:
$ cat -n /usr/local/src/parrot-0.1.1/MANIFEST.SKIP
1 \.o$
2 ^\.cvsignore$
3 /\.cvsignore$
4 \.cvsignore$
5 CVS/[^/]+$
6 ^include/parrot/config\.h$
7 ^include/parrot/has_header\.h$
8 ^include/parrot/platform\.h$
9 ^Makefile$
10 /Makefile$
11 ^lib/Parrot/Config\.pm$
12 ^platform\.c$
..................
1 package Parse::Yard;
2 use 5.008004; # Versión mı́nima de Perl para trabajar
3 use strict; # Por defecto = a la de la instalación
4 use warnings;
5 require Exporter;
6 our @ISA = qw(Exporter);
7
8 # Items to export into callers namespace by default. Note: do not export
9 # names by default without a very good reason. Use EXPORT_OK instead.
10 # Do not simply export all your public functions/methods/constants.
11
12 # This allows declaration use Parse::Yard ’:all’;
13 # If you do not need this, moving things directly into @EXPORT or @EXPORT_OK
14 # will save memory.
15 our %EXPORT_TAGS = ( ’all’ => [ qw( ) ] );
16 our @EXPORT_OK = ( @{ $EXPORT_TAGS{’all’} } );
17 our @EXPORT = qw( );
18 our $VERSION = ’0.01’;
19
20 # Preloaded methods go here.
21 1;
22 __END__
El marcador __END__ indica el final del código. El esqueleto construı́do por h2xs también contiene
define la estructura de la documentación:
260
23 # Below is stub documentation for your module. You’d better edit it!
24
25 =head1 NAME
26
27 Parse::Yard - Perl extension for blah blah blah
28
29 =head1 SYNOPSIS
30
31 use Parse::Yard;
32 blah blah blah
33
34 =head1 DESCRIPTION
35
36 Stub documentation for Parse::Yard, created by h2xs. It looks like the
37 author of the extension was negligent enough to leave the stub
38 unedited.
39
40 Blah blah blah.
41
42 =head2 EXPORT
43
44 None by default.
45
46 =head1 SEE ALSO
47
48 Mention other useful documentation such as the documentation of
49 related modules or operating system documentation (such as man pages
50 in UNIX), or any relevant external documentation such as RFCs or
51 standards.
52
53 If you have a mailing list set up for your module, mention it here.
54
55 If you have a web site set up for your module, mention it here.
56
57 =head1 AUTHOR
58
59 Lenguajes y Herramientas de Programacion, E<lt>lhp@E<gt>
60
61 =head1 COPYRIGHT AND LICENSE
62
63 Copyright (C) 2004 by Lenguajes y Herramientas de Programacion
64
65 This library is free software; you can redistribute it and/or modify
66 it under the same terms as Perl itself, either Perl version 5.8.4 or,
67 at your option, any later version of Perl 5 you may have available.
68
69 =cut
La Construcción de blib
Ahora podemos hacer make:
$ make
cp lib/Parse/Yard.pm blib/lib/Parse/Yard.pm
Manifying blib/man3/Parse::Yard.3pm
261
Como consecuencia la estructura del proyecto ha cambiado:
$ cd Parse-Yard/ $ tree
Parse-Yard$ tree .
$ tree |-- Changes
. |-- MANIFEST
|-- Changes |-- Makefile
|-- MANIFEST |-- Makefile.PL
|-- Makefile |-- README
|-- Makefile.PL |-- blib
|-- README | |-- arch
|-- lib | | ‘-- auto
| ‘-- Parse | | ‘-- Parse
| ‘-- Yard.pm | | ‘-- Yard
‘-- t | |-- lib
‘-- Parse-Yard.t | | |-- Parse
| | | ‘-- Yard.pm
| | ‘-- auto
| | ‘-- Parse
| | ‘-- Yard
| ‘-- man3
| ‘-- Parse::Yard.3pm
|-- lib
| ‘-- Parse
| ‘-- Yard.pm
|-- pm_to_blib
‘-- t
‘-- Parse-Yard.t
Este paso cobra importancia cuando el módulo contiene partes escritas en lenguajes externos (por
ejemplo en C) y es necesaria la construcción de librerı́as dinámicas conteniendo código objeto.
make test
Para comprobar que el módulo funciona se hace make test:
$ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" \
"-e" "test_harness(0, ’blib/lib’, ’blib/arch’)" t/*.t
t/Parse-Yard....ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs ( 0.04 cusr + 0.01 csys = 0.05 CPU)
Una vez escritas las diferentes componentes del módulo, podemos podemos construir una versión para
su distribución mediante:
La distribución resultante puede ser instalada siguiendo los pasos explicados en la sección 5.14.1.
262
5.18. La Documentación en Perl
La documentación en Perl se hace en formato pod (plain old documentation). Esta documentación
se inserta en nuestro programa. Se trata de un lenguaje de marcas. Las lı́neas que siguen a __END__ en
el esqueleto generado por h2xs (véase la sección 5.17) muestran el uso de pod. Cuando Perl encuentra
un comando pod (todos los comandos comienzan por el signo igual) ignora las lı́neas a las que afecta.
Para indicar el final de una zona de documentación se usa el comando =cut al comienzo de un nuevo
párrafo.
Una herramienta que permite el proceso inverso, esto es pasar de formato man a otros formatos es
rman 4 .
Si se ha instalado la documentación de Perl en tu sistema, puedes acceder a ella utilizando info,
man, tkpod5 o perldoc. La orden man perl te mostrará las diferentes secciones existentes y sobre que
tratan. Asi man perlboot te introducirá en la programación orientada a objetos en Perl.
Para saber mas sobre el lenguaje de marcas pod escribe man perlpod.
Un documento POD consiste de párrafos separados por lı́neas en blanco. Existen tres tipos de
párrafos: verbatim, comandos y texto.
Texto verbatim
Cuando un párrafo esta sangrado (comienza con un espacio o tabulador) se reproduce exactamente
como aparece.
Un comando de párrafo
=head1 cabecera
=head2 cabecera
=item texto
=over N
=back
=cut
=pod
=for X
=begin X
=end X
=pod, =cut
Es útil cuando mezclas código y documentación. Le indica a Perl que lo que sigue es documen-
tación hasta que se alcance el siguiente =cut.
3
http://perl.jonallen.info/projects/pod2pdf
4
http://polyglotman.sourceforge.net/
5
Si se instaló Tk::Pod
263
=head1, =head2
Primer y segundo nivel de encabezamiento. El texto cabecera debe estar en el mismo párrafo
que la directiva.
=over 4
=item *
=item *
=back
Mantenga la consitencia en la presentación: use =item * para producir puntos o bien la forma
=item 1., =item 2., etc.
=for, =begin, =end Nos permiten incluir secciones que no contienen texto POD sino que van
dirigidas a formatos especiales. La directiva =for tiene como ámbito el siguiente párrafo:
Texto normal El texto será formateado y se pueden usar secuencias de formateado como
I<texto> itálicas
B<texto> negritas
264
5.19. Bancos de Pruebas y Extreme Programming
Queremos comprobar si nuestro código funciona. ¿Cómo hacerlo?. Lo adecuado es llevar una apro-
ximación sistemática que permita validar el código. Esa es la función del programa test.pl que se
genera automáticamente con h2xs.
En general, la filosofı́a aconsejable para realizar un banco de pruebas de nuestro módulo es la
que se articula en la metodologı́a denominada Extreme Programming, descrita en múltiples textos, en
concreto en el libro de Scott [14]:
La aplicación deberı́a pasar todas las pruebas después de cualquier modificación importante y
también al final del dı́a
Pueden haber algunas diferencias entre el esquema que se describe aqui y su versión de Perl. Lea
detenidamente el capı́tulo Test Now, test Forever ” del libro de Scott [14].
$ pwd
/home/lhp/projects/perl/src/tmp/PL/Tutu/tutu_src
$ cat pruebalex.pl
#!/usr/bin/perl -w -I..
#use PL::Tutu;
use Tutu;
Observa como la opción -I.. hace que se busque por las librerı́as en el directorio padre del actual.
Cuando ejecutamos pruebalex.pl obtenemos la lista de terminales:
$ ./pruebalex.pl
prog = int a,b; string c; c = "hello"; a = 4; b = a +1; p b
tokens = INT INT ID a PUN , ID b PUN ; STRING STRING ID c PUN ;
ID c PUN = STR hello PUN ; ID a PUN = NUM 4 PUN ; ID b PUN =
ID a PUN + NUM 1 PUN ; P P ID b
La última lı́nea ha sido partida por razones de legibilidad, pero consituye una sóla lı́nea. Editemos el
fichero test.pl en el directorio del módulo. Sus contenidos son como sigue:
$ cat -n test.pl
1 # Before ‘make install’ is performed this script should be runnable with
2 # ‘make test’. After ‘make install’ it should work as ‘perl test.pl’
3
4 #########################
5
6 # change ’tests => 1’ to ’tests => last_test_to_print’;
265
7
8 use Test;
9 BEGIN { plan tests => 1 };
10 use PL::Tutu;
11 ok(1); # If we made it this far, we’re ok.
12
13 #########################
14
15 # Insert your test code below, the Test module is use()ed here so read
16 # its man page ( perldoc Test ) for help writing this test script.
17
En la lı́nea 9 se establece el número de pruebas a realizar. La primera prueba aparece en la lı́nea 11.
Puede parecer que no es una prueba, ¡pero lo es!. Si se ha alcanzado la lı́nea 11 es que se pudo cargar
el módulo PL::Tutu y eso ¡tiene algún mérito!.
Seguiremos el consejo de la lı́nea 15 y escribiremos nuestra segunda prueba al final del fichero
test.pl:
La lı́nea 22 ha sido partida por razones de legibilidad, pero constituye una sóla lı́nea. Ahora podemos
ejecutar make test y comprobar que las dos pruebas funcionan:
$ make test
PERL_DL_NONLAZY=1 /usr/bin/perl -Iblib/arch -Iblib/lib -I/usr/lib/perl/5.6.1 \
-I/usr/share/perl/5.6.1 test.pl
1..2
ok 1
ok 2
¿Recordaste cambiar la lı́nea 9 de test.pl? ¿Añadiste los nuevos ficheros a la lista en MANIFEST?
266
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.
Writing PL-Tutu/lib/PL/Tutu.pm
Writing PL-Tutu/Makefile.PL
Writing PL-Tutu/README
Writing PL-Tutu/t/PL-Tutu.t
Writing PL-Tutu/Changes
Writing PL-Tutu/MANIFEST
$ tree
.
‘-- PL-Tutu
|-- Changes
|-- MANIFEST
|-- Makefile.PL
|-- README
|-- lib
| ‘-- PL
| ‘-- Tutu.pm
‘-- t
‘-- PL-Tutu.t
4 directories, 6 files
$ cd PL-Tutu/
$ ls -l
total 24
-rw-r--r-- 1 lhp lhp 154 Nov 3 12:59 Changes
-rw-r--r-- 1 lhp lhp 63 Nov 3 12:59 MANIFEST
-rw-r--r-- 1 lhp lhp 552 Nov 3 12:59 Makefile.PL
-rw-r--r-- 1 lhp lhp 1196 Nov 3 12:59 README
drwxr-xr-x 3 lhp lhp 4096 Nov 3 12:59 lib
drwxr-xr-x 2 lhp lhp 4096 Nov 3 12:59 t
$ perl Makefile.PL
Checking if your kit is complete...
Looks good
Writing Makefile for PL::Tutu
$ ls -ltr
total 44
drwxr-xr-x 2 lhp lhp 4096 Nov 3 12:59 t
drwxr-xr-x 3 lhp lhp 4096 Nov 3 12:59 lib
-rw-r--r-- 1 lhp lhp 1196 Nov 3 12:59 README
-rw-r--r-- 1 lhp lhp 552 Nov 3 12:59 Makefile.PL
-rw-r--r-- 1 lhp lhp 63 Nov 3 12:59 MANIFEST
-rw-r--r-- 1 lhp lhp 154 Nov 3 12:59 Changes
-rw-r--r-- 1 lhp lhp 19471 Nov 3 13:03 Makefile
267
$ make test
cp lib/PL/Tutu.pm blib/lib/PL/Tutu.pm
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" \
"-e" "test_harness(0, ’blib/lib’, ’blib/arch’)" t/*.t
t/PL-Tutu....ok
All tests successful.
Files=1, Tests=1, 0 wallclock secs ( 0.04 cusr + 0.02 csys = 0.06 CPU)
Observe que, como consecuencia de esta primera ejecución se han creado nuevos directorios:
$ tree
.
|-- Changes
|-- MANIFEST
|-- Makefile
|-- Makefile.PL
|-- README
|-- blib
| |-- arch
| | ‘-- auto
| | ‘-- PL
| | ‘-- Tutu
| |-- lib
| | |-- PL
| | | ‘-- Tutu.pm
| | ‘-- auto
| | ‘-- PL
| | ‘-- Tutu
| ‘-- man3
|-- lib
| ‘-- PL
| ‘-- Tutu.pm
|-- pm_to_blib
‘-- t
‘-- PL-Tutu.t
14 directories, 9 files
$ cat t/PL-Tutu.t
# Before ‘make install’ is performed this script should be runnable with
# ‘make test’. After ‘make install’ it should work as ‘perl PL-Tutu.t’
#########################
#########################
268
# Insert your test code below, the Test::More module is use()ed here so read
# its man page ( perldoc Test::More ) for help writing this test script.
$ pwd
/home/lhp/perl/src/topdown/PL/5.8/PL-Tutu/t
$ ls -l
total 8
-rw-r--r-- 1 lhp lhp 446 Nov 3 15:03 02lex.t
-rw-r--r-- 1 lhp lhp 465 Nov 3 13:11 PL-Tutu.t
$ mv PL-Tutu.t 01load.t
Que los prefijos de los nombres 01, 02, . . . nos garanticen el orden de ejecución
Introduciendo una Prueba Adicional El programa 02lex.t pone a prueba una subrutina
Lexical::Analysis::scanner que hemos escrito. Se supone que su ejecución con entrada $a debe
producir como resultado que la lista @PL::Tutu::tokens contenga una secuencia dada de caracteres:
$ cat 02lex.t
# change ’tests => 1’ to ’tests => last_test_to_print’;
#########################
$ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM"
"-e" "test_harness(0, ’blib/lib’, ’blib/arch’)" t/*.t
t/01load....ok
t/02lex.....ok
All tests successful.
Files=2, Tests=3, 0 wallclock secs ( 0.15 cusr + 0.02 csys = 0.17 CPU)
269
5.20. Práctica: Construcción de una Distribución
Construya siguiendo los pasos descritos en las secciones 5.17 y 5.18 una distribución conteniendo
un módulo Sub::Wrap que provea una función wrap como la descrita en las secciones 4.15.9 y 4.15.10.
Escriba al menos una prueba adicional (consulte perldoc Test::More). Compruebe que puede crear la
distribución (make dist) e instalarla (make; make test; make install) como se hace con cualquier
distribución CPAN (relea si es necesario la sección 5.14.1.
1 for my $c (0..$M) {
2 $f[0][$c] = ($w[0] <= $c)? $p[0] : 0;
3 }
4
5 for my $k (1..$N-1) {
6 for my $c (0..$M) {
7 my $n = $f[$k-1][$c];
8 if ($c >= $w[$k]) {
9 my $y = $f[$k-1][$c-$w[$k]]+$p[$k];
10 $f[$k][$c] = ($n < $y)? $y : $n;
11 }
12 else { $f[$k][$c] = $n; }
13 }
14 }
En las lı́neas 1-3 inicializamos la tabla @f. Después aparecen dos bucles anidados: el primero en los
objetos y el segundo en las capacidades. Las lı́neas 8-11 no son mas que la transcripción de la ecuación
de estado 5.1.
5.21.2. El Módulo
A continuación comentamos los contenidos del módulo:
270
~/Lperl/src/threads/knapsack/Algorithm-Knap01DP/lib/Algorithm$ cat -n Knap01DP.pm
1 package Algorithm::Knap01DP;
2 use 5.008004;
3 use strict;
4 use warnings;
5 use Carp;
6 use IO::File;
7
8 use Exporter; # Warning!! h2xs generated a require Exporter;
9 our @ISA = qw(Exporter);
10 our @EXPORT_OK = qw/Knap01DP ReadKnap/;
271
50 for (0..$N-1) {
51 $w[$_] = <$file>;
52 $p[$_] = <$file>;
53 }
54 chomp @w; chomp @p;
55 return ($M, \@w, \@p);
56 }
57
58 1;
59 __END__
5.21.3. La Documentación
A partir del final del código hemos colocado la documentación. No olvides nunca actualizarla de
acuerdo con los cambios que hagas en tu módulo.
61 =head1 NAME
62
63 Algorithm::Knap01DP - Solves the 0-1 Knapsack problem using
the Dynamic Programming Technique
64
65 =head1 SYNOPSIS
66
67 use Algorithm::Knap01DP;
68
69 my ($M, $w, $p) = ReadKnap($file);
70
71 my @f = KnapP01DP($M, $w, $p);
72
73 =head1 DESCRIPTION
74
75 Solves the 0-1 Knapsack problem using the Dynamic Programming Technique.
76
77 my @f = KnapP01DP($M, $w, $p);
78
79 $M is the capacity
80 $w is a reference to the array of weights
81 $p is a reference to the array of profits
82
83 Returns the table $f[$k][$c] containing the optimal value for
84 objects 0..$k and capacity $c.
85
86 =head2 EXPORT
87
88 None by default.
89
90 =head1 SEE ALSO
91
92 L<Algorithm::Knapsack>
93
94 =head1 AUTHOR
95
96 Casiano Rodriguez Leon E<lt>[email protected]<gt>
97
272
98 =head1 COPYRIGHT AND LICENSE
99
100 Copyright (C) 2005 by Casiano Rodriguez Leon
101
102 This library is free software; you can redistribute it and/or modify
103 it under the same terms as Perl itself, either Perl version 5.8.4 or,
104 at your option, any later version of Perl 5 you may have available.
105
106
107 =cut
Observe las salidas y los comentarios en inglés. Si tu intención es hacer público el módulo en CPAN
es recomendable que las salidas, los nombres de variables y los comentarios estén en ese idioma.
5.21.4. MANIFEST
Añadimos un fichero MANIFEST.SKIP que nos ayude a construir de manera automática el MANIFEST:
Ahora podemos construir el fichero MANIFEST sin mas que hacer make manifest:
273
Added to MANIFEST: TODO
lhp@nereida:~/Lperl/src/threads/knapsack/Algorithm-Knap01DP$ cat MANIFEST
Changes
lib/Algorithm/Knap01DP.pm
Makefile.PL
MANIFEST This list of files
MANIFEST.SKIP
README
t/01alltests.t
t/02bench.t
t/example.pl
t/knap.dat
t/knap21.dat
t/knap22.dat
t/knap23.dat
t/knap25.dat
t/knapanderson.dat
t/kwalitee.t
t/usealknap.pl
TODO
274
El tiempo invertido en Comprobar (testing) y el tiempo invertido en Depurar (debugging) man-
tiene una relación inversa. Cuanto menor es el tiempo invertido en la preparación de pruebas mayor
será nuestro tiempo de depuración. Cuanto mejor sea nuestra suite de pruebas menor será el tiempo
que necesitemos para fijar errores en nuestros programas.
You can write all the prose, or create all the diagrams you want, describing how a class
should behave and what it looks like, but nothing is as real as a set of tests. The former
is a wish list, but the tests are a contract that is enforced by the compiler and the running
program. It’s hard to imagine a more concrete description of a class than the tests
Cuando escriba las pruebas invierta su escala de valores: Alégrese cuando una prueba descubre un
error. Es peor que el error esté ahı́ y no sea descubierto.
Que Comprobar
Estudie las interacciones y dependencias entre recursos que se da por sentado que están (pero
que a veces pueden no estar. Por ejemplo, un fichero con cuya existencia se cuenta)
Compruebe que toda subrutina se prueba al menos una vez. Prepare para cada subrutina impor-
tante al menos una prueba que cubra el caso promedio y otra que compruebe su funcionamiento
en casos lı́mite.
$ pwd
/home/lhp/Lperl/src/threads/knapsack/Algorithm-Knap01DP/t
$ ls -l
-rw-r--r-- 1 lhp lhp 1693 2005-05-17 19:57 01MartelloAndTothBook.t
-rw-r--r-- 1 lhp lhp 134 2005-05-16 18:37 knap21.dat
275
-rw-r--r-- 1 lhp lhp 125 2005-05-16 18:37 knap22.dat
-rw-r--r-- 1 lhp lhp 123 2005-05-16 18:37 knap23.dat
-rw-r--r-- 1 lhp lhp 158 2005-05-16 18:37 knap25.dat
Además del programa 01MartelloAndTothBook.t tenemos cuatro ficheros con cuatro diferentes pro-
blemas de la mochila. Los números corresponden a las páginas del clásico libro de Martello y Toth
[15] en el que aparece el correspondiente problema. Puede encontrar el texto completo del programa
01MartelloAndTothBook.t en la sección 13.42.
$ cat -n 01MartelloAndTothBook.t
1 # Before ‘make install’ is performed this script should be runnable with
2 # ‘make test’. After ‘make install’ it should work as ‘perl Algorithm-Knap01DP.t’
3
4 #########################
5 use strict;
6 use Test::More tests => 11;
7
8 BEGIN { use_ok(’Algorithm::Knap01DP’, qw/Knap01DP ReadKnap/); }
10 ### main
11 my @inputfiles = qw/knap21.dat knap22.dat knap23.dat knap25.dat/;
12 my @sol = (280, 107, 150, 900);
13 my $knap21 = [’102’, [ ’2’, ’20’, ’20’, ’30’, ’40’, ’30’, ’60’, ’10’ ],
14 [ ’15’, ’100’, ’90’, ’60’, ’40’, ’15’, ’10’, ’1’ ]];
15 my $knap22 = [’50’, [ ’31’, ’10’, ’20’, ’19’, ’4’, ’3’, ’6’ ],
16 [ ’70’, ’20’, ’39’, ’37’, ’7’, ’5’, ’10’ ]];
17 my $knap23 = [’190’, [ ’56’, ’59’, ’80’, ’64’, ’75’, ’17’ ],
18 [ ’50’, ’50’, ’64’, ’46’, ’50’, ’5’ ]];
19 my $knap25 = [’104’, [ ’25’, ’35’, ’45’, ’5’, ’25’, ’3’, ’2’, ’2’ ],
20 [ ’350’, ’400’, ’450’, ’20’, ’70’, ’8’, ’5’, ’5’ ]];
21
22 my $knapsackproblem = [$knap21, $knap22, $knap23, $knap25];
En nuestro caso esa definición de la correspondencia entre una entrada y su correspondiente salida
correcta se traduce en:
Las variables $knap21 . . . $knap25 contienen estructuras de datos que definen los problemas:
capacidad de la mochila, vector de pesos y vector de beneficios.
276
Uso de Data::Dumper y del Depurador para la Preparación de las Pruebas
En el proceso de elaboración de una prueba para una subrutina es necesario tener una descripción
Perl de
DB<5> x $x
0 ARRAY(0x840a11c)
0 70
1 20
2 39
3 37
4 7
5 5
6 10
Cuando la función ReadKnap lee un fichero de datos devuelve una estructura como la descrita.
De hecho, es usando el depurador, cortando y pegando que hemos construido parte del código de
pruebas definiendo las estructuras de datos $knapXX:
10 ### main
11 my @inputfiles = qw/knap21.dat knap22.dat knap23.dat knap25.dat/;
12 my @sol = (280, 107, 150, 900);
13 my $knap21 = [’102’, [ ’2’, ’20’, ’20’, ’30’, ’40’, ’30’, ’60’, ’10’ ],
14 [ ’15’, ’100’, ’90’, ’60’, ’40’, ’15’, ’10’, ’1’ ]];
15 my $knap22 = [’50’, [ ’31’, ’10’, ’20’, ’19’, ’4’, ’3’, ’6’ ],
16 [ ’70’, ’20’, ’39’, ’37’, ’7’, ’5’, ’10’ ]];
17 my $knap23 = [’190’, [ ’56’, ’59’, ’80’, ’64’, ’75’, ’17’ ],
18 [ ’50’, ’50’, ’64’, ’46’, ’50’, ’5’ ]];
19 my $knap25 = [’104’, [ ’25’, ’35’, ’45’, ’5’, ’25’, ’3’, ’2’, ’2’ ],
20 [ ’350’, ’400’, ’450’, ’20’, ’70’, ’8’, ’5’, ’5’ ]];
21
22 my $knapsackproblem = [$knap21, $knap22, $knap23, $knap25];
277
Las Funciones is deeply e is
A continuación leeemos cada fichero y comprobamos que ambas ReadKnap y Knap01DP dan los
resultados esperados.
La función is deeply nos dice si dos estructuras de datos son equivalentes. Véase perldoc Test::More
para mas información sobre el módulo Test::More y las funciones is_deeply e is .
24 my $i = 0;
25 my ($M, $w, $p);
26 my @f;
27
28 # Now 2*@inputfiles = 8 tests
29 for my $file (@inputfiles) {
30 ($M, $w, $p) = ReadKnap((-e "t/$file")?"t/$file":$file);
31 is_deeply($knapsackproblem->[$i], [$M, $w, $p], "ReadKnap $file");
32 my $N = @$w;
33 @f = Knap01DP($M, $w, $p);
34 is($sol[$i++], $f[$N-1][$M], "Knap01DP $file");
35 }
Para mas funciones de Comparación Profunda véase el módulo Test::Deep.
278
55 return ($M, \@w, \@p);
56 }
por tanto, pasarle a la rutina vectores de distinto tamaño hace que el programa muera. Es por esto
que protegeremos la ejecución dentro de un eval:
37 # test to check when weights and profits do not have the same size
38 $M = 100; @$w = 1..5; @$p = 1..10;
39 eval { Knap01DP($M, $w, $p) };
40 like $@, qr/Profits and Weights don’t have the same size/;
Ahora la llamada a croak dentro de Knap01DP sólo produce la finalización del eval . El mensaje
emitido por croak o die queda en la variable especial $@.
La función like comprueba que el primer argumento casa con la expresión regular especificada
en el segundo argumento.
El Módulo Test::Exception
Una alternativa es usar el módulo Test::Exception si está instalado:
BEGIN {
$test_exception_installed = 1;
eval { require Test::Exception };
$test_exception_installed = 0 if $@;
}
para posteriormente aplicar la prueba:
SKIP: {
skip "Test::Exception not installed", 1 unless $test_exception_installed;
Test::Exception::lives_ok
{ Parse::Eyapp->new_grammar( input=>$translationscheme) }
’No errors in input translation scheme’;
}
Pruebas SKIP
El código anterior muestra una prueba SKIP. Una prueba SKIP declara un bloque de pruebas que
- bajo ciertas circustancias - puede saltarse. Puede ser que sepamos que ciertas pruebas sólo funcionan
en ciertos sistemas operativos o que la prueba requiera que ciertos paquetes están instalados o que la
máquina disponga de ciertos recursos (por ejemplo, acceso a internet).
Pruebas TODO
Vamos a hacer una prueba mas. Supongamos que tengo la intención de añadir una función GenKnap
que genere aleatoriamente un problema de la mochila. Como no esta hecho, lo declaramos como una
prueba a hacer ( TODO ). Es decir, se trata de un test que fallará, pero que se espera que deje de hacerlo
en el futuro.
42 TODO: {
43 local $TODO = "Randomly generated problem";
44 can_ok(’Algorithm::Knap01DP’, ’GenKnap’);
45 }
Primero una ejecución a mano:
~/Lperl/src/threads/knapsack/Algorithm-Knap01DP/t$ perl \
-I/home/lhp//Lperl/src/threads/knapsack/Algorithm-Knap01DP/lib 01MartelloAndTothBook.t
1..11
ok 1 - use Algorithm::Knap01DP qw/Knap01DP ReadKnap/;;
279
ok 2 - ReadKnap knap21.dat
ok 3 - Knap01DP knap21.dat
ok 4 - ReadKnap knap22.dat
ok 5 - Knap01DP knap22.dat
ok 6 - ReadKnap knap23.dat
ok 7 - Knap01DP knap23.dat
ok 8 - ReadKnap knap25.dat
ok 9 - Knap01DP knap25.dat
ok 10
not ok 11 - Algorithm::Knap01DP->can(’GenKnap’) # TODO Randomly generated problem
# Failed (TODO) test (01MartelloAndTothBook.t at line 45)
# Algorithm::Knap01DP->can(’GenKnap’) failed
Obsérvese que:
Estabamos en el directorio t.
Nos da el mensaje: not ok 11 - ... # TODO Return ... indicando que falla y que es una
prueba TODO.
Sigue una ejecución con make test (un directorio por encima):
Observa como ahora se informa que todas las pruebas fueron correctamente. Se ha ocultado que hay
una prueba TODO y su fallo no se considera significativo para la posible instalación del módulo. De este
modo el directorio de pruebas puede ser utilizado como lista recordatorio de objetivos y requerimientos
a realizar.
La ejecución toma ahora mas tiempo. Al ejecutar cover de nuevo obtenemos una tabla con las es-
tadı́sticas de cubrimiento:
280
lhp@nereida:~/Lperl/src/threads/knapsack/Algorithm-Knap01DP$ cover
Reading database from /home/lhp/projects/perl/src/threads/knapsack/Algorithm-Knap01DP/cover_db
El HTML generado nos permite tener una visión mas detallada de los niveles de cubrimiento.
lhp@nereida:~/Lperl/src/threads/knapsack/Algorithm-Knap01DP$ cd cover_db/
lhp@nereida:~/Lperl/src/threads/knapsack/Algorithm-Knap01DP/cover_db$ ls -l
total 68
-rw-r--r-- 1 lhp lhp 3424 2007-05-16 15:48 blib-lib-Algorithm-Knap01DP-pm--branch.html
-rw-r--r-- 1 lhp lhp 3127 2007-05-16 15:48 blib-lib-Algorithm-Knap01DP-pm--condition.html
-rw-r--r-- 1 lhp lhp 34909 2007-05-16 15:48 blib-lib-Algorithm-Knap01DP-pm.html
-rw-r--r-- 1 lhp lhp 2207 2007-05-16 15:48 blib-lib-Algorithm-Knap01DP-pm--subroutine.html
-rw-r--r-- 1 lhp lhp 3345 2007-05-16 15:48 cover.12
-rw-r--r-- 1 lhp lhp 2004 2007-05-16 15:48 coverage.html
-rw-r--r-- 1 lhp lhp 1348 2007-05-16 15:48 cover.css
drwx------ 2 lhp lhp 4096 2007-05-16 15:48 runs
drwx------ 2 lhp lhp 4096 2007-05-16 15:48 structure
Para mejorar el cubrimiento de tu código comienza por el informe de cubrimiento de subrutinas.
Cualquier subrutina marcada como no probada es un candidato a contener errores o incluso a ser
código muerto.
real 0m0.065s
user 0m0.060s
sys 0m0.000s
281
Al ejecutar dprofpp se muestran las subrutinas ordenadas según su consumo de CPU:
$ make test
PERL_DL_NONLAZY=1 /usr/bin/perl "-MExtUtils::Command::MM" \
"-e" "test_harness(0, ’blib/lib’, ’blib/arch’)" \
t/*.t
test_harness($verbose, @test_libs)
Lo que hace es ejecutar todas las pruebas usando un módulo denominado Test::Harness . Si verbose
se pone a 1 se obtiene información mas completa:
La Subrutina runtests
El módulo Test::Harness controla a los otros módulos de la familia Test::. Exporta la función
runtests(@test_files) la cual ejecuta todas los programas de prueba pasados en @test_files. La
función runtests intercepta la salida de las pruebas y las interpreta.
282
El Protocolo TAP
Las pruebas deben ajustar su salida al protocolo TAP: Test All Protocol Se supone que toda prueba
debe escribir primero la cadena 1..M donde M es el número de pruebas y después, por cada prueba la
salida debe contener la cadena ok N, donde N es el numeral de la prueba. Cuando una prueba es TODO
su salida debe contener la cadena # TODO después de not ok o de ok. El texto que sigue después de
esto es la descripción de lo que se supone que deberı́a hacerse.
La Utilidad prove
La utilidad prove provee un acceso a las funcionalidades de Test:Harness y constituye una
alternativa a make test durante el desarrollo del módulo. Veamos un ejemplo e uso:
Si se quieren ejecutar las pruebas de un cierto programa de prueba t/*.t mientras se está editando
con vim se puede poner el siguiente atajo en el fichero ~/.vimrc6 :
El objetivo testdb
El objetivo testdb del Makefile generado permite correr las pruebas bajo el control del depurador:
283
1..15
ok 1 - use GRID::Machine;
main::(t/13pipes.t:6): my $test_exception_installed;
main::(t/13pipes.t:7): BEGIN {
DB<1> c
Name "DB::single" used only once: possible typo at blib/lib/GRID/Machine/REMOTE.pm line 569.
ok 2 - No fatals creating a GRID::Machine object
ok 3 - No fatals opening not redirected output pipe
ok 4 - No fatals sending to pipe 10
ok 5 - No fatals sending to pipe 9
ok 6 - No fatals sending to pipe 8
ok 7 - No fatals sending to pipe 7
ok 8 - No fatals sending to pipe 6
ok 9 - No fatals sending to pipe 5
ok 10 - No fatals sending to pipe 4
ok 11 - No fatals sending to pipe 3
ok 12 - No fatals sending to pipe 2
ok 13 - No fatals sending to pipe 1
ok 14 - No fatals sending to pipe 0
0
1
2
3
4
5
6
7
8
9
10
ok 15 - No fatals closing pipe
Debugged program terminated. Use q to quit or R to restart,
use o inhibit_exit to avoid stopping after program termination,
h q, h R or h o to get additional info.
DB<1> q
pp2@nereida:~/LGRID_Machine$
5.21.9. Ejecutables
Si se tienen guiones ejecutables que acompañan el módulo es conveniente hacerlo notar en Makefile.PL
usando la opción EXE_FILES de WriteMakefile. El valor para esta clave es una referencia a un array
de ficheros ejecutables. Por ejemplo:
284
VERSION => "0.03",
PREREQ_PM => { ’Getopt::Long’ => 2.00, ’IO’ => 0 },
EXE_FILES => [ map { "script/$_" } @scripts ],
# *.pm files will be picked up automatically from ./lib
);
Los ficheros serán copiados al directorio INST_SCRIPT (por defecto ./blib/script) cuando se
haga make.
use strict;
use 5.006;
use ExtUtils::MakeMaker qw(prompt WriteMakefile);
my %prereq = (
’Digest::MD5’ => 0,
’IO::Socket’ => 0,
);
my %SSH_PREREQ = (
1 => {
’String::CRC32’ => ’1.2’,
’Math::GMP’ => ’1.04’,
’Scalar::Util’ => 0,
},
2 => {
’Digest::SHA1’ => ’2.10’,
.................. => ......,
},
);
285
$SSH_PREREQ{3} = { map %{$SSH_PREREQ{$_}}, 1..2 };
print<<MSG;
This is Net::SSH::Perl.
MSG
my @cryptmod = (
[ ’IDEA’, ’Crypt::IDEA’ ],
[ ’DES’,’Crypt::DES’ ],
[ ’DES3’, ’Crypt::DES’ ],
[ ’Blowfish’, ’Crypt::Blowfish’ ],
[ ’RC4’, ’’ ],
);
print<<MSG;
Some of the Net::SSH::Perl ciphers depend on a Crypt:: module from
....................................................................
MSG
my $i = 1;
for my $ciph(sort { $a->[0] <=> $b->[0] } @cryptmod) {
printf " [%d] %s\n", $i++, $ciph->[0];
}
my $c = prompt("\nEnter your choices, separated by spaces:", 1);
print "\n";
286
if (read_yes_or_no("Would you like to install it? (y/n)", "y")) {
$prereq{’Digest::BubbleBabble’} = ’0.01’;
}
print "\n";
}
WriteMakefile(
NAME => ’Net::SSH::Perl’,
DISTNAME => ’Net-SSH-Perl’,
VERSION_FROM => ’lib/Net/SSH/Perl.pm’,
PREREQ_PM => \%prereq,
AUTHOR => ’David Robins <[email protected]>’,
ABSTRACT => ’Perl client interface to SSH’,
);
sub read_yes_or_no {
my($prompt, $def) = @_;
my $ans = prompt($prompt, $def);
$ans =~ /^y/i;
}
sub have_module {
my($name, $ver) = @_;
eval("use $name" . ($ver ? " $ver;" : ";"));
!$@;
}
287
2 use Test::More;
3
4 eval { require Test::Kwalitee; Test::Kwalitee->import() };
5
6 plan( skip_all => ’Test::Kwalitee not installed; skipping’ ) if $@;
Autentificación Automática
SSH es un protocolo de red y un conjunto de estándares que permiten una conexión encriptada
entre dos computadoras. Usa criptografı́a de clave pública para autentificar al computador y al usuario.
Por defecto suele usar el puerto TCP 22.
SSH soporta autentificación basada en RSA. RSA fué el primer algoritmo publicado que permite
el encriptado y la firma digital. Se cree que es seguro si las claves son lo suficientemente largas. Se
usa aún en comercio electrónico. Una opción alternativa es DSA que corresponde a Digital Signature
Algorithm. DSA es propiedad del gobierno de los Estados Unidos de America.
Hay criptosistemas -a los que RSA pertenece - en los cuales el encriptado y desencriptado se hace
utilizando claves separadas y no es posible derivar la clave de desencriptado del conocimiento de la clave
de encriptado. El cliente utiliza un par de claves pública/privada para la autentificación. El servidor
sólo conoce la clave pública. Funciona como una pareja llave/cerradura en la que el conocimiento de
la cerradura no permite deducir la forma de la llave.
288
Generación de una Pareja de Claves Pública-Privada
Para la generación de una pareja de claves pública-privada se realiza ejecutando en el cliente
ssh-keygen:
$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/user/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/user/.ssh/id_rsa.
Your public key has been saved in /home/user/.ssh/id_rsa.pub.
The key fingerprint is:
xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx user@machine
$ ls -l
total 12
-rw------- 1 user user 883 2005-08-13 14:16 id_rsa
-rw-r--r-- 1 user user 223 2005-08-13 14:16 id_rsa.pub
-rw-r--r-- 1 user user 1344 2005-08-04 02:14 known_hosts
Los ficheros id_rsa e id_rsa.pub contienen respectivamente las claves privada y pública. El fichero
known_hosts contiene la lista de las claves públicas de las máquinas reconocidas.
ssh-copy-id es un script que se conecta a la máquina y copia el archivo (indicado por la opción
-i) en ~/.ssh/authorized_keys, y ajusta los permisos a los valores adecuados.
Si no se dispone del programa ssh-copy-id se puede realizar una copia manual a la máquina
remota del fichero conteniendo la clave pública (por ejemplo usando scp o sftp) y añadir su contenido
al fichero ~/.ssh/authorized_keys.
Ahora la conexión deberı́a funcionar sin necesidad de introducir la clave. Si no es ası́ es posible
que sea un problema de permisos en los ficheros. Los permisos correctos deben ser similares a estos:
289
Ejecutando las Pruebas en un Conjunto de Máquinas
El siguiente programa muestra un guión sencillo que permite ejecutar las pruebas en un conjunto de
máquinas remotas con las que se ha establecido un sistema de autentificación automática. Al ejecutar
el programa se transfiere la distribución, se desempaqueta y se pasan las pruebas sobre cada máquina:
290
15
16 return <<"EOT";
17 remotetest:
18 remotetest.pl \${DISTVNAME}.tar.gz @machines
19 EOT
20 }
$ cat -n /usr/local/bin/remotetest.pl
1 #!/usr/bin/perl -w
2
3 eval ’exec /usr/bin/perl -w -S $0 ${1+"$@"}’
4 if 0; # not running under some shell
5 use strict;
6 use GRID::Machine;
7
. ....................................................................
28
29 my $dist = shift or die "Usage:\n$0 distribution.tar.gz machine1 machine2 ... \n";
30
31 die "No distribution $dist found\n" unless -r $dist;
32
33 die "Distribution does not follow standard name convention\n"
34 unless $dist =~ m{([\w.-]+)\.tar\.gz$};
35 my $dir = $1;
36
37 die "Usage:\n$0 distribution.tar.gz machine1 machine2 ... \n" unless @ARGV;
38 for my $host (@ARGV) {
39
40 my $m = eval {
41 GRID::Machine->new(host => $host)
42 };
43
44 warn "Cant’ create GRID::Machine connection with $host\n", next unless UNIVERSAL::isa($
45
46 my $r = $m->eval(q{
47 our $tmpdir = File::Temp::tempdir;
48 chdir($tmpdir) or die "Can’t change to dir <$tmpdir>\n";
49 }
50 );
51
52 warn($r),next unless $r->ok;
53
54 my $preamble = $host.".preamble.pl";
55 if (-r $preamble) {
56 local $/ = undef;
57
58 my $code = slurp_file($preamble);
59 $r = $m->eval($code);
60 warn("Error in $host preamble: $r"),next unless $r->ok;
61 }
291
62
63 $m->put([$dist]) or die "Can’t copy distribution in $host\n";
64
65 $r = $m->eval(q{
66 my $dist = shift;
67
68 eval(’use Archive::Tar’);
69 if (Archive::Tar->can(’new’)) {
70 # Archive::Tar is installed, use it
71 my $tar = Archive::Tar->new;
72 $tar->read($dist,1) or die "Archive::Tar error: Can’t read distribution $dist\n";
73 $tar->extract() or die "Archive::Tar error: Can’t extract distribution $dist\n";
74 }
75 else {
76 system(’gunzip’, $dist) or die "Can’t gunzip $dist\n";
77 my $tar = $dist =~ s/\.gz$//;
78 system(’tar’, ’-xf’, $tar) or die "Can’t untar $tar\n";
79 }
80 },
81 $dist # arg for eval
82 );
83
84 warn($r), next unless $r->ok;
85
86 $m->chdir($dir)->ok or do {
87 warn "$host: Can’t change to directory $dir\n";
88 next;
89 };
90
91 print "************$host************\n";
92 next unless $m->run(’perl Makefile.PL’);
93 next unless $m->run(’make’);
94 next unless $m->run(’make test’);
95
96 # Clean files
97 $m->eval( q{
98 our $tmpdir;
99 chdir "$tmpdir/..";
100 system (’rm -fR $dir’);
101 });
102 }
292
• Perl Testing Reference Card por Ian Langworth.
• Chapter 4: Distributing Your Tests (and Code) del libro Perl Testing: A Developer’s Notebook
• Lea 431702
Lea 536312
Añada pruebas que comprueben el funcionamiento de ReadKnap cuando los ficheros no existen
o no contienen los datos deseados (no tienen números, contienen números negativos, etc.).
Añada al módulo la función GenKnap y añada al programa de pruebas una nueva prueba para
comprobar su funcionamiento. Use las funciones rand y srand. Use la función int para obtener
un entero (p. ej int(rand(10) devuelve un entero entre 0 y 9). La prueba puede consistir en
comprobar que los pesos y los beneficios están en un rango dado. Otra posible prueba es llamar
a GenKnap dos veces y comprobar que los problemas generados son distintos.
Una prueba SKIP declara un bloque de pruebas que - bajo ciertas circustancias - puede saltarse.
Puede ser que sepamos que ciertas pruebas sólo funcionan en ciertos sistemas operativos o que
la prueba requiera que ciertos paquetes están instalados o que la máquina disponga de ciertos
recursos (por ejemplo, acceso a internet). En tal caso queremos que los tests se consideren si
se dan las circustancias favorables pero que en otro caso se descarten sin protestas. Consul-
te la documentación de los módulos Test::More y Test::Harness sobre pruebas tipo SKIP.
Construya una prueba SKIP para el módulo Algorithm::Knap01DP. Por ejemplo, si el módulo
Algorithm::Knapsack está instalado en la máquina, genere un problema aleatorio y compruebe
que la solución dada por ambos algoritmos es la misma.
Utilice el módulo Test::Warn para comprobar que los mensajes de warning (uso de warn and
carp) se muestran correctamente.
podemos hacer:
293
nereida:~/projects/coro/xpp/check> cat -n electrotest.pl
1 #!/usr/bin/perl -w
2 use Test::LectroTest;
3 Property {
4 ##[ x <- Float(range=>[0,1_000]) ]##
5 sqrt( $x * $x ) == $x;
6 }, name => "sqrt satisfies defn of square root";
7
8 Property {
9 ##[ x <- Int ]##
10 $x + $x >= $x;
11 }, name => "2*x is greater than x";
nereida:~/projects/coro/xpp/check> ./electrotest.pl
1..2
ok 1 - ’sqrt satisfies defn of square root’ (1000 attempts)
not ok 2 - ’2*x is greater than x’ falsified in 3 attempts
# Counterexample:
# $x = -3;
Como se ve en la lı́nea 17 se provee también una función holds la cual somete a pruebas la propiedad
que recibe como argumento.
La Función holds
La función holds:
holds(property, opts...)
294
holds( $prop_nonnegative, trials => 100 );
holds(
Property {
##[ x <- Int ]##
my_function2($x) < 0;
}, name => "my_function2 is non-positive"
);
El Módulo a Comprobar
La prueba t/01lectro.t la usamos para comprobar el siguiente módulo Titi.pm. Esta es la
jerarquı́a de directorios:
2 directories, 7 files
En concreto la prueba t/01lectro.t vista antes comprueba que la función my_function abajo de-
vuelve valores positivos:
295
17 }
18
19 1;
Ejecución
Cuando ejecutamos las pruebas obtenemos un contraejemplo a nuestra aserción de que my_function
devuelve valores positivos:
Un Algebra de Generadores
El módulo Test::LectroTest::Generator provee generadores para los tipos de datos mas comunes.
Provee además un algebra de combinadores de generadores que permite la construcción de generadores
complejos.
$ perl -wde 0
DB<1> use Test::LectroTest::Generator qw(:common Gen)
DB<2> p 1<<31
2147483648
DB<3> $tg = Int(range=>[0, 2_147_483_647], sized=>0)
DB<4> x $tg
0 Test::LectroTest::Generator=HASH(0x84faa78)
’generator’ => CODE(0x84fa9a0)
-> &Test::LectroTest::Generator::\
__ANON__[/usr/local/share/perl/5.8.7/Test/LectroTest/Generator.pm:212]\
in /usr/local/share/perl/5.8.7/Test/LectroTest/Generator.pm:210-212
296
DB<3> x $tg->generate
0 1760077938
DB<4> x scalar localtime $tg->generate
0 ’Wed Jan 14 00:07:07 1998’
297
DB<19> $english_dist_vowel_gen = Frequency([8.167,Unit("a")], [12.702,Unit("e")], \
[6.996,Unit("i")], [ 7.507,Unit("o")],[2.758,Unit("u")] )
DB<20> x $english_dist_vowel_gen->generate
0 ’i’
DB<21> x $english_dist_vowel_gen->generate
0 ’e’
DB<22> x $english_dist_vowel_gen->generate
0 ’i’
0 ’o’
DB<34> $gen = Hash( String( charset=>"A-Z", length=>3 ), Float( range=>[0.0, 100.0] ), lengt
DB<35> x $gen->generate
0 HASH(0x8576a30)
’FSW’ => 0.998256374071719
’KLR’ => 0.0577333231717212
’PEV’ => 0.834037952293134
’TNK’ => 0.0146371360307889
298
El combinador OneOf aplica uno de varios generadores:
El combinador Each permite generar listas formados de aplicar cada uno de los generadores:
DB<39> $gen = Each( Char(charset => "aeiou"), Int( range=>[0,10], sized => 0 ) )
DB<40> x $gen->generate
0 ARRAY(0x857b770)
0 ’o’
1 3
DB<41> x $gen->generate
0 ARRAY(0x85771bc)
0 ’e’
1 5
DB<42> x $gen->generate
0 ARRAY(0x857b080)
0 ’i’
1 3
El combinador Apply aplica el código dado al resultado producido opr los generadores especifi-
cados:
299
Ejercicio 5.21.1. Construya un generador que produzca problemas de la mochila 0-1 representados
mediante una estructura de datos de la forma
lhp@nereida:~/Lperl/src/perl_testing_adn_examples/chapter_03/Algorithm-Knap01DP-0.25$ cat -n t
1 use strict;
2 use Test::More;
3 use Test::LectroTest::Generator qw(:common);
4 use List::Util qw(sum);
5
6 use Algorithm::Knap01DP;
7
8 my $t = shift || 100;
9 plan tests => $t;
10 my ($C, @sol);
11 for (1..$t) {
12 my $weights = List(Int(range => [5,100], sized=>0), length => 10);
13 my @w = @{$weights->generate};
14 my @p = @w;
15 my $set = List(Bool, length => 10);
16 do {
17 @sol = @{$set->generate};
18 $C = sum( @w[ grep { $sol[$_] } 0..9 ] );
19 } while ($C == 0);
20 my $knap = Algorithm::Knap01DP->new( capacity => $C, weights => \@w, profits => \@p);
21 $knap->Knap01DP();
22 is($C, $knap->{tableval}[-1][-1], "Random subset-sum problem");
23 }
Observe como el uso de plan en la lı́nea 9 nos permite ajustar dinámicamente el número de pruebas
a ejecutar. Podemos hacer una ejecución vı́a make:
lhp@nereida:~/Lperl/src/perl_testing_adn_examples/chapter_03/Algorithm-Knap01DP-0.25/t$ perl 0
1..4
300
ok 1 - Random subset-sum problem
ok 2 - Random subset-sum problem
ok 3 - Random subset-sum problem
ok 4 - Random subset-sum problem
Referencias
Para saber mas sobre Test::LectroTest lea las traparencias de Tim Moertel en http://community.moertel.co
Code has bugs. Tests are code. Ergo, tests have bugs.
Michael Schwern
o en otras palabras ¿Quien prueba las pruebas? ¿Quien controla la calidad de los controladores de
calidad? ¿Quién verifica el buen funcionamiento de los verificadores de buen funcionamiento?
301
at the coat room or leaving your car in valet parking. This is the ticket we track. Before we get into
typical applications, let’s dissect a generic ticketing system into its component parts, by building a list
of the things a typical ticketing system will do. You can use this list to decide whether or not you need
a ticketing system.
These are the bones of a true ticketing system:
Introducir Cambios
Luego hacemos los cambios y mejoras que consideremos necesarios:
pp2@nereida:/tmp/Algorithm-Knap01DP-0.01.new/lib/Algorithm$ vi Knap01DP.pm
......................................................................
Comprobarlos Cambios
Por supuesto, comprobamos que todo funciona correctamente y que las modificaciones corrigen los
errores que hemos detectado:
302
............................
All tests successful.
Files=1, Tests=12, 0 wallclock secs ( 0.07 cusr + 0.01 csys = 0.08 CPU)
Crear el Patch
Entonces procedemos a crear el patch usando el programa diff
pp2@nereida:/tmp$ diff -ur Algorithm-Knap01DP-0.01 Algorithm-Knap01DP-0.01.new/ > newknap.patc
La opción -u indica que usaremos formato unificado para la representación de las diferencias. Este
es el formato mas usado en el intercambio de código entre desarrolladores.
La opción -r indica que queremos que se analizen toda la jerarquı́a de ficehros y directorios y
se establezcan las diferencias entre ficheros, si estas existen. El fichero newknap.patch contiene una
descripción de las diferencias entre la vieja y la nueva versión:
pp2@nereida:/tmp$ cat -n newknap.patch
1 Sólo en Algorithm-Knap01DP-0.01.new/: blib
2 diff -ur Algorithm-Knap01DP-0.01/lib/Algorithm/Knap01DP.pm Algorithm-Knap01DP-0.01.new/lib
3 --- Algorithm-Knap01DP-0.01/lib/Algorithm/Knap01DP.pm 2005-05-20 07:40:28.000000000 +010
4 +++ Algorithm-Knap01DP-0.01.new/lib/Algorithm/Knap01DP.pm 2008-05-16 08:04:12.000000
5 @@ -4,6 +4,7 @@
6 use warnings;
7 use Carp;
8 use IO::File;
9 +use List::Util qw{first};
10
11 use Exporter;
12 our @ISA = qw(Exporter);
13 @@ -20,6 +21,7 @@
14 my @f;
15
16 croak "Profits and Weights don’t have the same size" unless scalar(@w) == scalar(@p);
17 + croak "Invalid weight/profit $_" if defined($_ = first { $_ <= 0 } (@w, @p));
18
19 for my $c (0..$M) {
20 $f[0][$c] = ($w[0] <= $c)? $p[0] : 0;
21 Sólo en Algorithm-Knap01DP-0.01.new/lib/Algorithm: Knap01DP.pm.original
22 Sólo en Algorithm-Knap01DP-0.01.new/lib/Algorithm: newknap.patch
23 Sólo en Algorithm-Knap01DP-0.01.new/: Makefile
24 Sólo en Algorithm-Knap01DP-0.01.new/: pm_to_blib
25 diff -ur Algorithm-Knap01DP-0.01/t/01MartelloAndTothBook.t Algorithm-Knap01DP-0.01.new/t/0
26 --- Algorithm-Knap01DP-0.01/t/01MartelloAndTothBook.t 2005-05-20 07:40:04.000000000 +010
27 +++ Algorithm-Knap01DP-0.01.new/t/01MartelloAndTothBook.t 2008-05-16 07:55:17.000000
28 @@ -3,7 +3,7 @@
29
30 #########################
31 use strict;
32 -use Test::More tests => 11;
33 +use Test::More tests => 12;
34
35 BEGIN { use_ok(’Algorithm::Knap01DP’, qw/Knap01DP ReadKnap/); }
36
37 @@ -39,6 +39,11 @@
38 eval { Knap01DP($M, $w, $p) };
39 like $@, qr/Profits and Weights don’t have the same size/;
303
40
41 +# test to check when weights and profits have negative value
42 +$M = 100; @$w = @$p = (1,7,-2,3..8);
43 +eval { Knap01DP($M, $w, $p) };
44 +like $@, qr/Invalid weight/;
45 +
46 TODO: { # I plan to provide a function to find the vector solution ...
47 local $TODO = "Randomly generated problem";
48 can_ok(’Algorithm::Knap01DP’, ’GenKnap’);
La cabecera
indica que las diferencias que siguen se refieren a los ficheros Knap01DP.pm.
Un rango como @@ -4,6 +4,7 @@ establece un trozo que ha cambiado. Nos indica que las lı́neas de
la 4 a la 4+6 del original han sido cambiadas por las lı́neas de la 4 a la 4+7 en el nuevo. La presencia
del prefijo + indica que se ha insertado una nueva lı́nea:
El trozo:
28 @@ -3,7 +3,7 @@
29
30 #########################
31 use strict;
32 -use Test::More tests => 11;
33 +use Test::More tests => 12;
34
35 BEGIN { use_ok(’Algorithm::Knap01DP’, qw/Knap01DP ReadKnap/); }
indica que se cambió la lı́nea use Test::More tests => 11; por use Test::More tests => 12;
Parece que se ha añadido una prueba. En efecto, vemos que se han insertado 5 nuevas lı́neas que
comprueban el buen funcionamiento de la función Knap01DP cuando se le pasan pesos/beneficios
negativos:
37 @@ -39,6 +39,11 @@
38 eval { Knap01DP($M, $w, $p) };
39 like $@, qr/Profits and Weights don’t have the same size/;
40
41 +# test to check when weights and profits have negative value
42 +$M = 100; @$w = @$p = (1,7,-2,3..8);
43 +eval { Knap01DP($M, $w, $p) };
304
44 +like $@, qr/Invalid weight/;
45 +
46 TODO: { # I plan to provide a function to find the vector solution ...
47 local $TODO = "Randomly generated problem";
48 can_ok(’Algorithm::Knap01DP’, ’GenKnap’);
$ cd /tmp/Algorithm-Knap01DP-0.01
pp2@nereida:/tmp/Algorithm-Knap01DP-0.01$ patch -p1 < ../newknap.patch
patching file lib/Algorithm/Knap01DP.pm
patching file t/01MartelloAndTothBook.t
La opción -p1 utilizada indica que hay que saltarse/quitar el primer directorio en los caminos que
describen los nombres completos de los ficheros. Esto es, un camino como:
Algorithm-Knap01DP-0.01/t/01MartelloAndTothBook.t
es convertido en:
t/01MartelloAndTothBook.t
305
Capı́tulo 6
6.1. Moose
Moose is a complete object system for Perl 5. Consider any modern object-oriented language
(which Perl 5 definitely isn’t). It provides keywords for attribute declaration, object construction,
inheritance, and maybe more. These keywords are part of the language, and you don’t care how they
are implemented.
Moose aims to do the same thing for Perl 5 OO. We can’t actually create new keywords, but
we do offer sugar that looks a lot like them. More importantly, with Moose, you define your class
declaratively, without needing to know about blessed hashrefs, accessor methods, and so on.
With Moose, you can concentrate on the logical structure of your classes, focusing on what rather
than how. A class definition with Moose reads like a list of very concise English sentences.
Moose is built on top of Class::MOP, a meta-object protocol (aka MOP). Using the MOP, Moose
provides complete introspection for all Moose-using classes. This means you can ask classes about
their attributes, parents, children, methods, etc., all using a well-defined API. The MOP abstracts
away the symbol table, looking at @ISA vars, and all the other crufty Perl tricks we know and love(?).
Moose is based in large part on the Perl 6 object system, as well as drawing on the best ideas from
CLOS, Smalltalk, and many other languages.
306
22 }
23 }
24
25 1;
Modern Perl
Moose home
Moose en CPAN
Moose::Manual
Moose::Cookbook
6.2. Introducción
Las caracterı́sticas de la Programación orientada a objetos en Perl pueden resumirse en:
Para crear un objeto, se bendice (bless) una referencia. Los objetos Perl son datos normales
como hashes y arrays que han sido ”bendecidos” en un paquete.
Constructores: Son rutinas que retornan una referencia a un objeto recién creado e inicializado.
Destructores: Los destructores son rutinas que son llamadas cuando un objeto deja de existir
porque deja de ser referenciado o porque su ámbito termina.
package Biblio::Doc;
use strict;
{
my $_count = 0;
sub get_count { $_count }
sub _incr_count { $_count++ }
}
sub new {
my $class = shift;
my ($identifier, $author, $publisher, $title, $year, $url) = @_;
$class->_incr_count();
307
my $paper = {
_identifier => $identifier,
_author => $author,
_publisher => $publisher,
_title => $title,
_year => $year,
_url => $url
};
sub set_url {
my ($self, $url) = @_;
$self ->{_url} = $url if $url;
return ($self ->{_url});
}
1;
package Biblio::Doc;
use strict;
...
Paquetes y Clases
Los paquetes proporcionan un conjunto de caracterı́sticas que son comunes a las clases: propor-
cionan un espacio de nombres separado, agrupando ası́ un código que ofrece un conjunto de funcio-
nalidades relacionadas y aislándolo del resto; tienen un nombre que puede ser usado para identificar
los datos y subrutinas en el paquete (fully qualified names) y, si el paquete es un módulo, diferencian
entre la parte a exportar y la parte interna. Por todo ello Perl usa los paquetes para implantar las
clases.
{
my $_count = 0;
sub get_count { $_count }
sub _incr_count { $_count++ }
}
Es un convenio en Perl que las variables que comienzan con un guión bajo son variables privadas. Con-
secuencia de este convenio es que debemos entender que el autor pretende que $_count y _incr_count
sean privadas.
308
La variable privada $_count contiene el contador de documentos. Su acceso queda restringido no
ya al ámbito del módulo sino al ámbito léxico establecido por la clausura. La única forma de acceder
a $_count es a través de las funciones _incr_count y get_count.
En general, en programación orientada a objetos se aconseja que se envuelvan todos los accesos a
atributos en subrutinas y se prohiba la posible modificación exterior de los mismos ([18] .
El Constructor
A continuación viene el constructor de objetos new (su nombre es arbitrario, pero la costumbre es
llamar ası́ a los constructores) de la clase Biblio::Doc
1 sub new {
2 my $class = shift;
3 my ($identifier, $author, $publisher, $title, $year, $url) = @_;
4 $class->_incr_count();
5
6 my $paper = {
7 _identifier => $identifier,
8 _author => $author,
9 _publisher => $publisher,
10 _title => $title,
11 _year => $year,
12 _url => $url
13 };
14
15 bless $paper, $class;
16 }
La Operación bless
Un objeto Perl es un dato que ha sido bendecido con un paquete. La operación de bendición bless
toma como primer argumento una referencia y como segundo un nombre de paquete.
El argumento ”nombre del paquete” es opcional: se usará el nombre del package actual si se omite.
Una vez que un dato es ”bendecido” (¿Quizá deberı́amos decir ”bautizado como miembro de la
clase”?) el dato pasa a ”saber” a que clase/package pertenece.
En el ejemplo se bendice una referencia a un hash. Sin embargo, Perl admite que se bendiga una
referencia a cualquier otro tipo de objeto.
Lo que sucede cuando Perl ve una llamada a una subrutina seguida del nombre de un paquete
(new Biblio::Doc) o una llamada con la sintáxis de paquete seguido de flecha seguido de un método
(Biblio::Doc->new) es que llama a una subrutina con ese nombre en ese paquete, y le pasa como
primer argumento el nombre del paquete. Ası́, el primer argumento que recibe el constructor new es
el nombre de la clase, esto es Biblio::Doc. Ası́ pues la lı́nea 2
my $class = shift;
309
Obtiene la clase Biblio::Doc.
La llamada de la lı́nea 4:
$class->_incr_count();
es, por tanto, otro ejemplo de esta forma de llamada. En este caso tenemos además un referenciado
simbólico via $class.
La Referencia no es el Objeto
El hash referenciado por $paper en las lı́neas de la 6 a la 13 es bendecido en la lı́nea 15 con el
nombre de la clase.
1 sub new {
2 my $class = shift;
3 my ($identifier, $author, $publisher, $title, $year, $url) = @_;
4 $class->_incr_count();
5
6 my $paper = {
7 _identifier => $identifier,
8 _author => $author,
9 _publisher => $publisher,
10 _title => $title,
11 _year => $year,
12 _url => $url
13 };
14
15 bless $paper, $class;
16 }
Esto establece un campo interno dentro de la representación interna del hash anónimo. En cierto
sentido es %$paper quien es bendecido, no $paper. La figura 6.1 ilustra este punto: En la izquierda de
la figura aparecen la referencia y el referente antes de la bendición. En la parte de la derecha, después
de ejecutar bless $paper, $class. Obsérvese que se ha añadido una marca con el nombre de la
clase-paquete que identifica la estructura de datos como un objeto de esa clase.
$paper $paper
Biblio::doc
_identifier 1 _identifier 1
_author "Asimov" _author "Asimov"
_publisher "Bruguera" _publisher "Bruguera"
_title "Los ..." _title "Los ..."
_year 1989 _year 1989
_url "unknown" _url "unknown"
Figura 6.1: Al bendecir un objeto este es marcado con la clase a la que pertenece
Una vez que el dato apuntado por la referencia ha sido bendecido, el operador ref aplicado a
dicha referencia devuelve el nombre del paquete (esto es, el nombre de la clase) en vez de HASH.
Una comprobación de que quien es bendecido es el dato referido no la referencia la da el siguiente
código:
310
$obj = new Biblio::Doc(1, "Asimov", "Bruguera",
"Los propios dioses", 1989, "unknown");
$obj2 = $obj;
print "obj2 es un ",ref($obj2),"\n";
Sin embargo, llamar a Biblio::Doc::get_author como método conlleva una extensión a la sintáxis
flecha de Perl. Si se tiene que $obj es una referencia a un objeto de la clase Biblio::Doc, podemos
acceder a cualquier método del paquete usando la notación $obj->method donde method es cualquier
método de la clase. Por ejemplo:
$obj->get_author()
Es equivalente a
Biblio::Doc::get_author($obj);
Esto es: cuando una subrutina es llamada como método, la lista de argumentos que recibe comienza
con la referencia al objeto a través del cual fué llamada y va seguida de los argumentos que fueron
explicitados en la llamada al método.
Observe que la última llamada trata a get_author como una subrutina ordinaria, mientras que la
primera la usa ”como método”.
La sintáxis ”flecha” para la llamada a un método es consistente con el uso de la flecha para otros ti-
pos de referencias en Perl. Ası́, al igual que escribimos $hsh_ref->{clave} o bien $sub_ref->(4,7,8)
también escribimos $obj_ref->metodo(@args). Una diferencia de esta última notación con las ante-
riores estriba en que el referente tiene diferentes conductas, esto es, tiene varios metodos y la conducta
concreta que queremos producir se especifica escribiendo el nombre del método después de la flecha.
311
Comprobación de los Tipos de los Argumentos de un Método
Un programador C se sentirı́a tentado de usar prototipos para especificar mas claramente el tipo de
acceso a un método. Perl no comprueba prototipos cuando una subrutina de paquete es llamada como
método, utilizando la sintáxis $objref->method(@args). En el caso mas general resulta imposible
conocer hasta el momento de la ejecución que subrutina debe ser invocada en respuesta a una llamada
de un método.
La Función blessed
La función blessed en Scalar::Util tiene la sintáxis:
blessed EXPR
Si EXPR es una referencia que fué bendecida retorna el nombre del corespondiente paquete. En otro
caso retorna undef.
$scalar = "foo";
$class = blessed $scalar; # undef
$ref = [];
$class = blessed $ref; # undef
1 sub AUTOLOAD {
2 no strict "refs";
3
312
4 my $self = shift;
5 if ($AUTOLOAD =~ /\w+::\w+::get(_.*)/) {
6 my $n = $1;
7 die "Error in $AUTOLOAD: Illegal attribute\n" unless exists $self->{$n};
8
9 # Declarar el metodo get_*****
10 *{$AUTOLOAD} = sub { return $_[0]->{$n}; };
11
12 return $self->{$n};
13 } elsif ($AUTOLOAD =~ /\w+::\w+::set(_.*)/) {
14 my $n = $1;
15 die "Error in $AUTOLOAD: Illegal attribute\n" unless exists $self->{$n};
16 $self->{$n} = shift;
17
18 # Declarar el metodo set_*****
19 *{$AUTOLOAD} = sub { $_[0]->{$n} = $_[1]; };
20 } else {
21 die "Error: Function $AUTOLOAD not found\n";
22 }
23 }
En la lı́nea 5 obtenemos el nombre del atributo y lo guardamos (lı́nea 6) en $n. En la lı́nea 7 compro-
bamos que tal atributo existe. La subrutina anónima que crea AUTOLOAD en la lı́nea 10 es una clausura
con respecto a $n: el valor se recuerda, incluso después que se haya salido del ámbito.
Esto hace que se instale una entrada con el nombre del método deseado en la tabla de sı́mbolos.
Desactivación de strict
El use strict hace que el compilador se queje, ya que el lado derecho es una referencia a subrutina
y el lado izquierdo un ”typeglob”:
~/perl/src> use_Biblio_Doc.1.pl
Can’t use string ("get_author") as a symbol ref while "strict refs" in use at Biblio/Doc1.pm l
Has llamado a Biblio::Doc1::DESTROY() y no existe!
no strict "refs";
hace que strict deje de controlar el uso de referenciado simbólico en el ámbito en el que se ubique.
El Método DESTROY
El método DESTROY es llamado cada vez que el contador de referencias del objeto alcanza cero.
Como hemos escrito un AUTOLOAD la llamada automática a DESTROY que produce la salida de contexto
del objeto provoca una llamada a AUTOLOAD. Dado que el AUTOLOAD que hemos escrito no contempla
este caso deberemos evitar que la llamada DESTROY sea captada por AUTOLOAD. Para lograrlo basta
con definir un método DESTROY vacı́o:
sub DESTROY {}
313
Programa Cliente: Ejemplos de LLamadas
Sigue un ejemplo de programa cliente:
#!/usr/bin/perl -w -I.
use Biblio::Doc1;
$obj->set_author("Isaac Asimov");
print ("El nombre del autor definitivo es: ", $obj->get_author(), "\n");
Ejercicio
Ejercicio 6.3.1. ¿Que pasarı́a en el ejemplo si la llamada tiene la forma: get_issue($url, $year)?
(Suponemos que issue no es un atributo de la clase).
¿Que ocurrirı́a en tu programa si ya existiera un método con tal nombre?
no strict ’refs’;
for my $sub (@_) {
*{$package."::$sub"} = sub {
my $self = shift;
No Re-invente la Rueda
Un buen número de módulos en CPAN proveen mecanismos similares a los explicados en esta
sección para la construcción automática de métodos get/set y constructores. Estudie los siguientes
módulos:
Class::Accessor
Class::Accessor::Assert
Class::MethodMaker
Class::Struct
314
Class::Std
Class::Std::Utils
Class::MakeMethods::Emulator::MethodMaker
El Módulo Class::Accessor
El siguiente fragmento de un módulo ilustra el modo de uso de Class::Accessor . El módulo
provee un servicio de llamadas a procedimientos remotos desde un cliente Perl a un servidor Perl
situado en otra máquina. Los resultados de la llamada son objetos de la clase GRID::Machine::Result.
La lı́nea 3 (use base qw(Class::Accessor)) indica que no sólo se usa el módulo Class::Accessor
sino que se hereda del mismo. En la lı́nea 5 definimos una lista con los atributos públicos del objeto.
En la lı́nea 6 se construyen getter/setters para esos atributos.
El constructor del ejemplo comprueba que todos los nombres de los argumentos son legales en la
lı́nea 14.
El siguiente código muestra un ejemplo de llamada:
$r = GRID::Machine::Result->new(%result);
print $r->rstdout;
sub new {
my $class = shift;
my $self = {
capacity => 0, # total capacity of this knapsack
numobjects => 0, # number of objects
weights => [], # weights to be packed into the knapsack
profits => [], # profits to be packed into the knapsack
tableval => [], # f[k][c] DP table of values
tablesol => [], # x[k][c] DP table of sols
315
# (0 = out, 1 = in, 2 = in and out)
solutions => [], # list of lists of object indexes
filename => "", # name of the file the problem was read from
@_,
};
6.4. Constructores
Se suelen llamar constructores a aquellos métodos de la clase que asignan memoria para la estruc-
tura de datos en la que subyace el nuevo objeto, inicializándolo y bendiciéndolo.
3. Los constructores suelen tener muchos parámetros. Se debe aplicar la estrategia (sección 1.15.7)
de llamada con nombres.
4. Es necesario comprobar que los nombres usados en la llamada son legales y que los tipos de los
argumentos se ajustan a los tipos de los atributos.
5. Hay que proveer valores por defecto en la inicialización, de manera que campos no especificados
por el usuario resulten inicializados de manera apropiada.
Ejemplo
A continuación veamos un código que, siguiendo las recomendaciones establecidas, separa el proceso
de iniciación, organiza los parámetros del constructor según un ”hash” y provee valores por defecto.
1 package Biblio::Doc;
2 use strict;
3 use vars(’$AUTOLOAD’);
4
5 # Separamos inicialización de construcción
6
7 {
8 my $_count = 0;
9 sub get_count { $_count }
10 sub _incr_count { $_count++ }
316
11 sub _decr_count { $_count-- }
12 }
13
El Hash defaults
14 {
15 my %_defaults = ( # Default Access
16 _identifier => ["unknown",’read’],
17 _author => ["unknown",’read/write’],
18 _publisher => ["unknown",’read’],
19 _title => ["unknown",’read’],
20 _year => ["unknown",’read’],
21 _url => ["unknown",’read/write’]
22 );
Se ha introducido un hash que contiene los valores por defecto y el tipo de acceso permitido (lectu-
ra/escritura) para cada clave.
24 sub _standard_keys {
25 return keys %_defaults;
26 }
Queremos además disponer de un método que nos diga que claves forman el objeto. Esa es la función
del método privado _standard_keys.
28 sub _default_for {
29 my ($self, $attr) = @_;
30
31 return $_defaults{$attr}[0];
32 }
El Método accesible
34 sub _accesible {
35 my ($self, $attr, $access) = @_;
36 return $_defaults{$attr}[1] =~ /$access/;
37 }
El método _accesible nos dice si una clave esta accesible para el tipo de acceso requerido.
El Método init
El método privado _init inicializa el hash referenciado por $self según lo indicado en el hash
%args.
39 sub _init {
40 my ($self, %args) = @_;
41 my %inits;
42 my ($i, $j);
317
43
44 for $i ($self->_standard_keys) {
45 $j = $i;
46 $j =~ s/_//;
47 $inits{$i} = $args{$j} || $self->_default_for($i);
48 }
49 %$self = %inits;
50 }
51 }
Las claves en %args no van precedidas de guión bajo (se supone que la llamada desde el programa
cliente usará los nombres de los atributos sin guión bajo). Si la clave no figura en %args se inicializa
al valor por defecto.
El Constructor
En la lı́nea 57 se bendice el objeto aún sin inicializar.
53 sub new {
54 my $class = shift;
55 my $self = {};
56
57 bless $self, ref($class) || $class;
58
59 $self->_incr_count();
60 $self->_init(@_);
61
62 return $self;
63 }
El Destructor
Perl elimina automáticamente la memoria de un objeto cuando es claro que ya no va a ser utilizado.
Un método con el nombre especial DESTROY se dispara cuando la memoria asociada con un objeto
debe ser eliminada (normalmente por que salimos de su ámbito de existencia).
318
65 sub DESTROY {
66 my $self = shift;
67
68 $self->_decr_count();
69 }
71 sub AUTOLOAD {
72 no strict "refs";
73 my $self = shift;
74 if (($AUTOLOAD =~ /\w+::\w+::get(_.*)/) && ($self->_accesible($1,’read’))) {
75 my $n = $1;
76 die "Error in $AUTOLOAD: Illegal attribute\n" unless exists $self->{$n};
77
78 # Declarar el metodo get_*****
79 *{$AUTOLOAD} = sub { return $_[0]->{$n}; };
80
81 return $self->{$n};
82 } elsif (($AUTOLOAD =~ /\w+::\w+::set(_.*)/) && ($self->_accesible($1,’write’))) {
83 my $n = $1;
84 die "Error in $AUTOLOAD: Illegal attribute\n" unless exists $self->{$n};
85 $self->{$n} = shift;
86
87 # Declarar el metodo set_*****
88 *{$AUTOLOAD} = sub { $_[0]->{$n} = $_[1]; };
89 } else {
90 die "Error: Function $AUTOLOAD not found\n";
91 }
319
Una llamada como $newobj = $objref->new(arg1 => "nuevo valor1", arg4 => "nuevo valor4")
deberı́a producir un objeto $newobj cuyos atributos son iguales a los de $objref, salvo que los atri-
butos arg1 y arg4 son cambiados a "nuevo valor1" y "nuevo valor4" respectivamente.
Modo de uso:
#!/usr/bin/perl -w -I.
use Biblio::Doc2;
...
$newobj = $obj->new(author => "Gardner", title => "Left and Right in the Universe");
$newobj->print();
6.6. Herencia
Una clase informa a Perl que desea heredar de otra clase añadiendo el nombre de esa clase a la
variable @ISA de su paquete. Por ejemplo:
package A;
@ISA = ( "B" );
Búsqueda de un Método
La herencia en Perl determina el recorrido de búsqueda de un método. Si el objeto no se puede
encontrar en la clase, recursivamente y en orden primero-profundo se busca en las clases de las cuales
esta hereda, esto es en las clases especificadas en el vector @ISA. Para ser mas precisos, cuando Perl
busca por una llamada a un método como $obj->method(), realiza la siguiente secuencia de búsqueda:
1. Si la clase en la cuál el objeto fué bendecido (digamos MyClass) tiene una subrutina method se
llama
2. Si no, si existe un vector @ISA, para cada una de las clases en el vector @ISA se repiten los pasos
1y2
3. Si no, si la clase UNIVERSAL (véase la sección 6.6) tiene un método con ese nombre, se le llama
5. Si no, si una de las clases antepasadas de esta (una vez mas búscadas en orden primero profundo)
contiene un método AUTOLOAD, se le llama
Esta búsqueda sólo se hace una vez por método. Una vez localizado el método se utiliza una
”cache” para acceder al método rápidamente. Si el vector @ISA o el vector @ISA de cualquiera de los
antepasados de la clase es modificado, se limpia la ”cache”.
La clase UNIVERSAL
Como vemos existe una clase especial denominada clase UNIVERSAL de la cual implı́citamente
hereda toda clase. Esta clase provee los métodos isa , can y VERSION (véase la sección 5.6). Es
posible añadir métodos o atributos a UNIVERSAL.
320
El método isa
El método isa nos permite saber si una clase hereda de otra:
El método isa memoriza los valores que retorna, de manera que una vez que conoce un par no necesita
realizar una segunda búsqueda. Sin embargo la modificación de los arrays @ISA en la jerarquı́a borra
las caches:
El método can
Hay ocasiones en las que lo que nos preocupa no es tanto a que clase pertenece un objeto como
saber si dispone de un cierto método. El método can devuelve verdadero si el objeto puede llamar al
método solicitado:
De hecho, el valor que devuelve can es una referencia al método por el que se pregunta.
321
19 package Some::Class;
20
21 sub new {
22 my $class = shift;
23
24 bless { @_ }, $class;
25 }
Ejecución de singleton.pl
lhp@nereida:~/Lperl/src/advanced_perl_programming2$ singleton.pl
$VAR1 = bless( {’chuf’ => [1,2,3,4,5],’yuyu’ => 4}, ’_Singletons::135852824’ );
Can’t locate object method "dump" via package "Some::Class" at ./singleton.pl line 17.
La Implantación de Singletons
El método singleton_method crea una clase para el objeto que hace la invocación - si no fué creada
anteriormente - y hace que esta nueva clase herede de la clase del objeto. El algoritmo garantiza que
objetos distintos tendrán clases distintas.
322
#!/usr/bin/perl -w package A;
@ISA = qw(B C);
package B;
package D;
package C; @ISA = qw(E F G);
sub new {
my ($self, %args) = @_; sub new {
print "C::new\n"; my ($self, %args) = @_;
bless { %args }, print "D::new\n";
ref($self) || $self; bless
} { %args }, ref($self) || $self;
}
package E;
sub new { package H;
my ($self, %args) = @_; @ISA = qw(A D);
print "E::new\n";
bless { %args }; package main;
}
my $a = H->new();
package F; print ref($a),"\n";
package G; my $b = G->new();
@ISA = qw(E C); print ref($b),"\n";
1 #!/usr/bin/perl -d
2
3 package _I;
323
C
B E
F G
A D
H
Figura 6.2: Formas de bendición: esquema de herencia del programa
4 use strict;
5
6 sub new {
7 my ($class, %args) = @_;
8 my $self = bless {}, ref($class) || $class;
9 $self->_init(%args);
10 return $self;
11 }
12
13 package A;
14 @A::ISA = qw( _I);
15
16 sub _init {
17 my ($self, %args) = @_;
18 $self->{_a1} = $args{a1};
19 $self->{_a2} = $args{a2};
20 }
21
22
23 package B;
24 @B::ISA = qw( _I);
25
26 sub _init {
27 my ($self, %args) = @_;
28 $self->{_b1} = $args{b1};
29 }
30
31 package C;
32 @C::ISA = qw( _I B A);
33
34 sub _init {
35 my ($self, %args) = @_;
36 $self->A::_init(%args);
37 $self->B::_init(%args);
38 $self->{_c1} = $args{c1};
39 }
40
324
41 package main;
42
43 C->new(a1=>"a1", a2=>"a2", b1=>"b1", c1=>"c1");
La idea es que la clase _I provee un constructor genérico. Las otras clases heredan dicho constructor y
no lo reescriben. La figura 6.3 muestra el esquema de herencia y el de delegación (flechas punteadas)
usado para el método _init.
_I
new
B A
_init _init
C
_init
Figura 6.3: Esquema de herencia/delegación del programa
Este esquema es posiblemente mas usual que el reemplazamiento completo del método por uno
nuevo.
Véase una ejecución con el depurador:
$ ./inicializadores.pl
Default die handler restored.
Obsérvese que:
325
El objeto ha sido bendecido en la clase llamadora C, no en la clase _I.
DB<2> s
C::_init(./inicializadores.pl:35): my ($self, %args) = @_;
DB<2>
C::_init(./inicializadores.pl:36): $self->A::_init(%args);
DB<2>
A::_init(./inicializadores.pl:17): my ($self, %args) = @_;
DB<2>
A::_init(./inicializadores.pl:18): $self->{_a1} = $args{a1};
DB<2>
A::_init(./inicializadores.pl:19): $self->{_a2} = $args{a2};
DB<2>
C::_init(./inicializadores.pl:37): $self->B::_init(%args);
DB<2>
B::_init(./inicializadores.pl:27): my ($self, %args) = @_;
DB<2>
B::_init(./inicializadores.pl:28): $self->{_b1} = $args{b1};
DB<2>
C::_init(./inicializadores.pl:38): $self->{_c1} = $args{c1};
DB<2>
_I::new(./inicializadores.pl:10): return $self;
DB<2> p %$self
_a2a2_b1b1_c1c1_a1a1
DB<3>
Debugged program terminated. Use q to quit or R to restart,
use O inhibit_exit to avoid stopping after program termination,
h q, h R or h O to get additional info.
El proceso de destrucción es equivalente. Puesto que se trata de métodos, se sigue el mismo algoritmo
de búsqueda. Si queremos que los destructores sean llamados de manera organizada, es responsabilidad
del programador el hacerlo.
6.6.5. Diamantes
El método de delegación expuesto anteriormente falla si una clase hereda de un ancestro por dos
caminos distintos (en la jerga esto se llama un diamante). En tal caso existe el riesgo de que una
llamada por delegación a un _init de un antepasado se repita dos veces (una por cada camino hasta
el antepasado) para el mismo objeto. Una solución es rastrear que inicializadores han sido visitados
y evitar las subsiguientes visitas. Por ejemplo, si la clase C del ejemplo anterior es un candidato
a inicializaciones repetidas (esto es, si puede ser la cúspide de un diamante), la podemos proteger
usando el condicional que aparece en la lı́nea 6 (Véase [18] para mas detalles):
1 package C;
2 @C::ISA = qw( _I B A);
326
3
4 sub _init {
5 my ($self, %args) = @_;
6 return if $self->{_init}{__PACKAGE__}++;
7 $self->A::_init(%args);
8 $self->B::_init(%args);
9 $self->{_c1} = $args{c1};
10 }
La macro __PACKAGE__ es igual al nombre del paquete actual (en este caso el paquete C). El uso de
__PACKAGE__ puede ser de ayuda si por alguna razón decidimos copiar el código de _init en otro
paquete: No tenemos que sustituir las apariciones de C por el nombre del nuevo paquete.
Una estrategia parecida puede aplicarse para evitar la llamada reiterada a destructores cuando se
producen estructuras jerarquicas en diamante.
$self->B::method();
es que si se cambia el nombre de la clase o se modifica la jerarquı́a de clases habrá que reescribir esa
parte del código. Es posible en ocasiones evitar dicha codificación con el nombre explı́cito de la clase
madre utilizando un identificador de paquete SUPER el cual nos permite referirnos a las clases madres
de la clase actual:
$self->SUPER::method();
Mas que un sinónimo de B, lo que hace SUPER es indicarle a Perl que la búsqueda del método debe
realizarse desde el vector @ISA de la clase en la que el método está siendo llamado. Veamos un ejemplo:
$x = A->new();
print ref($x),"\n";
$x->x();
327
$ cat ./A.pm $ cat B.pm $ cat C.pm
package A; package B; package C;
use B; sub new {
use C; sub new { my $class = shift;
my $class = shift; bless {}, $class;
@ISA = ("B", "C"); bless {}, $class; }
}
sub x { sub t {
$self = shift; 1; print "C\n";
}
$self->SUPER::t();
} 1;
sub t {
print "t de A\n";
}
1;
main::(-e:1): 0
DB<1> @A::ISA = qw{B}; @B::ISA = qw{C}; @C::ISA =()
DB<2> sub C::tutu { 8 }
DB<3> $x = bless {}, ’A’
DB<4> x $x->SUPER::tutu
Can’t locate object method "tutu" via package "main" at \
(eval 8)[/usr/share/perl/5.8/perl5db.pl:628] line 2.
DB<5> x $x->tutu
0 8
DB<6> package A; $x = bless {}, ’A’; print $x->SUPER::tutu,"\n"
8
328
#!/usr/bin/perl -w package B;
use strict; @B::ISA = qw(E);
package main;
my $a = A->new();
$a->toto();
definidos en todas las clases que heredan de la clase base abstracta. Es una declaración que indica la
necesidad de definir su funcionalidad en las clases descendientes, pero que no se define en la clase base.
Para conseguir este efecto, lo normal en Perl es que nos aseguremos que nuestro método abstracto
produce una excepción con el mensaje de error adecuado. Cuando el número de métodos abstractos
es grande puede suponer un ahorro utilizar un método genérico como este:
package Abstract;
sub ABSTRACT {
my $self = shift;
my ($file, $line, $method) = (caller(1))[1..3];
1;
Obsérve el uso de la función caller introducida en la sección 1.15.10 para determinar la localización
exacta de la llamada.
329
E
new D
titi titi
package main
my $a = A->new();
$a->toto();
B
titi C
toto ($self->SUPER::titi)
A
Figura 6.4: Esquema del programa
1 sub new {
2 my $class = shift;
3 my $self = {};
4
5 bless $self, ref($class) || $class;
6
7 $self->_incr_count();
8 $self->_init(@_);
9
10 return $self;
11 }
Observe que puesto que en la lı́nea 5 se usa la versión de bless con dos argumentos, el objeto es
bendecido en la clase llamadora, de manera que una llamada a Biblio::Doc::Article->new produce
un objeto de la clase Biblio::Doc::Article. Nótese también que en la llamada de la lı́nea 8, dado
que $self ya ha sido bendecido en Biblio::Doc::Article, la subrutina _init que es llamada es, si
existe, la de la clase Biblio::Doc::Article. Si no existe serı́a llamada la de Biblio::Doc, la cual ha
sido escrita como:
1 sub _init {
2 my ($self, %args) = @_;
3 my %inits;
4 my ($i, $j);
5
6 for $i ($self->_standard_keys) {
7 $j = $i;
8 $j =~ s/_//;
9 $inits{$i} = $args{$j} || $self->_default_for($i);
330
10 }
11 %$self = %inits;
12 }
Esta subrutina confı́a en la llamada $self->_standard_keys para obtener las claves del objeto. Es
posible entonces escribir un método Biblio::Doc::Article::_standard_keys el cual proporcione las
claves nuevas junto con las que produce el método equivalente en la clase padre SUPER::_standard_keys.
Esta estrategia es posible por que se envolvió la estructura de datos %_defaults
con los métodos _standard_keys y _default_for. Si en la lı́nea 6 del código de _init hubieramos
escrito:
for $i (keys %_defaults)
la resolución del problema por delegación serı́a mas complicada.
Algo análogo puede hacerse con la subrutina _default_for.
De la misma forma, la función AUTOLOAD de la clase Biblio::Doc es:
1 sub AUTOLOAD {
2 no strict "refs";
3 my $self = shift;
4 if (($AUTOLOAD =~ /\w+::\w+::get(_.*)/) && ($self->_accesible($1,’read’))) {
5 my $n = $1;
6 return unless exists $self->{$n};
7
8 # Declarar el metodo get_*****
9 *{$AUTOLOAD} = sub { return $_[0]->{$n}; };
10
11 return $self->{$n};
12 } elsif (($AUTOLOAD =~ /\w+::\w+::set(_.*)/) && ($self->_accesible($1,’write’))) {
13 my $n = $1;
14 return unless exists $self->{$n};
15 $self->{$n} = shift;
16
17 # Declarar el metodo set_*****
18 *{$AUTOLOAD} = sub { $_[0]->{$n} = $_[1]; };
19 } else {
20 @_ = map { "\"".$_."\""; } @_; # Comillas a los argumentos...
21 print "Has llamado a $AUTOLOAD(",join(", ",@_),")\n";
22 }
23 }
331
6.7. Destructores
Perl elimina automáticamente la memoria de un objeto cuando es claro que ya no va a ser utilizado.
Un método con el nombre especial DESTROY se dispara cuando la memoria asociada con un objeto
debe ser eliminada (normalmente por que salimos de su ámbito de existencia).
Observe el siguiente código:
$ cat destructors.pl
1 #!/usr/bin/perl -w
2
3 {
4 my $a = 4;
5
6 print $a,"\n";
7 }
8 $a = "objeto apuntado por \$b";
9 $b = \$a;
10 bless $b;
11
12 sub DESTROY {
13 my $self = shift;
14 printf("\n****\n$$self eliminado a las %s\n", scalar localtime);
15 }
$ ./destructors.pl
4
****
objeto apuntado por $b eliminado a las Thu May 20 14:31:42 2004
Una solución simple para esta situación consiste en definir un método DESTROY vacı́o.
332
6.8. Instalación Automática de Métodos con Class::Struct
El módulo Class::Struct forma parte de la distribución núcleo de Perl. El módulo exporta una
única función denominada struct . La función struct recibe como argumentos una lista que describe
la estructura de datos asociada con un objeto. El efecto es la generación de un método constructor
(que tiene por nombre new) y métodos de acceso.
Creación de Métodos
La siguiente sesión con el depurador muestra un ejemplo de uso:
my($r) = {};
if (defined $init{’c’}) {
if (ref $init{’c’} eq ’HASH’) {
$$r{’Foo::c’} = ’Tutu’->new(%{$init{’c’};});
}
elsif (UNIVERSAL::isa($init{’c’}, ’Tutu’)) {
$$r{’Foo::c’} = $init{’c’};
}
else {
croak(’Initializer for c must be hash or Tutu reference’);
}
}
333
croak(’Initializer for h must be hash reference’)
if defined $init{’h’} and ref $init{’h’} ne ’HASH’;
Construccion
Ahora podemos construir objetos de esas clases::
DB<6> x $b->a
0 ARRAY(0x84eb4e0)
0 1
1 2
2 3
DB<7> x $b->a(1)
0 2
DB<8> x $b->a(1,7)
0 7
DB<9> x $b->a(1)
0 7
334
DB<30> $b->a([9..12])
DB<31> x $b->a
0 ARRAY(0x85399f0)
0 9
1 10
2 11
3 12
DB<26> p Dumper(\&Foo::a)
$VAR1 = sub {
package Foo;
use strict ’refs’;
my $r = shift @_;
my $i;
# Si no quedan args retornamos la ref al array
@_ ? ($i = shift @_) : return($$r{’Foo::a’});
DB<10> x $b->s
0 ’hola’
DB<11> x $b->s(’mundo’)
0 ’mundo’
DB<12> x $b->s
0 ’mundo’
DB<13> x $b->h
0 HASH(0x851bed0)
’x’ => 1
’y’ => 2
DB<14> x $b->h(’x’)
0 1
DB<15> x $b->h(x => 3)
335
0 3
DB<16> x $b->h(’x’)
0 3
DB<17> x $b->c
0 Tutu=ARRAY(0x84eb024)
0 ’Juan’
1 ARRAY(0x8474e5c)
0 ’Gonzalez’
1 ’Hernandez’
DB<18> x $b->c->nombre
0 ’Juan’
DB<19> x $b->c->nombre(’Pedro’)
0 ’Pedro’
DB<20>
#!/usr/bin/perl -w
use strict;
use Math::BigFloat;
my $a = Math::BigFloat->new(’123_456_789_123_456_789’);
my $y = $a->copy()/1_000_000_000;
$ ./bigfloat.pl
a = 123456789123456789
-a = -123456789123456789
y = 123456789.123456789
a+y = 123456789246913578.123456789
y queremos que el módulo, como ilustra el ejemplo, sobrecargue las operaciones binarias y unarias
usuales ası́ como el uso de las constantes.
El Módulo overload
Los mecanismos para escribir un módulo como este los proporciona el módulo overload.pm debido
a Ilya Zakharevich, el cual se incluye en la distribución estandard de Perl. Este módulo permite la
sobrecarga de operadores. Para sobrecargar los operadores para una clase dada, hay que pasarle a la
sentencia use una lista de pares operador => referencia a código:
336
Categorı́a Operadores / Claves
Aritmética + - * / % ** x . neg
Bit << >> & | ^ ~
Asignación += -= *= /= %= **= <<= >>= x= .= ++ --
Comparación < <= > >= == != <=> lt le gt ge eq ne cmp
Funciones atan cos sin exp abs log sqrt
Conversiones q("") 0+ bool
Seudo-operadores nomethod fallback =
Cuadro 6.3: Operadores que pueden ser sobrecargados en Perl. neg es la negación unaria
package Math::BigFloat;
Cada pareja consiste de una clave, que especifica el operador a sobrecargar, y una referencia a una
subrutina, que será invocada cuando se encuentre el operador. La clave neg corresponde al operador
de negación unaria.
Las Claves
La clave puede ser cualquiera de las reseñadas en la tabla 6.9.
Los Valores
La referencia a la subrutina puede ser
$ cat -n ./bigfloat2.pl
1 #!/usr/bin/perl -d
2 use strict;
3 use Math::BigFloat;
4
5 my $a = Math::BigFloat->new(’123_456_789_123_456_789’);
6 my $y = $a->copy()/1_000_000_000;
7
8 print "a+y = ",$a+$y,"\n";
$ ./bigfloat2.pl
main::(./bigfloat2.pl:5): my $a = Math::BigFloat->new(’123_456_789_123_456_789’);
337
DB<1> n
main::(./bigfloat2.pl:6): my $y = $a->copy()/1_000_000_000;
DB<1>
main::(./bigfloat2.pl:8): print "a+y = ",$a+$y,"\n";
DB<1> s
Math::BigInt::CODE(0x82f9acc)(/usr/share/perl5/Math/BigInt.pm:50):
50: ’+’ => sub { $_[0]->copy()->badd($_[1]); },
DB<1> p "@_"
123456789123456789 123456789.123456789
DB<2> n
Math::BigInt::CODE(0x830c9f0)(/usr/share/perl5/Math/BigInt.pm:110):
110: ’""’ => sub { $_[0]->bstr(); },
esta es la conversión necesaria del resultado a una cadena para ser impresa, continuamos . . .
DB<2>
a+y = 123456789246913578.123456789
Debugged program terminated. Use q to quit or R to restart,
use O inhibit_exit to avoid stopping after program termination,
h q, h R or h O to get additional info.
Formas de LLamada
Si la sobrecarga fué especificada a través de una referencia a subrutina se usa una llamada como
subrutina mientras que si se especificó como referencia simbólica se usa la sintáxis de método. Por
ejemplo, si $a y $b son dos objetos Math::BigFloat, para las declaraciones
package Math::BigFloat;
1. El primer operando
338
2. El segundo operando (undef si no existe)
3. Un flag indicando cuando los operandos fueron intercambiados
La necesidad del flag proviene del requerimiento de que el primer argumento debe ser un objeto de la
clase sobrecargada (en el ejemplo la clase Math::BigFloat). Si Perl detecta una expresión de la forma
4+$a la traduce por $a->fadd(4,1), donde el segundo argumento avisa de la inversión producida.
my $a = Math::BigFloat->new(’123_456_789_123_456_789’);
339
Conflictos en la Sobrecarga
Si ambos operandos son objetos sobrecargados pertenecientes a clases distintas se aplica el método
correspondiente al primero:
Propagación de la Sobrecarga
El módulo overload.pm asume las relaciones habituales entre operadores y aprovecha este co-
nocimiento. Ası́, si se da una implementación para el - binario, el automáticamente sobrecargará el
operador de asignación -=, los dos de decremento (- -) y el - unario (-$x = 0 - $x).
Del mismo modo, si sobrecargamos el operador de comparación (<=>), automáticamente sobrecar-
gamos los restantes operadores de comparación, ya que estos pueden ser deducidos de aquél.
Esta sobrecarga automática se realiza si no se declara especı́ficamente la sobrecarga del operador.
En caso contrario se usará la definida por el programador.
2. Si no, se mira a ver si el mecanismo de propagación automático descrito en la página 339 puede
ser aplicado
3. En otro caso, se mira a ver si el pseudo operador nomethod fué definido. Si es ası́ se llama a la
subrutina asociada con nomethod.
2. Si no, se mira a ver si el mecanismo de propagación automático descrito en la página 339 puede
ser aplicado
3. En otro caso, se mira a ver si el pseudo operador nomethod fué definido. Si es ası́ se llama a la
subrutina asociada con nomethod
340
Búsqueda del Manejador con fallback Falso
Si fallback esta definido pero es falso, el mecanismo de propagación de la sobrecarga (anterior
paso 2) es eliminado y la secuencia es:
2. En otro caso, se mira a ver si el pseudo operador nomethod fué definido. Si es ası́ se llama a la
subrutina asociada con nomethod.
Este último orden proporciona un mecanismo para aquellas situaciones en las que se quiere evitar
el proceso de propagación automática de la sobrecarga. Por ejemplo, supongamos que queremos sobre-
cargar la operación de diferencia entre dı́as de la semana (lunes, martes, . . . ) de manera que podamos
determinar el número de dı́as entre las mismos. Parece que no tendrı́a sentido usar el operador de
negación unaria. Para ello podemos asociar con el operador neg una referencia a una subrutina que
provoque una excepción:
package DaysOfTheWeek;
use overload
"-" => \delta,
"nomethod" => sub { croak "No tiene sentido negar un dia de la semana\n" };
Sin embargo, este método es ineficiente si el número de operadores autogenerados cuyo uso se
quiere prohibir es grande. Una mejor solución es:
package DaysOfTheWeek;
use overload
"-" => \delta,
"fallback" => 0,
"nomethod" => sub { croak "No tiene sentido $_[3]\n" };
Argumentos de nomethod
Los argumentos que se pasan a la subrutina asociada con nomethod son:
1. El primer operando
Sobrecarga y Herencia
La sobrecarga puede combinarse con la herencia. Cualquier subclase heredará los métodos de la
clase materna y puede sobreescribir cualquier operador a conveniencia.
341
Sobrecarga y Contexto de Cadena
Para especificar como sobrecargar el operador convertir un objeto a cadena hay que sobrecargar el
operador de stringification denotado como "\"\"" ó también q("") o incluso ’""’. La rutina asociada
será llamada siempre que el objeto de la clase correspondiente aparezca en un contexto que requiera
una cadena.
package DaysOfTheWeek;
...
my @_day_name = qw(Sun Mon Tue Wed Thu Fri Sat)
use overload
q("") => sub { $_day_name[$_[0]->val] };
my $day = DaysOfTheWeek->new(3);
Concatenación de cadenas
Siempre que el objeto aparezca como operando del operador de rango (..)
342
Sobrecarga de Operadores Lógicos
El operador de sobrecarga bool se encarga de la conversión a valores lógicos. La correspondiente
subrutina será invocada en cualquier contexto en el que aparezca el objeto de la clase y Perl espere
un valor lógico.
Por ejemplo en el módulo Set::Scalar::Base debido a Jarkko Hietaniemi y que proporciona acceso
al álgebra de conjuntos vemos la siguiente declaración:
use overload
’+’ => \&_union_overload,
’*’ => \&_intersection_overload,
’-’ => \&_difference_overload,
’neg’ => \&_complement_overload,
’%’ => \&_symmetric_difference_overload,
’/’ => \&_unique_overload,
’eq’ => \&is_equal,
’==’ => \&is_equal,
’!=’ => \&is_disjoint,
’<=>’ => \&compare,
’<’ => \&is_proper_subset,
’>’ => \&is_proper_superset,
’<=’ => \&is_subset,
’>=’ => \&is_superset,
’bool’ => \&size;
Obsérvese como el manipulador para bool hace que un conjunto en un contexto lógico devuelva
su tamaño (\&size). El siguiente ejemplo que usa Set::Scalar ilustra este punto. Consideremos el
programa:
$ cat ./scalar_sets2.pl
#!/usr/local/bin/perl5.8.0 -d
use Set::Scalar;
$A = Set::Scalar->new(’a’..’z’);
Al ejecutarlo podemos contemplar como se llama a &size para evaluar la condición if $A:
$ ./scalar_sets2.pl
Loading DB routines from perl5db.pl version 1.19
Editor support available.
Enter h or ‘h h’ for help, or ‘man perldebug’ for more help.
main::(./scalar_sets2.pl:4): $A = Set::Scalar->new(’a’..’z’);
DB<1> n
main::(./scalar_sets2.pl:6): print "$A\n" if $A;
DB<1> s
Set::Scalar::Base::size(/usr/local/lib/perl5/site_perl/5.8.0/Set/Scalar/Base.pm:123):
123: my $self = shift;
DB<1> T
$ = Set::Scalar::Base::size(ref(Set::Scalar), undef, ’’) called from file ‘./scalar_sets2.pl’
DB<1>
Set::Scalar::Base::size(/usr/local/lib/perl5/site_perl/5.8.0/Set/Scalar/Base.pm:125):
343
125: return scalar keys %{ $self->{’elements’} };
DB<1> c
(a b c d e f g h i j k l m n o p q r s t u v w x y z)
my $d = fraction->new(4,5);
Mientras que para un número podemos escribir directamente $d = 4, ya que Perl es capaz de deducir
el tipo. Serı́a bueno escribir nuestra práctica de manera que constantes de tipo cadena o numéricas
conteniendo fracciones fueran convertidas en objetos de tipo fracción.
La Subrutina overload::constant
Para cambiar el modo en que Perl interpreta las constantes enteras, flotantes, cadenas y expresiones
regulares podemos crear un conjunto de ”manejadores” mediante la subrutina overload::constant.
Se espera que dicho manejador devuelve un valor escalar que es usado en lugar de la interpretación
normal. Por ejemplo, para usar overload::constant en el paquete Math::BigFloat para modificar
el modo en que las constantes enteras y flotantes se interpretan en Perl harı́amos:
package Math::BigFloat;
use Math::BigInt;
use overload;
my %_constant_handlers = (
integer => sub { return Math::BigInt->new($_[0]) },
float => sub { return Math::BigFloat->new($_[0]) }
);
Obsérve el uso de import (véase sección 5.7) que como sabemos es ejecutada cada vez que se usa
(use Math::BigFloat) el módulo. Aquı́ la función de import no es exportar sı́mbolos sino hacer
que se ejecute la llamada overload::constant %_constant_handlers produciendo la consiguiente
modificación de la interpretación de las constantes enteras y flotantes.
Argumentos de overload::constant
La subrutina overload::constant toma como argumento un hash y espera que las entradas tengan
una de las siguientes claves:
q, indicando el manipulador para las cadenas constantes (esto es, las cadenas ’...’, "...",
q{...}, y qq{...}, los argumentos de tr/.../.../, o el segundo argumento de una sustitución
s/.../.../,
qr, indicando el manipulador de una expresión regular (por ejemplo, el primer argumento de
m/.../ o de s/.../.../).
344
Constante Manejador Argumentos
"doble comi" q (’doble comi’, ’doble comi’, ’q’)
qq{qq comi} q (’qq comi’, ’qq comi’, ’qq’)
’comi simples’ q (’comi simples’, ’comi simples’, ’q’)
q{q comi} q (’q comi’, ’q comi’, ’q’)
qw{qw comi} q (’qw comi’, ’qw comi’, ’q’)
q ("docu \n", "docu \n", ’qq’)
<<HERE
docu
HERE
q, indicando que la cadena viene de un contexto no interpolable como ’...’ o q{...} o qw{...},
345
qq, indicando que la cadena aparece en un contexto interpolable como "..." o qq{...} o m/.../
o el primer argumento de una sustitución s/.../.../,
tr, indicando que la cadena aparece en tr/.../.../ o y/.../.../,
s, indicando que la cadena es el segundo argumento de una sustitución s/.../.../.
Esta sobrecarga se consigue definiendo import en la lı́nea 22 del listado que sigue a continuación,
para que llame a overload::constant sobre el hash %_const_handlers.
En este caso el hash tiene una única clave, ya que sólo estamos interesados en tratar las constantes
de tipo cadena. El constructor new será especificado en la sección 6.9.5: Cuando recibe la cadena
produce el objeto Number::Fraction.
1
B::Terse provee mecanismos para visualizar el árbol sintáctico y el código máquina generado por el compilador de
Perl
346
1 package Number::Fraction;
2
3 use 5.006;
4 use strict;
5 use warnings;
6 use Carp;
7
8 our $VERSION = sprintf "%d.%02d", ’$Revision: 1.34 $ ’ =~ /(\d+)\.(\d+)/;
9
10 use overload
11 q("") => ’to_string’,
12 ’0+’ => ’to_num’,
13 ’+’ => ’add’,
14 ’*’ => ’mult’,
15 ’-’ => ’subtract’,
16 ’/’ => ’div’,
17 fallback => 1;
18
19 my %_const_handlers =
20 (q => sub { return __PACKAGE__->new($_[0]) || $_[1] });
21
22 sub import {
23 overload::constant %_const_handlers if $_[1] and $_[1] eq ’:constants’;
24 }
El import proveı́do hace que, en el caso de que el constructor no devuelva un objeto válido, se
devuelva el segundo argumento, que es el valor que Perl le da por defecto a la constante. El resultado
es la conversión correcta de aquellas cadenas que pueden ser interpretadas como fracciones y que otras
cadenas como ’1hola’ sean interpretadas de acuerdo a las tradiciones de Perl.
Nótese la condición para la importación en la lı́nea 23: if $_[1] and $_[1] eq ’:constants’.
Esto significa que para activar el manejador de constantes de tipo cadena el programa cliente ha de
declarar:
esto es una buena idea: cambiar el modo en que funcionan las constantes es un cambio lo suficien-
temente trascendental como para sólo hacerlo bajo petición explı́cita del usuario.
Seguimos:
26 sub unimport {
27 overload::remove_constant(q => undef);
28 }
29
30 sub new {
31 ...
32 }
Recuerde que la función unimport se ejecutará cuando el usuario haga una llamada en su programa
a no Number::Fraction. En este caso la negación produce el abandono del manejo de las constantes.
347
$v = $d/$t;
trata a $d y $t como valores en vez de como las referencias que son a los correspondientes objetos.
Consideremos una asignación como:
$v1 = $v;
La asignación hace que $v1 y $v sean una referencia al mismo objeto. Si posteriormente incrementamos
$v1:
$v1++;
tendremos un efecto lateral, ya que $v se ve incrementado. Este efecto es, cuando menos, inconsistente
con el uso normal de las operadores sobrecargados, en el sentido de que estos ”trabajan” los referentes
(objetos) y la asignación ”trabaja” con las referencias.
Los operadores que cambian el valor de sus operandos se llaman ”mutantes”. El módulo overload
proporciona un medio para interceptar a los mutantes y clonar los objetos que están siendo mutados
de manera que la mutación sólo afecte a la copia del objeto. Esta intercepción se hace sobrecargando
la operador engañosamente denominado =
package Math::BigFloat;
use overload
...
’++’ => "incr",
’=’ => "copy";
Esta sobrecarga no cambia la conducta del operador de asignación. La rutina copy es llamada antes
que la subrutina asociada con cualquier mutante. Asi pues, el código:
$v1++;
Se traduce por:
$v1 = $v1->copy(undef,"");
$v1->incr(undef,"");
$ cat -n ./fractions2.pl
1 #!/usr/local/bin/perl5.8.0 -w
2 use Number::Fraction ’:constants’;
3
4 my $t = ’1/2’;
5 my $x = $t;
6 $t += ’1/2’;
7 print "t=$t, ref(t)=",ref($t),"\n";
8 print "x=$x, ref(x)=",ref($x),"\n";
$ ./fractions2.pl
t=1/1, ref(t)=Number::Fraction
x=1/2, ref(x)=Number::Fraction
348
¿Porqué la asignación de la lı́nea 5 ($x = $t) no parece comportarse como una asignación de referen-
cias como se señaló en la sección anterior?. La modificación de $t no produce un efecto lateral sobre
$x. Nótese que el operador = no ha sido sobrecargado en Number::Fraction. Puede comprobarlo en
el listado de la cabecera de Number::Fraction que aparece en la sección 6.9.3 pagina 343.
Para ayudar a entender lo que esta ocurriendo, aqui tiene el código del método add que sobrecarga
el operador +
sub add {
my ($l, $r, $rev) = @_;
if (ref $r) {
if (UNIVERSAL::isa($r, ref $l)) { # Ambos heredan de ref $l
return (ref $l)->new( # Puede que ’new’ hay sido sobreescrito
$l->{num} * $r->{den} + $r->{num} * $l->{den},
$r->{den} * $l->{den}
);
} else {
croak "Can’t add a ", ref $l, " to a ", ref $r;
}
} else {
if ($r =~ /^[-+]?\d+$/) { # $r es un número entero
return $l + (ref $l)->new($r, 1);
} else { # r es un double: convertimos
return $l->to_num + $r;
}
}
}
349
DB<7> x Number::Fraction->new($b) # Un argumento objeto: se copia
0 Number::Fraction=HASH(0x8528410)
’den’ => 5
’num’ => 2
El Constructor: Solución
Para simplificar la práctica aquı́ esta un constructor que cumple los requerimientos:
350
La Función de Normalización de una Fracción
A continuación sigue el código de normalise. La función ”normaliza” un quebrado, esto es lo
reduce a una fracción irreducible.
sub normalise {
my $self = shift;
sub _hcf {
my ($x, $y) = @_;
($x, $y) = ($y, $x) if $y > $x;
return $x if $x == $y;
while ($y) {
($x, $y) = ($y, $x % $y);
}
return $x;
}
La subrutina _hcf es ”puro cálculo” por lo que la implementación Perl es obviamente inferior en
rendimiento a la correspondiente versión C:
$ cat -n hcf.c
1 int hcf(int x, int y) {
2 int t;
3
4 if (y > x) {
5 t = x; x = y; y = t;
6 }
7 if (x == y) return x;
8 while (y) {
9 t = x;
10 x = y;
11 y = t % y;
12 }
13 return x;
14 }
351
El Módulo P5NCI
Para poder llamar a la versión C de la subrutina desde Perl usando el módulo P5NCI lo único que
necesitamos es crear una librerı́a dinámica:
$ cc -shared hcf.o -o libhcf.so
$ ls -ltr | tail -1
-rwxr-xr-x 1 lhp lhp 5446 2007-05-29 16:52 libhcf.so
$ nm libhcf.so # nm nos lista los sı́mbolos en la librerı́a
00001650 A __bss_start
000003c0 t call_gmon_start
00001650 b completed.4463
00001558 d __CTOR_END__
00001554 d __CTOR_LIST__
w __cxa_finalize@@GLIBC_2.1.3
000004f0 t __do_global_ctors_aux
000003f0 t __do_global_dtors_aux
00001648 d __dso_handle
00001560 d __DTOR_END__
0000155c d __DTOR_LIST__
00001568 a _DYNAMIC
00001650 A _edata
00001654 A _end
00000534 T _fini
00000450 t frame_dummy
00000550 r __FRAME_END__
00001634 a _GLOBAL_OFFSET_TABLE_
w __gmon_start__
0000048c T hcf # Nuestra función máximo común divisor
00000485 t __i686.get_pc_thunk.bx
0000036c T _init
00001564 d __JCR_END__
00001564 d __JCR_LIST__
w _Jv_RegisterClasses
0000164c d p.4462
352
El constructor admite la opción path que permite especificar el path de búsqueda para la librerı́a.
En este caso podemos usar el ”nombre” oficial de la librerı́a (hcf) y no la especifciación completa.
También dispone de una opción package que permite especificar el paquete en el que se aloja la
función.
$ cat -n usehcf2.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use P5NCI::Library;
4
5 my $lib = P5NCI::Library->new( library => ’hcf’, path => ’.’ );
6 print "Path de búsqueda:\n@DynaLoader::dl_library_path\n";
7 $lib->install_function( ’hcf’, ’iii’ );
8
9 print "hcf( 20, 10 ) = ",hcf( 20, 10 ), "\n";
10 print "hcf( 12, 9 ) = ",hcf( 12, 9 ), "\n";
11 print "hcf( 18, 12 ) = ",hcf( 18, 12 ), "\n";
Ejecución
Ahora al ejecutar el programa obtenemos el máximo común divisor para cada una de las tres
parejas:
$ usehcf2.pl
Path de búsqueda:
. /usr/local/lib /lib /usr/lib
hcf( 20, 10 ) = 10
hcf( 12, 9 ) = 3
hcf( 18, 12 ) = 6
La portabilidad de P5NCI
La portabilidad de P5NCI es todavı́a insuficiente. El porcentaje de plataformas en las que funciona
es bajo aún. Existen otros mecanismos para empotrar otros lenguajes en Perl: XS, Inline, etc. que son
mas robustos.
El Módulo Inline
El módulo Inline proporciona los medios para realizar las pasarelas entre el lenguaje Perl y otro
lenguaje de programación. En particular Inline::C facilita la integración entre código C y código Perl.
Veamos un ejemplo:
$ cat -n hcfinline.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use Inline ’C’;
4
5 print hcf( 20, 10 ), "\n";
6 print hcf( 12, 9 ), "\n";
7 print hcf( 18, 12 ), "\n";
8
9 __END__
10 __C__
11
353
12 int hcf(int x, int y) {
13 int t;
14
15 if (y > x) {
16 t = x; x = y; y = t;
17 }
18 if (x == y) return x;
19 while (y) {
20 t = x;
21 x = y;
22 y = t % y;
23 }
24 return x;
25 }
El código C puede almacenarse dentro del fichero DATA (esto es, a partir de la aparición del marcador
__END__ en una sección determinada por el marcador __C__. También puede guardarse en una cadena
ordinaria que es pasada a Inline::C:
$ cat -n shcfinline.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use Inline ’C’ => q{
4 int hcf(int x, int y) {
5 int t;
6
7 if (y > x) {
8 t = x; x = y; y = t;
9 }
10 if (x == y) return x;
11 while (y) {
12 t = x;
13 x = y;
14 y = t % y;
15 }
16 return x;
17 }
18 };
19
20 print hcf( 20, 10 ), "\n";
21 print hcf( 12, 9 ), "\n";
22 print hcf( 18, 12 ), "\n";
$ time shcfinline.pl
10
3
6
real 0m1.797s
user 0m1.520s
sys 0m0.250s
$ time shcfinline.pl
354
10
3
6
real 0m0.065s
user 0m0.060s
sys 0m0.010s
Vemos que la segunda vez tarda menos que la primera. Esto es ası́ porque la primera vez Inline
genera una librerı́a dinámica que - por defecto - se guarda en el subdirectorio __Inline:
$ ls -ltr | tail -1
drwxr-xr-x 4 lhp lhp 4096 2007-05-31 07:42 _Inline
$ tree _Inline/
_Inline/
|-- build
|-- config
‘-- lib
‘-- auto
‘-- shcfinline_pl_677d
|-- shcfinline_pl_677d.bs
|-- shcfinline_pl_677d.inl
‘-- shcfinline_pl_677d.so
4 directories, 4 files
Además genera el código de traducción de las llamadas desde Perl a las funciones C.
Durante las ejecuciones posteriores Inline comprueba que el código fuente no ha cambiado. Si es
ası́ carga la librerı́a dinámica generada.
en las parejas tipo identificador, ... puede usarse cualquier tipo que esté definido en el fichero
typemap .
Las siguientes definiciones no serán reconocidas:
355
3
4 use Inline Python => <<’END_OF_PYTHON_CODE’;
5 def add(x,y):
6 return x + y
7
8 def subtract(x,y):
9 return x - y
10
11 END_OF_PYTHON_CODE
12
13 print "9 + 16 = ", add(9, 16), "\n";
14 print "9 - 16 = ", subtract(9, 16), "\n";
lhp@nereida:~/Lperl/src/testing$ python.pl
9 + 16 = 25
9 - 16 = -7
356
d1 = d1*d2;
Inline_Stack_Vars;
Inline_Stack_Reset;
Inline_Stack_Push(sv_2mortal(newSViv(n1)));
Inline_Stack_Push(sv_2mortal(newSViv(d1)));
}
La función Inline Stack Vars define un conjunto de variables internas que son necesarias para
poder acceder a la pila del intérprete Perl.
En general, cuando la función sea de tipo void como es el caso del ejemplo, se deben usar las
macros con nombre Inline Stack . También si la función tiene un número variable de argumentos
(uso de la elı́psis ...).
Antes de comenzar a empujar items en la pila se debe llamar a la función Inline Stack Reset .
Inicializa el puntero de pila interno al comienzo de la pila.
A partir del númerador n1 = n1*d2 + n2*d1 debemos construir el escalar Perl que guarda el
número. La función newSViv es la que lo permite. La notación newSViv significa new Scalar Value
from an Integer Value. El valor retornado por esta función es la estructura de datos que representa un
valor escalar Perl, conocido en la jerga como SV (véase perlguts). Existe una familia de constructores
de valores escalares SV a partir de diferentes tipos de fuentes: newSVuv para enteros sin signo, newSVnv
para doubles, etc. (véase perlapi).
Como sabemos el recolector de basura de Perl usa un contador de referencias para saber que valores
están en desuso y liberar la memoria correspondiente. La función sv 2mortal marca el SV recién
creado como mortal para que su memoria pueda ser liberada por el sistema de gestión de memoria
cuando sea seguro hacerlo.
my $self;
if (@_ == 2) {
return $class->$orig({ @_ }) unless ($_[0] =~ $isint and $_[1] =~ $isint);
$self->{num} = $_[0];
unless ($_[1]) {
croak "Can’t make a $class with demominator 0";
}
$self->{den} = $_[1];
}
elsif (@_ == 1) {
if (ref $_[0]) { # Number::Fraction->new($x) y $x hashref. Lo copiamos
if (reftype($_[0]) eq ’HASH’) {
%$self = %{$_[0]}
}
elsif (reftype($_[0]) eq ’ARRAY’) {
%$self = @{$_[0]}
}
357
else {
carp "Can’t make a $class from a ", ref $_[0];
}
} else { # Es una cadena ’num/num’ o ’num’
carp "(@_) is not a fraction" unless $_[0] =~ $isfrac;
$self->{num} = $1;
$self->{den} = $3 || 1;
}
} # @_ == 1
elsif (!@_) { # no arguments
$self->{num} = 0;
$self->{den} = 1;
}
else {
$self = { @_ };
}
return $class->$orig($self);
};
sub BUILD {
my $self = shift;
$self->normalise;
}
}
{
my $isint = qr{^\s*-?\d+\s*$};
my $isfrac = qr{^\s*(-?\d+)(/(-?\d+))?$};
my %_const_handlers =
(q => sub {
if ($_[1] =~ $isfrac) {
return __PACKAGE__->new(num => $1, den => $3 || 1) || $_[1];
}
else {
$_[1];
}
});
sub import {
overload::constant %_const_handlers if $_[1] and $_[1] eq ’:constants’;
}
sub unimport {
overload::remove_constant(q => undef);
}
358
....
__PACKAGE__->meta->make_immutable;
no Moose;
sub subtract {
my ($left, $r, $rev) = @_;
my $class = ref $left;
if (ref $r) {
if (UNIVERSAL::isa($r, __PACKAGE__)) {
return $class->new($left->num * $r->den - $r->num * $left->den,
$r->den * $left->den);
} else {
croak "Can’t subtract a ", $class, " from a ", ref $r;
}
} else {
if ($r =~ /^[-+]?\d+$/) {
$r = $class->new($r, 1);
return $rev ? $r - $left : $left - $r;
} else {
return $rev ? $r - $left->to_num : $left->to_num - $r;
}
}
}
Véanse
Moose::Manual::Construction
Moose::Manual::BestPractices
Los métodos dentro del paquete deben tener nombres especiales: TIESCALAR, FETCH, STORE y DESTROY.
El método TIESCALAR es invocado cuando se llama a la función tie . Se le pasan los mismos
argumentos que se hayan pasado a la función tie, excepto por la variable en cuestión $var. Ası́, la
llamada anterior se traduce en una llamada a Package::Name->TIESCALAR(). Si, por ejemplo atamos
una variable con:
La tarea de TIESCALAR es crear un nuevo objeto de la clase apropiada y devolver una referencia al
mismo. El objeto implementa el estado interno de la variable original.
359
El método FETCH se llama siempre que se lee el valor de la variable. Se le pasa como único
argumento una referencia al objeto que implementa la variable (el que fué creado por el constructor
TIESCALAR). El valor devuelto por FETCH se usa como valor de la variable.
La subrutina STORE se llama siempre que se actualiza el valor de $var. Se le pasan dos argumentos:
la referencia al objeto que implementa la variable y el nuevo valor a almacenar. No devuelve valor
alguno.
El destructor, DESTROY , es llamado cuando $var desaparece (técnicamente, cuando el contador
de referencias a $var que internamente lleva Perl alcanza cero). No suele ser necesaria, pero puede ser
usada para realizar las labores de limpieza que la implementación requiera. El destructor es llamado
también cuando se rompe explicitamente la atadura o compromiso usando la función untie:
untie $var;
1 #!/usr/bin/perl -w -I.
2 use Inspector;
3
4 $x = 10;
5 Inspector->scalar($x, 1);
6 $x = 20;
7 Inspector->debug($x,0);
8 $x = 30;
9 $x += 5;
10 Inspector->debug($x,1);
11 $x += 10;
~/perl/src> use_Inspector.pl
val=20 use_Inspector.pl:6:main
val=45 use_Inspector.pl:11:main
La idea es que una llamada a Inspector->scalar($x, 1) ata la variable $x (que será su argumento
$_[1]) a la clase Inspector (argumento $_[0]). El tercer argumento lógico, indica si queremos activar
o desactivar la depuración de la variable. Una vez atada, podemos usar el método Inspector->debug
para controlar el rastreo de la variable.
package Inspector;
sub TIESCALAR {
my ($class, $val, $debug) = @_;
bless { val => $val, debug => $debug }, $class;
}
sub FETCH {
my $impl = shift;
return $impl->{val};
}
sub STORE {
my ($implementation, $newval) = @_;
360
$implementation->{val} = $newval;
if ($implementation->{debug}) {
my ($cur_pkg, $cur_file, $cur_line) = caller;
print STDERR "val=$implementation->{val} $cur_file:$cur_line:$cur_pkg\n";
}
}
sub scalar {
my ($class, $var, $debug) = @_;
tie $_[1], $class, $_[1], $debug;
}
sub debug {
my $impl = tied($_[1]);
my $deb = $_[2];
$impl->{debug} = $deb;
}
1;
La llamada a Inspector->scalar($x, 1) conlleva a través de tie una llamada a la función TIESCALAR
que es la que construye el ”hash” anónimo. La entrada val guarda el valor de la variable. La entrada
debug indica si los valores de la variable deben o no ser volcados a STDERR cuando se cambia su valor.
Por último TIESCALAR bendice el ”hash” y devuelve su referencia como el objeto que reimplementa a
la variable original.
Toda modificación de la variable se hace a través de STORE, la cuál después de cumplir con la
reglamentaria asignación imprime los valores por STDERR si es el caso.
Puesto que no hemos provisto de métodos de acceso a la implementación interna de la variable
hemos tenido que usar la función tied . Esta función recibe una variable como argumento y, si esta
”atada” a un objeto devuelve una referencia al objeto. En otro caso devuelve undef. Asi pues, debug
llama primero a tied y después establece el valor de la entrada debug del ”hash”.
361
17
18 sub FETCH {
19 my ($self) = @_;
20 $ENV{$$self};
21 }
22
23 sub STORE {
24 my ($self, $value) = @_;
25 if (defined($value)) {
26 $ENV{$$self} = $value;
27 } else {
28 delete $ENV{$$self};
29 }
30 }
31
32 1;
La función import obtiene en la lı́nea 4 el nombre del paquete que usa Myenv. Después, si se han
explicitado argumentos en el pragma use se dejan en @vars (lı́nea 6) y si no @vars es inicializada con
todas las claves del hash %ENV. Repase la sección 5.7 para mas detalles sobre el proceso de importación.
En el bucle de las lı́neas 9-11 instalamos en la tabla de sı́mbolos del paquete usuario las entradas a las
variables de entorno.
Sigue el programa de uso:
#!/usr/bin/perl -d
use Myenv qw(HOME);
$ ./env.pl
Default die handler restored.
362
Myenv::STORE(Myenv.pm:25): if (defined($value)) {
DB<2>
Myenv::STORE(Myenv.pm:26): $ENV{$$self} = $value;
DB<2>
main::(./env.pl:6): system ’echo $HOME’;
DB<2>
/tmp/
Debugged program terminated. Use q to quit or R to restart,
use O inhibit_exit to avoid stopping after program termination,
h q, h R or h O to get additional info.
363
Capı́tulo 7
Templates
The term template, when used in the context of software engineering has various techni-
cal specifications, but is generally identified as any processing element that can be combined
with a data model and processed by a template engine to produce a result document.
A web template is a tool used to separate content from presentation in web design, and
for mass-production of web documents. It is a basic component of a web template system.
Web templates can be used to set up any type of website. In its simplest sense, a web
template operates similarly to a form letter for use in setting up a website.
7.1. Introducción
lhp@nereida:~/Lperl/src/template_toolkit/src$ cat -n ex1.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use Template;
4
5 my $tt = Template->new({
6 INCLUDE_PATH => "$ENV{HOME}/tt"
7 });
8 my %vars = (
9 nombre => ’Casiano’,
10 nota => ’8’,
11 practica => ’Template Toolkit’,
12 );
13 $tt->process(’carta’, \%vars);
7.2. Depuración
hp@nereida:~/Lperl/src/template_toolkit/src/tt$ tpage
[%
364
nombre = ’Casiano’
nota = 8
practica = ’Template Toolkit’
%]
[%
PROCESS carta
%]
^D
Hola Casiano,
El profesor
lhp@nereida:~/Lperl/src/template_toolkit/src$ ex1.pl
Hola Casiano,
El profesor
lhp@nereida:~/Lperl/src/template_toolkit/src$ ex2.pl
365
ex1.pl has size 266
ex2.pl has size 288
$ cat greeting.html
366
<html>
<body>
<h2>[% message %]</h2>
</body>
lhp@nereida:~/public_html/cgi-bin/tt$ ex1.pl
Content-type: text/html
<html>
<body>
<h2>Hello World
</h2>
</body>
7.6. Filtros
lhp@nereida:~/public_html/cgi-bin/tt$ cat -n ttcgi3.pl
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4 use Template;
5
6 $| = 1;
7 print "Content-type: text/html\n\n";
8
9 my $tt = Template->new({
10 WRAPPER => ’page’
11 });
12
13 my $vars = {
14 planet => ’Earth’,
15 captain => ’Prostetnic Vogon Jeltz’,
16 time => ’two of your earth minutes’,
17 title => ’A test with tt’,
18 bgcol => ’lightblue’,
19 };
20
21
22
23 $tt->process(\*DATA, $vars)
24 || die $tt->error( );
25
26 __DATA__
27
28 [% FILTER html_para %]
29
30 People of [% planet %], your attention please.
31 This is [% captain %] of the
32 Galactic Hyperspace Planning Council.
33
34 As you will no doubt be aware, the plans
35 for development of the outlying regions
36 of the Galaxy require the building of a
367
37 hyperspatial express route through your
38 star system, and regrettably your planet
39 is one of those scheduled for destruction.
40
41 The process will take slightly less than
42 [% time %].
43
44 [% END %]
7.7. CGI
lhp@nereida:~/public_html/cgi-bin/tt$ cat -n ttcgi4.pl
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4 use Template;
5 use CGI;
6
7 $| = 1;
8
9 my $cgi = CGI->new();
10 my $tt = Template->new();
11 my $input = ’cgiparams.html’;
12
13 my $vars = {
14 cgi => $cgi,
15 };
16
17 print $cgi->header(-charset => ’utf-8’ );
18
19 $tt->process($input, $vars)
20 || die $tt->error( );
368
7.7.1. ttcgi
lhp@nereida:~/public_html/cgi-bin/tt$ cat -n ttcgi5.pl
1 #!/usr/bin/perl
2 use strict;
3 use warnings;
4 use Template;
5 use CGI;
6
7 $| = 1;
8
9 my $cgi = CGI->new();
10 my $tt = Template->new(
11 WRAPPER => ’page’
12 );
13 my $input = ’cgicookie.html’;
14 my @cookies;
15
16 my $vars = {
17 cgi => $cgi,
18 cookies => \@cookies,
19 bgcol => ’lightblue’,
20 };
21
22 my $output;
23
24 $tt->process($input, $vars, \$output)
25 || die $tt->error( );
26
27 if (@cookies) {
28 @cookies = (’-cookie’, [ @cookies ]);
29 }
30
31 print $cgi->header(@cookies), $output;
369
19 [% END %]
$ cat -n userinfo.html
1 [% INCLUDE header
2 title = ’Template Toolkit CGI Test’
3 %]
4
5 Author: <a href="mailto:[% me.email %]">[% me.name %]</a>
6
7 <p>This is version [% version %]</p>
8
9 <h3>Projects</h3>
10 <ul>
370
11 [% FOREACH project IN worklist(me.id) %]
12 <li> <a href="[% project.url %]">[% project.name %]</a>
13 [% END %]
14 </ul>
15
16 [% INCLUDE footer
17 %]
$ cat -n header
1 <html>
2 <head>
3 <title>[% author %]: [% title %]</title>
4 </head>
5
6 <body bgcolor="[% bgcol %]">
7 <h1>[% title %]</h1>
$ cat -n footer
1 <hr />
2
3 <div align="middle">
4 © Copyright [% year %] [% author %]
5 </div>
6 </body>
7 </html>
371
Capı́tulo 8
SQLite
8.1. Introducción
Para crear una base de datos ejecute el comando sqlite3 :
$ sqlite3 test.db
SQLite version 3.3.8
Enter ".help" for instructions
sqlite> create table t1 (t1key INTEGER PRIMARY KEY,data TEXT,num double,timeEnter DATE);
sqlite> insert into t1 (data,num) values (’This is sample data’,3);
sqlite> insert into t1 (data,num) values (’More sample data’,6);
sqlite> insert into t1 (data,num) values (’And a little more’,9);
sqlite> .q
lhp@nereida:~/Lperl/src/SQLITE/examples$ ls -l
total 4
-rw-r--r-- 1 lhp lhp 2048 2007-08-17 11:02 test.db
El comando .table permite conocer los nombres de las tablas en la base de datos. Se puede
obtener una información mas completa haciendo consultas a la tabla predefinida sql master :
sqlite> .table
t1
sqlite> select * from sqlite_master;
table|t1|t1|2|CREATE TABLE t1 (t1key INTEGER PRIMARY KEY,data TEXT,num double,timeEnter DATE)
El comando .dump permite volcar información sobre los datos insertados en la base de datos:
372
sqlite> .dump
BEGIN TRANSACTION;
CREATE TABLE t1 (t1key INTEGER PRIMARY KEY,data TEXT,num double,timeEnter DATE);
INSERT INTO "t1" VALUES(1, ’This is sample data’, 3.0, NULL);
INSERT INTO "t1" VALUES(2, ’More sample data’, 6.0, NULL);
INSERT INTO "t1" VALUES(3, ’And a little more’, 9.0, NULL);
INSERT INTO "t1" VALUES(4, ’Another item’, 12.0, NULL);
COMMIT;
El siguiente comando muestra como usar un filtro para obtener una modificación de la base de
datos inicial:
8.2. Triggers
En el ejemplo anterior la clave timeEnter no tiene valores por defecto. Para que se incialice
automáticamente es necesario installar un trigger . El ejemplo muestra como escribir un trigger:
Si ahora introducimos un nuevo registro vemos que tiene inicializado su campo timeEnter:
373
1|This is sample data|3.0|
2|More sample data|6.0|
3|And a little more|9.0|
4|Another item|12.0|
5|First entry with timeEnter|19.0|2007-08-17 10:53:09
8.3. Logging
lhp@nereida:~/Lperl/src/SQLITE/examples$ cat examScript
-- *******************************************************************
-- examScript: Script for creating exam table
-- Usage:
-- $ sqlite3 examdatabase < examScript
--
-- Note: The trigger insert_exam_timeEnter
-- updates timeEnter in exam
-- *******************************************************************
-- *******************************************************************
CREATE TABLE exam (ekey INTEGER PRIMARY KEY,
fn VARCHAR(15),
ln VARCHAR(30),
exam INTEGER,
score DOUBLE,
timeEnter DATE);
-- *******************************************************************
-- examLog: Script for creating log table and related triggers
-- Usage:
-- $ sqlite3 examdatabase < examLOG
--
--
-- *******************************************************************
-- *******************************************************************
CREATE TABLE examlog (lkey INTEGER PRIMARY KEY,
ekey INTEGER,
ekeyOLD INTEGER,
fnNEW VARCHAR(15),
fnOLD VARCHAR(15),
lnNEW VARCHAR(30),
lnOLD VARCHAR(30),
374
examNEW INTEGER,
examOLD INTEGER,
scoreNEW DOUBLE,
scoreOLD DOUBLE,
sqlAction VARCHAR(15),
examtimeEnter DATE,
examtimeUpdate DATE,
timeEnter DATE);
values (new.ekey,old.ekey,old.fn,new.fn,old.ln,
new.ln,old.exam, new.exam,old.score,
new.score, ’UPDATE’,old.timeEnter,
DATETIME(’NOW’),DATETIME(’NOW’) );
END;
--
-- Also create an insert trigger
-- NOTE AFTER keyword ------v
CREATE TRIGGER insert_examlog AFTER INSERT ON exam
BEGIN
INSERT INTO examlog (ekey,fnNEW,lnNEW,examNEW,scoreNEW,
sqlAction,examtimeEnter,timeEnter)
values (new.ekey,new.fn,new.ln,new.exam,new.score,
’INSERT’,new.timeEnter,DATETIME(’NOW’) );
END;
values (old.ekey,old.fn,old.ln,old.exam,old.score,
’DELETE’,DATETIME(’NOW’) );
END;
-- *******************************************************************
-- *******************************************************************
375
$ sqlite3 examdatabase "update exam set score=82 where ln=’Anderson’ and fn=’Bob’ and exam=2"
$ sqlite3 examdatabase "select * from examlog"
1|2||Bob||Anderson||2||80.0||INSERT|||2007-08-17 11:44:32
2|2|2|Bob|Bob|Anderson|Anderson|2|2|80.0|80.0|UPDATE||2007-08-17 11:44:32|2007-08-17 11:44:32
3|2|2|Bob|Bob|Anderson|Anderson|2|2|82.0|80.0|UPDATE|2007-08-17 11:44:32|2007-08-17 11:45:07|2
376
Capı́tulo 9
DBI
377
Capı́tulo 10
$ cat -n tiearray.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use Tie::File;
4
5 my @array;
6
7 tie @array, ’Tie::File’, ’introcomp.tex.old’ or die "$!";
8
9 print "Lı́nea 43: $array[42]\n"; # El array comienza en cero ...
10
11 my $n_recs = @array;
12
13 print "Número de lı́neas: $n_recs\n";
14
15 for (@array) {
16 s/\\section/\\SECTION/g;
17 }
18 print "Lı́nea 1: $array[0]\n";
19
20 push @array, "Penúltima\n", "Última\n";
21 print "Última lı́nea: $array[-1]\n";
22
23 for (@array) {
24 s/\\SECTION/\\section/g;
25 }
26 print "Lı́nea 1: $array[0]\n";
27
28 untie @array;
378
El fichero completo no es cargado en memoria. La primera vez que se itera sobre el fichero se
construye un ı́ndice conteniendo las desviaciones de cada uno de los registros del fichero. De esta
manera los accesos subsiguientes pueden obtenerse de manera directa mediante un seek(). Además
los registros accedidos se guardan en memoria. El tamaño de la cache puede ajustarse mediante el
argumento memory del comando tie.
Al ejecutar este programa obtenemos una saldia similar a esta:
~/Lperl/src$ ./tiearray.pl
Lı́nea 43: Looks good
Número de lı́neas: 2402
Lı́nea 1: \SECTION{Las Bases}
Última lı́nea: Última
Lı́nea 1: \section{Las Bases}
Desafortunadamente, los cambios en el array (en particular inserciones y supresiones en medio del
mismo) son costosos.
La función dbmopen
La función dbmopen nos permite crear hashes persistentes. Veamos el siguiente ejemplo dbm.pl
que utiliza un hash DBM para almacenar una gramática:
lhp@nereida:~/Lperl/src$ cat -n dbm.pl
1 #!/usr/bin/perl -w
2 use strict;
3 my %DATA;
4
5 dbmopen(%DATA, "my_gramma", 0644) or die "can not create my_gramma $!";
6 $DATA{TOKENS} = "+|-|/|*|(|)|num";
7 $DATA{START} = "E";
8 $DATA{E} = "E+T|E-T";
9 $DATA{T} = "T*F|T/F";
10 $DATA{F} = "(E)|num";
$DATA{TOKENS} contiene los terminales de la gramática, $DATA{START} el sı́mbolo de arranque y
$DATA{A} contiene las reglas de producción de la variable sintáctica A.
Antes de la ejecución de dbm.pl tenemos dos programas Perl dbm.pl y dbm2.pl en nuestro direc-
torio:
>ls -l
-rwxrwx--x 1 pl users 194 Mar 10 08:44 dbm.pl
-rwxrwx--x 1 pl users 300 Mar 10 09:54 dbm2.pl
Después de la ejecución de dbm.pl aparecen dos nuevos ficheros que contienen las claves y valores
del hash persistente:
>ls -l
-rwxrwx--x 1 pl users 194 Mar 10 08:44 dbm.pl
-rwxrwx--x 1 pl users 300 Mar 10 09:54 dbm2.pl
-rw-r----- 2 pl users 12333 Mar 10 10:54 my_gramma.pag
-rw-r----- 2 pl users 12333 Mar 10 10:54 my_gramma.dir
379
Veamos ahora otro programa que usa el hash persistente:
> dbm2.pl
TOKENS: +,-,/,*,(,),num
E -> E+T|E-T
F -> (E)|num
T -> T*F|T/F
El Módulo DB File
Una alternativa a dbmopen es usar el módulo DB File . El siguiente ejemplo muestra como usarlo:
$ cat -n testdbfile.pl
1 #!/usr/local/bin/perl -w
2 use strict;
3 use DB_File;
4 my $file = shift || ’test’;
5 my %h;
6 tie (%h, ’DB_File’, $file) or die $!;
7
8 $h{abc} = "ABC"; $h{def} = "DEF"; $h{ghi} = "GHI";
9 untie (%h);
10
11 my %g;
12 tie (%g, ’DB_File’, $file) or die $!;
13
14 print "$_ => $g{$_}\n" for keys %g;
15
16 untie(%g);
al ejecutar se obtiene:
380
$ testdbfile.pl
abc => ABC
ghi => GHI
def => DEF
Usando estos filtros podemos serializar valores del hash que sean estructuras de datos complejas.
En el siguiente código el hash DBM %h contiene como valores referencias a cadenas. El filtro vuel-
ca con Data::Dumper el valor complejo y lo comprime usando la función compress del módulo
Compress::Zlib . El proceso inverso consiste en descomprimir y evaluar:
$ cat -n dbwithfilter.pl
1 #!/usr/bin/perl
2 use warnings;
3 use Compress::Zlib;
4 use DB_File;
5 use Data::Dumper;
6
7 unlink ’mldbmtest.dat’;
8
9 $h = tie my %db1, ’DB_File’, ’mldbmtest.dat’, O_CREAT | O_RDWR, 0666
10 or die "No se pudo inicializar el fichero MLDBM: $!\n";
11
12 $h->filter_store_value(sub { $_ = compress(Dumper($_)) });
13 $h->filter_fetch_value(sub { $_ = eval(uncompress($_)) });
14
15 %db1 = (
16 ’alu2511’ => [ ’a’x30 ],
17 ’alu2233’ => [ ’b’x30 ]
18 );
19
20 print Data::Dumper->Dump( [ \%db1 ] );
381
Este otro programa lee el hash DBM creado por el programa anterior:
$ cat -n dbwithfilterretrieve.pl
1 #!/usr/bin/perl
2 use warnings;
3 use Compress::Zlib;
4 use DB_File;
5 use Data::Dumper;
6
7 my $h = tie my %db2, ’DB_File’, ’mldbmtest.dat’, O_RDWR, 0666
8 or die "No se pudo leer el fichero MLDBM: $!\n";
9 $h->filter_store_value(sub { $_ = compress(Dumper($_)) });
10 $h->filter_fetch_value(sub { $_ = eval(uncompress($_)) });
11
12 print Data::Dumper->Dump( [ \%db2 ] );
$ dbwithfilter.pl
$VAR1 = {
’alu2233’ => [ ’bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb’ ],
’alu2511’ => [ ’aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa’ ]
};
lhp@nereida:~/Lperl/src$ dbwithfilterretrieve.pl
$VAR1 = {
’alu2233’ => [ ’bbbbbbbbbbbbbbbbbbbbbbbbbbbbbb’ ],
’alu2511’ => [ ’aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa’ ]
};
MLDBM
El módulo Multi level DBM MLDBM permite guardar estructuras de datos Perl en ficheros DBM
(Véase 10.2). Para tenerlo instalado es necesario tener previamente instaladas las rutinas Berkeley
Database Manager. El módulo utiliza DBM, proveyendo la funcionalidad para serializar estructuras
de datos anidadas. DBM es una librerı́a que maneja tablas hash en disco. La librerı́a ha dado lugar a un
buen número de variantes: SDBM, NDBM, GDBM, etc las cuales pueden ser accedidas a través de los
correspondientes módulos Perl, los cualés hacen uso de tie para proporcionar un acceso transparente
a la tabla almacenada en el disco.
Es por esto que cuando se usa admite como parámetros una especificacción de las librerı́as de
manejo de DBM y del serializador. Por ejemplo:
382
9 tie my %db1, ’MLDBM’, ’mldbmtest.dat’, O_CREAT | O_RDWR, 0666
10 or die "No se pudo inicializar el fichero MLDBM: $!\n";
11
12 %db1 = (
13 ’alu2511’ => {
14 nombre => ’Josefina Fernández Pérez’,
15 tel => ’922 00 00 00’,
16 fecha => ’22/07/84’
17 },
18 ’alu2233’ => {
19 nombre => ’Ana Feliú Forner’,
20 tel => ’922 00 11 22’,
21 fecha => ’14/06/85’
22 }
23 );
24
25 untie %db1;
26
27 tie my %db2, ’MLDBM’, ’mldbmtest.dat’, O_RDWR, 0666
28 or die "No se pudo leer el fichero MLDBM: $!\n";
29
30 print Data::Dumper->Dump( [ \%db2 ] );
31
32 untie %db2;
33
34 exit;
./mldbmtest2
$VAR1 = {
’alu2511’ => {
’fecha’ => ’22/07/84’,
’tel’ => ’922 00 00 00’,
’nombre’ => ’Josefina Fernández Pérez’
},
’alu2233’ => {
’fecha’ => ’14/06/85’,
’tel’ => ’922 00 11 22’,
’nombre’ => ’Ana Feliú Forner’
}
};
Limitaciones
Una limitación que proviene de que los MLDBM y los DBM son atados es que sólo la escrituras
directas del tipo $db1{one} = { a => ’e’ } produce actualización. Una escritura indirecta como
$db1{one}{a} = ’e’ no produce actualización. Véase el siguiente ejemplo:
$ cat -n dbwithfilter2.pl
1 #!/usr/bin/perl
2 use warnings;
3 use DB_File;
4 use Data::Dumper;
5
383
6 unlink ’mldbmtest.dat’;
7
8 $h = tie my %db1, ’DB_File’, ’mldbmtest.dat’, O_CREAT | O_RDWR, 0666
9 or die "No se pudo inicializar el fichero MLDBM: $!\n";
10
11 $h->filter_store_value(sub { $_ = Dumper($_) });
12 $h->filter_fetch_value(sub { $_ = eval($_) });
13
14 %db1 = (
15 ’one’ => { a => ’b’ },
16 ’two’ => { c => ’d’ }
17 );
18
19 $db1{one}{a} = ’e’;
20
21 $Data::Dumper::Indent = 0;
22 print "Asignacion indirecta:\n",Dumper( \%db1 ),"\n";
23
24 $db1{one} = { a => ’e’ };
25
26 print "Asignacion directa:\n",Dumper( \%db1 ),"\n";
lhp@nereida:~/Lperl/src$ dbwithfilter2.pl
Asignacion indirecta:
$VAR1 = {’one’ => {’a’ => ’b’},’two’ => {’c’ => ’d’}};
Asignacion directa:
$VAR1 = {’one’ => {’a’ => ’e’},’two’ => {’c’ => ’d’}};
La explicación reside en que al hacer $db1{one}{a} = ’e’ no se produce una llamada a STORE;
de hecho se produce una llamada a FETCH. Como no se llama a STORE no se produce la modificación
de la estructura de datos atada.
10.4. Class::DBI
10.4.1. Instalar una base de Datos
Debe existir una base de datos previa. Además el módulo DBI deberá estar instalado.
Sigue la descripción SQL de la base de datos que usaremos en las siguietnes secciones:
384
13 trackid INTEGER PRIMARY KEY,
14 cd INTEGER NOT NULL REFERENCES cd(cdid),
15 title TEXT NOT NULL
16 );
$ cat -n synopsis1.pl
385
1 package Music::DBI;
2 use strict;
3
4 use base ’Class::DBI’;
5 Music::DBI->connection("dbi:SQLite:example.db");
Lo primero es conocer como acceder a la base de datos. Esto se hace por medio del método
connection . Esto hace que todas las clases/tabla que heredan de la clase base compartan la misma
conexión.
386
artist INTEGER NOT NULL REFERENCES artist(artistid),
title TEXT NOT NULL
);
INSERT INTO "cd" VALUES(1, 1, ’Thriller’);
INSERT INTO "cd" VALUES(2, 1, ’Bad’);
INSERT INTO "cd" VALUES(3, 2, ’The Marshall Mathers LP’);
INSERT INTO "cd" VALUES(4, 3, ’October’);
CREATE TABLE track (
trackid INTEGER PRIMARY KEY,
cd INTEGER NOT NULL REFERENCES cd(cdid),
title TEXT NOT NULL
);
INSERT INTO "track" VALUES(1, 3, ’The Way I Am’);
INSERT INTO "track" VALUES(2, 3, ’Stan’);
INSERT INTO "track" VALUES(3, 1, ’Billie Jean’);
INSERT INTO "track" VALUES(4, 2, ’Leave Me Alone’);
INSERT INTO "track" VALUES(5, 2, ’Smooth Criminal’);
INSERT INTO "track" VALUES(6, 1, ’Beat It’);
INSERT INTO "track" VALUES(7, 2, ’Dirty Diana’);
COMMIT;
sqlite>
10.5. DBIx::Class
mkdir app
cd app
mkdir db
cd db
387
$ cat example.sql
CREATE TABLE artist (
artistid INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE cd (
cdid INTEGER PRIMARY KEY,
artist INTEGER NOT NULL REFERENCES artist(artistid),
title TEXT NOT NULL
);
388
Capı́tulo 11
El Compilador de Perl
Recorrer el Árbol
El siguiente programa Btree3.pl implanta una subrutina treetrav que muestra el árbol generado
por el compilador para una subrutina dada.
389
La llamada a B::svref 2object sobre la subrutina referenciada por $s nos produce un objeto B::CV
que representa el árbol de codigo resultante.
Cuando treetrav es llamada con el objeto y un sangrado inicial igual a 0 nos produce la siguiente
salida:
pp2@nereida:~/src/perl/B$ Btree3.pl
*** Tree ***
leavesub( #subroutine exit
lineseq( #line sequence
sassign( #scalar assignment
shift( #shift
rv2av( #array dereference
gv( #glob value
) # end gv
) # end rv2av
) # end shift
padsv( #private variable
) # end padsv
) # end sassign
nextstate( #next statement
) # end nextstate
print( #print
add( #addition (+)
padsv( #private variable
) # end padsv
const( #constant item
) # end const
) # end add
) # end print
) # end lineseq
) # end leavesub
La función treetrav usa los métodos name y desc de la subclase B::OP para obtener el nombre y la
descripción de la operación actual. El método kids en B::Utils devuelve la lista de hijos:
10 sub treetrav {
11 my $op = shift;
12 my $indent = shift;
13
14 my $spaces = " "x$indent;
15 print $spaces.$op->name."(\t\t#".$op->desc;
16 print "\n";
17 for ($op->kids()) {
18 treetrav($_, $indent+2);
19 }
20 print "$spaces) # end ".$op->name;
21 print "\n";
22 }
Hay varias subrutinas de recorrido del árbol de códigos. Una de las mas sencillas es walkoptree simple
(en B::Utils):
390
Orden de Ejecución En el párrafo anterior recorrı́amos el árbol según su orden jerárquico. Sin
embargo el árbol está a su vez hilvanado en orden de ejecución. Esto es: superpuesta a la estructura
de árbol existe una lista que recorre el árbol según el flujo de control. A esta estructura de datos se la
denomina Grafo de Flujo de Control o Control flow Graph (CFG).
El método START de un objeto B::CV nos da el punto de entrada en la ejecución. El método next
de un objeto B::OP proporciona el siguiente nodo a ejecutar.
Es necesario comprobar que $op no es un objeto B::NULL ya que el puntero next es un puntero C
que B deja disponible sin envoltorio alguno.
La ejecución produce la secuencia de operaciones en orden de ejecución:
391
Capı́tulo 12
Control de Versiones
392
Capı́tulo 13
Revision control, also known as version control, source control or (source) code mana-
gement (SCM), is the management of changes to documents, programs, and other infor-
mation stored as computer files. It is most commonly used in software development, where
a team of people may change the same files. Changes are usually identified by a number or
letter code, termed the ’revision number’, ’revision level’, or simply ’revision’.
Software tools for revision control are essential for the organization of multi-developer
projects.
You can think of every object in the repository as existing in a sort of two-dimensional
coordinate system. The first coordinate is a particular revision tree, and the second coordi-
nate is a path within that tree
Parece que en banot esta instalado subversion. Para crear un repositorio emita el comando
svnadmin create:
-bash-3.1$ uname -a
Linux banot.etsii.ull.es 2.6.24.2 #3 SMP Fri Feb 15 10:39:28 WET 2008 i686 i686 i386 GNU/Linux
-bash-3.1$ svnadmin create /home/loginname/repository/
-bash-3.1$ ls -l repository/
total 28
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 conf
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 dav
drwxr-sr-x 5 loginname apache 4096 feb 28 12:09 db
-r--r--r-- 1 loginname apache 2 feb 28 11:58 format
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 hooks
drwxr-xr-x 2 loginname apache 4096 feb 28 11:58 locks
-rw-r--r-- 1 loginname apache 229 feb 28 11:58 README.txt
393
[loginname@tonga]~/src/perl/> uname -a
Linux tonga 2.6.24.2 #1 SMP Thu Feb 14 15:37:31 WET 2008 i686 i686 i386 GNU/Linux
[loginname@tonga]~/src/perl/> pwd
/home/loginname/src/perl
[loginname@tonga]~/src/perl/> ls -ld /home/loginname/src/perl/Grammar-0.02
drwxr-xr-x 5 loginname Profesor 4096 feb 28 2008 /home/loginname/src/perl/Grammar-0.02
[loginname@tonga]~/src/perl/> svn import -m ’Grammar Extended Module’ \
Grammar-0.02/ \
svn+ssh://banot/home/loginname/repository/Grammar
A~
nadiendo Grammar-0.02/t
A~
nadiendo Grammar-0.02/t/Grammar.t
A~
nadiendo Grammar-0.02/lib
A~
nadiendo Grammar-0.02/lib/Grammar.pm
A~
nadiendo Grammar-0.02/MANIFEST
A~
nadiendo Grammar-0.02/META.yml
A~
nadiendo Grammar-0.02/Makefile.PL
A~
nadiendo Grammar-0.02/scripts
A~
nadiendo Grammar-0.02/scripts/grammar.pl
A~
nadiendo Grammar-0.02/scripts/Precedencia.yp
A~
nadiendo Grammar-0.02/scripts/Calc.yp
A~
nadiendo Grammar-0.02/scripts/aSb.yp
A~
nadiendo Grammar-0.02/scripts/g1.yp
A~
nadiendo Grammar-0.02/Changes
A~
nadiendo Grammar-0.02/README
Commit de la revisión 2.
* mkdir /tmp/nombreProyecto
* mkdir /tmp/nombreProyecto/branches
* mkdir /tmp/nombreProyecto/tags
* mkdir /tmp/nombreProyecto/trunk
* svn mkdir file:///var/svn/nombreRepositorio/nombreProyecto -m ’Crear el proyecto nombreProye
* svn import /tmp/nombreProyecto \
file:///var/svn/nombreRepositorio/nombreProyecto \
-m "Primera versión del proyecto nombreProyecto"
394
A Grammar/scripts/grammar.pl
A Grammar/scripts/Calc.yp
A Grammar/scripts/Precedencia.yp
A Grammar/scripts/aSb.yp
A Grammar/scripts/g1.yp
A Grammar/Changes
A Grammar/README
Revisión obtenida: 2
Ahora disponemos de una copia de trabajo del proyecto en nuestra máquina local:
3 directories, 12 files
[loginname@tonga]~/src/perl/>
[loginname@tonga]~/src/perl/> cd Grammar
[loginname@tonga]~/src/perl/Grammar/> ls -la
total 44
drwxr-xr-x 6 loginname Profesor 4096 feb 28 2008 .
drwxr-xr-x 5 loginname Profesor 4096 feb 28 2008 ..
-rw-r--r-- 1 loginname Profesor 150 feb 28 2008 Changes
drwxr-xr-x 3 loginname Profesor 4096 feb 28 2008 lib
-rw-r--r-- 1 loginname Profesor 614 feb 28 2008 Makefile.PL
-rw-r--r-- 1 loginname Profesor 229 feb 28 2008 MANIFEST
-rw-r--r-- 1 loginname Profesor 335 feb 28 2008 META.yml
-rw-r--r-- 1 loginname Profesor 1196 feb 28 2008 README
drwxr-xr-x 3 loginname Profesor 4096 feb 28 2008 scripts
drwxr-xr-x 6 loginname Profesor 4096 feb 28 2008 .svn
drwxr-xr-x 3 loginname Profesor 4096 feb 28 2008 t
Observe la presencia de los subdirectorios de control .svn.
395
total 40
drwxr-xr-x 6 loginname Profesor 4096 feb 28 2008 .
drwxr-xr-x 5 loginname Profesor 4096 feb 28 12:34 ..
-rw-r--r-- 1 loginname Profesor 150 feb 28 12:34 Changes
drwxr-xr-x 3 loginname Profesor 4096 feb 28 12:34 lib
-rw-r--r-- 1 loginname Profesor 614 feb 28 12:34 Makefile.PL
-rw-r--r-- 1 loginname Profesor 229 feb 28 12:34 MANIFEST
-rw-r--r-- 1 loginname Profesor 1196 feb 28 12:34 README
drwxr-xr-x 3 loginname Profesor 4096 feb 28 12:34 scripts
drwxr-xr-x 6 loginname Profesor 4096 feb 28 2008 .svn
drwxr-xr-x 3 loginname Profesor 4096 feb 28 12:34 t
[loginname@tonga]~/src/perl/Grammar/> echo "Modifico README" >> README
[loginname@tonga]~/src/perl/Grammar/> svn commit -m ’Just testing ...’
Eliminando META.yml
Enviando README
Transmitiendo contenido de archivos .
Commit de la revisión 3.
Observe que ya no es necesario especificar el lugar en el que se encuentra el repositorio: esa información
esta guardada en los subdirectorios de administración de subversion .svn
El servicio de subversion parece funcionar desde fuera de la red del centro. Véase la conexión desde
una maquina exterior:
pp2@nereida:/tmp$ svn checkout svn+ssh://[email protected]/home/loginname/reposito
[email protected]’s password:
[email protected]’s password:
A Grammar/t
A Grammar/t/Grammar.t
A Grammar/MANIFEST
A Grammar/lib
A Grammar/lib/Grammar.pm
A Grammar/Makefile.PL
A Grammar/scripts
A Grammar/scripts/grammar.pl
A Grammar/scripts/Calc.yp
A Grammar/scripts/Precedencia.yp
A Grammar/scripts/aSb.yp
A Grammar/scripts/g1.yp
A Grammar/Changes
A Grammar/README
Revisión obtenida: 3
Actualizar el proyecto
svn update
396
13.5. Autentificación Automática
Para evitar la solicitud de claves cada vez que se comunica con el repositorio establezca autenti-
ficación SSH automática. Para ver como hacerlo puede consultar las instrucciones en la sección ?? o
en:
http://search.cpan.org/ casiano/GRID-Machine/lib/GRID/Machine.pod#INSTALLATION
Consulte también las páginas del manual Unix de ssh, ssh-key-gen, ssh_config, scp, ssh-agent,
ssh-add, sshd
Ejercicio 13.9.1. Edite el mismo fichero en dos copias de trabajo. Introduzca primero un cambio
que pueda ser mezclado. Actualice. ¿Que observa?
397
¿Que hace :12,20diffput?
:1,5yank a
:6,10yank b
:tabedit | put a | vnew | put b
:windo diffthis
Lea lo que dice el libro de svn en su sección Resolve Conflicts (Merging Others’ Changes)
Cree un conflicto en el proyecto editando el mismo fichero en dos copias de trabajo. En lı́neas
equivalentes de cada una de las copias introduzca cambios incompatibles. Resuélva el conflicto
usando vimdiff
¿Que hace :diffpatch filename? (Véase la sección Patches o Parches en los apuntes de LHP)
398
Veamos que es ası́. Escribimos el siguiente programa de prueba:
Ejecución:
El argumento -u indica que se debe usar unified format y el argumento -L especifica la etiqueta que
describe la correspondiente versión.
Es necesario escribir un wrapper que prescinda de esas opciones y que se quede con los nombres
de los dos ficheros:
Ahora podemos establecer que este programa sea nuestro comando diff para svn editando el fichero
de configuración ~/.subversion/config:
399
13.11. Tracking Systems
13.12. Protocolos y Esquemas
Subversion admite una variedad de protocolos de red para conectarse con el repositorio. Con
subversion viene el programa svnserve que escucha por conexiones de red y soporta un modo de
autentificación simple. Sólo debe usarse si esta en una LAN privada. En el siguiente ejemplo arranco
el daemon svnserve en banot:
bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i38
-bash-3.2$ svnserve --listen-port=4040 -d -r repository
-bash-3.2$ ps -fA | grep svn
casiano 11876 1 0 11:16 ? 00:00:00 svnserve --listen-port=4040 -d -r repository
casiano 12036 11698 0 11:22 pts/0 00:00:00 grep svn
casiano@tonga:~$ cd chuchu
casiano@tonga:~/chuchu$ echo prueba > prueba.txt
casiano@tonga:~/chuchu$ svn add prueba.txt
A prueba.txt
casiano@tonga:~/chuchu$ svn commit prueba.txt -m ’added prueba.txt’
svn: Falló el commit (detalles a continuación):
svn: falló la autorización
Habrı́a que dar permisos de escritura al repositorio y crear usuarios (véase svnserve, a custom server
y svnserve.conf para los detalles)
400
pp2@nereida:~/LBench-Test/lib/Bench$ svn annotate Test.pm | tail -25
3063 casiano for my $exp_name ( @{ $self->{SELECTED} } ) {
3064 lgforte $self->{EXPERIMENTS}{$exp_name}->connect;
3063 casiano $self->{EXPERIMENTS}{$exp_name}->execute_preamble;
3063 casiano }
2248 casiano
3063 casiano # Tomamos el array TEST como array para mantener el orden
3063 casiano # Por ello, no recorremos con for (keys @array)
3063 casiano # Usamos entonces while (@array) { $key, $value = splice ... }
3063 casiano #
3063 casiano while ( @{ $self->{TESTS} } ) {
3063 casiano my ( $test, $params ) = splice @{ $self->{TESTS} }, 0, 2;
2248 casiano
3063 casiano for my $exp_name ( @{ $self->{SELECTED} } ) {
3129 lgforte $self->{EXPERIMENTS}{$exp_name}->save_result( $params, $test );
3063 casiano }
3063 casiano }
3063 casiano
3063 casiano for my $exp_name ( @{ $self->{SELECTED} } ) {
3063 casiano $self->{EXPERIMENTS}{$exp_name}->execute_postamble;
3063 casiano }
2248 casiano }
2248 casiano
2248 casiano 1;
2248 casiano
2248 casiano __END__
13.14. Propiedades
Las propiedades son metadatos asociados con los ficheros o directorios en el repositorio. Las pro-
piedades pueden ser modificadas y actualizadas por los usuarios de la misma forma que los ficheros.
Del mismo modo esta actividad puede dar lugar a conflictos. Las propiedades se usan para asociar
datos extra con un fichero. Por ejemplo cada imagen en un repositorio conteniendo imágenes puede
tener asociada una propiedad binaria que sea un thumbnail.
Properties
svn:eol-style es útil cuando debemos compartir ficheros de texto entre diferentes S.O. Puede
tomar uno de los valores: native, CRLF, LF y CR.
svn:ignore nos permite ignorar ciertos tipos de ficheros en comandos como svn status
401
? perlexamples
X booktt
? perlexamples.lof
? perlexamples.log
? perlexamples.toc
? perlexamples.aux
? perlexamples.lot
M 5463 practicaAndSVN.tex
? perlexamples.blg
? perlexamples.pdf
? perlexamples.ind
? new
Estado respecto a la revisión: 5463
402
? booktt/preamble.bak
Estado respecto a la revisión: 5470
svn:mime-type
Véase:
• File Portability
• Lista de extensiones/tipos MIME
• RFC2045
Ejemplo:
13.17. Autopropiedades
El directorio ~/.subversion contiene algunos ficheros de control:
4 directories, 6 files
403
### Set enable-auto-props to ’yes’ to enable automatic properties
### for ’svn add’ and ’svn import’, it defaults to ’no’.
### Automatic properties are defined in the section ’auto-props’.
enable-auto-props = yes
Subversion no hace automáticamente commit de los cambios en las copias de los subproyectos externos.
Es necesario cambiar al directorio en cuestión y ejecutar svn commit.
404
Averiguando el estado del recurso externo en ’booktt’
Estado respecto a la revisión: 5463
Obsérvese el uso de las comillas simples protegiendo al segundo argumento de svn propset. Ahora
un update cargará el subproyecto externo:
You should seriously consider using explicit revision numbers in all of your externals
definitions. Doing so means that you get to decide when to pull down a different snapshot
of external information, and exactly which snapshot to pull. Besides avoiding the surprise
of getting changes to third-party repositories that you might not have any control over,
405
using explicit revision numbers also means that as you backdate your working copy to a
previous revision, your externals definitions will also revert to the way they looked in that
previous revision, which in turn means that the external working copies will be updated to
match they way they looked back when your repository was at that previous revision. For
software projects, this could be the difference between a successful and a failed build of an
older snapshot of your complex codebase.
Externals Definitions
Change the setting of each shell option OPTNAME. Without any option
arguments, list all shell options with an indication of whether or not each
is set.
Options:
406
-o restrict OPTNAMEs to those defined for use with ‘set -o’
-p print each shell option with an indication of its status
-q suppress output
-s enable (set) each OPTNAME
-u disable (unset) each OPTNAME
Exit Status:
Returns success if OPTNAME is enabled; fails if an invalid option is
given or OPTNAME is disabled.
Sin argumentos muestra los valores actuales de las opciones:
casiano@exthost:~$ shopt
autocd off
cdable_vars off
cdspell off
checkhash off
checkjobs off
checkwinsize on
cmdhist on
compat31 off
compat32 off
dirspell off
dotglob off
execfail off
expand_aliases on
extdebug off
extglob on
extquote on
failglob off
force_fignore on
globstar off
gnu_errfmt off
histappend off
histreedit off
histverify off
hostcomplete on
huponexit off
interactive_comments on
lithist off
login_shell on
mailwarn off
no_empty_cmd_completion off
nocaseglob off
nocasematch off
nullglob off
progcomp on
promptvars on
restricted_shell off
shift_verbose off
sourcepath on
xpg_echo off
Las opciones extglob y progcomp gobiernan la forma en la que se completan los comandos cuando
se presiona la tecla TAB:
407
$shopt -s extglob progcomp
Por otro lado el comando complete permite especificar como se completa un comando:
complete: complete [-abcdefgjksuv] [-pr] [-o option] [-A action] [-G globpat] \
[-W wordlist] [-F function] [-C command] [-X filterpat] \
[-P prefix] [-S suffix] [name ...]
Specify how arguments are to be completed by Readline.
Options:
-p print existing completion specifications in a reusable format
-r remove a completion specification for each NAME, or, if no
NAMEs are supplied, all completion specifications
When completion is attempted, the actions are applied in the order the
uppercase-letter options are listed above.
Exit Status:
Returns success unless an invalid option is supplied or an error occurs.
Ası́, si hacemos:
complete -W ’add blame praise annotate cat checkout co cleanup commit ci copy delete del \
remove rm diff di export help h import info list ls log merge mkdir move mv \
rename ren propdel pdel pd propedit pedit pe propget pget pg proplist plist pl \
propset pset ps resolved revert status stat st switch sw update up’ svn
http://svn.apache.org/repos/asf/subversion/trunk/tools/client-side/bash completion
$ . bash_completion
permite realizar una copia consistente de un repositorio. No es necesario detener la actividad de los
clientes durante el proceso.
408
13.22. Volcado y Carga de los contenidos de un Repositorio
Si queremos migrar un repositorio en una versión mas antigua de subversion a una mas nueva
o queremos hacer una copia del repositorio que sea mas independiente de la versión de Subversion
utilizada podemos usar los comandos svnadmin dump y svnadmin load. Estos dos comandos usan un
sencillo formato de volcado consistente en cabeceras RFC 822 (como los headers en e-mail) que son
sencillos de analizar y en los contenidos en bruto de los ficheros del repositorio. Una copia por volcado
y carga nos protege también contra posibles cambios en la versión de la librerı́a DBD subyacente.
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i38
-bash-3.2$ svnlook youngest repository/
6
-bash-3.2$ svnadmin dump repository/ > dumprep.6
* Revisión 0 volcada.
* Revisión 1 volcada.
* Revisión 2 volcada.
* Revisión 3 volcada.
* Revisión 4 volcada.
* Revisión 5 volcada.
* Revisión 6 volcada.
-bash-3.2$ ls -ltr | tail -1
-rw-r--r-- 1 casiano apache 14962 abr 3 18:29 dumprep.6
Para restaurar el repositorio en otra máquina debemos primero crear el repositorio y a continuación
cargarcon svnadmin load el fichero volcado en la operación anterior:
409
* editando ruta : acme-svn/trunk/Makefile.PL ... hecho.
Ahora el nuevo repositorio puede ser accedido desde cualquier otra máquina:
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i38
y después las de la 5 a la 6:
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-128.1.16.el5 #1 SMP Tue Jun 30 06:10:28 EDT 2009 i686 i686 i38
410
-bash-3.2$ svnadmin dump --incremental --revision 5:6 repository > dumprep.5to6
* Revisión 5 volcada.
* Revisión 6 volcada.
411
------- Commit de la revisión 6 >>>
pp2@nereida:~$
lgforte modification
------------------------------------------------------------------------
r4 | casiano | 2009-03-05 15:56:20 +0000 (jue, 05 mar 2009) | 1 line
branches
------------------------------------------------------------------------
Si múltiples usuarios estan accediendo al repositorio vı́a svn+ssh será necesario también garantizar
que los permisos del repositorio son los adecuados. Por ejemplo:
Ejercicio 13.23.1. Escriba un guión que copie su repositorio de forma incremental en un dispositivo
portable o en una máquina remota. Compruebe que el repositorio resultante es utilizable.
13.24. Etiquetas
Veamos un ejemplo de creación de un tag. Primero creamos el proyecto:
412
If you intend this module to be compatible with earlier perl versions, please
specify a minimum perl version with the -b option.
Writing SVN-Example/lib/SVN/Example.pm
Writing SVN-Example/Makefile.PL
Writing SVN-Example/README
Writing SVN-Example/t/SVN-Example.t
Writing SVN-Example/Changes
Writing SVN-Example/MANIFEST
Committed revision 4.
casiano@exthost:~/src/subversion$ svn ls svn+ssh://banot/home/casiano/repository/ejemplo/trunk
Changes
MANIFEST
Makefile.PL
README
lib/
t/
casiano@exthost:~/src/subversion/ejemplo$
413
A TRY-MGM-cache-pages/lib/SVN/Example.pm
A TRY-MGM-cache-pages/Makefile.PL
A TRY-MGM-cache-pages/Changes
A TRY-MGM-cache-pages/README
Checked out revision 7.
casiano@exthost:~/src/subversion$ cd TRY-MGM-cache-pages/
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ vi lib/SVN/Example.pm
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn commit -mm
Sending lib/SVN/Example.pm
Transmitting file data .
Committed revision 8.
casiano@exthost:~/src/subversion/TRY-MGM-cache-pages$ svn diff lib/SVN/Example.pm -r PREV
Index: lib/SVN/Example.pm
===================================================================
--- lib/SVN/Example.pm (revision 7)
+++ lib/SVN/Example.pm (working copy)
@@ -28,6 +28,12 @@
our $VERSION = ’0.01’;
+sub g1{
+}
+
+sub g2{
+}
+
# Preloaded methods go here.
1;
414
casiano@exthost:~/src/subversion/ejemplo$ svn diff lib/SVN/Example.pm -r PREV
Index: lib/SVN/Example.pm
===================================================================
--- lib/SVN/Example.pm (revision 4)
+++ lib/SVN/Example.pm (working copy)
@@ -30,6 +30,12 @@
+sub new_functionality1 {
+}
+
+sub new_functionality2 {
+}
+
1;
__END__
# Below is stub documentation for your module. You’d better edit it!
+sub new_functionality1 {
+}
+
+sub new_functionality2 {
+}
+
+sub new_functionality3 {
415
+}
+
1;
__END__
# Below is stub documentation for your module. You’d better edit it!
Many users (especially those new to version control) are initially perplexed about the
proper syntax of the command and about how and when the feature should be used. But fear
not, this command is actually much simpler than you think! There’s a very easy technique
for understanding exactly how svn merge behaves.
The main source of confusion is the name of the command. The term \merge" somehow
denotes that branches are combined together, or that some sort of mysterious blending of
data is going on. That’s not the case. A better name for the command might have been
svn diff-and-apply, because that’s all that happens: two repository trees are compared,
and the differences are applied to a working copy.
casiano@exthost:~/src/subversion/ejemplo$ vi lib/SVN/Example.pm
casiano@exthost:~/src/subversion/ejemplo$ svn commit -m ’some bug fixed’
Sending lib/SVN/Example.pm
Transmitting file data .
Committed revision 11.
sub new_functionality3 {
+ # some bug fixed here
}
1;
416
13.27. Las Mezclas Pueden Producir Conflictos
After performing the merge, you might also need to resolve some conflicts (just as you
do with svn update) or possibly make some small edits to get things working properly.
Remember: just because there are no syntactic conflicts doesn’t mean there aren’t any
semantic conflicts!
If you encounter serious problems, you can always abort the local changes by running
svn revert . -R (which will undo all local modifications) and start a long what’s going
on? discussion with your collaborators.
If you don’t like the results of the merge, simply run svn revert . -R to revert the
changes from your working copy and retry the command with different options. The merge
isn’t final until you actually svn commit the results.
...
While it’s perfectly fine to experiment with merges by running svn merge and svn revert
over and over, you may run into some annoying (but easily bypassed) roadblocks.
For example, if the merge operation adds a new file (i.e., schedules it for addition),
svn revert won’t actually remove the file; it simply unschedules the addition. You’re left
with an unversioned file. If you then attempt to run the merge again, you may get conflicts
due to the unversioned file \being in the way."
Solution? After performing a revert, be sure to clean up the working copy and remove
unversioned files and directories. The output of svn status should be as clean as possible,
ideally showing no output.
In revision control, changesets are a way to group a number of modifications that are
relevant to each other in one atomic package, that may be cancelled or propagated as needed.
417
13.30. Funciones de la Gestión de Configuraciones
The four main functions of Configuration Management may be summarised as:
Controlling all the elements of the IT infrastructure configuration with a sufficient le-
vel of detail and managing this information using the configuration database (CMDB).
Providing accurate information about the IT configuration to all the various manage-
ment processes.
Interacting with Incident, Problem, Change and Release Management so that they can
resolve incidents effectively by finding the cause of the problem rapidly, making the
changes necessary to solve it, and keeping the CMDB up-to-date at all times.
Periodically monitoring the configuration of the systems in the production environ-
ment and comparing it with that held in the CMDB to correct any discrepancies.
418
13.33. Dificultades a la Hora de Usar Gestión de Configuraciones
The main activities difficulties in Configuration Management are:
Incorrect planning: it is essential to programme the necessary activities correctly to
avoid duplications or mistakes.
Inappropriate CMDB structure: keeping an excessively detailed and exhaustive con-
figuration database up-to-date can be a time-consuming process requiring too many
resources.
Inappropriate tools: it is essential to have the right software to speed up the data entry
process and make the best use of the CMDB.
Lack of Coordination between Change and Release Management making it impossible
to maintain the CMDB correctly.
Lack of organisation: it is important for there to be a correct assignment of resources
and responsibilities. Where possible, it is preferable for Configuration Management to
be undertaken by independent specialist personnel.
Lack of commitment: the benefits of Configuration Management are not immediate
and are almost always indirect. This can lead to a lack of interest on the part of
management and consequently a lack of motivation among the people involved.
A changeset is just a collection of changes with a unique name. The changes might
include
textual edits to file contents,
modifications to tree structure, or
tweaks to metadata.
In more common speak, a changeset is just a patch with a name you can refer to.
In Subversion, a global revision number N names a tree in the repository: it’s the way
the repository looked after the Nth commit. It’s also the name of an implicit changeset: if
you compare tree N with tree N−1, you can derive the exact patch that was committed. For
this reason, it’s easy to think of revision N as not just a tree, but a changeset as well.
If you use an issue tracker to manage bugs, you can use the revision numbers to refer
to particular patches that fix bugs—for example, ’this issue was fixed by r9238.’ Somebody
can then run
to read about the exact changeset that fixed the bug, and run
419
13.35. Mezclas en svnbook
The general act of replicating changes from one branch to another is called merging,
and it is performed using various invocations of the svn merge command.
Véase:
What is a Branch
Basic Merging
Advanced Merging
13.36. Hooks
A hook script is a program triggered by some repository event, such as the creation of a
new revision or the modification of an unversioned property. Each hook is handed enough
information to tell what that event is, what target(s) it’s operating on, and the username
of the person who triggered the event. Depending on the hook’s output or return status, the
hook program may continue the action, stop it, or suspend it in some way.
To actually install a working hook, you need only place some executable program or
script into the repos/hooks directory, which can be executed as the name (such as start-
commit or post-commit) of the hook.
pp2@nereida:~$ ls -l svnrep/hooks/
total 40
-rw-r--r-- 1 pp2 pp2 2000 2010-04-12 10:33 post-commit.tmpl
-rw-r--r-- 1 pp2 pp2 1690 2010-04-12 10:33 post-lock.tmpl
-rw-r--r-- 1 pp2 pp2 2307 2010-04-12 10:33 post-revprop-change.tmpl
-rw-r--r-- 1 pp2 pp2 1606 2010-04-12 10:33 post-unlock.tmpl
-rwxr-xr-x 1 pp2 pp2 110 2010-04-19 08:30 pre-commit
-rw-r--r-- 1 pp2 pp2 2982 2010-04-19 07:45 pre-commit.tmpl
-rw-r--r-- 1 pp2 pp2 2038 2010-04-12 10:33 pre-lock.tmpl
-rw-r--r-- 1 pp2 pp2 2764 2010-04-12 10:33 pre-revprop-change.tmpl
-rw-r--r-- 1 pp2 pp2 1980 2010-04-12 10:33 pre-unlock.tmpl
-rw-r--r-- 1 pp2 pp2 2758 2010-04-12 10:33 start-commit.tmpl
420
pp2@nereida:~$ cat -n /home/pp2/src/perl/subversion/pre-commit.pl
1 #!/usr/bin/perl -w
2 use strict;
3 # creating scalar variables that holds some values
4
5 open my $file, ’> /tmp/mylog’;
6 print $file "executing hook\n";
7 close($file);
8
9 my $min = 8;
10 my $svnlook = ’/usr/bin/svnlook’;
11 #--------------------------------------------
12 my $repos = shift;
13 my $txn = shift;
14
15 unless (defined($repos) and defined($txn)) {
16 warn "Error: Expected repos and txn args\n";
17 exit(3);
18 }
19
20 my $msg;
21 eval {
22 $msg = ‘$svnlook log -t "$txn" "$repos" 2>&1‘;
23 };
24
25 if ($@ or $?) {
26 warn qq{Error executing ’$svnlook log -t "$txn" "$repos"’\nmessage:\n$msg\n};
27 exit(2);
28 }
29 warn "repos=$repos txn=$txn msg=$msg\n";
30 chomp($msg);
31
32 if (length($msg) < $min) {
33 warn "Message should be at least $min characters in length\n";
34 exit(1);
35 }
36
37 exit(0);
Ahora modificamos un fichero en un proyecto y hacemos un commit con un mensaje corto:
pp2@nereida:~/src/perl/subversion/project$ svn commit -mm
Enviando trunk/Makefile.PL
Transmitiendo contenido de archivos .svn: Falló el commit (detalles a continuación):
svn: Commit bloqueado por hook pre-commit (código de salida 1) con salida:
repos=/home/pp2/svnrep txn=16-j msg=m
421
Implementación de Ganchos para un Repositorio
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-164.15.1.el5 #1 SMP Wed Mar 17 11:37:14 EDT 2010 i686 i686 i38
-bash-3.2$ /usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl
/usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl: too few arguments.
usage (commit mode):
/usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl REPOS REVNUM [[-m regex]
usage: (revprop-change mode):
/usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-email.pl --revprop-change REPOS RE
[[-m regex] [options] [email_addr ...]] ...
options are:
--from email_address Email address for ’From:’ (overrides -h)
-h hostname Hostname to append to author for ’From:’
-l logfile Append mail contents to this log file
-m regex Regular expression to match committed path
-r email_address Email address for ’Reply-To:’
-s subject_prefix Subject line prefix
--diff y|n Include diff in message (default: y)
(applies to commit mode only)
Any of the following -h, -l, -r, -s and --diff command line options
and following email addresses are associated with this project. The
next -m resets the -h, -l, -r, -s and --diff command line options
and the list of email addresses.
’revprop-change’ mode:
422
The message will contain a copy of the diff_file if it is provided,
otherwise a copy of the (assumed to be new) property value.
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-164.15.1.el5 #1 SMP Wed Mar 17 11:37:14 EDT 2010 i686 i686 i38
bash-3.2$ pwd
/home/casiano/newrepository/hooks
-bash-3.2$ ls -ltra post-commit
-rwxr-xr-x 1 casiano apache 280 abr 20 14:08 post-commit
-bash-3.2$ cat post-commit
#!/bin/sh
REPOS="$1"
REV="$2"
pp2@nereida:~/src/perl/subversion/banotnewrepproject2$ vi Makefile.PL
pp2@nereida:~/src/perl/subversion/banotnewrepproject2$ svn commit -m ’checking post-commit’
Enviando Makefile.PL
Transmitiendo contenido de archivos .
Commit de la revisión 20.
from [email protected]
reply-to [email protected]
to [email protected]
date 20 April 2010 14:09
subject r20 - project2/trunk
mailed-by ull.es
Author: aluXXX
Date: 2010-04-20 14:09:35 +0100 (Tue, 20 Apr 2010)
New Revision: 20
Modified:
project2/trunk/Makefile.PL
Log:
423
checking post-commit
Modified: project2/trunk/Makefile.PL
===================================================================
--- project2/trunk/Makefile.PL 2010-04-20 08:32:45 UTC (rev 19)
+++ project2/trunk/Makefile.PL 2010-04-20 13:09:35 UTC (rev 20)
@@ -1,11 +1,4 @@
use ExtUtils::MakeMaker;
-
-
-#
-#
-#
-#
-#
WriteMakefile(
NAME => ’project2’,
VERSION_FROM => ’lib/project2.pm’, # finds $VERSION
-bash-3.2$ uname -a
Linux banot.etsii.ull.es 2.6.18-164.15.1.el5 #1 SMP Wed Mar 17 11:37:14 EDT 2010 i686 i686
-bash-3.2$ pwd
/home/casiano/newrepository/hooks
-bash-3.2$ ls -l pre-commit
-rwxr-xr-x 1 casiano apache 281 abr 20 17:17 pre-commit
-bash-3.2$
-bash-3.2$ cat pre-commit
#!/bin/sh
REPOS="$1"
TXN="$2"
2. Asegúrese que otro compañero puede acceder a su repositorio usando el protocolo svn+ssh. Para
ello, si no lo ha hecho ya, genere una pareja de claves y publique la clave en el servidor subversion.
Recuerde el formato en el fichero authorized_keys para identificarle:
424
-bash-3.2$ cat ~/.ssh/authorized_keys
.............................................................
[casiano permissions]
match = .*
users = casiano
access = read-write
4. Compruebe que su compañero tiene el acceso limitado a las correspondientes partes del proyec-
to. El compañero crea una entrada en su configuración ssh para facilitar el acceso via svn al
repositorio:
425
A svn/project1/branches/branch1/t/project1.t
A svn/project1/branches/branch1/MANIFEST
A svn/project1/branches/branch1/lib
A svn/project1/branches/branch1/lib/project1.pm
A svn/project1/branches/branch1/Makefile.PL
A svn/project1/branches/branch1/Changes
A svn/project1/branches/branch1/README
A svn/project2
A svn/project2/trunk
A svn/project2/trunk/t
A svn/project2/trunk/t/project2.t
A svn/project2/trunk/MANIFEST
A svn/project2/trunk/lib
A svn/project2/trunk/lib/project2.pm
A svn/project2/trunk/Makefile.PL
A svn/project2/trunk/Changes
A svn/project2/trunk/README
Revisión obtenida: 24
aluXXXX@nereida:/tmp$ cd svn/project1/branches/branch1
aluXXXX@nereida:/tmp/svn/project1/branches/branch1$ echo ’# comentario’>>Makefile.PL
aluXXXX@nereida:/tmp/svn/project1/branches/branch1$ svn commit -m ’checking permits’
Enviando branch1/Makefile.PL
Transmitiendo contenido de archivos .svn: Falló el commit (detalles a continuación):
svn: El hook ’pre-commit’ falló con la siguiente salida de error:
/home/casiano/newrepository/hooks/commit-access-control.pl: user ‘aluXXXX’ does not have p
project1/branches/branch1
project1/branches/branch1/Makefile.PL
aluXXXX@nereida:/tmp/svn/project1/branches/branch1$ cd /tmp/svn/project1/trunk/
aluXXXX@nereida:/tmp/svn/project1/trunk$ echo ’# comentario’>>Makefile.PL
aluXXXX@nereida:/tmp/svn/project1/trunk$ svn commit -m ’checking permits’
Enviando trunk/Makefile.PL
Transmitiendo contenido de archivos .
Commit de la revisión 25.
aluXXXX@nereida:/tmp/svn/project1/trunk$
Véanse:
Fichero /usr/share/doc/subversion-1.4.2/tools/hook-scripts/commit-access-control.pl
en banot y en https://svn.apache.org/repos/asf/subversion/trunk/tools/hook-scripts/commit-access-control.
(Para entender el código necesitará repasar las secciones ??, ?? y ??)
A sample configuration might look like the following, in which users mother, father,
dick, jane, and spot (along with any other users who have unix-file-permission access
to the repository) have read-access on testproj-a and testproj-b; but only dick and
jane can write (commit) to testproj-a. Only spot can write (commit) to any part
426
of testproj-b, but father can commit to the bbq directory of testproj-b, in the
branches, tags, and/or trunk directories of that project.
Note the special case login, mother, who can write to all directories and files in this
repository - including SVN/ which is where the commit permission configuration file is
versioned. Some account with this level of privilege is necessary if new projects are to
be created in the repository (as siblings - if you will - to testproj-a and testproj-b).
It is mother who would import the new projects and, presumably, modify the commit
access configuration file to allow write access on the new project to appropriate users.
13.39. Locking
Locking
427
Chapter Beautiful Debugging
Ejercicio 13.40.1. Haga un checkout del proyecto parse-eyapp en google-code. Utilize el método de
la bisección con svn-bisect para averiguar en que versión del proyecto se introdujo la funcionalidad
que comprueba el test t/73dynamicshiftreduceconflictresolution.t
13.42. Referencias
Consulte
1. http://svnbook.red-bean.com/en/1.5/ .
3. Vea los capı́tulos disponibles del libro Subversion in Action de la editorial Manning
428
Apéndice
429
Código de 01MartelloAndTothBook.t
430
43 local $TODO = "Randomly generated problem";
44 can_ok(’Algorithm::Knap01DP’, ’GenKnap’);
45 }
431
Código de Calc.yp
1 #
2 # Calc.yp
3 #
4 # Parse::Yapp input grammar example.
5 #
6 # This file is PUBLIC DOMAIN
7 #
8 #
9 %right ’=’
10 %left ’-’ ’+’
11 %left ’*’ ’/’
12 %left NEG
13 %right ’^’
14
15 %%
16 input: #empty
17 | input line { push(@{$_[1]},$_[2]); $_[1] }
18 ;
19
20 line: ’\n’ { $_[1] }
21 | exp ’\n’ { print "$_[1]\n" }
22 | error ’\n’ { $_[0]->YYErrok }
23 ;
24
25 exp: NUM
26 | VAR { $_[0]->YYData->{VARS}{$_[1]} }
27 | VAR ’=’ exp { $_[0]->YYData->{VARS}{$_[1]}=$_[3] }
28 | exp ’+’ exp { $_[1] + $_[3] }
29 | exp ’-’ exp { $_[1] - $_[3] }
30 | exp ’*’ exp { $_[1] * $_[3] }
31 | exp ’/’ exp {
32 $_[3]
33 and return($_[1] / $_[3]);
34 $_[0]->YYData->{ERRMSG}
35 = "Illegal division by zero.\n";
36 $_[0]->YYError;
37 undef
38 }
39 | ’-’ exp %prec NEG { -$_[2] }
40 | exp ’^’ exp { $_[1] ** $_[3] }
41 | ’(’ exp ’)’ { $_[2] }
42 ;
43
432
44 %%
45
46 sub _Error {
47 exists $_[0]->YYData->{ERRMSG}
48 and do {
49 print $_[0]->YYData->{ERRMSG};
50 delete $_[0]->YYData->{ERRMSG};
51 return;
52 };
53 print "Syntax error.\n";
54 }
55
56 sub _Lexer {
57 my($parser)=shift;
58
59 $parser->YYData->{INPUT}
60 or $parser->YYData->{INPUT} = <STDIN>
61 or return(’’,undef);
62
63 $parser->YYData->{INPUT}=~s/^[ \t]//;
64
65 for ($parser->YYData->{INPUT}) {
66 s/^([0-9]+(?:\.[0-9]+)?)//
67 and return(’NUM’,$1);
68 s/^([A-Za-z][A-Za-z0-9_]*)//
69 and return(’VAR’,$1);
70 s/^(.)//s
71 and return($1,$1);
72 }
73 }
74
75 sub Run {
76 my($self)=shift;
77 $self->YYParse( yylex => \&_Lexer, yyerror => \&_Error) #, yydebug => 0x10 );
78 }
79
433
Índice alfabético
434
destructor, 331 libreria, 206
Destructores, 306 listas, 57
diamante, 325 listas perezosas, 200
Digital Signature Algorithm, 287 Llamada con flecha
directory handle, 110 nombre completo del método, 325
Distribution id, 235 lvalue, 152
divide-y-vencerás, 197
documentos aqui, 189 método, 306
DSA, 287 método abstracto, 327
método de objeto, 310
ejecutables standalone, 256 método dinámico, 310
Ejercicio método singleton, 320
SUPER, 327 módulo, 206
Búsqueda de Métodos, 322 manejador de directorio, 110
Identificadores entre LLaves, 155 mantra de instalación, 227
Prioridad de Operaciones, 94 memoizing, 195
Significados de la Coma, 95 MLDBM, 381
elsif, 47 Moses Schönfinkel, 198
Emulación de un Switch, 170 mutator, 310
envolver, 178
espacio de nombres, 85, 203 objeto, 306
especificación completa del nombre, 87, 203 objeto de clase, 310
evaluación perezosa, 200 one-liner, 250
exportar, 210 one-liners, 16
Extreme Programming, 264 Opción de perl -i, 101
Opción de perl -n, 101
fábrica de funciones, 199 Opción de perl -p, 100
fichero patch, 301 opciones de lı́nea, 100, 138
filehandle, 102 operador, 55, 145
flecha operadores de bit, 30
sintáxis, 310
for, 55 package, 93
fully qualifying the name, 87, 203 paquete, 93
función, 195 paquete main, 204
funciones de orden superior, 169 parche, 301
function factory, 199 patch, 301
PDL, 155
Gottlob Frege, 198 perfilado, 280
Grafo de Flujo de Control, 390 Perl Archive Toolkit, 256
Perl Archives, 250
hash anónimo, 148 Perl Data Language, 155
Haskell Curry, 198 Perl Package Descriptor, 258
here-document, 189 Perl packager, 256
heredoc, 189 persistencia, 380
herencia, 319 pila, 62
hexadecimales, 30 plain old documentation, 262
highest common factor, 350 PPD, 258
Práctica
importar, 210
Ancestros de un Objeto, 321
incident ticket system, 300
Area de un Cı́rculo, 53
interpolación, 26
AUTOLOAD, 217
introspección, 213
Conjuntos a través de Hashes, 149
iterador, 190, 191
Construcción de un wrapper, 178
iterador privado, 77
Construcción de una Distribución, 269
435
Constructores-Copia, 318 scratchpads, 171, 204
CPAN, 250 sensibles al contexto, 30
Currificación de grep, 200 separador de elementos de un array, 59
Descenso Recursivo en Subdirectorios, serialización, 380
119 singletons, 320
Emulación de un Switch, 170 sizing guidance, 296
En Orden ASCIIbético, 70 sobrecarga de operadores, 335
Fichero en Orden Inverso, 69 software de rastreo de errores, 300
Ficheros Grandes y Viejos, 106 software de rastreo de problemas, 300
Generación de Pruebas con Test::LectroTest,
SSH, 287
300 stash, 212
Herencia, 329 stringification, 341
Indexación, 71 subrutina, 82
Inserción de una Subrutina, 181 subrutina anónima, 148
Instalación Automática de Métodos, 314 Subset Sum Problem o SSP, 299
Instalar un Módulo, 227 Symbol Table Hash, 212
Iterador de Archivos, 194 Synchronization Models, 416
La Función memoize, 197
Listas Perezosas, 202 There is more than one way to do it, 79
Memoización de un Divide y Vencerás, 196 ticket, 300
Números Fraccionarios, 348 TIMTOWTDI, 79
Ordenación Internacional, 74 trouble ticket system, 300
Ordenar por Calificaciones, 80 typeglob, 171
Polares a Cartesianas, 96 Typeglob Selectivo, 175
Postfijo, 72
unified format, 398
Postfijo y Subrutina, 96
unless, 36
Pruebas, 291
unwinding, 75
Referenciado Simbólico, 168
Sin Distinguir Case, 70 valores, 48, 74
Stash, 213 variable, 156
Suma de Prefijos, 186 variable léxica, 33
Tie Escalar, 362 variable mágica por defecto, 55
Un Método Universal de Volcado, 322 variable por defecto, 32
Un Módulo OOP Simple, 311 variable privada, 33
pragma, 41 variables ‘‘mágicas’’, 30
Problema de la Mochila 0-1, 269 variables privadas, 307
problema del subconjunto suma, 299 versión, 210
procesador software, 388
profiler, 280 wrapper, 177
profiling, 280 wrapping, 198
Programación Dinámica, 269
promesa, 200
promise, 200
prototipo backslash, 183
prototipos, 181, 182
scalar context, 57
Scheme, 187
436
Bibliografı́a
[1] Schwartz L. and Phoenix T. Learning Perl. O’Reilly, USA, 2001. ISBN 0-596-00132-0.
[2] Srinivasan Sriram. Advanced Perl Programming. O’Reilly, USA, 1997. ISBN 1-56592-220-4.
[3] Wall L., Christiansen T., and Schwartz R. L. Programming Perl. O’Reilly, USA, 1996.
[4] Richard Foley and Andy Lester. Pro Perl Debugging. Apress, Berkely, CA, USA, 2005.
[5] Jefrrey E.F. Friedl. Mastering Regular Expressions. O’Reilly, USA, 1997. ISBN 1-56592-257-3.
[6] Lincoln D. Stein. Network Programming with Perl. Addison Wesley, USA, 2001. ISBN 0-201-
61571-1.
[7] Christiansen T. and Schwartz L. Perl Cookbook. O’Reilly, USA, 1998. ISBN 1-56592-243-3.
[8] Tim Jenness and Simon Cozens. Extending and embedding Perl. Manning Publications Co.,
Greenwich, CT, USA, 2003.
[9] Joseph Hall and Randall L. Schwartz. Effective Perl Programming. Writing Better Programs with
Perl. Addison Wesley, USA, 2001. ISBN 0-201-41975-0.
[10] Alfred V. Aho, Ravi Sethi, and Jeffrey D. Ullman. Compilers: Princiles, Techniques, and Tools.
Addison-Wesley, 1986.
[11] Damian Conway. Perl Best Practices. O’Reilly Media, Inc., 2005.
[12] Michael Pilato. Version Control With Subversion. O’Reilly & Associates, Inc.
http://svnbook.red-bean.com/en/1.4/svn-book.html, Sebastopol, CA, USA, 2004.
[13] Mark Jason Dominus. Higher-Order Perl. Morgan Kaufmann Publishers, 2005.
[14] Peter Scott. Perl Medic: Maintaining Inherited Code. Addison Wesley, USA, 2004. ISBN
0201795264.
[15] S. Martello and P. Toth. Knapsack Problems. John Wiley and Sons, Chichester, 1990.
[16] Jesse Vincent, Dave Rolsky, Darren Chamberlain, Richard Foley, and Robert Spier. RT Essentials.
O’Reilly Media, Inc., 2005.
[17] Sam Tregar. Writing Perl Modules for CPAN. Apress. Descarguelo desde
http://www.apress.com/free/download free.html, Berkely, CA, USA, 2002.
[19] Simon Cozens. Advanced Perl Programming, Second Edition. O’Reilly Media, 2005.
437