Cesar Systems

Herramientas Informaticas

13.5. Desarrollo de prototipos frente a planificación

En este capítulo mostramos una aproximación al desarrollo de programas a la que llamamos desarrollo de prototipos. En cada caso, escribimos un esbozo basto (o prototipo) que realizaba el calculo básico y luego lo probamos sobre unos cuantos casos, corrigiendo los fallos tal como los encontrábamos.

Aunque este enfoque puede ser efectivo, puede conducirnos a código que es innecesariamente complicado, ya que trata con muchos casos especiales, y poco fiable, porque es difícil saber si encontró todos los errores.

Una alternativa es el desarrollo planificado, en el que una comprensión del problema en profundidad puede hacer la programación mucho mas fácil. En este caso, el enfoque es que un objeto Hora es en realidad un numero de tres dígitos
en base 60! El componente segundo es la “columna de unidades”, el componente
es la columna de las sesentas” y el componente hora es la columna de las tresmilseiscentenas“.

Cuando escribimos sumaHora e incremento, en realidad estábamos haciendo una suma en base 60, que es por lo que debíamos acarrear de una columna a la siguiente.

Esta observación sugiere otro enfoque para el problema. Podemos convertir un objeto Hora en un simple numero y sacar provecho del hecho de que la maquina sabe la aritmética necesaria. La siguiente función convierte un objeto Hora en un entero:

   1: def convierteASegundos(t):

   2: minutos = t.horas * 60 + t.minutos

   3: segundos = minutos * 60 + t.segundos

   4: return segundos

 

Ahora, solo necesitamos una forma de convertir un entero en un objeto Hora:

 

   1: def haceHora(segundos):

   2:     hora = Hora()

   3:     hora.horas = segundos/3600

   4:     segundos = segundos - hora.horas * 3600

   5:     hora.minutos = segundos/60

   6:     segundos = segundos - hora.minutos * 60

   7:     hora.segundos = segundos

   8:     return hora

Puede que tenga usted que pensar un poco para convencerse de que esta técnica para convertir de una base a otra es correcta. Suponiendo que esta usted convencido, puede usar estas funciones para reescribir sumaHora:

   1: def sumaHora(t1, t2):

   2: segundos = convierteASegundos(t1) + convierteASegundos(t2)

   3: return haceHora(segundos)

Esta versión es mucho mas corta que la original, y es mucho mas fácil de demostrar que es correcta (suponiendo, como es habitual, que las funciones a las que llama son correctas).

Como ejercicio, reescriba incremento de la misma forma.

13.6. Generalización

De algún modo, convertir de base 60 a base 10 y de vuelta es mas difícil que simplemente manejarse con las horas. La conversión de base es mas abstracta; nuestra intuición para tratar con las horas es mejor.

Pero si tenemos la comprensión para tratar las horas como números en base 60, y hacer la inversión de escribir las funciones de conversión (convierteASegundos y haceHora), obtenemos un programa que es mas corto, mas fácil de leer y
depurar y mas fiable.

También es mas fácil añadir funcionalidades mas tarde. Por ejemplo, imagine restar dos Horas para hallar el intervalo entre ellas. La aproximación ingenua sería implementar la resta con acarreo. Con el uso de las funciones de conversión será mas fácil y con mayor probabilidad, correcto.

Irónicamente, a veces hacer un problema mas complejo (o mas general) lo hace mas fácil (porque hay menos casos especiales y menos oportunidades de error).

13.7. Algoritmos

Cuando escribe una solución general para una clase de problemas, en contraste con una solución específica a un problema concreto, ha escrito un algoritmo.

Mencionamos esta palabra antes pero no la definimos con precisión. No es fácil de definir, así que probaremos un par de enfoques.

Primero, piense en algo que no es un algoritmo. Cuando usted aprendió a multiplicar números de una cifra, probablemente memorizo la tabla de multiplicar.

En efecto, memorizo 100 soluciones específicas. Ese tipo de conocimiento no es algorítmico.

Pero si usted era “haragán” probablemente hizo trampa aprendiendo algunos trucos.
Por ejemplo, para encontrar el producto de n por 9, puede escribir n-1 como el primer dígito y 10 – n como el segundo dígito. Este truco es una solución general para multiplicar cualquier numero de una cifra por 9.  ¡Eso es un algoritmo!

De forma similar, las tecnicas que aprendió para la suma y la resta con acarreo y la división larga son todas algoritmos. Una de las características de los algoritmos es que no requieren inteligencia para llevarse a cabo. Son procesos mecánicos en los que cada paso sigue al anterior de acuerdo a un conjunto simple de reglas.

