miércoles, 18 de enero de 2012

(ALGO INTERESANTE) - FUNCIONES COMO PARAMETROS

Quizás a esta altura ya hayan olvidado las funciones que habíamos creado en algún momento, en algún articulo de este BLOG. :). Por esta razón vamos a refrescar un poco su memoria y vamos a volver a escribirlas en la consola interactiva de Python.

>>> def sumar(num1, num2):
...     return num1+num2
...
>>> def mostrarMensaje(mensaje):
...     print mensaje
...
>>> def multiplicar(num1, num2):
...     return num1*num2
...
>>> mostrarMensaje("Otro dia")
Otro dia
>>> sumar(3,5)
8
>>> multiplicar(5,5)
25

Como se dijo previamente estas dos funciones van a ser accesibles solo desde esta sesión de consola.
Esto quiere decir que si cerramos y abrimos la consola interactiva de Python vamos a tener que volver a crear dichas funciones si deseamos utilizarlas.
De todas formas esto es suficiente para mostrar el ejemplo siguiente:

LISTA DE FUNCIONES

En Python las funciones son objetos de primer grado, esto quiere decir que se le pueden dar varios usos importantes, entre ellos se puede destacar que a las funciones se las puede pasar como parámetros o cargar dentro de una lista como si fueran variables, lo cual puede llegar a ser de muchísima ayuda. Esta funcionalidad  esta implementada muuuuuy sencillamente en Python a diferencia de otros lenguajes como por ejemplo C.

Veamos como se utiliza esto:

Antes de meternos de lleno en lo que son las listas de funciones, estaría bueno mostrarles como se hace en Python para pasar una función como parámetro. La realidad es que esto es muy simple.
Lo primero que hay que hacer es definir la funcion que va a recibir otra funcion por parametro.. :)
Luego internamente esa función se debería usar como se utilizaría normalmente.

>>> def operacion(funcion, operando1, operando2):
...     result = funcion(operando1,operando2)
...     return result
...
>>> operacion(multiplicar, 5,5)
25
>>> operacion(sumar,5,5)
10

Cabe aclarar que hay que tener cuidado con estos temas ya que pueden provocar muchos problemas en tiempo de ejecución. Por ejemplo si los tipos de los parámetros no concuerdan con los que debería recibir la función o si la cantidad de parámetros tampoco es la correcta, se producirán errores como se muestra a continuación.

>>> operacion(sumar,'a','b')
'ab'
>>> operacion(multiplicar,'b',7)
'bbbbbbb'

Como se explico anteriormente las cadenas pueden concatenarse utilizando el operador '+', y también puede multiplicarse una cadena por un entero X, lo cual genera la repeticion de la cadena X cantidad de veces.
En el ejemplo el carácter 'b' se multiplica por 7, lo que genera 'bbbbbbb'.
Lo que daría un error seria tratar de multiplicar dos cadenas.

>>> operacion(multiplicar,'a','b')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in operacion
  File "<stdin>", line 2, in multiplicar
TypeError: can't multiply sequence by non-int of type 'str'

Así como también tratar de llamar a la función mostrarMensaje con más de un parámetro.

>>> operacion(mostrarMensaje,'Hola','Mundo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in operacion
TypeError: mostrarMensaje() takes exactly 1 argument (2 given)

Una lista de funciones se define de la misma manera que una lista de enteros, o una lista de cadenas.

Veamos un ejemplo.

Supongamos que deseamos aplicarle un conjunto de funciones a dos enteros en particular, para esto lo que hacemos es meter en una lista las funciones que deseamos aplicar y luego creamos un método que reciba esta lista de funciones y los enteros a los que le tenemos que aplicar las operaciones.

>>> # Se crea la lista de funciones
>>> listaFunciones = [sumar,multiplicar]
>>>
>>> # Se crea la funcion que recibe dicha lista y los enteros.
>>> def aplicarFunciones(lfunc,num1,num2):
...     for f in lfunc:
...             result = f(num1,num2)
...             print result
...
>>> # Llamamos a la funcion que aplicara cada una de las operaciones a ambos enteros.
>>> aplicarFunciones(listaFunciones,5,5)
10
25

Como puede observarse esto es muuuy potente, el hecho de poder modificar el comportamiento en tiempo de ejecución de una función en particular, nos da muchísima versatilidad para escribir código. Es cierto  que si se abusa de estas técnicas el código puedo volverse difícil de mantener y de seguir, pero los beneficios que conlleva la utilización de esta técnica, muchas veces puede superar ampliamente las problemáticas.

(MANEJO DE LISTAS)

Una de las cosas que más me atrajo de Python es la simplicidad con la que maneja las listas. Python brinda muchisimas facilidades que hacen que trabajar con listas no sea una tarea compleja.

Vamos a divertirnos un rato.. :)

