Next Up Previous Hi Index

Chapter 5

Funciones productivas

5.1 Valores de retorno

Algunas de las funciones internas que hemos usado, como las funciones math o funciones matem�ticas, han producido resultados. Llamar a la funci�n genera un nuevo valor, que normalmente asignamos a una variable pasa usar como parte de una expresi�n.

    import math

    e = math.exp(1.0)
    altura = radio * math.sin(angulo)

Pero hasta ahora, ninguna de las funciones que hemos escrito ha devuelto un valor.

En este cap�tulo escribiremos funciones que devuelvan valores, que llamaremos funciones productivas, a falta de un nombre mejor. El primer ejemplo es \texttt{area, que devuelve el �rea de un c�rculo con un radio dado: \begin{verbatim} import math def area(radio): temporal = math.pi * radio**2 return temporal \end{verbatim} Ya hemos visto antes la sentencia \texttt{return}, pero en una funci�n productiva la sentencia \texttt{return} incluye un valor de retorno. Esta sentencia quiere decir {}``retorna inmediatamente de la funci�n y usa la siguiente expresi�n como valor de retorno''. La expresi�n dada puede ser arbitrariamente complicada; as� pues, podr�amos haber escrito esta funci�n m�s concisamente: \begin{verbatim} def area(radio): return math.pi * radio**2 \end{verbatim} Por otra parte, las \textbf{variables temporales} como \texttt{temporal} suelen hacer m�s f�cil el depurado. \index{variables temporales} \index{temporales!variables} A veces es �til disponer de varias sentencias de retorno, una en cada rama de una condici�n: \begin{verbatim} def valorAbsoluto(x): if x < 0: return -x else: return x \end{verbatim} Puesto que estas sentencias \texttt{return} est�n en una condici�n alternativa, s�lo se ejecutar� una de ellas. En cuanto se ejecuta una de ellas, la funci�n termina sin ejecutar ninguna de las sentencias siguientes. El c�digo que aparece despu�s de una sentencia \texttt{return} o en cualquier otro lugar donde el flujo de ejecuci�n no pueda llegar, recibe el nombre de \textbf{c�digo muerto}. \index{c�digo muerto} En una funci�n productiva es una buena idea asegurarse de que cualquier posible recorrido del programa alcanza una sentencia \texttt{return}. Por ejemplo: \begin{verbatim} def valorAbsoluto(x): if x < 0: return -x elif x > 0: return x \end{verbatim} Este programa no es correcto porque si resulta que \texttt{x} vale 0, entonces no se cumple ninguna de ambas condiciones y la funci�n termina sin alcanzar la setencia \texttt{return}. En este caso, el valor de retorno es un valor especial llamado \textbf{None}: \index{None} \begin{verbatim} >>> print valorAbsoluto(0) None \end{verbatim} \begin{quote} \emph{Como actividad, escriba una funci�n} \texttt{\emph{comparar}} \emph{que devuelva} \texttt{\emph{1}} \emph{si} \texttt{\emph{x > y}}\emph{,} \texttt{\emph{0}} \emph{si} \texttt{\emph{x == y}}\emph{, y} \texttt{\emph{-1}} \emph{si} \texttt{\emph{x < y}}\emph{.} \end{quote} \section{Desarrollo de programas} \label{program development} \index{andamiaje} Llegados a este punto, tendr�a que poder mirar a funciones Python completas y adivinar qu� hacen. Tambi�n, si ha hecho los ejercicios, habr� escrito algunas funcioncillas. Tal como vaya escribiendo funciones mayores puede empezar a experimentar m�s dificultades, especialmente con los errores en tiempo de ejecuci�n y los sem�nticos. Para lidiar con programas de complejidad creciente, vamos a sugerirle una t�cnica que llamaremos \textbf{desarrollo incremental}. El objetivo del desarrollo incremental es sustituir largas sesiones de depuraci�n por la adici�n y prueba de peque�as porciones de c�digo en cada vez. \index{desarrollo incremental} \index{m�todos de desarrollo!incremental} Por ejemplo, supongamos que desea encontrar la distancia entre dos puntos, dados por las coordenadas \( (x_{1},y_{1}) \) y \( (x_{2},y_{2}) \). Por el teorema de Pit�goras, podemos escribir la distancia es: \begin{equation} distancia=\sqrt{(x_{2}-x_{1})^{2}+(y_{2}-y_{1})^{2}} \end{equation} El primer paso es considerar qu� aspecto tendr�a una funci�n \texttt{distancia} en Python. En otras palabras, �cu�les son las entradas (par�metros) y cu�l es la salida (valor de retorno)? En este caso, los dos puntos son los par�metros, que podemos representar usando cuatro par�metros. El valor de retorno es la distancia, que es un valor en coma flotante. Ya podemos escribir un bosquejo de la funci�n: \begin{verbatim} def distancia(x1, y1, x2, y2): return 0.0 \end{verbatim} Obviamente, la funci�n no calcula distancias; siempre devuelve cero. Pero es sint�cticamente correcta y se ejecutar�, lo que significa que podemos probarla antes de complicarla m�s. Para comprobar la nueva funci�n, tenemos que llamarla con valores de ejemplo: \begin{verbatim} >>> def distancia(x1, y1, x2, y2): ... return 0.0 ... >>> distancia(1, 2, 4, 6) 0.0 >>> \end{verbatim} Elegimos estos valores de tal forma que la distancia horizontal sea igual a 3 y la distancia vertical sea igual a 4; de esa manera el resultado es 5 (la hipotenusa del tri�ngulo 3-4-5). Cuando se comprueba una funci�n, es �til saber la respuesta correcta. Hasta el momento, hemos comprobado que la funci�n es sint�cticamente correcta, as� que podemos empezar a a�adir l�neas de c�digo. Despu�s de cada cambio incremental, comprobamos de nuevo la funci�n. Si en un momento dado aparece un error, sabremos d�nde est� exactamente: en la �ltima l�nea que hayamos a�adido. El siguiente paso en el c�lculo es encontrar las diferencias entre \( x_{2}-x_{1} \) y \( y_{2}-y_{1} \). Almacenaremos dichos valores en variables temporales llamadas \texttt{dx} y \texttt{dy} y las imprimiremos. \begin{verbatim} def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 print "dx es", dx print "dy es", dy return 0.0 \end{verbatim} Si la funci�n funciona, valga la redundancia, las salidas deber�an ser 3 y 4. Si es as�, sabemos que la funci�n recibe correctamente los par�metros y realiza correctamente el primer c�lculo. Si no, s�lo hay unas pocas l�neas que revisar. Ahora calculamos la suma de los cuadarados de \texttt{dx} y \texttt{dy}: \begin{verbatim} def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dalcuadrado = dx**2 + dy**2 print "dalcuadrado es: ", dalcuadrado return 0.0 \end{verbatim} F�jese en que hemos eliminado las sentencias \texttt{print} que escribimos en el paso anterior. Este c�digo se llama \textbf{andamiaje} porque es �til para construir el programa pero no es parte del producto final. De nuevo querremos ejecutar el programa en este estado y comprobar la salida (que deber�a dar 25). Por �ltimo, si hemos importado el m�dulo math, podemos usar la funci�n \texttt{sqrt} para calcular y devolver el resultado: \begin{verbatim} def distancia(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 dalcuadrado = dx**2 + dy**2 resultado = math.sqrt(dalcuadrado) return resultado \end{verbatim} Si esto funciona correctamente, ha terminado. Si no, podr�a ser que quisiera usted imprimir el valor de \texttt{resultado} antes de la sentencia return. Al principio, deber�a a�adir solamente una o dos l�neas de c�digo cada vez. Conforme vaya ganando experiencia, puede que se encuentre escribiendo y depurando trozos mayores. Sin embargo, el proceso de desarrollo incremental puede ahorrarle mucho tiempo de depurado. Los aspectos fundamentales del proceso son: \begin{enumerate} \item Comience con un programa que funcione y h�gale peque�os cambios incrementales. Si hay un error, sabr� exactamente d�nde est�. \item Use variables temporales para mantener valores intermedios, de tal manera que pueda mostrarlos por pantalla y comprobarlos. \item Una vez que el programa est� funcionando, tal vez prefiera eliminar parte del andamiaje o aglutinar m�ltiples sentencias en expresiones compuestas, pero s�lo si eso no hace que el programa sea dif�cil de leer. \end{enumerate} \begin{quote} \emph{Como actividad, utilice el desarrollo incremental para escribir una funci�n de nombre} \texttt{\emph{hipotenusa}} \emph{que devuelva la longitud de la hipotenusa de un tri�ngulo rect�ngulo, dando como par�metros los dos catetos. Registre cada estado del desarrollo incremental seg�n vaya avanzando.} \end{quote} \section{Composici�n} \index{composici�n} \index{funciones!composici�n} Como seguramente a estas alturas ya supondr�, se puede llamar a una funci�n desde dentro de otra. Esta habilidad se llama \texttt{\emph{composici�n}}. Como ejemplo, escribiremos una funci�n que tome dos puntos, el centro del c�rculo y un punto del per�metro, y calcule el �rea del c�rculo. Supongamos que el punto central est� almacenado en las variables \texttt{xc} e \texttt{yc}, y que el punto del per�metro lo est� en \texttt{xp} e \texttt{yp}. El primer paso es hallar el radio del c�rculo, que es la distancia entre los dos puntos. Afortunadamente hay una funci�n, \texttt{distancia}, que realiza esta tarea: \begin{verbatim} radio = distancia(xc, yc, xp, yp) \end{verbatim} El segundo paso es encontrar el �rea de un c�rculo con ese radio y devolverla: \begin{verbatim} resultado = area(radio) return resultado \end{verbatim} Envolviendo todo esto en una funci�n, obtenemos: \begin{verbatim} def area2(xc, yc, xp, yp): radio = distancia(xc, yc, xp, yp) resultado = area(radio) return resultado \end{verbatim} Hemos llamado a esta funci�n \texttt{area2} para distinguirla de la funci�n \texttt{area} definida anteriormente. S�lo puede haber una �nica funci�n con un determinado nombre dentro de un m�dulo. Las variables temporales \texttt{radio} y \texttt{area} son �tiles para el desarrollo y el depurado, pero una vez que el programa est� funcionando, podemos hacerlo m�s conciso integrando las llamadas a las funciones en una sola l�nea: \begin{verbatim} def area2(xc, yc, xp, yp): return area(distancia(xc, yc, xp, yp)) \end{verbatim} \begin{quote} \emph{Como actividad, escriba una funci�n} \texttt{\emph{pendiente(x1, y1, x2, y2)}} \emph{que devuelva la pendiente de la l�nea que atraviesa los puntos (x1, y1) y (x2, y2). Luego use esta funci�n en una funci�n que se llame} \texttt{\emph{intercepta(x1, y1, x2, y2)}} \emph{que devuelva la {[}{[}y-intercepta{]}{]} de la l�nea a trav�s de los puntos (x1, y1) y (x2, y2).} \end{quote} \section{Funciones booleanas} \label{boolean} \index{funciones booleanas} \index{booleanas!funciones} Las funciones pueden devolver valores booleanos, lo que a menudo es conveniente para ocultar complicadas comprobaciones dentro de funciones. Por ejemplo: \begin{verbatim} def esDivisible(x, y): if x % y == 0: return 1 # it's true else: return 0 # it's false \end{verbatim} La funci�n lleva por nombre \texttt{esDivisible}. Es habitual dar a las funciones booleanas nombres que suenan como preguntas s�/no. Devuelve \texttt{1} � \texttt{0} para indicar si la \texttt{x} es o no divisibelo por \texttt{y}. Podemos reducir el tama�o de la funci�n aprovech�ndonos del hecho de que la sentencia condicional que hay despu�s del \texttt{if} es en s� misma una expresi�n booleana. Podemos devolverla directamente, evitando a la vez la sentencia \texttt{if:} \begin{verbatim} def esDivisible(x, y): return x % y == 0 \end{verbatim} La siguiente sesi�n muestra a la nueva funci�n en acci�n: \begin{verbatim} >>> esDivisible(6, 4) 0 >>> esDivisible(6, 3) 1 \end{verbatim} El uso m�s com�n para las funciones booleanas es dentro de sentencias condicionales: \begin{verbatim} if esDivisible(x, y): print "x es divisible entre y" else: print "x no es divisible entre y" \end{verbatim} Puede parecer tentador escribir algo como: \beforeverb \begin{verbatim} if esDivisible(x, y) == 1: \end{verbatim} \afterverb % Pero la comparaci�n extra es innecesaria. \begin{quote} \emph{Como actividad, escriba una funci�n} \texttt{\emph{estaEntre(x, y, z)}} \emph{que devuelva} \texttt{\emph{1}} \emph{en caso de que} \texttt{\emph{y <= x <= z}} \emph{y que devuelva 0 en cualquier otro caso.} \end{quote} \section{M�s recursividad} \index{recursividad} \index{lenguaje completo} \index{lenguaje!completo} \index{Turing, Alan} \index{tesis de Turing} Hasta ahora, usted ha aprendido solamente un peque�o subconjunto de Python, pero puede que le interese saber que ese subconjunto es ya un lenguaje de programaci�n \emph{completo}; con esto queremos decir que cualquier cosa que pueda computarse se puede expresar en este lenguaje. Cualquier programa que se haya escrito alguna vez puede reescribirse utilizando �nicamente las caracter�sticas del lenguaje que ha aprendido hasta el momento (de hecho, necesitar�a algunas �rdenes para controlar dispositivos como el teclado, el rat�n, los discos, etc, pero eso es todo). Probar tal afirmaci�n es un ejercicio nada trivial, completado por primera vez por Alan Turing, uno de los primeros cient�ficos inform�ticos (algunos argumentar�n que era un matem�tico, pero muchos de los cient�ficos inform�ticos pioneros comenzaron como matem�ticos). En correspondencia, se la conoce como la tesis de Turing. Si estudia un curso de Teor�a de la Computaci�n, tendr� oportunidad de ver la prueba. Para darle una idea de lo que puede hacer con las herramientas que ha aprendido hasta ahora, evaluaremos una serie de funciones matem�ticas que se definen recursivamente. Una definici�n recursiva es semejante a una definici�n circular, en el sentido de que la definici�n contiene una referencia a lo que se define. Una definici�n verdaderamente circular no es muy �til: \begin{description} \item [frangoso:] adjetivo que describe algo que es frangoso \end{description} \index{frangoso} \index{definici�n circular} \index{definici�n!circular} Si usted viera esa definici�n en el diccionario, se quedar�a confuso. Por otra parte, si ha buscado la definici�n de la funci�n matem�tica factorial, habr� visto algo sejemante a lo siguiente: \begin{eqnarray*} & & 0!=1\\ & & n!=n\cdot (n-1)! \end{eqnarray*} Esta definici�n establece que el factoral de 0 es 1, y que el factorial de cualquier otro valor, \( n \), es \( n \) multiplicado por el factorial de \( n-1 \). As� pues, \( 3! \) es 3 veces \( 2! \), que es 2 veces \( 1! \), que es una vez \( 0! \). Junt�ndolos todos, , \( 3! \) es igual a 3 veces 2 veces 1 vez 1, que es 6. \index{funci�n factorial} \index{funci�n!factorial} Si puede escribir una definici�n recursiva de algo, normalmente podr� escribir un programa de Python para evaluarlo. El primer paso es decidir cu�les son los par�metros para esta funci�n. Con poco esfuerzo llegar� a la conclusi�n de que \texttt{factorial} toma un �nico par�metro: \begin{verbatim} def factorial(n): \end{verbatim} Si resultase que el argumento fuese 0, todo lo que hemos de hacer es devolver 1: \begin{verbatim} def factorial(n): if n == 0: return 1 \end{verbatim} En otro caso, y he aqu� la parte interesante, tenemos que hacer una llamada recursiva para hallar el factorial de \( n-1 \) y luego multiplicarlo por \( n \): \begin{verbatim} def factorial(n): if n == 0: return 1 else: recursivo = factorial(n-1) resultado = n * recursivo return resultado \end{verbatim} El flujo de ejecuci�n de este programa es similar al de \texttt{cuenta\_atras} de la Secci�n~\ref{recursion}. Si llamamos a \texttt{factorial} con el valor 3: Puesto que 3 no es 0, tomamos la segunda rama y calculamos el factorial de \texttt{n-1}... \begin{quote} Puesto que 2 no es 0, tomamos la segunda rama y calculamos el factorial de \texttt{n-1}... \begin{quote} Puesto que 1 no es 0, tomamos la segunda rama y calculamos el factorial de \texttt{n-1}... \begin{quote} Puesto que 0 \emph{es} 0, tomamos la primera rama y devolvemos el valor 1 sin hacer m�s llamadas recursivas. \end{quote} El valor de retorno (1) se multiplica por \( n \), que es 1, y se devuelve el resultado. \end{quote} El valor de retorno (1) se multiplica por \( n \), que es 2, y se devuelve el resultado. \end{quote} El valor de retorno (2) se multiplica por \( n \), que es 3, y el resultado 6, se convierte en el valor de retorno de la llamada a la funci�n que comenz� todo el proceso. He aqu� el aspecto que tiene el diagrama de pila para esta secuencia de llamadas a funci�n: \vspace{0.1in} \centerline{\includegraphics{../illustrations/stack3.eps} } \vspace{0.1in} Los valores de retorno se muestran seg�n se pasan hacia la parte superior de la pila. En cada marco, el valor de retorno es el valor de \texttt{resultado}, que es el producto de \texttt{n} por \texttt{recursivo}. N�tese que en el �ltimo marco las variables locales \texttt{recursivo} y \texttt{resultado} no existen porque la rama que las crea no se ejecuta. \section{Acto de fe} \index{recursividad} \index{acto de fe} Seguir el flujo de ejecuci�n es una de las maneras de leer programas; pero puede volverse r�pidamente una tarea laber�nitca. La alternativa es lo que llamamos el {}``acto de fe''. Cuando llegamos a una funci�n, en lugar de seguir el flujo de ejecuci�n, \emph{damos por sentado} que la funci�n trabaja correctamente y devuelve el valor apropiado. De hecho, usted ya practica dicho salto de fe cuando usa funciones internas. Cuando llama a \texttt{math.cos} o a \texttt{math.exp}, no examina la implementaci�n de dichas funciones. Simplemente da por sentado que funcionan porque los que escribieron las bibliotecas internas de Python son buenos programadores. Lo mismo se aplica cuando llama a una de las funciones programadas por usted. Por ejemplo en la Secci�n~\ref{boolean}, escribimos una funci�n llamada \texttt{esDivisible} que determina si un n�mero es divisible por otro. Una vez que nos hayamos convencido de que dicha funci�n es correcta, comprobando y examinando el c�digo, podremos usar la funci�n sin tener siquiera que volver a mirar el c�digo otra vez. Lo mismo vale para los programas recursivos. Cuando llegue a la llamada recursiva, en lugar de seguir el flujo de ejecuci�n, tendr�a que dar por supuesto que la llamada recursiva funciona (es decir, devuelve el resultado correcto) y luego preguntarse: ``suponiendo que puedo hallar el factorial de \( n-1 \), �puedo hallar el factorial de \( n \)?'' En este caso, est� claro que s� puede, multiplic�ndolo por \( n \). Por supuesto, es un tanto extra�o dar por supuesto que la funci�n est� bien cuando ni siquiera ha acabado de escribirla, pero precisamente por eso se llama acto de fe. \section{Un ejemplo m�s} \label{one more example} En el ejemplo anterior, usamos variables temporales para ir apuntando los resultados y para hacer que el c�digo fuese m�s f�cil de depurar, pero podr�amos habernos ahorrado unas cuantas l�neas: \begin{verbatim} def factorial(n): if n == 0: return 1 else: return n * factorial(n-1) \end{verbatim} De ahora en adelante, tenderemos a usar la versi�n m�s concisa, pero le recomendamos que utilice la versi�n m�s expl�cita mientras se halle desarrollando c�digo. Cuando lo tenga funcionando, lo podr� acortar, si se siente inspirado. \index{Fibonacci function} Despu�s de \texttt{factorial}, el ejemplo m�s com�n de una funci�n matem�tica recursivamente definida es \texttt{fibonacci}, que presenta la siguiente definici�n: \begin{eqnarray*} & & fibonacci(0)=1\\ & & fibonacci(1)=1\\ & & fibonacci(n)=fibonacci(n-1)+fibonacci(n-2); \end{eqnarray*} Traducido a Python, es como sigue: \begin{verbatim} def fibonacci (n): if n == 0 or n == 1: return 1 else: return fibonacci(n-1) + fibonacci(n-2) \end{verbatim} Si intenta seguir el flujo de ejecuci�n aqu�, incluso para valores relativamente peque�os de \( n \), le puede dar un dolor de cabeza. Pero si confiamos en el acto de fe, si da por supuesto que las dos llamadas recursivas funcionan correctamente, entonces estar� claro que obtiene el resultado correcto al sumarlas juntas. \section{Comprobaci�n de tipos} \index{tipos!comprobaci�n} \index{comprobaci�n de errores} \index{funci�n factorial} �Qu� sucede si llamamos a \texttt{factorial} y le damos 1.5 como argumento? \begin{verbatim} >>> factorial (1.5) RuntimeError: Maximum recursion depth exceeded \end{verbatim} Tiene todo el aspecto de una recursi�n infinita. Pero, �c�mo ha podido ocurrir? Hay una condici�n de salida o caso base: cuando \texttt{n == 0}. El problema es que el valor de \texttt{n} yerra el caso base. \index{recursividad infinita} \index{recursividad!infinita} En la primera llamada recursiva, el valor de \texttt{n} es 0.5. En la siguiente vez su valor es -0.5. A partir de ah�, se vuelve m�s y m�s peque�o, pero nunca ser� 0. Tenemos dos opciones. Podemos intentar generalizar la funci�n \texttt{factorial} para que trabaje con n�meros de coma flotante, o podemos hacer que \texttt{factorial} compruebe el tipo de su par�metro. La primera opci�n se llama funci�n gamma, y est� m�s all� del objetivo de este libro. As� pues, tomemos la segunda. \index{funci�n gamma} Podemos usar la funci�n \texttt{type} para comparar el tipo del par�metro con el tipo de un valor entero conocido (por ejemplo 1). Ya que estamos en ello, podemos asegurarnos de que el par�metro sea positivo: \begin{verbatim} def factorial (n): if type(n) != type(1): print "El factorial est� definido s�lo para enteros." return -1 elif n < 0: print "El factorial est� definido s�lo para enteros\ positivos." return -1 elif n == 0: return 1 else: return n * factorial(n-1) \end{verbatim} Ahora tenemos tres condiciones de salida o casos base. El primero filtra los n�meros no enteros. El segundo evita los enteros negativos. En ambos casos, se muestra un mensaje de error y se devuelve un valor especial, -1, para indicar a quien hizo la llamada a la funci�n que algo fue mal: \begin{verbatim} >>> factorial (1.5) El factorial esta definido solo para enteros. -1 >>> factorial (-2) El factorial esta definido solo para enteros positivos. -1 >>> factorial ("paco") El factorial esta definido solo para enteros. -1 \end{verbatim} Si pasamos ambas comprobaciones, entonces sabemos que \( n \) es un entero positivo y podemos probar que la recursi�n termina. Este programa muestra un patr�n que se llama a veces \textbf{guardi�n}. Las primeras dos condicionales act�an como guardianes, protegiendo al c�digo que sigue de los valores que pudieran causar errores. Los guardianes hacen posible demostrar la correcci�n del c�digo. \section{Glosario} \begin{description} \item [funci�n~productiva:]Funci�n que devuelve un valor de retorno. \item [valor~de~retorno:]El valor obtenido como resultado de una llamada a una funci�n. \item [variable~temporal:]Variable utilizada para almacenar un valor intermedio en un c�lculo complejo. \item [c�digo~muerto:]Parte de un programa que no podr� ejecutarse nunca, a menudo debido a que aparece tras una sentencia de \texttt{return}. \item [None:]Valor especial de Python que devuelven funciones que o bien no tienen sentencia de \texttt{return} o bien tienen una sentencia de \texttt{return} sin argumento. \item [desarrollo~incremental:]Un m�todo de desarrollo de programas que busca evitar el depurado a�adiendo y probando una peque�a cantidad de c�digo en cada paso. \item [andamiaje:]El c�digo que se usa durante el desarrollo del programa pero que no es parte de la versi�n final. \item [guardi�n:]Una condici�n que comprueba y maneja circunstancias que pudieran provocar un error. \index{variable!temporal} \index{temporal!variable} \index{valor de retorno} \index{c�digo muerto} \index{None} \index{desarrollo incremental} \index{andamiaje} \index{guardi�n}\end{description} %\end{document}


Next Up Previous Hi Index

" + str + "

Close window

Hosted by www.Geocities.ws

"); } //-->
Hosted by www.Geocities.ws

1