En nuestra opinión, es un poco vergonzoso que los humanos pasen tanto tiempo en la escuela aprendiendo a ejecutar algoritmos que, de forma bastante similar, no exigen inteligencia.

Por otra parte, el proceso de diseñar algoritmos es interesante, un desafío intelectual y una parte primordial de lo que llamamos programar.

Algunas de las cosas que la gente hace naturalmente, sin dificultad ni pensamiento consciente, son las mas difíciles de expresar algorítmicamente. Entender el lenguaje natural es un buen ejemplo. Todos lo hacemos, pero hasta el momento nadie ha sido capaz de explicar como lo hacemos, al menos no en la forma de un algoritmo.

Nomas te vengo avisar

Nomas te vengo avisar, que no puedo venir a verte
y que un mañana quizás, yo sufra por no tenerte.
Te vengo a pedir perdón, perdón porque yo me alejo
Por causas tal vez ajenas, y que me marcan esta condena.

TE QUIERO TANTO Y TENGO MIEDO,
DE QUE ME OLVIDES POR QUE NO PUEDO
VENIR A VERTE COMO QUEDAMOS,
POR QUE MAÑANA MARCHAR ME DEBO.

Te vengo a pedir perdón, perdón porque yo me alejo
Por causas tal vez ajenas, y que me marcan esta condena.

TE QUIERO TANTO Y TENGO MIEDO,
DE QUE ME OLVIDES POR QUE NO PUEDO
VENIR A VERTE COMO QUEDAMOS,
POR QUE MAÑANA MARCHAR ME DEBO.

TE QUIERO TANTO Y TENGO MIEDO,
DE QUE ME OLVIDES POR QUE NO PUEDO
VENIR A VERTE COMO QUEDAMOS,
POR QUE MAÑANA MARCHAR ME DEBO.

 

13.8. Glosario

función pura: Una función que no modifica ninguno de los objetos que recibe como parámetros. La mayor³a de las funciones puras son rentables.

modificador: Una función que modifica uno o mas de los objetos que recibe como parámetros. La mayor³a de los modificadores no entregan resultado.

estilo funcional de programación: Un estilo de programación en el que la mayor³a de las funciones son puras.

desarrollo de prototipos: Una forma de desarrollar programas empezando con un prototipo y probándolo y mejorándolo gradualmente.

desarrollo planificado: Una forma de desarrollar programas que implica una comprensión de alto nivel del problema y mas planificación que desarrollo incremental o desarrollo de prototipos.

algoritmo: Un conjunto de instrucciones para solucionar una clase de problemas por medio de un proceso mecánico sin intervención de inteligencia.

Clases y métodos

14.1. Características de la orientación a objeto

Python es un lenguaje de programación orientado a objetos, lo que significa que proporciona características que apoyan la programación orientada a objetos.

No es fácil definir la programación orientada a objetos, pero ya hemos visto
algunas de sus características:

Los programas se hacen a base de definiciones de objetos y definiciones de funciones, y la mayor parte de la computación se expresa en términos de operaciones sobre objetos.

Cada definición de un objeto se corresponde con un objeto o concepto del mundo real, y las funciones que operan en ese objeto se corresponden con las formas en que interactúan los objetos del mundo real.

Por ejemplo, la clase Hora definida en el Capítulo 13 se corresponde con la forma en la que la gente registra la hora del día, y las funciones que definimos se corresponden con el tipo de cosas que la gente hace con las horas. De forma similar, las clases Punto y Rectangulo se corresponden con los conceptos matemáticos de un punto y un rectangulo.

Hasta ahora, no nos hemos aprovechado de las características que Python nos ofrece para dar soporte a la programación orientada a objetos. Hablando estrictamente, estas características no son necesarias. En su mayor³a, proporcionan una sintaxis alternativa para cosas que ya hemos hecho, pero en muchos casos,la alternativa es mas concisa y expresa con mas precisión a la estructura del programa.

Por ejemplo, en el programa Hora no hay una conexión obvia entre la definición de la clase y las definiciones de las funciones que siguen. Observando bien, se hace patente que todas esas funciones toman al menos un objeto Hora como
parámetro.

Esta observación es la que motiva los métodos. Ya hemos visto varios métodos, como keys y values, que se invocan sobre diccionarios. Cada metodo esta asociado con una clase y esta pensado para invocarse sobre instancias de esa clase.

  • Los métodos son como las funciones, con dos diferencias:
  • Los métodos se definen dentro de una definición de clase para explicitar la relación entre la clase y el metodo.

La sintaxis para invocar un metodo es diferente de la de una llamada a una función.