NOTA: El primer elemento de la lista se encuentra en la posicion 0.



Creemos una lista con 5 elementos ordenados aleatoriamente:

>>> lista = [1,4,3,5,2]
>>> lista
[1, 4, 3, 5, 2]
>>> lista[0]
1

Si por ejemplo quisieramos tomar solo los elementos que se encuentran en la posicion 2 y 3 , es decir tomar una revanada de la lista, lo unico que deberiamos hacer seria lo siguiente:

>>> revanada = lista[2:4]
>>> revanada
[3, 5]

NOTA: Observar que se toman los elementos que se encuentran en el intervalo [2:4) es decir que no se toma el elemento que se encuentra en la posicion 4.

Si queremos tomar una revenada de la lista desde una posicion especifica hasta el final de la lista, hay que escribir lo siguiente:

>>> revanada = lista[2:]
>>> revanada
[3, 5, 2]

Como puede verse, se esta dejando abierto el final del intervalo mediante la expresión lista[2:]. De esta misma manera para tomar una revanada desde el principio de la lista hasta una posicion especifica, lo unico que habria que hacer es dejar abierto el intervalo pero de esta forma lista[:4].

>>> revanada = lista[:4]
>>> revanada
[1, 4, 3, 5]

Si haz pasado por otros lenguajes sabras que en la mayoria de ellos ordenar listas suele ser bastante tedioso, sin embargo Python trae un metodo de ordenamiento incorporado denominado sort. Podes probarlo haciendo lo siguiente:

>>> lista.sort()
>>> lista
[1, 2, 3, 4, 5]

Si te hiciera falta dar vuelta una lista, es decir que el primer elemento pase a ser el ultimo y que el ultimo pase a ser el primero, existe un método en Python que hace este trabajo por nosotros.

>>> lista = [1,2,3,4,5]
>>> lista
[1, 2, 3, 4, 5]
>>> lista.reverse()
>>> lista
[5, 4, 3, 2, 1]

NOTA: Tener en cuenta que el reverse es una operación "destructiva" ya que modifica el objeto sobre el cual se aplica la operación.

Se pueden ordenar listas heterogeneas como se muestra a continuación:

>>> listaHeterogenea = [2,1,'b','a']
>>> listaHeterogenea 
[2, 1, 'b', 'a']
>>> listaHeterogenea.sort()
>>> listaHeterogenea 
[1, 2, 'a', 'b']

Si tenes que contar la cantidad de apariciones de un elemento dentro de una lista, no te compliques que Python tiene una función que hace esto más fácil.

>>> lista = [1,2,3,2,2,5,3]
>>> lista.count(2)
3
>>> lista.count(3)
2
>>> lista.count(5)
1

Para agregar un elemento al final de una lista lo que hay que hacer es utilizar la función append() de listas y LISTO.. :)

>>> lista = [1,2,3,4]
>>> lista
[1, 2, 3, 4]
>>> lista.append(5)
>>> lista
[1, 2, 3, 4, 5]

