Pensando en Python - Universidad de Deusto

Anuncio
Python 121.qxd
17/12/04
01:06
Página 36
MIDDLEWARE
Pensando en Python (y IV):
XML, Java e IA
DIEGO LZ. DE IPIÑA GZ. DE ARTAZA (profesor del
Departamento de Ingeniería del Software de la Facultad
de Ingeniería (ESIDE) de la Universidad de Deusto)
Acabamos esta serie sobre Python
examinando su capacidad para el
procesamiento de XML
e introduciendo Jython, la
implementación Java de Python.
También discutiremos una
implementación más inteligente del
juego de tres en raya que usa un
algoritmo de inteligencia artificial.
Introducción
XML define
datos portables
y Python define
el código fuente
SOLO PROGRAMADORES nº 121
36
En los últimos años el estándar XML (eXtensible
Markup Language) se ha establecido como el principal mecanismo para el intercambio de datos entre
aplicaciones. XML es un metalenguaje que permite
diseñar otros lenguajes. Algunos ejemplos de lenguajes derivados de XML son WML, MathML o
XHTML. Con XML cualquiera puede desarrollar su
propio lenguaje de etiquetas adecuado al tipo de
aplicación que más le convenga. Un documento XML
puede ser fácilmente procesado y sus datos manipulados, por medio de APIs estándar disponibles en lenguajes como Java, C, C++, C# y, por supuesto,
Python. En esencia, XML define datos portables, al
igual que Python o Java definen código portable.
Por otra parte, el lenguaje de programación Java ha
experimentado desde su nacimiento en 1995 un crecimiento extraordinario. En los últimos 10 años Java
no sólo ha ido madurando como lenguaje de programación si no que gracias al ofrecimiento gratuito por
parte de Sun de la JDK (Java Development Kit) su
adopción ha sido espectacular. Como resultado ha
habido numerosísimas contribuciones de código
abierto que facilitan el desarrollo de cualquier problema informático que podamos concebir. Jython, la
implementación Java de Python, fue creado para
poder aprovechar todas estas contribuciones del
mundo Java y tener acceso a su funcionalidad desde
la elegante sintaxis de Python.
En este artículo describiremos cómo programar aplicaciones que usan XML y acceden a paquetes des-
arrollados en Java desde Python. Concluiremos con
una discusión del algoritmo de inteligencia artificial
MiniMax que permitiría a la máquina ser un contrincante de entidad en el juego de tres en raya.
XML
En las próximas líneas vamos a hacer un repaso de la
tecnología XML, aunque este tema ha sido tratado
con profundidad en ediciones anteriores de Sólo
Programadores. Si el lector quiere ampliar lo aquí
expuesto, puede consultar los números 110, 111, 114,
115 y 116.
XML no es sólo un lenguaje de lenguajes, es también
un conjunto de tecnologías que permiten su representación y procesamiento, tales como XPath, XLink,
XPointer o XML. La especificación de XML 1.0 puede
encontrarse en la dirección de Internet
http://www.w3.org/TR/REC-xml. Un documento XML
está estructurado en los siguientes componentes:
z Instrucciones de procesamiento (processing
instructions – PI). Las PI son instrucciones para
el procesador del documento XML. El prólogo
del documento es una PI obligatoria:
<?xml version=”1.0” encoding=”iso-8859-1”?>
z
Declaraciones de tipo de documento.
Especifican la estructura y sintaxis de un documento XML a través de una gramática bien en
la forma de un DTD (Document Type
Definition) o un Schema. Se declaran en un
documento en el siguiente modo:
<!DOCTYPE <elemento-raíz> SYSTEM
“<fichero-con-dtd”>
z
A través del DTD una instancia de un documento puede ser validada contra la gramática
que define los elementos y posición de los mismos en un documento.
Comentarios. Sirven para realizar anotaciones
útiles al autor del documento. Un ejemplo sería:
<!— Esto es un comentario —>
z
Elementos. Aportan estructura semántica al
documento. Se delimitan por etiquetas de
apertura y cierre o vienen sin elementos interiores (etiquetas vacías como <br/>). Las etiquetas de apertura y las vacías suelen venir
Python 121.qxd
17/12/04
01:06
Página 37
MIDDLEWARE
Pensando en Python (y IV): XML, Java e IA
z
acompañadas de atributos, que parametrizan el elemento.
Referencias a entidades. Sirven para
insertar un determinado contenido definido previamente. Por ejemplo:
<!ENTITY Empresa “Universidad de
Deusto”>...<pie>Creado por &Empresa;
</pie>
z
Secciones CDATA. Contienen texto que
no queremos sea procesado por el parser
de XML. Por ejemplo:
<![CDATA[<saludo>Hola, mundo!
</saludo>]]>
Las razones fundamentales del éxito de XML
son:
z Permite etiquetar los datos con su significado (self-describing data).
z Estructurar la información de acuerdo a
las necesidades de la aplicación.
z Tratar automáticamente la información a
través de procesadores libremente disponibles, que se ajustan a APIs estándar.
XML es la base de una nueva generación de
aplicaciones web, siendo un estándar para el
intercambio de datos. Entre otras cosas XML
se utiliza como formato de datos en sitios web
para separar contenido y presentación, como
medio de comunicación para el intercambio
de datos entre organizaciones o para la transmisión de mensajes en servicios web (SOAP).
Procesamiento XML
La popularidad de XML estriba en no tener que
crear un nuevo compilador por cada lenguaje de
representación de datos que queremos diseñar.
Las herramientas que llevan a cabo la validación
léxica y sintáctica de los documentos y su transformación en una representación manejable por
programas vienen ya dados. A cambio, los diseñadores de nuevos tipos de documentos XML
sólo deben seguir las restricciones que un documento XML bien formado debe cumplir:
z Debe tener un prólogo.
z Debe tener una etiqueta (elemento) raíz.
z Las etiquetas se hallan correctamente
anidadas.
z Se usan caracteres válidos y bien aplicados.
z Los valores de los atributos vienen encerrados entre comillas.
La industria utiliza tres estándares principales para el procesamiento de documentos
XML: SAX, DOM y XSL. A continuación, pasamos a describirlos en más detalle.
Procesamiento de XML con SAX
SAX es el estándar “de facto” para el procesamiento de XML basado en eventos. Fue definido
por David Megginson, siendo su especificación
disponible en http://www.megginson.com/SAX.
No es un analizador XML, si no que define una
interfaz común implementada por los analizadores XML SAX. Un analizador SAX genera un
evento por cada componente significativo del
documento detectado. Por ejemplo el comienzo
o final del documento, o el comienzo y final del
elemento. Un programador que usa SAX debe
implementar manejadores de los eventos notificados. SAX opera sobre un documento leyendo
secuencialmente de principio a fin, sin cargar
todo el documento en memoria. Presenta como
principal ventaja una gran eficiencia en cuanto
al tiempo y la memoria empleados en el análisis.
La principal desventaja de SAX es que tenemos
que mantener estado intermedio, a diferencia
de otros estándares como DOM.
Procesamiento de XML con DOM
Un documento XML es para DOM un árbol de
nodos, donde cada nodo representa a un elemento XML. Los elementos hijos y el texto
contenido dentro de un elemento son subnodos. La especificación DOM es un estándar
W3C disponible en http://www.w3.org/DOM/.
La principal ventaja de DOM es que permite
acceder a datos en función de la jerarquía de
elementos, así como modificar el contenido
de los documentos e incluso crearlos desde
cero. La principal desventaja es el coste en
tiempo y memoria que conlleva construir el
árbol. A diferencia de SAX que efectúa una
pasada del documento de izquierda a derecha y de arriba abajo, carga todo el documento XML en memoria. Para documentos
largos la demanda de memoria puede ser
ingente. Como consecuencia de ello, DOM no
es adecuado para efectuar procesamiento de
XML en dispositivos empotrados con poca
capacidad de cómputo y memoria.
Procesamiento de XML con XSL
Dada la diversidad de lenguajes de presentación (WML, HTML, cHTML), existen dos alternativas para desarrollar aplicaciones basadas en páginas:
z Desarrollar versiones de los procesos de
generación de presentación (JSP, ASP,
CGI,..) para cada lenguaje de marcado final.
z Desarrollar sólo una versión que genere
XML y conversores de XML a los lenguajes de presentación.
El lenguaje de programación XSL fue diseñado para responder al segundo enfoque, y el
lector debería ser conocedor de ello, pues
XSL-FO es objeto de nuestro estudio desde
hace unos meses. XSL consta de dos partes:
z XML Stylesheet Transformation Language
(XSLT)
z XSL Formatting Language (XSL-FO)
XSLT es el componente de XSL que transforma un documento XML en otro documento
XML. XSL-FO formatea y estiliza documentos en varios modos. La especificación de
XSLT se encuentra disponible en
http://www.w3.org/TR/xslt. Las principales
ventajas de XSLT son:
z No asume un único formato de salida de
documentos.
z Permite manipular de muy diversas
maneras un documento XML: reordenar
elementos, filtrar, añadir, borrar, etc.
Las desventajas de XSLT son:
z Su utilización es más compleja que un
lenguaje de programación convencional.
z Consume bastante memoria y capacidad
de proceso, usa DOM por debajo.
Programación XML en Python
Para ilustrar el poder de Python en la programación de aplicaciones XML vamos a considerar el fragmento de código XML del listado
1. Este listado corresponde a la cartelera de
todos los cines en una ciudad. Por cada cine
listado, incluimos las películas que se muestran en él y los horarios de sus sesiones.
Procesamiento SAX en Python
El soporte para SAX en Python es ofrecido por
el módulo “xml.sax” de la librería estándar de
Python (http://docs.python.org/lib/modulexml.sax.html). Este módulo define una función
principal, “make_parser()”, a través de la cual
crea y devuelve un objeto de tipo “XMLReader”,
que corresponde con un procesador SAX. La
definición de esta clase se encuentra en el
módulo “xml.sax.xmlreader”. El módulo
“xml.sax.handler” define manejadores de eventos para SAX. Para asignar un manejador de
eventos a un procesador SAX se invoca el
método “setContentHandler(handler)”, donde
“handler” es una referencia a un objeto que
implementa la interfaz “xml.sax.hander.
ContentHandler”. Esta interfaz define métodos
tales como “startdocument” o “endelement”
que son invocados cuándo se genera un even37
SOLO PROGRAMADORES nº 121
Python 121.qxd
17/12/04
01:06
Página 38
MIDDLEWARE
LISTADO 1
peliAttribs.getNamedItem(“codigo”).
Documento Cartelera.xml
<?xml version=”1.0” encoding=”iso-8859-1”?>
<Cartelera>
<Cine codigo=’1’ nombre=’Guridi Zinemak’ direccion=’San Prudencio’
poblacion=’Vitoria-Gasteiz’>
<Pelicula codigo=’1’ titulo=’Malena’
director=’Santiago Segura’
actores=’Bo Derek, Al Pacino, Robert Reford’>
<Sesion>16:00</Sesion>
<Sesion>19:30</Sesion>
<Sesion>22:00</Sesion>
</Pelicula>
<Pelicula codigo=’2’ titulo=’Los Otros’
director=’Alejandro Amenabar’
actores=’Nicole Kidman’>
<Sesion>16:30</Sesion>
<Sesion>19:45</Sesion>
<Sesion>22:30</Sesion>
</Pelicula>
</Cine>
...
</Cartelera>
nodeValue
Permiten recuperar el contenido del atributo cuyo
nombre se pasa al método “getNamedItem”. La
figura 2 muestra el resultado de tal procesamiento. Puede verse este ejemplo en el fichero
“ejemploDOM.py” incluido en el CD-ROM.
Procesamiento XSLT en Python
XSLT es un lenguaje de transformación de documentos XML muy potente y complejo que
requeriría para comprenderlo una serie de artículos por si mismo. Hay varias implementaciones
XSLT en Python. Para una discusión sobre las
alternativas disponibles remitimos al lector a
http://uche.ogbuji.net/tech/akara/nodes/200301-01/python-xslt. Nosotros nos hemos decantado por los bindings Python de las librerías XML
de Gnome libxml2 y libxlt. Versiones Windows de
las mismas pueden encontrarse en http://
users.skynet.be/sbi/libxml-python/. Los rpms de
estas librerías para su instalación en UNIX pueden encontrarse en http://rpmfind.net/
linux/rpm2html/search.php?query=libxml2python y http://rpmfind.net/linux/rpm2html/
search.php?query=libxslt-python, respectivamente. El Listado 4 muestra cómo transformar
un documento XML en un documento HTML por
medio de XSLT (documento “xsltexample.py” disponible en el CD-ROM). Al aplicar a
“Cartelera.xml” la hoja de XSLT, “Cartelera.xsl”,
incluida también en el CD-ROM, se obtiene la
ción correspondiente
a las películas encontradas en el mismo. El
método DOM principal que se utiliza es
“getElementsByTag
Name(<nombre-elemento>)”, que dado el
nombre de un elemento recupera todas
Figura 1. Ejecución del programa “ElementCounterSAX.py”
las instancias del
aplicado a “Cartelera.xml”.
mismo encontradas
to al entrar en una parte significativa del docu- en un árbol DOM. El atributo de un elemento
mento. El listado 2 muestra un ejemplo de DOM attributes se usa para recuperar los atricódigo Python que procesa el fichero butos de cada elemento película. Las senten“Cartelera.xml” para contar el número de ele- cias en la forma:
mentos y atributos XML encontrados en él.
LISTADO 2
Procesamiento del fichero Cartelera.xml usando SAX
Tanto el documento XML (Cartelera.xml) como
el script Python (ElementCounterSAX.py) están
# Uso: python ElementCounterSAX.py Cartelera.xml
disponibles en el CD-ROM que acompaña a la
import sys
from xml.sax import make_parser, handler
revista. La figura 1 muestra el resultado de la
class ElementCounter(handler.ContentHandler):
ejecución de este script.
def __init__(self):
Procesamiento DOM en Python
Python provee en el módulo “xml.dom.minidom” una implementación sencilla de DOM. La
función “parse” de este modulo genera a partir
de un fichero un objeto DOM. Este objeto tiene
todos los métodos y atributos característicos del
estándar DOM. Por ejemplo: “hasChildNodes()”,
“childNodes” o “getElementsByTagName()”.
Para más información sobre procesamiento
DOM XML en Python se recomienda visitar la
página http://pyxml.sourceforge.net/topics/. La
distribución PyXML, no incluida por defecto en
la distribución estándar de Python, implementa un procesador de DOM más completo.
El listado 3 usa el estándar DOM para procesar
el fichero “Cartelera.xml” y obtener la informa-
SOLO PROGRAMADORES nº 121
38
self._elems = 0
self._attrs = 0
self._elem_types = {}
self._attr_types = {}
def startElement(self, name, attrs):
self._elems = self._elems + 1
self._attrs = self._attrs + len(attrs)
self._elem_types[name] = self._elem_types.get(name, 0) + 1
for name in attrs.keys():
self._attr_types[name] = self._attr_types.get(name, 0) + 1
def endDocument(self):
print “There were”, self._elems, “elements.”
print “There were”, self._attrs, “attributes.”
print “—-ELEMENT TYPES”
for pair in self._elem_types.items():
print “%20s %d” % pair
print “—-ATTRIBUTE TYPES”
for pair in self._attr_types.items():
print “%20s %d” % pair
parser = make_parser()
parser.setContentHandler(ElementCounter())
parser.parse(sys.argv[1])
Python 121.qxd
17/12/04
01:06
Página 39
MIDDLEWARE
Pensando en Python (y IV): XML, Java e IA
LISTADO 3
Procesamiento del fichero Cartelera.xml usando DOM
#!/usr/bin/env python
# -*- coding: iso-8859-1 -*import xml.dom.minidom, sys
class Pelicula:
def __init__(self, codigo, titulo, director, actores):
self.codigo = codigo
self.titulo = titulo
self.director = director
self.actores = actores
def __repr__(self):
return “Codigo: “ + str(self.codigo) + “ - titulo: “ + self.titulo +
“ - director: “ + self.director + “ - actores: “ + self.actores
class PeliculaDOMParser:
def __init__(self, filename):
self.dom = xml.dom.minidom.parse(filename)
self.peliculas = []
def getPeliculas(self):
# Si no hemos ya recuperado las películas en el documento ...
if not self.peliculas:
peliculaNodes = self.dom.getElementsByTagName(“Pelicula”)
numPelis = len(peliculaNodes)
for i in range(numPelis):
pelicula = peliculaNodes.item(i)
# Recuperar los attributes de cada nodo Pelicula
peliAttribs = pelicula.attributes
codigo = peliAttribs.getNamedItem(“codigo”).nodeValue
titulo = peliAttribs.getNamedItem(“titulo”).nodeValue
director = peliAttribs.getNamedItem(“director”).nodeValue
actores = peliAttribs.getNamedItem(“actores”).nodeValue
# crear un nuevo objeto de tipo película
self.peliculas.append(Pelicula(codigo,titulo,director,actores))
return self.peliculas
if __name__ == ‘__main__’:
domParser = PeliculaDOMParser(sys.argv[1])
for peli in domParser.getPeliculas():
print peli
LISTADO 4
Procesamiento del fichero Cartelera. XSLT
# Ejecuta: python xsltexample.py Cartelera.xml Cartelera.xsl Cartelera.html
import libxslt
import sys
if len(sys.argv) != 4:
print ‘Usage: python xsltexample <xml-file> <xslt-file> <output-file>’
sys.exit(0)
else:
styledoc = libxml2.parseFile(sys.argv[2])
style = libxslt.parseStylesheetDoc(styledoc)
doc = libxml2.parseFile(sys.argv[1])
result = style.applyStylesheet(doc, None)
style.saveResultToFilename(sys.argv[3], result, 0)
style.freeStylesheet()
doc.freeDoc()
result.freeDoc()
página web de la figura 3. El listado 5 contiene
sólo un pequeño fragmento de la hoja de estilo
“Cartelera.xsl”.
Una hoja XSLT es un documento XML bien formado que tiene como elemento raíz el elemento
“xsl:stylesheet”. Este elemento contiene un conjunto de “xsl:templates”, o plantillas de transformación que asocian un fragmento del documento XML de entrada con otro del documento
XML de salida. En la especificación de XSLT
http://www.w3.org/TR/xslt, puede encontrarse
una minuciosa descripción de este lenguaje.
integra de manera transparente con la plataforma Java. Jython complementa a Java y es especialmente indicada para las siguientes tareas:
z Empotrar scripts en aplicaciones Java, de
modo que los usuarios finales puedan escribir scripts que añadan funcionalidad a la
aplicación.
z Experimentación interactiva por medio del
intérprete interactivo suministrado por
Jython que permite interactuar con los
paquetes Java o aplicaciones en ejecución.
z Desarrollo rápido de aplicaciones. Los programas en Python son típicamente entre 2
y 10 veces más cortos que los programas
Java. Esto se traduce en una mayor productividad en la programación. La integración
transparente de Jython y Java permite
combinar estos dos lenguajes en productos.
Existen otros lenguajes de scripting que se
integran transparentemente con Java como
Jacl, implementación en Java de Tcl, Rhino,
implementación en Java de JavasScript o
BeanShell. Sin embargo Jython se distingue de
otras alternativas por:
z Compilación dinámica de Jython en bytecodes de Java (conduce al mayor rendimiento posible).
z Habilidad para extender las clases Java en
Jython.
z Compilación estática opcional, que permite la creación de applets, servlets y beans.
Lo hace a través de la herramienta jythonc
que transforma ficheros .PY en .CLASS.
z Uso del lenguaje Python que combina
poder con simplicidad sintáctica, soportando el modelo orientado a objetos de Java.
Las herramientas de compilación/ejecución de
Jython son disponibles en http://www.
jython.org/download.html. Para instalarlo simplemente hay que ejecutar el comando java jython21, que lanzará el instalador de Jython. Como
prerrequisito, la máquina donde se instale debe
tener preinstalada una máquina virtual de Java. Si
no se dispone de ella, puede ser obtenida desde
http://java.sun.com/j2se/1.4.2/download.html.
Programación en Jython
Jython básico
Jython (http://www.jython.org) es una implementación Open Source en Java de Python que se
La figura 4 muestra una sesión interactiva de
Jython. Para ejecutar el intérprete de Jython
Figura 2. Ejecución del programa “EjemploDOM.py” aplicado a “Cartelera.xml”.
39
SOLO PROGRAMADORES nº 121
Python 121.qxd
17/12/04
01:06
Página 40
MIDDLEWARE
simplemente hay que escribir en la línea de
comando “jython”. El ejemplo muestra cómo
el usuario puede crear una instancia de la
clase Java “java.util.Random” para obtener
una serie de números aleatorios. Este ejemplo ha sido “prestado” de http://www.jython.org/docs/usejava. html. Como observamos en la figura, para crear una instancia de
una clase Java se usa la convención de
Python, es decir una función con el nombre
de la clase no precedida del operador “new”.
En la invocación de métodos de clases Java
desde Jython hay que tener en cuenta la
conversión de los tipos de datos de Python a
Java, y cuando se reciben resultados lo
opuesto, es decir, la conversión de tipos de
datos Java en Python. Dado que Python y
Java definen casi los mismos tipos de datos
primitivos, apenas hay nunca problemas
durante esta tarea de coerción (conversión
de tipos de datos).
3.- Tres en Raya modo
gráfico en Jython
con acceso a base
de datos MySQL.
Tres en Raya modo
texto en Jython
Nuestra implementación
original de Tres en Raya en
modo texto, a la que llamamos “tresenraya.py”, requiere mínimos cambios
para ser ejecutable en
Jython. Tan solo debemos
añadir dos variables globales “True” y “False” con los
valores 1 y 0, respectiva3. Página web resultante de aplicar “Cartelera.xsl” a
mente. Esto es debido a que Figura
“Cartelera.xml”.
Jython no soporta todavía
el tipo booleano de Python. Hemos incluido la ver- listado 6 muestra el constructor de la clase
sión modificada de este script en el CD-ROM bajo “JuegoTresEnRayaJythonGUI”, incluida en el CDel nombre “tresenrayajython.py”. Las dos únicas ROM bajo el nombre “tresenrayajythongui.py”,
Transformando la aplicación Tres
líneas que difieren del script original presentado en junto con la implementación del método
la primera entrega de este curso son:
“onClick”. La lógica principal de esta aplicación
en Raya en una aplicación Jython
está gobernada por el método “jugar” no incluido
True = 1
Qué mejor manera de evaluar el potencial de
por falta de espacio pero disponible también en el
False = 0
Jython que retomar nuestra conocida aplicación
CD-ROM. La clase “JuegoTresEnRayaJythonGUI”
del Tres en Raya y aplicar los cambios requeridos La ejecución de este script Jython se presen- hereda de “tresenrayajython.JuegoTresEnRaya”,
para poderla ejecutar en Jython. Vamos a dividir ta en la figura 5.
que es la clase base del juego. En el constructor de
esta tarea de transformación en tres fases:
Tres en Raya modo gráfico en Jython
esta clase: (1) se obtiene una instancia a la imple1.- Tres en Raya modo texto en Jython.
El siguiente paso en la transformación del juego mentación de la clase del registro de jugadores
2.- Tres en Raya modo gráfico en Jython Tres en Raya en Jython es crear la interfaz gráfica que guarda datos en un mapa persistente, (2) se
con acceso a mapa persistente.
del mismo usando el paquete Swing de Java. El invoca al constructor de la clase base y (3) luego
simplemente se crea un “Frame” donde se coloLISTADO 5
Fragmento hoja de estilo Cartelera.xsl
can los botones correspondientes a las casillas del
tablero de tres en raya.
<?xml version=”1.0” encoding=”iso-8859-1”?>
Obsérvese lo sencillo que es en Jython asig<xsl:stylesheet xmlns:xsl=http://www.w3.org/1999/XSL/Transform version=”1.0”>
<xsl:output method=”html”/>
nar un manejador de eventos a un botón:
<xsl:template match=”/”>
<html>
<head>
<style type=”text/css”>
table {font-family: arial, ‘sans serif’; margin-left: 15pt;}
th,td {font-size: 80%;}
th {background-color:#FAEBD7}
</style>
</head>
<body>
<table border=”1”>
<xsl:apply-templates/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match=”Cartelera”>
<xsl:for-each select=”Cine”>
<tr>
<th><xsl:text>Cine</xsl:text></th>
<th><xsl:text>Dirección</xsl:text></th>
<th><xsl:text>Población</xsl:text></th>
<th></th>
</tr>
...
</xsl:stylesheet>
SOLO PROGRAMADORES nº 121
40
button = swing.JButton(self.icono
Vacio, actionPerformed=self.onClick)
Si hubiéramos efectuado una traducción
literal del código Java a Jython hubiéramos
necesitado el siguiente código:
class action(awt.event.ActionListener):
def onClick(self,event):
# la implementación del manejador
button = swing.JButton(self.iconoVacio)
button.addActionListener(action())
Tres en Raya modo gráfico con acceso a
BD en Jython
El tercer y último ejercicio de transformación que
hemos realizado ha sido convertir la clase
“RegistroJugadoresDB” que vimos en la tercera
entrega a Jython. En esta ocasión no usamos la
Python 121.qxd
17/12/04
01:06
Página 41
MIDDLEWARE
Pensado en Python (y IV): XML, Java e IA
Figura 4. Sesión interactiva de Jython.
DB API de Python, si no que JDBC para acceder a
la base de datos que creamos en la tercera entrega. El script SQL de creación de la base de datos
“tresenraya” ha sido incluido en el CD-ROM
junto con las instrucciones de cómo ejecutar este
script en MySQL. El listado 7 muestra un fragmento de la clase transformada a la que hemos
denominado “RegistroJugadores” “DBJython”.
Como las otras variedades de registros de jugadores que hemos implementado deriva de la
clase base “RegistroJugadores”. Si se desconoce la
manera de interactuar con JDBC se recomienda
al lector que repase los contenidos de los números 105, 106 y 107 de Sólo Programadores y/o
que visite la página http://java.sun.com/products/jdbc/. En pocas palabras, en el constructor
de esta clase se carga el driver JDBC para MySQL
(disponible en http://www.mysql.com/products/
connector/j/) en memoria, se obtiene una conexión a la base de datos “tresenraya” y luego se
utiliza esa conexión para ejecutar comandos
LISTADO 6
(Statements) SQL. Las figuras 6 y 7 corresponden a
pantallazos de la versión
Jython del juego Tres en
Raya en modo gráfico.
IronPython: Python
para .NET
En Marzo del 2004 en la
conferencia PyCon fue pre- Figura 5. Sesión interactiva de “tresenrayajython.py”.
sentado IronPython (http://
ironpython.com/), una implementación Python z Dinámico, soporta el modo de ejecución
orientada a las plataformas .NET y Mono. Las
interactivo como Python.
características principales de IronPython son:
z Opcionalmente estático, se pueden comz Tan rápida como la versión estándar de
pilar ficheros Python para producir ejecuPython.
tables (.EXE) que pueden ejecutarse direcz Integrada de modo transparente con la
tamente o incluso DLLs.
Common Language Runtime de .NET. Desde z Soporta código gestionado (managed code).
IronPython podemos usar las librerías de z No finalizado, todavía en versión alfa no
clases .NET y extender otras clases .NET.
puede ser utilizada para producción de
software.
Clase JuegoTresEnRayaJythonGUI
En definitiva, IronPython parece una contribución prometedora que nos permitirá programar aplicaciones .NET desde la cómoda y
sencilla sintaxis de Python. Manténgase
JOptionPane
atento a los progresos en este proyecto.
import java
from java import awt
from javax import swing
from javax.swing import
...
class JuegoTresEnRayaJythonGUI(tresenrayajython.JuegoTresEnRaya):
def __init__(self):
registro = tresenrayajython.RegistroJugadoresPersistente()
tresenrayajython.JuegoTresEnRaya.__init__(self, registro, ‘solop’, ‘solop’)
self.frame = swing.JFrame(‘Juego Tres en Raya’,
visible=1, windowClosing=self.exit)
gridSizer = awt.GridLayout(3,3)
self.frame.getContentPane().setLayout(gridSizer)
self.iconoMaquina = swing.ImageIcon(‘Images\\x.PNG’)
self.iconoUsuario = swing.ImageIcon(‘Images\\o.PNG’)
self.iconoVacio = swing.ImageIcon(‘Images\\blank.PNG’)
self.buttons = []
for i in range(9):
button = swing.JButton(self.iconoVacio, actionPerformed=self.onClick)
button.setBackground(awt.Color.WHITE)
self.frame.contentPane.add(button)
self.buttons.append(button)
self.frame.pack()
...
def onClick(self, event):
indexButtonClicked = self.__get_index_button_clicked(event.getSource())
if self.buttons[indexButtonClicked].getIcon() == self.iconoVacio:
if not self.__jugar(indexButtonClicked):
self.resetGame()
self.__resetTablero()
...
El algoritmo MiniMax
No quería concluir esta serie sobre Python, en la
que el juego Tres en Raya nos ha ayudado a
entender tantos conceptos, sin proponer un algoritmo más sofisticado referente a la elección de
casillas por la máquina. En definitiva, muchos de
vosotros os habréis preguntado cómo hacer que
la máquina piense y elija la mejor casilla en cada
momento. Este razonamiento es posible a través
del algoritmo de inteligencia artificial, MiniMax.
Figura 6. Jugando al Tres en Raya con la
versión Jython.
41
SOLO PROGRAMADORES nº 121
Python 121.qxd
17/12/04
01:06
Página 42
MIDDLEWARE
LISTADO 7
Clase RegistroJugadoresDBJython.py
from java import lang
from java import sql
import tresenrayajython, string, sys
class RegistroJugadoresDBJython(tresenrayajython.RegistroJugadores):
def __init__(self):
tresenrayajython.RegistroJugadores.__init__(self)
try:
lang.Class.forName(“org.gjt.mm.mysql.Driver”)
except Exception, e:
print “Se ha producido un error en la carga del driver”, e
try:
url = “jdbc:mysql://localhost:3306/tresenraya”
self.con = sql.DriverManager.getConnection(url,”tresenraya”,”tresenraya”)
except Exception, e:
print “Se ha producido un error en el establecimiento de la
conexion con la base de datos”, e
# Asegurarse que si no existe un usuario solop se añada
usuarioSolop = self._executeSQLCommand(“select * from usuario where
nombreUsuario=’solop’”)
...
def _executeSQLCommand(self, command):
# si la consulta devuelve resultados lo hará como una lista de tuplas,
# cada tupla es una fila de la tabla, y cada elemento de la tupla una columna
resultado = []
command = string.strip(command)
if len(command):
try:
stmt = self.con.createStatement()
if string.lower(command).startswith(‘select’): # si es una select ...
rs = stmt.executeQuery(command)
metadata = rs.getMetaData()
# recupera todas las filas
while rs.next():
# recupera todas las columnas
contenidoFila = []
for i in range(1, metadata.getColumnCount()+1):
try:
columnType = metadata.getColumnType(i)
contenidoColumna = None
if columnType == sql.Types.INTEGER:
contenidoColumna = rs.getInt(i)
elif columnType == sql.Types.VARCHAR:
contenidoColumna = rs.getString(i)
else:
contenidoColumna = rs.getObject(i)
contenidoFila.append(contenidoColumna)
except Exception, e:
print e
break
resultado.append(tuple(contenidoFila))
else:
stmt.execute(command)
except:
raise u’Excepcion producida en ejecucion de comando’ + command
return resultado
Antes de explicar el algoritmo es necesaria
una breve introducción a árboles de búsqueda. Un árbol de búsqueda es una notación
para representar búsquedas. La figura 8
representa uno de estos árboles. Los cuadrados son conocidos como nodos y representan puntos de decisión en la búsqueda. Los
nodos están conectados por ramas. La búsqueda comienza en el nodo raíz, en la parte
superior de la figura. En cada punto de decisión, los nodos para cada posible camino de
búsqueda son generados, hasta que más
SOLO PROGRAMADORES nº 121
42
decisiones no sean posibles. Los nodos al
final de la búsqueda son los nodos raíz.
El algoritmo MiniMax considera la existencia
de dos jugadores, el MAX y el MIN. Un árbol
de búsqueda es generado, depth-first,
comenzando con la posición actual del juego
hasta la posición final del juego. Luego, la
posición final es evaluada desde el punto de
vista de MAX. A continuación, los nodos interiores del árbol son rellenados de abajo a arriba con los valores evaluados. Los nodos que
corresponden al jugador MAX reciben el
Figura 7. Versión Jython de la pantalla de
estadísticas.
máximo valor de sus hijos. Los nodos del
jugador MIN el mínimo de sus hijos. Los valores representan cómo de bueno es el movimiento. El jugador MAX tratará de elegir el
movimiento que maximice el valor al final.
Sin embargo, el jugador MIN, mientras tanto,
seleccionará los movimientos que son mejores para él, minimizando en todo lo posible el
resultado de MAX.
Para más detalles sobre este algoritmo y otros
basados en IA recomendamos permanecer atentos a las novedades que incluirá Sólo
Programadores en los próximos meses. En el CDROM ofrecemos una implementación de la aplicación de Tres en Raya que hace uso de MiniMax.
Conclusiones
Aquí acabamos nuestro recorrido por el lenguaje de programación Python. Confío que a
lo largo de estas cuatro entregas el lector haya
podido descubrir el potencial de este gran lenguaje de scripting que combina a la vez potencia y sencillez. La aplicación de tres en raya
que hemos ido desarrollando tan solo ha mostrado cómo implementar en Python la funcionalidad correspondiente a las tareas programáticas más típicas: programación gráfica, de
acceso a bases de datos, web y XML. Por falta
de tiempo no hemos podido descubrir otras
muchas áreas en las que Python también
juega un papel destacado. Espero que, tras lo
descrito en esta serie, el “poder de Python” os
acompañe en vuestras próximas tareas de
programación.
Figura 8. Ejemplo de árbol de búsqueda.
Descargar