Introduccion a Funciones

Visión general

Una función juega el mismo rol que un ladrillo en la construcción de una casa: is a building block for a program.

Permite crear dos tipos de abstracciones:

Process abstraction: permite olvidarnos del proceso, abstraernos del algoritmo que está detrás del resultado de la función

Task decomposition: permite particionar un projecto en varias partes. Divide and conquer.

Functional programming: es otro paradigma de la programación basado en funciones de una sola variable

Call - Define

Son los dos elementos claves para comprender a una función:

- Llamar a una función: a function call

- Definir a una función: como establecemos el proceso/ el algoritmo que realiza una función.



Calls

Hemos incluido aquí las anotaciones que surgen del Syntax for Variable Annotations:

El objetivo es especificar el tipo de objeto que estoy creando.

Observar que el function call implica los ()

Un objeto se dice callable cuando podemos ejecutarlo como una función.

Una cuestión importante: no nos interesa lo que está detrás del proceso de rounding: confiamos que está bien hecho, testeado, etc...

El call es una expresión que se evalua en un resultado de un tipo específico.

Importante: None es un tipo de resultado, i.e., si no retorna nada, eso es algo.

Define a function.

Define el algoritmo que se ejecuta cuando llamo a la función.

La ejecución se realiza detrás de la escena, no se vé, solo se vé el resultado: process abstraction

3 tipos de definiciones:

- built-ins
- 3rd party functions: import....
- user functions

Problema: queremos sortar un regalo con tirando un dado.

Buscamos y encontramos que podemos hacerlo con el método randint del modulo random

Esto es:

Observación

El proceso anterior nos permite abstraernos del proceso que genera el resultado.

Sin embargo, tengamos en cuenta esta analogía:

1.- Cuando buscamos restaurante vamos a TripAdvisor.

2.- Cuando compramos un coche te segunta mano, vamos a Carfax

Por tanto, si es la primera vez que usamos una función que no conocemos, deberíamos:

Veremos ahora

  1. La sintáxis

  2. Construir nuestras propias funciones definidas por el usuario.

El objetivo es ganar comprensión lectora

Empecemos por un ejemplo sencillo

Problema: tenemos una lista de nombres y queremos encontrar los nombres que empiezan por la letra M.

First, solve the problem. Then, write the code

Esto implica necesariamente distinguir entre:

    - Conocimientos técnicos: resolución del problema

    - Concimientos de programación o código.

Por tanto, lo primero que vamos a hacer es resolver el problema en un papel en blanco.

¿Cómo puedo destructurar el problema en una serie de pasos que me lleve a la solución?

En otras palabras, cómo puedo construir un algoritmo redactado con mis palabras, no en la sintáxis del programa

Mi pseudo-código casero:

1.- Mirar el primer nombre de la lista.
2.- Mirar a la primera letra.
3.- Si la primera letra de ese nombre es la que me interesa, guardo el nombre.
4.- Si no es, sigo al siguiente nombre.
5.- Repito puntos 2-4 con los hasta que se agoten los nombres.

Problema: esto no es generalizable a cualqueir otra lista

Una función permite generalizar la solución a cualquier tipo de lista o letra del nombre que me interese.

El principio de reusabilidad: si tenemos un fragmento de código usado en muchos sitios, la mejor solución sería pasarlo a una función.

El principio de modularidad: en vez de escribir largos trozos de código, es mejor crear módulos o funciones que agrupen ciertos fragmentos de código en funcionalidades específicas, haciendo que el código resultante sea más fácil de leer.

Como en mátematicas, ua función tendrá un nombre, parametros, un código a ejecutar y un output, e.g.,