Algo muy complejo es insertar un elemento en una posición específica de la lista, ya que hay que correr todos los elementos un espacio hacia adelante, para hacer un hueco en el cual pueda entrar el elemento que se desea insertar. Pero como esta es una función bastante común, Python cuenta con ella entre sus funciones integradas.
La función se llama insert y recibe la posición y el elemento en ese orden. Otro punto a favor.. :)

>>> lista = [1,2,3,4,5]
>>> lista.insert(0,0)
>>> lista
[0, 1, 2, 3, 4, 5]
>>> lista.insert(8,6)
>>> lista
[0, 1, 2, 3, 4, 5, 6]
>>> lista.insert(4,9)
>>> lista
[0, 1, 2, 3, 9, 4, 5, 6]

En el ejemplo puede observarse que se ingreso el elemento 0 (cero) en la posición 0 (cero) es decir al inicio de la lista. Luego se inserto el elemento 6 en la posición 8, como la lista tiene menos de 8 elementos se ingresa el elemento 6 al final de la lista. Y por ultimo se inserto el elemento 9 en la posición 4 de la lista. Todos los corrimientos se hacen automáticamente lo cual nos ahorra una gran cantidad de líneas de código.

Si lo que queremos saber es la posicion en la que aparece un elemento en la lista, debemos utilizar la funcion index() de listas.

>>> lista = [5,4,3,2,1]
>>> posicion = lista.index(5)
>>> posicion
0

Puede observarse que el numero 5 se encuentra en la posicion 0 (cero) de la lista.

NOTA: Hay que tener cuidado cuando se utiliza esta funcion ya que si se intenta realizar un index, con un elemento que no pertenece a la lista, se produce un error.

>>> posicion = lista.index(6)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: list.index(x): x not in list

Podría utilizarse en conjunto con la funcion "count()", ya que si se verifica que count es mayor que 0 (cero) es seguro que la funcion "index()" nos devolvera algo.
Cabe aclarar tambien que la funcion index() solo devolverá la posición de la primera aparición del elemento.

Para borrar un elemento de la lista se utiliza la función remove().

>>> lista = [5,4,3,2,1]
>>> lista
[5,4,3,2,1]
>>> lista.remove(2)
>>> lista
[5, 4, 3, 1]

Para concatenar dos o más listas hay dos formas sencillas, una es utilizando la función "extend()" y la otra utilizando el operador '+'.


Concatenacion con  extend()

>>> lista = [1,2,3]
>>> lista
[1, 2, 3]
>>> lista.extend([4,5])
>>> lista
[1, 2, 3, 4, 5]

con el operador '+'

>>> lista = [1,2,3]
>>> lista2 = [4,5]
>>> lista3 = lista + lista2
>>> lista3
[1, 2, 3, 4, 5]

NOTA: Al igual que con las cadenas con las listas también es valido utilizar el operador '*' (Multiplicación).

El resultado es el siguiente:

>>> lista = [1,2,3]
>>> lista2 = lista*2
>>> lista2
[1, 2, 3, 1, 2, 3]

Para tomar el mínimo y el máximo elemento de una lista, Python nos provee los métodos min() y max(), los cuales se utilizan de la siguiente manera:

>>> lista = [1,2,5,4,3]
>>> min(lista)
1
>>> max(lista)
5

Para saber el tamaño de una lista contamos con la funcion len(), la cual tambien puede aplicarse a cadenas y a otros tipos de datos que se veran más adelante.

>>> lista = [1,2,5,4,3]
>>> len(lista)
5

Para sumar los elementos de una lista Python nos provee una funcion llamada sum(), la cual recibe una lista de  numero y devuelve la suma de todos ellos.

>>> lista = [1,2,5,4,3]
>>> sum(lista)
15



Hay que tener cuidado al usar esta funcion ya que si la lista contiene elementos no numericos, la misma tirara un error como se muestra a continuacion.

>>> lista = [1,2,3,'a','b']
>>> sum(lista)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

Si se quiere obtener una lista sin repeticiones se puede utilizar el metodo set(), que nos retorna un conjunto con los elementos de la lista sin repeticiones.

