lunes, diciembre 24, 2007

Asombrosa solución del problema

Pues si, no se si alguien lo ha intentado pero la resolución es asombrosa. Parece mentira que la cosa pueda funcionar y seguramente la mayoría dude de que funcione:

Creamos un fichero de texto llamado hanoi.lsp por ejemplo.

(defun hanoi (num-discos torre-origen torre-auxiliar torre-destino)
(when (> num-discos 0)
(hanoi (1- num-discos) torre-origen torre-destino torre-auxiliar)
(format t "~%Mover disco superior de la torre ~D a la torre ~D."
torre-origen torre-destino)
(hanoi (1- num-discos) torre-auxiliar torre-origen torre-destino)
)
T
)

Cargamos la función en el interprete lisp:
> (load 'hanoi)

y ya podemos verlo en funcionamiento:

Para 5 discos, torre 1 de origen, torre 2 auxiliar y torre 3 destino...

> (hanoi 5 1 2 3)



El resultado para (hanoi 4 1 2 3) es el siguiente:

> (hanoi 4 1 2 3)

Mover disco superior de la torre 1 a la torre 2.
Mover disco superior de la torre 1 a la torre 3.
Mover disco superior de la torre 2 a la torre 3.
Mover disco superior de la torre 1 a la torre 2.
Mover disco superior de la torre 3 a la torre 1.
Mover disco superior de la torre 3 a la torre 2.
Mover disco superior de la torre 1 a la torre 2.
Mover disco superior de la torre 1 a la torre 3.
Mover disco superior de la torre 2 a la torre 3.
Mover disco superior de la torre 2 a la torre 1.
Mover disco superior de la torre 3 a la torre 1.
Mover disco superior de la torre 2 a la torre 3.
Mover disco superior de la torre 1 a la torre 2.
Mover disco superior de la torre 1 a la torre 3.
Mover disco superior de la torre 2 a la torre 3.
T



Lo más importante es que para estas soluciones mágicas hay que asegurarse bien de que se pueda solucionar recursivamente. En el caso de HANOI era fácil, ya que es el problema recursivo más clásico de todos.

Saludos.

sábado, diciembre 08, 2007

Continuación de Torres de Hanoi en Lisp...

En el post anterior planteé el problema e hice un planteamiento inicial, pero no llegué a terminarlo. En este post espero llegar a la resolución en pseudocódigo.

Dejé el tema planteando el problema para 4 discos.
Recordemos que el planteamiento de 3 discos se puede resumir así:

  1. Si hay 3 discos en la torre A, conseguir mover los 2 de arriba a la torre B.
  2. Una vez que estén en la torre B, mover el disco grande de la torre A a la C.
  3. Para terminar mover los 2 discos de la torre B a la torre C.
Sabiendo que hay solución RECURSIVA, ¿podemos extrapolar esta solución a N discos?

Sería tanto como decir que si hay 4 discos tenemos que hacer...

  1. Conseguir como podamos mover los 3 discos más pequeños a la torre B.
  2. Mover el disco más grande de la torre A a la C.
  3. Mover los 3 discos más pequeños de la torre B a la C
Ahora nos centramos en el problema de como mover los 3 discos más pequeños de la Torre A y colocarlos perfectamente en la Torre B. Esto antes de escribirlo voy a pensarlo...

  1. Mover el más pequeño a la torre B.
  2. Mover el mediano a la torre C.
  3. Mover el pequeño a la C encima del mediano.
  4. Mover el grande a la B.
  5. Mover el pequeño a la A.
  6. Mover el mediano a la B.
  7. Mover el pequeño a la B.
¡Eh, para, para!
Ya veo la luz...
Resulta que el caso de 3 discos es un caso muy similar al de 4.

En ambos casos se hace lo siquiente:
  1. Miramos cuantos discos hay en la Torre A. Movemos todos menos el más grande a la
    Torre C.
  2. Mover el disco más grande de todos a la torre B.
  3. Mover los otros discos a la torre B.
¡UY! Ahora veo otra cosa. Antes me he centrado en mover los discos de forma ordenada a la torre C, y en cambio en otras ocasiones tengo que mover de forma ordenada a la torre B. Sin embargo el algoritmo curiosamente es el mismo. Voy a simplificar renombrando las torres. En lugar de torre A, B y C, las llamaré torre origen, destino y auxiliar. Esto se debe a que dependiendo del movimiento general que queramos hacer las torres origen, destino y auxiliar varían.

Por lo tanto, para mover una pirámide de discos de ORIGEN a DESTINO debemos hacer lo siguiente:

  1. Si hay N discos, hay que mover N-1 discos de ORIGEN a AUXILIAR.
  2. El disco más grande de todos lo movemos directamente de ORIGEN a DESTINO.
  3. Por último movemos los N-1 discos de AUXILIAR a DESTINO.
Ya vemos que el caso más sencillo va a ser el de 2 discos.
Luego en el de 3 discos hay que mover primero los 2 de arriba como hemos visto y luego mover el que queda.
Con 4, hay que mover los 3 de arriba (como hemos visto en la línea anterior) y por último el más grande.
Así hasta el infinito.

En el post de mañana la solución lisp.
A ver si alguien se anima y cuelga la solución en forma de comentario...

Saludos.

viernes, diciembre 07, 2007

Torres de Hanoi en Lisp

Hola,

La ventaja de que nadie siga tu blog es que puedes cambiar de tema sin que nadie se flipe.

Por eso vuelvo al tema del Lisp. Que yo recuerde la última vez lo deje en la función fibonacci recursiva.

Hoy vamos a resolver el primer problema puramente recursivo. Y ¿eso que significa? Que no se si está matematicamente demostrado o no (creo que SI, pero tampoco importa), pero cualquier problema que se puede resolver de forma recursiva puede resolverse también de forma no recursiva.

Ahora bien, en un problema puramente recursivo lo que ocurre es que la solución revursiva es sencilla de entender y en cambio la no recursiva se vuelve enormemente complicada.

Pensemos en el problema tal y como lo planteé en su día: http://aitoreus.blogspot.com/2006/02/torres-de-hanoi.html

O sea, tenemos 3 torres. Las llamaremos A, B y C.
Hay 64 discos montados en la torre A que hay que llevar a la torre C.
Las reglas son 2:
  1. No se puede mover más de un disco cada vez.
  2. No se puede colocar un disco mayor sobre otro menor.

No lo he dicho, pero los 64 discos que inicialmente están en la torre A se encuentran cumpliendo la regla 2.

Tenemos una pista buenísima y es que el problema es RECURSIVO.

O sea, podemos encontrar la solución pensando en verde.

Ummm,

Como empezaríamos a hacerlo manualmente...

Espera tengo una idea, en temas de recursividad muchas veces hay que simplificar el problema.

Si sólo hubiera un disco en la torre A... Movemos el disco a la torre C y problema terminado.

Si hay 2 discos. Movemos el primero (el más pequeño) a la torre B. Luego el segundo (el más grande que estaba debajo) a la torre C. Por último el pequeño a la torre C y problema terminado.

Si hay 3 discos. ¿Movemos el primero a la torre B?. Bueno, entonces el siguiente disco (el mediano de los 3) va a la torre C. Luego estamos obligados a colocar el pequeño de la torre B en la torre C. Una vez hecho esto podemos sacar el grande y ponerlo en B. Ahora bien, hay que librar la torre C de los 2 discos y eso nos lleva a un montón de movimientos. Era mejor hacer exactamente lo mismo pero empezando a mover a la torre C. Así los 2 discos pequeños se quedan colocaditos en B y la C está libre para el grande.

Bueno, ya sólo queda mover el grande al C, el pequeño al A, el mediano al C y el pequeño al C.

¿Que ocurre en el caso de 4 discos? Ahora si que toca pensar, ya que el número de movimientos puede ser brutal.

Sorpresas en Ruby

Jo, hacía tiempo que no echaba un vistazo a la evolución de este lenguaje y me encuentro con lo siguiente:

Breaking News! (20th November 2007)

Ruby.NET version 0.9 has just been released

Casi no me lo puedo creer. ¿Seguro que Ruby.NET significa Ruby sobre .NET?

Miro mejor en el sitio del proyecto (http://rubydotnet.googlegroups.com/web/Home.htm), y efectivamente es un bombazo, ya que según afirman...

  1. Ruby se ejecuta de forma nativa sobre .NET.
  2. Se pueden hacer desarrollo conjuntos con otros lenguajes .NET.
  3. Puede utilizar toda la funcionalidad ofrecida por la plataforma .NET (incluidas las herramientas de depuración, framework, librerías de clases, etc. etc).
En los próximos días haremos unas pruebas a ver que tal va la cosa.

Saludos.


miércoles, diciembre 05, 2007

8 lenguajes de programación que deberías aprender


Que conste que el título no es mío.

Es un artículo que se encuentra en http://www.tufuncion.com/diferentes-lenguajes-programacion



En el gráfico que muestran llama la atención el bajón que según parece sufre Java y lo que no es tan sorprendente la recuperación de .NET. Digo que no es sorprendente porque digamos que la plataforma de Microsoft llegó tarde y comenzó bajo la sobra del primero.

En cuanto a la parte baja ruby pasa a python por primera vez, aunque no nos engañemos, probablemente se deba al uso en su lugar de nacimiento, esto es Japón.

Saludos.

sábado, diciembre 01, 2007

La venganza de los nerds.

En el año 2002, Paul Graham presentó una ponencia en el foro de investigación ICAD (http://dev.icad.org/).

El término NERD hace referencia a un tipo de persona experto en un tema pero con dificultades en sus relaciones sociales. En la wikipedia tenemos una definición más exacta: http://es.wikipedia.org/wiki/Nerds

La versión traducida al castellano se encuentra en http://kapcoweb.com/p/static/docs/la-venganza-de-los-nerds/la-venganza-de-los-nerds.html
y tiene interés en general para todos a los que nos gusta observar las diferencias entre lenguajes de programación.

Entre otras cosas hace una crítica feroz al "jefe de cabello picudo" que según nos dice "combina milagrosamente dos cualidades que son comunes por sí solas, pero rara vez son vistas juntas: (a) él no sabe nada en absoluto sobre tecnología, y (b) él siempre tiene opiniones imponentes al respecto.

Saludos.

miércoles, noviembre 21, 2007

Lisp, por fin algo útil y potente como la recursividad

¿Que es la recursión, o recursividad?

Podemos decir que es una forma de resolver problemas.

Si analizamos un problema, y descubrimos que se puede resolver por partes en las que se vuelve a plantear el problema original pero de forma más "reducida", entonces podemos resolver por recursión.

Ya veis que es algo dificil de explicar, pero se puede ver muy fácilmente.

El ejemplo típico es la función factorial.

El factorial de 6 se representa: 6! y es igual a 6 x 5 x 4 x3 x 2 x 1. Por lo tanto 6! = 720.

Si nos fijamos un poco podemos ver que el factorial de 5 es igual a 5 x 4 x 3 x 2 x 1.

Por lo tanto el factorial de 6 es igual a 6 x el factorial de 5.
Y el factorial de 5 es igual a 5 x el factorial de 4.
etc. etc.

Por convenio en matemáticas el factorial de 0 es igual a 1.

Vemos que podemos usar la función factorial al intentar calcular el factorial de un número.

Función factorial recursiva:

(defun factorial (n)
(if (zerop n) 1
(* n (factorial (1- n)))
)

)

Explicación de la función:

El factorial de un número n es:
1-Si n = 0 entonces el factorial es 1.
2-Si n no es 0, entonces el factorial es n x factorial(n-1)

Avisos:
La función (1- n) devuelve el número n menos 1.
Existe la función (1+ n) que devuelve el número n más 1.

martes, noviembre 20, 2007

Lisp, carga de programas .lsp

Se que os recomendé un interprete lisp llamado clisp.

Bueno pues hoy si queréis podéis probar este otro, que al menos tiene más logrado el interfaz gráfico para windows. Probarlo y me entenderéis...

Se llama XLisp-Plus y se encuentra en http://almy.us/xlisp.html.

XLisp es un Lisp basado en Scheme (uno de los dialectos de Lisp). Lo que ocurre es que no se distribuye XLisp en formato ejecutable, por lo que he preferido bajarme el XLisp-Plus.

Bueno, bajamos el intérprete y la documentación.

Los descomprimimos y colocamos los ficheros resultantes donde más nos interese tenerlos.

Vamos a la carpeta donde se encuentra el ejecutable xlwin32 y creamos un fichero que llamaremos hola-mundo.lsp. Editamos el fichero y escribimos:

(print ‘”Hola mundo”)

Cerramos y guardamos el fichero.

Ahora entramos en el interprete Lisp haciendo doble clic en xlwin32.exe.

Para cargar y ejecutar un fichero lisp (tiene extensiones .lsp) hacemos lo siguiente:

(load ‘hola-mundo.lsp)

> “Hola mundo”
> “Hola mundo”


La función print muestra en pantalla el string que le hemos pasado como argumento, pero a su vez devuelve un valor que es la evaluación del argumento. Por eso el mismo string aparece dos veces.

Bueno, pues ya estamos preparados para escribir programas LISP.

Saludos a tod@s,

viernes, noviembre 16, 2007

Lisp, quinta lección

Bueno, por fin tenemos elementos suficientes para empezar a utilizar Lisp.

De todas formas, vamos a ver primero las funciones CONDICIONALES.

IF

(if expresion expresion)
Es el típico if...then de otros lenguajes.

En este caso se evalúa el primer argumento. Si el valor es distinto a NIL entonces se evalua el segundo argumento y el valor devuelto es lo que devuelve la función IF.

(if (listp '(a)) 'LISTA)
> LISTA

(if (listp 'a) 'LISTA)
> NIL

(if expresion expresion expresion)
Es el típico if...then...else de otros lenguajes.

Si el valor del primer argumento es T entonces se evalúa el segundo argumento. Si no es así se evalúa el tercer argumento.

(if (listp '(a)) 'LISTA 'NO-LISTA)
> LISTA

(if (listp 'a) 'LISTA 'NO-LISTA)
> NO-LISTA

WHEN

Es igual que la función IF.

(when expresion expresion) es equivalente a (if expresion expresion).

UNLESS

Esta función es un poco dudosa y también prescindible igual que WHEN.

(unless expresion expresion)
Si el valor del primer argumento es falso se devuelve el valor del segundo argumento.
Si el valor del primer argumento es verdadero devuelve NIL.

Es totalmente equivalente a (if expresion NIL expresion).

COND

Parecido a la instrucción SELECT CASE de otros lenguajes.
Es como un montón de if...then...else.

(cond (expresion-test1 expresion-consecuencia1 expresion-consecuencia2...)
(expresion-test1 expresion-consecuencia2 expresion-consecuencia2...)
....
(t expresion-consecuenciaN expresionconsecuenciaN+1........)
)

La última claúsula (la de t) no es obligatoria.
Si no se cumple ninguna expresion-test de cond se devuelve NIL.

miércoles, noviembre 14, 2007

Lisp, 5ª lección

Funciones predicado

Me imagino (no tengo mis apuntes a mano) que se llaman así porque sirven para obtener información de los predicados, o sea, de sus argumentos.

Creo que esta es la última lección que necesitamos antes de empezar a resolver problemas. Bueno, esto no es del todo exacto. Necesitamos también funciones para el control de flujo (for, while y esas cosas).

¿Qué devuelven las funciones predicado?

Devuelven el símbolo t para indicar que se cumple la condición (t de true).

Devuelven el símbolo NIL para indicar que la condición no se cumple. NIL es un símbolo y una lista a la vez: la lista vacía.

NIL = ()

Por eso (rest ‘(a)) devuelve NIL, o sea la lista vacía.

Por cierto, la mayoría de las funciones predicado terminan con la letra P (de predicado).

Función SYMBOLP

Sirve para saber si el valor del argumento es un símbolo o no.

(symbolp NIL)
> t


(symbolp t)
> t


(symbolp (first '(a b)))
> t


Función LISTP

Indica si el valor del argumento es una lista.

(listp ‘(a b c))
> t

(listp NIL)
> t


Función NUMBERP

(numberp 5)
> t

Función ATOM

Los símbolos y números son átomos.

(atom 'a)
> t

(atom 5)
> t

(atom '(a))
> NIL

Función ZEROP

Indica si el valor del argumento es cero o no.

Función MINUSP

Para saber si el valor del argumento es negativo.

Función PLUSP

Para saber si el valor del argumento es positivo.

Función EVENP

Nos dice si es par.

Función ODDP

Para saber si es impar.

Función "<" (menor)

(<>
> NIL

(<>
> t

Función ">" (mayor)

(> 7 6 5)
> t

Función "=" (igualdad)

(= 4 4)
> t

(= 4 4.0)
> t

(= 4 (first '(4 2)))
> t

Función EQ

Similar a la función "igualdad", pero los valores de los argumentos tienen que ser símbolos.

(eq 'a (first '(a b)))
> t

(eq 'a (rest '(a b)))
> ERROR.

Función EQL

Similar a los 2 anteriores, pero los valores de los argumentos tienen que ser números o símbolos.

(eql 'a (first '(a)))
> t

(eql 4 4.0)
> NIL por ser diferentes números (uno entero y otro decimal).

Función EQUAL

Sirve para números, símbolos y listas. En el caso de los números el comportamiento es igual que en la función EQL.

(equal '(a b 4) (list 'a 'b 4.0))
> NIL

Bueno, ya está bien por hoy.
Saludos...

Otra vez salvado por Perl

Si, hoy no voy a continuar con el tema de Lisp.

Resulta que me han solicitado ayuda por un tema:

Un programa comercial genera un fichero de texto como salida, pero luego resulta que el organismo oficial que lo requiere nos solicita un formato diferente.

Para un tema así de conversión de formatos LISP no es el lenguaje más adecuado. Perl en cambio está especializado en estas tareas aburridas y pesadas.

Hay va mi programita Perl. Si alguien tiene dudas de lo que hace que me deje un comentario...


$fichero="origen.txt";
open(FICH, $fichero) or die("Error al abrir el fichero");

open(SALIDA, ">destino.txt");

while(){
($texto, $cae, $fecha, $hora, $matricula, $carburante, $litros) = split(';',$_);
$anyo=substr($texto, 0, 4);
$periodo=substr($texto, 4, 2);
$numeracion=substr($texto, 6, 12);
$litros=$litros*100;
printf SALIDA "A11025131%4s%2GAST%12s%s%-12s%s%s%7d%s\n",$anyo,$periodo,$numeracion,$cae,$matricula,$fecha,$hora,$litros,$carburante;
}

close FICH;
close SALIDA;

lunes, noviembre 12, 2007

Lisp, 4ª lección, segunda parte

Bueno, y ahora hablemos un poco de funciones superfluas.

LET*

Si nos fijamos un poquito nos damos cuenta de que...
Se puede sustituir LET* por un LET dentro de otro LET:

Si hacemos

(let ( (x 100)
)
(let ( (y (+ x 200)))
)
)




Comprobamos que es totalmente equivalente a...



(let* ( (x 100)
(y (+ x 200))
)

)


¡Ja! así que podemos prescindir de LET* tranquilamente.

Hay muchas otras funciones prescindibles.

Por ejemplo la función SECOND.

Second sirve para elegir el segundo elemento de una lista.



(second '(a b c)
> b



En la primera lección de Lisp vimos las funciones FIRST y REST.

Podemos definir una nueva función llamada SEGUNDO.



(defun segundo (lista)
(first (rest lista))
)




(segundo '(a b c))
> b



De esta forma podemos hacer nuestro propio LISP fácilmente.

4ª Lección de Lisp

Bueno, hoy toca intensivo,

Primero 2 funciones que pueden venir bien en cualquier situación:

LENGTH

(length lista)
Devuelve el número de elementos de la lista.

(length ‘(a b c))
> 3

REVERSE

(reverse lista)
Devuelve la misma lista pero invierte el orden.

(reverse ‘(a b c))
> (c b a)


Y dos funciones muy importantes en lisp:

LET

Esta función permite hacer asignación de variables de forma local.
Se ve claro en el siguiente ejemplo.



(setf a ‘HOLA)
(setf b ‘ADIOS)
(let ( (a 6)
(b 9)
)
(+ a b)
)
> 15
a
> HOLA
b
> ADIOS



Esto es, dentro del cuerpo del LET las variables tienen el valor que se ha preasignado, y no tiene que ver con el valor que tenían fuera de ese ámbito.

LET*

Es muy similar a LET, pero con el asterisco la asignación es dinámica. ¿Que significa esto? Veámoslo con otro ejemplo:



(let (
(x 100)
(y (+ x 200))
)
)



Esto devuelve un error si se hace con la función LET. Es porque cuando intenta asignar un valor a “y”, “x” todavía no tiene un valor válido.
En cambio si utilizamos LET*, primero asigna el valor a “x” y luego a “y”, de forma que no se produce un error.

jueves, noviembre 08, 2007

Lisp, 3ª lección

Hoy voy a improvisar un poco porque no tengo los apuntes a mano.

Para empezar a hacer algo con Lisp vamos a necesitar un mínimo de operaciones aritméticas.

Funciones matemáticas básicas en Lisp:

La útil suma:

(+ 7 37 39 2)
Devuelve 85.

La imprescindible resta:
(- 7 37 39 2)
Devuelve -71.

¡Ah!, entonces como habréis imaginado...

La multiplicación:
(* 6 6)
Devuelve 36.

Y
(/ 36 6)
Devuelve 6.

Ahora otras funciones algo más complejas (pero poco):

(mod 36 6)
Devuelve 0 por ser el resto de la división.

Entonces,
(mod 37 6)
Devuelve 1.

La raiz cuadrada:

(sqrt 25)
Devuelve 5.

Y por si alguien tiene dudas:
(sqrt 91)
Devuelve 9.539392.

Ahora unas funciones de lo más prescindibles:
(max 7 37 39 2) devuelve el máximo valor.
Por lo tanto devuelve 39.

(min 7 37 39 2) devuelve el mínimo valor.
Por lo tanto devuelve 2.

(abs -2) devuelve el valor absoluto.
Por lo tanto devuelve 2.

¿Por qué digo que son funciones prescindibles?
Porque son fácilmente implementables en Lisp.

Veamos como...

Para definir funciones en lisp utilizamos la función defun.

Voy a crear una función con el nombre suma que servirá para sumar 2 argumentos.
(defun suma (argumento1 argumento2)
(+ argumento1 argumento2)
)


Cuando terminemos de meter las 3 líneas en el interprete nos devolverá el prompt de Lisp. Ahora la función se encuentra cargada en memoria y podemos utilizarla.

(suma 45 5)
Devolverá 50.

¡Que útil!
Ya podemos ampliar Lisp con nuevas funciones.

sábado, noviembre 03, 2007

Lisp. 2ª lección

FUNCIONES CONSTRUCTORAS: Cons, List, Append

Son las funciones para creación de listas.


CONS

(cons expresion lista)
Devuelve una lista que contiene como primer elemento el valor de la expresión, y los siguientes elementos son los de la lista.

(cons 'a '(b c))
Devuelve (a b c)

LIST

(list expresion expresion ...)
Devuelve una lista que contiene los valores de las expresiones.

(list 5 7 9)
Devuelve (5 7 9)

APPEND

(append lista1 lista2 ...)
Coge los elementos de las listas y los fusiona en una sola lista.

(append '(a) (list 2 (cons 'b '(3)) 4) '(5))
Devuelve (a 2 (b 3) 4 5)

OTRAS FUNCIONES:

LENGTH

(length lista)
Devuelve un número que indica los elementos de la lista.

(lenght '(a b c (d e))
Devuelve 4.

REVERSE

(reverse lista)
Devuelve una lista con los elementos en orden inverso.

(reverse '(a b c d))
Devuelve (d c b a)

Todavía es un poco pronto para empezar con resolución de problemas, pero en los próximos días cuando veamos la definición de funciones, algunas funciones matemáticas y las funciones predicado podremos empezar con las funciones recursivas y toda su potencia.

martes, octubre 30, 2007

Lisp. 1ª lección

Lo primero la descarga del interprete.

Lo mejor es descargar una implementación del ANSI Common Lisp. En el siguiente enlace podremos seleccionar un ejecutable adecuado para nuestro Sistema Operativo:

http://clisp.cons.org/ Seleccionar SourceForge download.

La versión de Clisp que me he bajado es la 2.42. Ocupa algo menos que 8 Mbytes.

Descomprimimos el contenido y ejecutamos clisp.exe

El interprete nos contestará con el prompt:

>

Ahora un poquito de teoría:

Tipos de datos en Lisp:

Átomos: símbolos y números. Por ejemplo: 5, 6, a, persona, aula_5

Listas. Por ejemplo: (5 a 7 5)


Como separadores se permiten el espacio y el tabulador.

Las expresiones Lisp se evalúan en el momento y se devuelven un resultado.

Los números se evalúan a si mismo.

Por lo tanto >5 devuelve 5.

Las funciones son de 2 tipos: primitivas de Lisp o las definidas por el usuario.

La notación siempre es prefijo. Primero se indica la función y luego se pasan los argumentos.

(funcion arg1 arg2)


Evalación en Lisp:

Cuando Lisp detecta un paréntesis izquierdo espera encontrar un nombre de función. Si no es así devuelve un error.

>(+ 500 300)

Devuelve 800.

(* 3 4)

Devuelve 12.

Variables:

Setf es una función primitiva especial.

>(setf simbolo expresion)


Si no hay error se asigna el valor de la expresión al símbolo.

Además Lisp devuelve el valor de la expresión.


>(setf a 5)

Devuelve 5.

Setf permite múltiples parejas:

>(setf var1 8

var2 15

var3 5000)

En un caso así devuelve el último valor: 5000.

Evaluación de variables:

>Var2

Devuelve 15.

Quote es otra función especial.

Consigue que las expresiones no se evalúen.

(quote expresion)

(quote Carlos)

Devuelve Carlos pero sin evaluar.

>(setf var1 (quote a))

Devuelve a.

> var1

Devuelve a.


FUNCIONES DE SELECCIÓN


(first lista)

Devuelve el primer elemento de la lista.


(rest lista)

Devuelve la lista quitándole el primer elemento.


(nth entero lista)

Selecciona el elemento de la lista especificado por el número.

(nth 1 (quote (a (b c) d e )))

Devuelve (b c)

(nthcdr entero lista)

Devuelve la lista que queda al eliminar por delante tantos elementos como indica el entero.

(last lista)

Devuelve la lista que contiene al último elemento.

(butlast lista)

Coge toda la lista menos el último elemento.

En lugar de quote se puede sustituir por el carácter reservado comilla: ‘.

(quote xxxx) equivale a ‘xxxx

Bueno, ya está bien por hoy.
Mañana la segunda lección y el planteamiento del primer problema típico de la asignatura "Inteligencia Artificial".

lunes, octubre 29, 2007

Lisp

Lisp fue el segundo lenguaje de alto nivel creado. El primer compilador lo desarrolló John Backus y fue de Forfran. En 1958 John McCarthy propone Lisp en el ámbito de la inteligencia artificial.

Debido a la antiguedad y las características "especiales" del lenguaje no es de extrañar que lenguajes más modernos recojan algunas características.

Los "arrays asociativos" de Perl por ejemplo recuerdan técnicas muy comunes en Lisp.

Lisp viene de List processing (procesamiento de listas) y es una expresión muy acertada. Todo en lisp son listas. Los programas son listas y los datos son listas.

Esta circunstancia hace que sea muy sencillo hacer un programa lisp cuyo resultado sea un programa generado en tiempo real en base a los datos de entrada. También es sencillo modificar o ampliar el lenguaje. Símplemente hay que crear nuevas listas para ello.

En general se suele decir que Lisp es un lenguaje de programación programable.

Durante la carrera tuve que aprender Lisp y recuerdo que me gustaba mucho. Con este post comienzo un mini curso de Lisp basándome en los apuntes de aquellos años.

miércoles, octubre 24, 2007

Si alguien leyó la nota de ayer puede que se preguntara que es eso de “CGI”.
Perl nació como herramienta para la administración de sistemas. Por ello era interpretado en lugar de compilado.

Al ser interpretado resultó más sencillo también hacerlo multiplataforma. Otra característica innata de Perl fue la rapidez de proceso.

Todo ello hizo que Perl fuera en aquella época el lenguaje perfecto para programación CGI.

CGI (Common Gateway Interface) realmente es un interfaz estándar (común) para la comunicación entre el servidor Web y programas pasarela.

Para entenderlo mejor imaginemos que tenemos un servidor Web Windows con IIS. Elegimos el lenguaje de programación que más nos guste (Perl, Pascal, C++, etc.).

Una persona se conecta al servidor por medio de un navegador. Para ello utiliza el protocolo http.

El estándar CGI determina como va a proporcionar el servidor web a nuestro programa los datos. Básicamente se utiliza la entrada estándar y las variables de entorno.

Por otra parte, nuestro programita CGI se ejecuta y por ejemplo accede a una base de datos.

Puede que quiera devolver esos datos a la persona que se ha conectado al servidor.

Según el estándar CGI, el programa tendrá que utilizar la salida estándar y devolver los datos en formato MIME. O sea, que el programa tendrá que construir la página web que quiere devolver junto con los datos que ha obtenido.

CGI permitió subir un peldaño más en la evolución de Internet.

Lo que antes eran páginas estáticas mayormente informativas se transformaron en páginas dinámicas con unas posibilidades de interacción desconocidas hasta el momento.

martes, octubre 23, 2007

Breve historia de Perl

La visión del creador de Perl:





Aprovecho que mi última entrada fue sobre Perl para hacer una recapitulación de este simpático lenguaje que en unos días cumplirá sus 20 años.

En 1987 un programador/administrador Unisys llamado Larry Wall anuncia la versión 1.0 de Perl. Esto ocurre el 18-12-1987. En un principio el lenguaje se enfoca a la administración de sistemas por lo que tiene que ser a la fuerza interpretado.

Sin embargo será en 1991 cuando junto con la publicación del famoso “libro del dromedario” se difundirá la versión 4 de Perl. Realmente no hay diferencias entre Perl 3 y Perl4. Simplemente a la versión del Perl publicada en el libro se le pasó a llamar Perl4.

Para tener un poco de perspectiva es necesario recordad que es en este año cuando el Finlandés Linus Benedict Torvalds anuncia la primera versión de Linux.

La siguiente versión, y en definitiva, la más utilizada en la actualidad fue Perl 5. Esta versión contenía grandes novedades para un lenguaje como Perl, como la orientación a objetos y los módulos. En cuanto a la fecha hay cierta confusión, ya que aunque la versión 5.0.0.0 se publica el 17-10-1994 otros se han quedado con la versión 5.0.0.1 del 13-03-1995.

El 26-10-1995 se crea CPAN, el repositorio distribuido donde podemos encontrar millones de fuentes, documentación y módulos en Perl.

Yo conocí Perl en 1997 en un Master de especialización en Internet. En aquella época era muy utilizado en el mundo Web como lenguaje de programación para utilizar en CGI-s (Common Gateway Interface o Interfaz común de pasarela).

Para localizar todas las fechas de versiones de Perl: http://search.cpan.org/dist/perl/pod/perlhist.pod










miércoles, mayo 23, 2007

Hola otra vez

Hace tiempo que no escribía, pero bueno, que se le va a hacer...

Hoy en el trabajo he tenido que utilizar el ya veterano lenguaje PERL.
Realmente es como una navaja multiusos que te permite solucionar de una forma bastante rápida los problemas.
Normalmente se utiliza para trabajos demasiado pesados para hacerlos a mano.
Por algo es el lenguaje del camello (es el animal que lo simboliza).

¿Qué había que hacer?
Se trata de una migración de datos de un servidor a otro diferente. Al ser el sistema operativo diferente resulta que hay que exportar cada directorio.
1-Una persona me pasa un fichero con 2.000 directorios.
2-Tengo que ejecutar 3 comandos SAS (SAS es un software muy utilizado en banca, compañías de seguros, estadística, etc.) con cada uno de los 2.000 directorios. Un trabajo pesadísimo si hay que hacerlo a mano.

Los comandos SAS son los siguientes:

Para el primer directorio:
Libname lib1 ‘directorio1’;
Proc cport lib=lib1 file=’C:\lib1.cpo’;
Run;


Para el segundo directorio:
Libname lib2 ‘directorio2’;
Proc cport lib=lib2 file=’C:\lib2.cpo’;
Run;

Y así sucesivamente para los 2.000 directorios.

¿Solución? Programita Perl que me genera automáticamente las 6.000 líneas:


print "Generacion de script SAS\n\n";

open(FICHENTRADA, "<entrada.txt")die "No se puede abrir el fichero de entrada: $!";

open(FICHSALIDA, ">script.sas")die "No se puede abrir el fichero de salida: $!";



$num=0;

while(<FICHENTRADA>){

chomp($_);

s/^\s*(.*\S)\s*$/$1/;

print FICHSALIDA "libname LIB" . $num . " '" . $_ . "';\n";

print FICHSALIDA "proc cport lib=" . "LIB" . $num . " file='\\\\servidor\\d\$\\sas\\LIB" . $num . ".cpo';\n";

print FICHSALIDA "run;\n";

$num ++;

}



close(FICHENTRADA);

close(FICHSALIDA);