From emhn at telcel.net.ve Sun Nov 3 10:02:45 2002 From: emhn at telcel.net.ve (Ernesto Hernandez-Novich) Date: Wed Aug 4 23:59:33 2004 Subject: [l-linux] Duda de perl... In-Reply-To: <20021102202811.81234.qmail@web10108.mail.yahoo.com> Message-ID: On Sat, 2 Nov 2002, Salvador Sosa wrote: > Hola a los gurus de Perl... > Todav?a estoy traduciendo en script csv2vcard de > evolution (dispongo de poco tiempo para invertir en > esto). > > El problema lo tengo con la fecha. > Microsoft Outlook exporta las fechas a veces en > formato no compatible con Y2k (01/11/02) y a veces en > formato "extendido" (01/11/2002). El problema que > tengo es que cuando tiene que interpretar la fecha no > la interpreta (dd/mm/yyyy) sino (mm/dd/yyyy). Alguien > puede orientarme donde cambio esto, imagino que en > Perl? O en el script? esto viene de algo como \d\d. [...] > if ($temp =~ /(\d\d)\/(\d\d)\/(\d\d)/) { > # Y2k !! MS Didn't learn anything. > # Hope no one was born before 1915 > if ((1900 + $3) < 1915) { > print $fh "$key:20$3-$1-$2\n"; > } else { > print $fh "$key:19$3-$1-$2\n"; > } Cuando se trabaja con expresiones regulares (man perlre) uno puede "guardar" los resultados de una coincidencia en variables generadas autom?ticamente. $temp =~ /(\d\d)\/(\d\d)\/(\d\d)/ quiere decir "verifica si $temp coincide con el siguiente patr?n dos d?gitos (gu?rdalos) una barra dos d?gitos (gu?rdalos) una barra dos d?gitos (gu?rdalos)" Los "gu?rdalos" quieren decir, col?calos en $1, $2, $3 ..., de modo que en el bloque de c?digo inmediato puedes usar esas variables. En este caso, la persona que escribi? el c?digo asume (no se si bien o mal) que $1 es el mes, $2 es el d?a y $3 es el a?o; lo que te queda es cambiar esa interpretaci?n y listo (modificando los print). Por cierto, notar?s los \/ para escapar la /. El autor no es muy versado en Perl, puesto que el operador de "matching" s puede cambiar sus delimitadores para hacerlo ver m?s bonito, i.e. $temp =~ /(\d\d)\/(\d\d)\/(\d\d)/ es equivalente a $temp =~ m/(\d\d)\/(\d\d)\/(\d\d)/ que es equivalente a (por ejemplo) $temp =~ m|(\d\d)/(\d\d)/(\d\d)| o bien $temp =~ m#(\d\d)/(\d\d)/(\d\d)# N?tese que al usar "|" o "#" como delimitador, el prop?sito de la b?squeda es mucho m?s claro y evito tener que escapar las barras. -- Ernesto Hern?ndez-Novich - Running Linux 2.4.19 i686 - Unix: Live free or die! Geek by nature, Linux by choice, Debian of course. If you can't apt-get it, it doesn't exist or isn't important. GPG Key Fingerprint = 438C 49A2 A8C7 E7D7 1500 C507 96D6 A3D6 2F4C 85E3 ------------------------------------------------------------------------ Enviar e-mail a colocando en el cuerpo: "UNSUBSCRIBE caracas-pm-list" para desuscribirse. "INFO caracas-pm-list" para conocer las reglas de etiqueta. ------------------------------------------------------------------------ From emhn at telcel.net.ve Mon Nov 25 13:53:10 2002 From: emhn at telcel.net.ve (Ernesto Hernandez-Novich) Date: Wed Aug 4 23:59:33 2004 Subject: =?ISO-8859-1?Q?Las_virtudes_de_la_programci=F3n_funcional?= Message-ID: Hoy es un buen d?a para hackear. Casi cualquiera que dice "saber programar", probablemente ha adquirido toda su experiencia utilizando el paradigma Imperativo (tambi?n conocido como Procedural), en el cual se indica al computador exactamente todos los pasos que debe realizar para cumplir determinada tarea, adem?s de estar basado en "efectos laterales", es decir la posibilidad de mantener un estado (variables globales) y sabiendo que cada acci?n que ejecuta el computador depende de ese estado y puede modificarlo. No hay nada malo en conocer solamente ese paradigma, pero si hay mucho muy bueno en los dem?s paradigmas que los hacen superiores en varios aspectos, y a la vez brindan al programador maneras novedosas de enfrentar problemas de programaci?n. No es mi intenci?n discutir aqu? los cuatro paradigmas fundamentales (Imperativo, Objetivo u "Orientado a Objetos", Funcional y L?gico Declarativo); el interesado debe referirse a libros de texto cualesquiera en el ?mbito de Lenguajes de Programaci?n. Mi intenci?n con esta nota (y las dos que le seguir?n) es ilustrar como el paradigma Funcional es _much?simo_ m?s expresivo que el Imperativo, mucho m?s conciso, facilita la demostraci?n de correctitud de los programas y permite hacer cosas que con el paradigma Imperativo simplemente no se pueden [1]. Perl es uno de los pocos lenguajes de programaci?n Turing-completos que incluye _tres_ paradigmas simult?nea y complementariamente [2]. Lo ?nico que hace falta es agregar nuevas formas de pensar para aprovecharlos. En este primer art?culo, tratar? de cubrir los aspectos pr?cticos de la programaci?n funcional en el "d?a a d?a". Hay un s?lo aspecto te?rico que es importante mantener presente: en la programci?n funcional pura _no_ existen las variables globales. Es decir, cuando uno plantea la soluci?n a un problema, lo hace combinando aplicaciones de funciones que reciben N argumentos y producen M resultados, pero no hay _nada_ compartido entre las funciones ni mecanismo alguno de comunicaci?n entre ellas (salvo el pasaje de par?metros y la captura de valores de retorno). Desde el punto de vista del dise?o de programas, eso implica que se utiliza la metodolog?a Top-Down. Si se trabaja en un lenguaje puramente funcional como Scheme, ML o Haskell, no hay otra forma de hacer las cosas; Perl es un caso excepcional, porque si permite mezclar ambos paradigmas. Lo primero que uno debe aprender cuando trabaja funcionalmente es que todo se logra aplicando funciones. El que est? acostumbrado a la programaci?n Imperativa piensa sub foo { ... return ($bar); } ($ret1,$ret2,$ret3) = foo(arg1,arg2,arg,3) y se siente feliz. Sin embargo esto es lo m?nimo que un lenguaje funcional debe soportar (aplicaci?n de funciones). Tomemos un ejemplo cl?sico: se tiene una lista de longitud _desconocida_ a priori y se desea operar un c?lculo espec?fico sobre cada elemento de la lista, para obtener una nueva lista con los resultados. "?F?cil!" @nueva = (); for ($i = 0; $i < @original; $i++) { $valor = operar-sobre($original[$i]); push(@nueva,$valor); } es la soluci?n cl?sica imperativa y "cabeza cuadrada" [3]: 1. Crear una lista nueva. 2. Determinar el largo de la lista, y con otra variable iterar desde cero hasta all?. 3. Usar otra variable para operar sobre el original. 4. Crear la nueva lista agregando al final los nuevos valores Algo un poco mejor ser?a hacer foreach $elemento (@original) { operar-sobre($elemento) } como foreach es una implementaci?n parcial de un operador funcional (un "iterador" para ser precisos) no es necesario calcular la longitud de la lista... sin embargo no es un operador puro porque se basa precisamente en un "efecto lateral": $elemento se convierte en una referencia al valor original en la lista, de modo que estoy trabajando "en el sitio" [4]. Para ser completamente funcionales, lo que deseamos es expresar "aplica la funci?n operar-sobre() sobre cada elemento de la lista y genera una nueva lista", de la forma m?s sucinta posible. @nueva = map { operar_sobre($_) } @original Eso es todo. Si se utiliza el m?dulo Benchmark para comparar las velocidades se puede apreciar la verg?enza que pasa el programador Imperativo por desconocer la t?cnica Funcional [5]. #!/usr/bin/perl -w use Benchmark; sub operar { my $x = shift; return $x * 2; } timethese(20000, { imperativo => 'my @lista = (1..100); my @nueva = (); for (my $i = 0; $i < $#lista; $i++) { push(@nueva,operar($lista[$i])); }', mediocamino => 'my @lista = (1..100); my @nueva = (); foreach (@lista) { push(@nueva,operar($_)); }', funcional => 'my @lista = (1..100); my @nueva = map { operar($_) } @lista;' }); Es decir, se ejecut? 20000 veces cada proceso trabajando con listas de 100 elementos. Saquen sus conclusiones en cuanto a claridad y velocidad (edit? los resultados, pero pru?benlo en sus m?quinas): Benchmark: timing 20000 iterations of funcional, imperativo, mediocamino... imperativo: 42 wallclock secs mediocamino: 31 wallclock secs funcional: 25 wallclock secs As? que con el estilo funcional soy 60% m?s r?pido que con el imperativo, y hay que fumar algo peor que crack para negar que el funcional es m?s claro y conciso. Ahora bien, utilizar map solamente para hacer ciclos impl?citos como el que he presentado, aunque es un avance para el programador, no llega a ser tan efectivo como podr?a serlo. Supongan que tienen una lista de _referencias_ a "cosas" (hashes de listas de hashes de ..., no importa) que representan elementos comparables, i.e. tienen una lista de referencias a datos de clientes. Digamos que la lista apunta a un hash que tiene una clave 'montos' que es una referencia a una lista de tres elementos, y resulta que ustedes tienen que ordenar la lista de clientes seg?n cada uno de esos campos, i.e. tienen ( ... clientes ..., * .... ) <=== Ordenar esto seg?n | valor en A, B y/o C | +-----> { nombre => ..., cedula => ..., montos => [ A, B, C ] ... } Los Imperativos ya est?n diciendo alg?n improperio. Yo les presento lo siguiente: @clientes = ( ... el mamotreto antes visto ... ); for my $i (0..2) { $ordenada[$i] = [ map { $_->[1] } sort { $a->[0] <=> $b->[0] } map { [ $_->{montos}->[$i], $_ ] } @clientes ]; } y en $ordenada[$i] tienen una referencia a una lista que es @clientes ordenada por el campo $i-?simo de la lista de montos. As? de simple. ?A?n est?n pensando como hacerlo imperativo? Uno de los secretos para comprender el paradigma funcional es desplazarse "de adentro afuera": map { [ $_->{montos}->[$i], $_ ] } @clientes Esto toma la lista de clientes y le aplica la funci?n entre llaves. Esa funci?n devuelve una referencia a una lista que tiene dos elementos: el valor del campo $i-?simo de la clave 'montos' del cliente, y una referencia al cliente original. Entonces, map me devolver? una lista de pares ($i-?simo monto, referencia al cliente). sort { $a->[0] <=> $b->[0] } (...) Esto se aplica a la lista que sali? del map. Por lo tanto ordena la lista de pares en orden ascendente por el 0-?simo elemento del par, es decir, el monto. Entonces sort me devolver? la lista de pares ordenada seg?n el monto. map { $_->[1] } Esto se aplica a la lista que sali? del sort. Por lo tanto, genera una nueva lista que tiene las referencias a los clientes originales; como la lista que sali? del sort ya tiene el orden que me interesa... $ordenada[$i] = [ map/sort/map ] ... tomo la referencia a esa lista. Y listo. ?A?n tratan de pensar como hacerlo de forma Imperativa? [6] Incidentalmente, la t?cnica map/sort/map que he descrito se conoce en el argot Perl como "maniobra Schwartziana" porque fue descubierta por Randal Schwartz; existe una t?cnica similar map/grep/map que no tiene nombre. El ?ltimo t?pico a cubrir en esta introducci?n es lo que d? el nombre al paradigma funcional: las funciones. ?Qu? puede ser manipulado como _dato_ en un lenguaje Imperativo? "Bueno, los n?meros, las cadenas, apuntadores a n?meros y cadenas, estructuras de datos. ?Por?". En el paradigma Funcional, se pueden manipular las _funciones_ [7]. Seguramente, no les resultar? extra?o ver algo como sub cuadrado { my $x = shift; return ($x * $x); } print cuadrado(4); "?Duh! Una funci?n que retorna el cuadrado de un numero. ?Gran cosa!" Al igual que en los lenguajes Imperativos, los lenguajes Funcionales permiten definir funciones como ?sta; nada del otro mundo. Sin embargo, en los lenguajes Funcionales se pueden definir funciones an?nimas, que se usan con una referencia, en Perl, por ejemplo my $funcion = sub { my $x = shift; return ($x * $x); } y $funcion ahora tendr? una _referencia_ a una funci?n. De modo que puedo aprovecharla haciendo print $funcion->(4) que quiere decir "sigue la referencia a la funci?n con los siguientes argumentos". Si no le ven la utilidad inmediatamente para crear callbacks en interfaces gr?ficas, o para hacer un despachador de rutinas... no han programado mucho que se diga. Pero a?n as?, aparentemente no es muy ?til... Si una funci?n puede manipularse igual que cualquier objeto, ?ser? posible escribir una funci?n que _retorne_ una funci?n? Por supuesto, y eso es el coraz?n de la programaci?n Funcional. Continuando con el ejemplo, es trivial darse cuenta que sub retornafuncion { return sub { my $x = shift; return ($x * $x); } } my $cuadrado = retornafuncion(); print $cuadrado->(4) hace lo mismo que los programas anteriores. "?Gran cosa!". Ahora tienen que escribir funciones para generar secuencias. Digamos que necesitan una secuencia para n?meros de transacci?n, otra secuencia para n?meros de clientes, y as?, necesitan tener varias secuencias que comiencen por n?meros diferentes. Es m?s, durante la vida del programa les van a pedir que agreguen secuencias. La soluci?n Imperativa gira por tener varias variables globales y una rutina para manipular cada una, etc. etc. etc. Horriblemente confuso y dif?cil de mantener. Comencemos por escribir una funci?n que dado un valor inicial, genere una funci?n que cuenta a partir de ese valor inicial: sub generador { my $n = shift; return sub { return $n++; }; } Y la uso de la siguiente manera: my $secuencia_cliente = generador(1000); my $secuencia_producto = generador(2000); print $secuencia_cliente->(), "\n", # 1000 $secuencia_producto->(), "\n", # 2000 $secuencia_producto->(), "\n", # 2001 $secuencia_producto->(), "\n", # 2002 $secuencia_cliente->(), "\n"; # 1001 Es decir, llamo a la rutina con 1000, y me devuelve una rutina que sabe contar desde 1000 y "acordarse" de mantener esa secuencia. Luego llamo a la rutina con 2000, y me devuelve una rutina que sabe contar desde 2000 y "acordarse" de mantener esa secuencia. Lo que ocurre en generador es que cuando se construye la rutina an?nima que va a ser retornada, el lenguaje se "d? cuenta" que cuando sea invocada debe "recordar" el valor que ten?a $n. Entonces la rutina reci?n construida lleva consigo copias de las variables l?xicas (locales definidas con my) para poder calcular en el mismo ambiente en el que fue definida. [8] Es decir, durante la ejecuci?n del programa hay "dos $n"; una es la que se utiliz? cuando se llam? a generador(1000) y se "guarda" junto con la rutina construida, y otra es la que se utiliz? cuando se llam? a generador(2000) y se "guarda" junto con esa rutina. Son independientes entre s?, y cada vez que se llame a la rutina particular, su "$n" privado ser? modificado apropiadamente. ?Puede ser que yo est? fumando lumpias? Por supuesto. Puedo tener clausuras que _compartan_ ambiente, i.e. una misma "$n" vista por dos (o m?s) clausuras al mismo tiempo. Voy a usar un ejemplo que no es "pr?ctico" pero que ilustra el poder del paradigma; quiero tener dos contadores, "m?s-p" y "m?s-q" que hagan lo que su nombre dice (contar de p en p y de q en q) pero a partir del valor que dej? el contador anterior... es decir si p es cuatro, q es siete y el valor inicial es 42, entonces mascuatro --> 46 massiete --> 53 massiete --> 60 mascuatro --> 64 Lo escribimos as? sub droga { my ($p,$q,$n) = @_; my $c1 = sub { return $n += $p; }; my $c2 = sub { return $n += $q; }; return ($c1,$c2); } my ($mascuatro,$massiete) = droga(4,7,42); my ($mastrece,$masquince) = droga(13,15,-4); print $mascuatro->(),"\n", # 46 $mastrece->(), "\n", # 9 $massiete->(), "\n", # 53 $massiete->(), "\n", # 60 $masquince->(), "\n", # 24 $mascuatro->(), "\n"; # 64 Se generan _cuatro_ rutinas an?nimas. Dos de ellas comparten un $n que comienza con 42 y al cual le suman 4 y/o 7 seg?n sean llamadas. Las otras dos comparten otro $n que comienza con -4 y al cual le suman 13 o 15 seg?n sean llamadas. Finalmente, otro de los elementos del paradigma es la recursi?n. La recursi?n es matem?ticamente natural para expresar c?lculos, y el caso cl?sico del factorial no deber?a necesitar explicaci?n. Usualmente los lenguajes Imperativos tienen la habilidad de utilizar recursi?n, pero los programadores la evitan por el famoso "stack overflow" producto de anidar muchas llamadas recursivas: ese es un problema de la implementaci?n imperativa y no de la recursi?n per s?. As?, el factorial recursivo ser?a sub fac { my $n = shift; return ($n > 0) ? fac($n - 1) : 1; } Queda como ejercicio para el lector escribir una funci?n que retorne una funci?n recursiva. El paradigma Funcional es bastante m?s expresivo que el Imperativo, adem?s que ense?a maneras alternas m?s eficientes y claras de resolver problemas de c?mputo. Soy firme creyente que un programador que nunca ha estado expuesto a la programaci?n Funcional siempre va a estar varios escalafones por debajo de un programador que si lo ha estado. Siento que lenguajes 100% funcionales como Scheme, ML o Haskell son muy "duros" de aprender pero deber?an ser obligatorios en la ense?anza de programaci?n. En la vida real, un lenguaje como Perl que representa una mezcla de ambos paradigmas es una herramienta invaluable. Comparando con otros lenguajes: - C/C++ soporta apuntadores a rutinas, pero no soporta clausuras. Uno podr?a implementar el map, pero es bastante engorroso el manejo de memoria. As? mismo, si quisieran implementar clausuras tendr?an que definir quiz?s una clase gen?rica para el callback y luego especializarla en cada clausura. IMNSHO eso es basura. - Python tiene clausuras d?biles: solamente se acuerda de las variables en su entorno local [9]. - Java no ofrece apuntadores a rutinas, mucho menos clausuras. No existe map y solamente se podr?a simular con un iterador. - PHP es a la programaci?n funcional lo que Basic es a la estructurada. En la segunda nota, voy a explorar la Jerarqu?a de Funciones de Kleene para demostrar que todo aquello que se pueda computar se puede hacer _solamente_ con el cero, el sucesor y dos operadores funcionales. Para eso, programar? de manera 100% funcional (sin ciclos, sin variables globales y sin efectos laterales), cinco funciones primitivas (denominadas primitivas recursivas) y de all? sacaremos suma, resta, multiplicaci?n, factorial, divisi?n, etc. [1] Traten de hacer los ejemplos en otro lenguaje que no sea Perl, por ejemplo Java, Python, PHP e incluso C o C++ y ver?n como desilusionan en su capacidad expresiva. [2] Imperativo, Objetivo y Funcional. Pueden usarse los tres al mismo tiempo o cualesquiera combinaciones de ellos a la vez. El paradigma L?gico Declarativo viene en Perl 6. [3] Pueden argumentar que en lugar de usar @lista en contexto escalar use length, que no necesito crear la lista nueva, etc. Ese no es el punto; el punto es que el lenguaje les obliga a explicar todo. [4] Si, podr?a usarse $_ y no definir $elemento, pero lo hago por claridad solamente. Optim?zenlo todo lo que quieran y ver?n que a?n as? el funcional es superior. [5] Todo fue probado en un PIII 550Mhz con 128Mb de RAM que adem?s est? corriendo SETI@Home ;-) [6] Cuando lo implementen, comparen la velocidad. Nuevamente la programaci?n funcional ser? superior. [7] Se dice que las funciones son "objetos de primer orden". [8] Se denomina "ambiente l?xico" y las funciones an?nimas que "llevan" ambiente consigo son denominadas "clausuras". [9] Eso se llama "shallow binding". Si se tiene { my $x; { my $y; { my $z; sub { ... } } } en Python solamente se puede llevar $x, en Perl se lleva $x, $y y $z. Se puede simular en Python con algunas artima?as. IMNSHO eso tambi?n es basura porque el entorno de ejecuci?n deber?a ser suficientemente inteligente para llevarse todo. -- Ernesto Hern?ndez-Novich - Running Linux 2.4.19 i686 - Unix: Live free or die! Geek by nature, Linux by choice, Debian of course. If you can't apt-get it, it isn't useful or doesn't exist. GPG Key Fingerprint = 438C 49A2 A8C7 E7D7 1500 C507 96D6 A3D6 2F4C 85E3 ------------------------------------------------------------------------ Enviar e-mail a colocando en el cuerpo: "UNSUBSCRIBE caracas-pm-list" para desuscribirse. "INFO caracas-pm-list" para conocer las reglas de etiqueta. ------------------------------------------------------------------------ From emhn at telcel.net.ve Fri Nov 29 07:33:39 2002 From: emhn at telcel.net.ve (Ernesto Hernandez-Novich) Date: Wed Aug 4 23:59:33 2004 Subject: =?ISO-8859-1?Q?Las_virtudes_de_la_programci=F3n_funcional?= In-Reply-To: Message-ID: On Mon, 25 Nov 2002, Ernesto Hernandez-Novich wrote: > Queda como ejercicio para el lector escribir una funci?n que > retorne una funci?n recursiva. Una funci?n que retorna el factorial y otra que retorna el n-?simo n?mero de Fibonacci. #!/usr/bin/perl -w # fac(n) = n * n-1 * n-2 * ... * 2 * 1 sub recfac { my $f; my $h = sub { my $n = shift; return($n > 0 ? $n * $f->($n - 1) : 1); }; $f = $h; return $h; } # fib(n) = fib(n-1) + fib(n-2), fib(0) = fib(1) = 1 sub recfib { my $f; my $h = sub { my $n = shift; return($n > 1 ? $f->($n - 1) + $f->($n - 2) : 1); }; $f = $h; return $h; } my $factorial = recfac(); print $factorial->(6); # 720 my $fibonacci = recfib(); map { print $fibonacci->($_)," " } (0..10); # 1 1 2 3 5 8 13 21 34 55 89 print "\n"; # El truco es crear una clausura con una variable definida por nombre ($f) pero que a?n no tiene valor, se construye la funci?n ($h) usando la variable $f como referencia a una funci?n. Una vez definida la funci?n $h, se hace que $f tenga el mismo valor que $h, i.e. se haga referencia a la misma funci?n. Finalmente se retorna la referencia a la funci?n $h (o $f, ser?a lo mismo). -- Ernesto Hern?ndez-Novich - Running Linux 2.4.19 i686 - Unix: Live free or die! Geek by nature, Linux by choice, Debian of course. If you can't apt-get it, it isn't useful or doesn't exist. GPG Key Fingerprint = 438C 49A2 A8C7 E7D7 1500 C507 96D6 A3D6 2F4C 85E3 ------------------------------------------------------------------------ Enviar e-mail a colocando en el cuerpo: "UNSUBSCRIBE caracas-pm-list" para desuscribirse. "INFO caracas-pm-list" para conocer las reglas de etiqueta. ------------------------------------------------------------------------ From jnatera at net-uno.net Fri Nov 29 13:06:49 2002 From: jnatera at net-uno.net (Juan Natera) Date: Wed Aug 4 23:59:33 2004 Subject: [RFC] ExpireForm-0.01 Message-ID: <3DE7BAC9.4010206@net-uno.net> Hola, Estoy desarrollando un modulo para hacer expirar formularios web, la version inicial la pueden descargar en: http://users.net-uno.net/jnatera/ExpireForm-0.01.tar.gz Se aceptan criticas, comentarios y sugerencias. Saludos y TIA Juan Jose ------------------------------------------------------------------------ Enviar e-mail a colocando en el cuerpo: "UNSUBSCRIBE caracas-pm-list" para desuscribirse. "INFO caracas-pm-list" para conocer las reglas de etiqueta. ------------------------------------------------------------------------