>>> lista = [1,2,3,4,4,4,5,6,7,6]
>>> lista2 = set(lista)
>>> lista2
set([1, 2, 3, 4, 5, 6, 7])

NOTA: Hay que tener en cuenta que los conjuntos (set) no son indexsables por lo cual la ejecucion de lista2[1] nos dara el error que se muestra a continuacion.

>>> lista2[1]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'set' object does not support indexing

Para acceder uno a uno a los elementos del set, lo mejor es hacerlo por medio de un "for" ya que el set es una estructura iterable.. :P

Por ultimo y para terminar con listas, puedo comentarles que Python nos da la posibilidad de trabajar con las listas como si fueran una estructura de datos de tipo cola o de tipo pila, ya que nos brinda la funcion append() para ingresar elementos al final de la "lista" y también nos brinda la función pop() para ir tomando el ultimo elemento de la lista.
Hay que tener en cuenta que las estructuras de datos de tipo Pila son aquellas en las que el ultimo elemento en insertarse es el primero en sacarse, es por esta razón que se dice que son de tipo LIFO (Last In First Out), por lo cual una Pila en Python estaría dada por una lista que utilice los metodos append() para insertar elementos y pop() para quitarlos.

Lista como Pila

>>> lista = [1,2,3]
>>> lista
[1, 2, 3]
>>> lista.append(4)
>>> lista
[1, 2, 3, 4]
>>> lista.pop()
4
>>> lista
[1, 2, 3]
>>> lista.pop()
3
>>> lista
[1, 2]

Por otro lado una cola, que es una estructura de datos de tipo FIFO (First In First Out) debido a que los elementos se van tomando por orden de llegada, es una estructura igual de facil de implementar pero menos eficiente.
Python esta muy bien preparado para tomar el último elemento de una lista y para insertar un elemento al final de una lista. Pero pierde bastante performance al tomar el primer elemento de una lista y al insertar un elemento al principio de una lista.
De todas formas si se va trabajar con listas pequeñas, esto no es un problema.
Se puede simular una cola mediante una lista que utiliza el método append() para la inserción de elementos y el método pop(0) para tomar los elementos. O tambien utilizando la funcion insert() para insertar al principio de la lista y la funcion pop() para tomar los elementos del final. Esto puede quedar mas claro con el siguiente ejemplo:

>>> lista = [1,2,3]
>>> lista.append(4)
>>> lista
[1, 2, 3, 4]
>>> lista.pop(0)
1
>>> lista.pop(0)
2
>>> lista.append(5)
>>> lista
[3, 4, 5]

NOTA: En Python existe una forma más eficiente de trabajar con Colas, pero lo miraremos mas adelante. Si alguno quiere investigar por su cuenta le comento solamente que deben importar desde collections la clase deque

            from collections import deque

(DEFINICION DE FUNCIONES)

En programación una función puede verse como un conjunto de sentencias relacionadas, las cuales pueden invocarse (ejecutarse) por medio de un nombre.
Las funciones en Python pueden o no recibir parámetros así como también pueden o no devolver un valor.

Para crear una función se utiliza la palabra reservada "def". Para comprender como se utiliza veamos un ejemplo.

Escriban lo siguiente en la consola interactiva de Python.

>>> def sumar(numero1, numero2):
...     resul = numero1 + numero2
...     return resul
...
>>>

Una vez hecho esto, la función "sumar", que recibe dos números como parámetros y devuelve uno que es el resultado de la suma de ambos, ya esta lista para ser utilizada en esta sesión de la consola interactiva de Python.

>>> var = sumar(2,3)
>>> var
5

Si sos novato como ejercicio podrías probar hacer las funciones:
                                               - Restar
                                               - Multiplicar

>>> def mostrarMensaje(mensaje):
...     print mensaje
...
>>> mostrarMensaje('Hola mundo!!!')
Hola mundo!!!
>>>

mostrarMensaje es una función que recibe una cadena por parámetro y la muestra por pantalla. En este caso la función mostrarMensaje no retorna ningún valor.