En las próximas secciones tomaremos las funciones de los capítulos anteriores y las transformaremos en métodos. Esta transformación es puramente mecánica; puede hacerla simplemente siguiendo una secuencia de pasos. Si se acostumbra
a convertir de una forma a la otra será capaz de elegir la mejor forma de hacer lo que quiere.

14.2. imprimeHora

En el Capítulo 13, definimos una clase llamada Hora y escribimos una función llamada imprimeHora, que debería ser parecida a esto:

   1: class Hora:

   2: pass

   3: def imprimeHora(hora):

   4: print str(hora.horas) + ":" +

   5: str(hora.minutos) + ":" +

   6: str(hora.segundos)

Para llamar a esta función, pasábamos un objeto Hora como parámetro:

   1: >>> horaActual = Hora()

   2: >>> horaActual.horas = 9

   3: >>> horaActual.minutos = 14

   4: >>> horaActual.segundos = 30

   5: >>> impriemHora(horaActual)

Para convertir imprimeHora en un metodo, todo lo que necesitamos hacer es mover la definición de la función al interior de la definición de la clase. Fíjese en como cambia el sangrado.

 

   1: class Hora:

   2:     def imprimeHora(hora):

   3:         print str(hora.horas) + ":" +

   4:         str(hora.minutos) + ":" +

   5:         str(hora.segundos)

Ahora podemos invocar imprimeHora usando la notación de punto.

   1: >>> horaActual.imprimeHora()

Como es habitual, el objeto sobre el que se invoca el metodo aparece delante del punto y el nombre del metodo aparece tras el punto.

El objeto sobre el que se invoca el metodo se asigna al primer parámetro, así que en este caso horaActual se asigna al parámetro hora.

Por convenio, el primer parámetro de un metodo se llama self. La razón de esto es un tanto rebuscada, pero se basa en una metáfora útil.

La sintaxis para la llamada a una función, imprimeHora(horaActual), sugiere que la función es el agente activo. Dice algo como ¡Oye imprimeHora! Aquí hay un objeto para que lo imprimas”.

En programación orientada a objetos, los objetos son los agentes activos. Una invocación como horaActual.imprimeHora() dice ¡Oye horaActual Imprímete!

Este cambio de perspectiva puede ser mas elegante, pero no es obvio que sea útil. En los ejemplos que hemos visto hasta ahora, puede no serlo. Pero a veces transferir la responsabilidad de las funciones a los objetos hace posible escribir funciones mas versátiles, y hace mas fácil mantener y reutilizar código.

14.3. Otro ejemplo

Vamos a convertir incremento (de la Sección 13.3) en un metodo. Para ahorrar espacio, dejaremos a un lado los métodos ya definidos, pero usted debería mantenerlos en su versión:

   1: class Hora:

   2:     #aquí van las definiciones anteriores de métodos...

   3:     def incremento(self, segundos):

   4:         self.segundos = segundos + self.segundos

   5:         while self.segundos >= 60:

   6:             self.segundos = self.segundos - 60

   7:             self.minutos = self.minutos + 1

   8:         while self.minutos >= 60:

   9:             self.minutos = self.minutos - 60

  10:             self.horas = self.horas + 1

La transformación es puramente mecánica; hemos llevado la definición del metodo al interior de la definición de la clase y hemos cambiado el nombre del primer parámetro.

Ahora podemos invocar incremento como un metodo.

   1: horaActual.incremento(500)

De nuevo, el objeto sobre el que invocamos el metodo se asigna al primer parámetro, self. El segundo parámetro, segundos toma el valor de 500.

Como ejercicio, convierta convertirASegundos (de la Sección 13.5) en un metodo de la clase Hora.

14.4. Un ejemplo más complicado

La función después es ligeramente mas complicada porque opera sobre dos objetos Hora, no solo sobre uno. Solo podemos convertir uno de los parámetros en self; el otro se queda como esta:

   1: class Hora:

   2: #aquí van las definiciones anteriores de métodos...

   3:     def despues(self, hora2):

   4:         if self.horas > hora2.horas:

   5:             return 1

   6:         if self.horas < hora2.horas:

   7:             return 0

   8:         if self.minutos > hora2.minutos:

   9:             return 1

  10:         if self.minutos < hora2.minutos:

  11:             return 0

  12:         if self.segundos > hora2.segundos:

  13:             return 1

  14:         return 0

Invocamos este método sobre un objeto y pasamos el otro como argumento:

   1: if horaHecho.despues(horaActual):

   2: print "El pan estará hecho después de empezar."

 

Casi puede leer la invocación como una mezcla de ingles y español: Si la horahecho es después de la horaactual, entonces…”

 

Página 120 de 143

Creado con WordPress & Tema de Anders Norén