```python def funcion(x): codigo return

Ejercicio: Defina una función que retorne el máximo de dos números

Function annotations: PEP 3107

Type Checker: testear si las annotaciones son correctas

Aspectos básicos en la definición de funciones

En Python, cuyo lema es que otro entienda lo que hago, hay reglas para escribir y presentar funciones

El Style general está expuesto en PEP 8 Style Guide y, en lo que respecta a funciones

Sintaxis básica

Veamos cómo escribir una una función simple en Python, i.e., $f(x) = 2 x + 1$

Function defition: is a signature a contract

La definición de una función es un function definition statement: el interprete lo mira y dice, aquí viene una bloque relacionado con una función.

def mi_maximo(a, b):
    pass
mi_maximo(5,4)

Pasemos entonces a definir la función anterior:

Es importante observar la sintáxis en esta definición: aquí tenemos un identation, cuatro espacios desde el borde.

Observar que en R, la sintáxis es distinta

recta <- function(x) {
        r <- 2*x + 1
    return(r)
}

pero la idea es la misma.

Para llamar una función hacemos un call.

Cuestión: solo podemos hacer un call en objetos callables, i.e., funciones o métodos.

Ejercicio:

Queremos una función que, como resultado, reporte el saldo de un deposito un depósito bancario al cabo de un número de meses en función de un tipo de interés.

Hay que tener cuidado con los identations:

Return

Una función puede tener un número arbitrario de declaraciones return, incluso ningúna declaración de return.

La ejecución de la función termina cuando se alcanza el primer retorno, lo que permite código como el siguiente ejemplo

Las funciones sin declaración de retorno devuelven automáticamente el objeto especial de Python None.

Ejercicio:

Convertir el script siguiente en una función con tres parámetros: el número de observaciones, la media y la varianza de la distribución normal.

La función on debe tener ningún retorno, solamente reportar el gráfico.

Importante: cuando algo no se conoce o no se entiende, se pregunta en la red.

Ejercicio:

1.- Utilizando el script anterior, escriba una función para la evolución de la inflación como una forma autorregresiva:: $$ \pi_{t} = \alpha \times \pi_{t-1} + e_{t} $$ donde $e_{t} \sim N(m,v)$ y $x_0 = z$

2.- Que pueda observar, en un solo gráfico, cómo la inflación futura depende del nivel de distintos $\alpha's$.

Argumentos de una función

Positional arguments

Los argumentos por posición o posicionales son la forma más básica e intuitiva de pasar parámetros.

Buscar en google: matplotlib lines line2D

Tener en cuenta que el objetivo es alcanzar una comprensión lectora: no memorizar

En la siguiente función, los argumentos para los parámetros a,b son posicionales: importa su posición.

El operador *

Se utiliza en muchos contextos: unpacking, regular expressions, etc..

En funciones hace referencia a unpacking

Volvamos a los argumentos posicionales

El orden en el que se introducen los argumentos posicionales importa

Por defecto, una funcion retorna `None`

The positional arguments are bound, assinged, first.

Vamos ahora a incluir default arguments (or keyword arguments)

Otra forma de llamar a una función, es usando el nombre del argumento con = y su valor.

Naturalmente, podemos modificar los argumentos por defecto

Otra forma de llamarlo, introducir los argumentos, sería con un diccionario, e.g., **kwargs

Variadic parameters

Podemos definir funciones que tengan un número no determinado de argumentos.

Por ejemplo una suma de todos los elementos que querramos

La sintaxis adecuada en Python para incluir parámetros de longitud variable

def f(*args,**kwargs):
    pass

Variadic positional arguments

*args

La sintaxis especial *args en las definiciones de función en Python se utiliza para pasar un número variable de argumentos a una función.

Observar el orden que se insertan los argumentos

Pero:

Como son argumentos posicionales, la primera y segunda llamada no pude hacerse con argumentos explícitos, i.e.

function(j="Hola j", g=" Hola g", 1, 2, 3, 4)
function(j="Hola j", 1, 2, 3, 4, g=" Hola g")

ya que daría un error posicional

SyntaxError: positional argument follows keyword argument

Variadic Keyword Parameters

**kwargs

La sintaxis especial **kwargs en las definiciones de funciones en Python se utiliza para pasar una lista de argumentos de longitud variable con palabras clave.

Orden de los argumentos:

Postitional, Default, *args, **kwargs

Puede colocar *args en cualquier lugar de la lista de parámetros. Sin embargo, cualquier parámetro definido después de *args debe especificarse utilizando el formato de palabra clave nombre_parámetro=valor al llamar a la función.

Un parámetro prefijado con ** sólo puede definirse al final de la lista de parámetros.

Solo el argumento *

Si incluimos un * en la definicion de la función, todos los argumentos posteriores se requiere incluirlos com names values:

def muchos_argumentos(*, a, b,c,d):

Siguientes temas relacionados con funciones

1.- Default arguements

2.- Scope

3.- Closures

4.- Decorators

5.- Lambda functions

6.- Partial https://www.thepythoncodingstack.com/p/pythons-functools-partial-and-partialmethod

La Flexibilidad de las Funciones Python

Las funciones de Python son muy flexibles.

En particular

Daremos ejemplos de lo sencillo que es pasar una función a una función en las secciones siguientes.

¿Por qué escribir funciones?

Las funciones definidas por el usuario son importantes para mejorar la claridad de su código al

(Escribir algo dos veces es casi siempre una mala idea)

Ejemplos

Extracciones aleatorias

Veamos como convertir en funciones lo que hemos visto en la clase anterior

Dividiremos este programa en dos partes:

  1. Una función definida por el usuario que genera una lista de variables aleatorias.
  2. La parte principal del programa que
    1. llama a esta función para obtener los datos
    2. traza los datos

Esto se realiza en el siguiente programa

Cuando el intérprete llega a la expresión generar_datos(100), ejecuta el cuerpo de la función con n igual a 100. El resultado neto es que el nombre datos está ligado a la lista e_valores devuelta por la función.

El resultado neto es que el nombre datos está ligado a la lista e_valores devuelta por la función.

Añadir condiciones

Nuestra función generar_datos() es bastante limitada.

Vamos a hacerla un poco más útil dándole la capacidad de devolver normales estándar o variables aleatorias uniformes en $ (0, 1) $ según sea necesario.

Esto se logra en la siguiente pieza de código.

Afortunadamente, la sintaxis de la cláusula if/else se explica por sí misma, y la sangría delimita de nuevo la extensión de los bloques de código.

Notas

Hay varias formas de simplificar el código anterior.

Por ejemplo, podemos deshacernos de los condicionales simplemente pasando el tipo de generador deseado como una función.

Para entender esto, considere la siguiente versión.

Ahora, cuando llamamos a la función generate_data(), pasamos np.random.uniform como segundo argumento.

Este objeto es una función.

Cuando se ejecuta la llamada a la función generar_datos(100, np.random.uniform), Python ejecuta el bloque de código de la función con n igual a 100 y el nombre tipo_generador generator_type "ligado" a la función np.random.uniform.

Este principio funciona de forma más general: por ejemplo, considere el siguiente fragmento de código

Aquí creamos otro nombre para la función incorporada max(), que luego podría utilizarse de la misma forma.

En el contexto de nuestro programa, la capacidad de vincular nuevos nombres a funciones significa que no hay problema en pasar una función como argumento a otra función-como hicimos anteriormente.

Ejercicio 1

La variable aleatoria binomial $ Y \sim Bin(n, p) $ representa el número de aciertos en $ n $ ensayos binarios, donde cada ensayo tiene éxito con probabilidad $ p $.

Sin ninguna importación aparte de from numpy.random import uniform, escribe una función que reporte un número sorteado entre $1$ y $n$, el número asignado a cada participantes. En otras palabras, sorteo_binomial_rv(n, p) debe reportar el número sorteado, i.e., el valor de $ Y $.

Ayuda: si $ U $ es uniforme en $ (0, 1) $ y la probabilidad, $ p \in [0,1] $, entonces la expresión U < p se evalúa como Verdadero con probabilidad $ p $.

Una posible solución:

Ejercicio 2

En primer lugar, escriba una función que devuelva una realización del siguiente dispositivo aleatorio

  1. Se lanza una moneda sin sesgo 10 veces.
  2. Si sale cara un número mayor o igual que k veces dentro de la misma secuencia, al menos un vez, paga un dólar.
  3. Si no, no pague nada.

Posible solucion

Ahora escriba otra función que realice la misma tarea excepto que la segunda regla del dispositivo aleatorio anterior pasa a ser

No uses ninguna importación aparte de from numpy.random import uniform.