Las funciones pueden ser tan sencillas o tan complejas como la situacion amerite. Si bien Python es un lenguaje que alienta la programacion minimalista yo te digo que mi punto de vista es algo diferenta ya que opino que si tu algoritmo hace lo que deseas que haga, puede considerarse exitoso. Por supuesto que puede mejorarse, y siempre es importante seguir una linea para que las futuras modificaciones no sean un dolor de cabeza, pero si ya conseguiste que el algoritmo trabaje como vos queres, probablemente estes cerca de alguna solucion mas simple.

FOR y WHILE (ESTRUCTURAS DE REPETICION) (1)

En Python existen 2 estucturas de repeticion:

El "for" que normalmente se usa para recorrer objetos que esten preparados para iterar uno a uno por sus elementos.
Y el "while" que analiza una expresion y mientras el valor de verdad de la misma sea True o equivalente, se ejecuta el bloque de codigo.

Ejemplo FOR:

>>> lista = [1,2,3,4]
>>> for item in lista:
...     print item
...
1
2
3
4

Como puede observarse el "for" va tomando uno a uno los elementos de la lista y los va poniendo en la variable item, la cual mostramos con el print. Es por esta razon que se muestra los valores 1, 2, 3, 4.
Como se dijo previamente es necesario que la variable sobre la cual se va a realizar el for sea iterable, sino tirara el siguiente error.

>>> numero = 5
>>> for cont in numero:
...     print cont
...

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'int' object is not iterable

Para que el "for" repita un bloque de codigo un numero especifico de veces, lo que hay que hacer es utilizar la funcion "range(numero)", que es una funcion que recibe un valor entero y devuelve una lista con enteros desde 0 (cero) hasta (numero-1).

Para ver esto podemos ejecutar en la consola interactiva de python la funcion "range(numero)"

>>> numero = 5
>>> range(numero)
[0, 1, 2, 3, 4]

Para ejecutar el bloque de código que se encuentra dentro del "for" 5 veces lo que se hace es lo siguiente:

>>> numero = 5
>>> for i in range(numero):
...     print "For " + str(i+1) + " ejecutado."
...
For 1 ejecutado.
For 2 ejecutado.
For 3 ejecutado.
For 4 ejecutado.
For 5 ejecutado.

NOTA 1: Para concatenar dos cadenas en Python, se utilizar el simbolo matematico "+". Si quieren ver esto prueben en la consola interactiva de Python ejecutar lo siguiente:

>>> a = 'd'
>>> b = 'e'
>>> c = a+b
>>> c
'de'

En este ejemplo se le asigna la letra 'd' a la variable "a" y la letra 'e' a la variable "b". Luego a  la variable "c" se le asigna a+b lo cual genera la cadena resultante de la concatenación de 'd' y 'e'.
Algo interesante que se puede hacer solo para probar es lo siguiente:

>>> a = 'd'
>>> c = a*4
>>> c
'dddd'

NOTA 2: Para castear cualquier objeto a cadena se utiliza la función str(objeto).
Cualquier objeto puede convertirse a cadena. Internamente cada uno sabe como mostrarse.


>>> lista = [1,2,3,4,5,6]
>>> cadena = str(lista)
>>> cadena
'[1, 2, 3, 4, 5, 6]'

Prestar atención que no es lo mismo [1,2,3,4,5,6] que '[1,2,3,4,5,6]'. Uno es una lista y el otro es una cadena.

Si hacemos lista[0] nos devolverá el valor 1 y si hacemos cadena[0] nos devolvera el valor '['

>>> lista[0]
1
>>> cadena[0]
'['

Ejemplo WHILE:

La estructura de repetición WHILE analiza una expresión y mientras esta sea verdadera se va a ejecutar el bloque de código que tenga contenido dentro.

>>> cont = 0
>>> while (cont<3):
...     cont = cont + 1
...     print cont
...
1
2
3

Esto podría leerse como "mientras cont sea menor que 3 mostrar cont".