Functions and recursion - Learn Python 3 - Snakify

Lesson 8
Funciones y recursión


1. Funciones

Recuerde que en matemáticas el factorial de un número n se define como n! = 1 ⋅ 2 ⋅ ... ⋅ n (como el producto de todos los números enteros de 1 a n). Por ejemplo, 5! = 1 ⋅ 2 ⋅ 3 ⋅ 4 ⋅ 5 = 120. Está claro que factorial es fácil de calcular, usando un bucle for. Imagine que necesitamos en nuestro programa calcular el factorial de varios números varias veces (o en diferentes lugares de código). Por supuesto, puede escribir el cálculo del factorial una vez y luego usar Copiar y Pegar para insertarlo donde lo necesite:

# calcular 3!
res = 1
for i in range(1, 4):
    res *= i
print(res)

# calcular 5!
res = 1
for i in range(1, 6):
    res *= i
print(res)

Sin embargo, si cometemos un error en el código inicial, este código erróneo aparecerá en todos los lugares donde hemos copiado el cálculo de factorial. Además, el código es más largo de lo que podría ser. Para evitar volver a escribir la misma lógica en los lenguajes de programación, hay funciones.

Las funciones son las secciones de código que están aisladas del resto del programa y se ejecutan solo cuando se las llama. Ya conociste la función sqrt() , len() e print() . Todos tienen algo en común: pueden tomar parámetros (cero, uno o varios) y pueden devolver un valor (aunque es posible que no regresen). Por ejemplo, la función sqrt() acepta un parámetro y devuelve un valor (la raíz cuadrada del número dado). La función print() puede tomar varios números de argumentos y no devuelve nada.

Ahora queremos mostrarle cómo escribir una función llamada factorial() que toma un único parámetro - el número, y devuelve un valor - el factorial de ese número.

def factorial(n):
    res = 1
    for i in range(1, n + 1):
        res *= i
    return res

print(factorial(3))
print(factorial(5))

Queremos brindar algunas explicaciones. Primero, el código de función debe colocarse al principio del programa (antes del lugar donde queremos usar la función factorial() , para ser precisos). La primera línea def factorial(n): de este ejemplo es una descripción de nuestra función; la palabra factorial es un identificador (el nombre de nuestra función). Justo después del identificador, aparece la lista de parámetros que recibe nuestra función (entre paréntesis). La lista consta de identificadores de los parámetros separados por comas; en nuestro caso, la lista consta de un parámetro n . Al final de la fila, pon dos puntos.

Luego va el cuerpo de la función. En Python, el cuerpo debe estar indentado (mediante tabulador o cuatro espacios, como siempre). Esta función calcula el valor de n! y lo almacena en la variable res . La última línea de la función es return res , que sale de la función y devuelve el valor de la variable res .

La declaración de return puede aparecer en cualquier lugar de una función. Su ejecución sale de la función y devuelve el valor especificado al lugar donde se llamó a la función. Si la función no devuelve un valor, la declaración de retorno no devolverá realmente ningún valor (aunque todavía se puede usar). Algunas funciones no necesitan devolver valores, y la declaración de retorno se puede omitir.

Nos gustaría brindar otro ejemplo. Aquí está la función max() que acepta dos números y devuelve el máximo de ellos (en realidad, esta función ya se ha convertido en parte de la sintaxis de Python).

def max(a, b):
    if a > b:
        return a
    else:
        return b

print(max(3, 5))
print(max(5, 3))
print(max(int(input()), int(input())))

Ahora puede escribir una función max3() que toma tres números y devuelve el máximo de ellos.

def max(a, b):
    if a > b:
        return a
    else:
        return b

def max3(a, b, c):
    return max(max(a, b), c)

print(max3(3, 5, 4))

La función incorporada max() en Python puede aceptar varios números de argumentos y devolver el máximo de ellos. Aquí hay un ejemplo de cómo se puede escribir dicha función.

def max(*a):
    res = a[0]
    for val in a[1:]:
        if val > res:
            res = val
    return res

print(max(3, 5, 4))
Todo lo que se pasa a esta función reunirá los parámetros en una sola tupla llamada a , que se indica con un asterisco.
Advertising by Google, may be based on your interests

2. Variables locales y globales

Dentro de la función, puede usar variables declaradas en algún lugar fuera de ella:

def f():
    print(a)

a = 1
f()

Aquí la variable a se establece en 1, y la función f() imprime este valor, a pesar de que cuando declaramos la función f esta variable no se inicializa. La razón es que, en el momento de llamar a la función f() (la última cadena), la variable a ya tiene un valor. Es por eso que la función f() puede mostrarlo.

Tales variables (declaradas fuera de la función pero disponibles dentro de la función) se llaman globales .

Pero si inicializa alguna variable dentro de la función, no podrá usar esta variable fuera de ella. Por ejemplo:

def f():
    a = 1

f()
print(a)

Recibimos el error NameError: name 'a' is not defined . Tales variables declaradas dentro de una función se llaman locales . No estarán disponibles después de salir de la función.

Lo realmente encantador aquí es lo que sucede si cambias el valor de una variable global dentro de una función:

def f():
    a = 1
    print(a)

a = 0
f()
print(a)

Este programa te imprimirá los números 1 y 0. A pesar de que el valor de la variable a cambió dentro de la función, ¡fuera de la función sigue siendo el mismo! Esto se hace con el fin de «proteger» las variables globales de los cambios de función inadvertidos. Por lo tanto, si se modifica alguna variable dentro de la función, la variable se convierte en una variable local y su modificación no cambiará una variable global con el mismo nombre.

Más formalmente: el intérprete de Python considera una variable local para la función, si en el código de esta función hay al menos una instrucción que modifica el valor de la variable. Entonces esa variable tampoco puede usarse antes de la inicialización. Instrucciones que modifican el valor de una variable: los operadores = , += y el uso de la variable como un bucle for parámetro. Sin embargo, incluso si la instrucción variable cambiante nunca se ejecuta, el intérprete no puede verificarla y la variable sigue siendo local. Un ejemplo:

def f():
    print(a)
    if False:
        a = 0

a = 1
f()

Se produce un error: UnboundLocalError: local variable 'a' referenced before assignment . A saber, en la función f() el identificador a convierte en una variable local, ya que la función contiene el comando que modifica la variable a . La instrucción de modificación nunca se ejecutará, pero el intérprete no la verificará. Por lo tanto, cuando intenta imprimir la variable a , apela a una variable local no inicializada.

Si desea que una función pueda cambiar alguna variable, debe declarar esta variable dentro de la función utilizando la palabra clave global :

def f():
    global a
    a = 1
    print(a)

a = 0
f()
print(a)

Este ejemplo imprimirá la salida de 1 1, porque la variable a se declara como global, y al cambiarla dentro de la función se produce el cambio global.

Sin embargo, es mejor no modificar los valores de las variables globales dentro de una función. Si su función debe cambiar alguna variable, permita que devuelva este valor, y usted elige cuando llama a la función asigna explícitamente una variable a este valor. Si sigue estas reglas, la lógica de las funciones funciona independientemente de la lógica del código, y por lo tanto, tales funciones se pueden copiar fácilmente de un programa a otro, ahorrándole tiempo.

Por ejemplo, supongamos que su programa debe calcular el factorial del número dado que desea guardar en la variable f. Así es como no debes hacerlo:

def factorial(n):
    global f
    res = 1
    for i in range(2, n + 1):
        res *= i
    f = res

n = int(input())
factorial(n)
print(f)
# haciendo otras cosas con variable f

Este es el ejemplo de código incorrecto , porque es difícil utilizarlo en otro momento. Si mañana necesita otro programa para usar la función "factorial", no podrá copiar esta función desde aquí y pegar su nuevo programa. Deberá asegurarse de que ese programa no contenga la variable f .

Es mucho mejor reescribir este ejemplo de la siguiente manera:

# inicio de fragmento de código que se puede copiar de programa a programa
def factorial(n):
    res = 1
    for i in range(2, n + 1):
        res *= i
    return res
# final de pieza de código

n = int(input())
f = factorial(n)
print(f)
# haciendo otras cosas con variable f

Es útil decir que las funciones pueden devolver más de un valor. Aquí está el ejemplo de devolver una lista de dos o más valores: <

return [a, b]

Puede llamar a la función de dicha lista y usarla en asignaciones múltiples: <

n, m = f(a, b)

    
Advertising by Google, may be based on your interests

3. Recursión

Como vimos anteriormente, una función puede llamar a otra función. ¡Pero las funciones también pueden llamarse a sí mismas! Para ilustrarlo, considere el ejemplo de la función de computación factorial. Es bien sabido que 0! = 1, 1! = 1. Cómo calcular el valor de n! para grande n? Si pudiéramos calcular el valor de (n-1) !, entonces calculamos fácilmente n! Ya que n! = N⋅ (n-1) !. Pero, ¿cómo calcular (n-1)? Si hemos calculado (n-2) !, entonces (n-1)! = (N-1) ⋅ (n-2) !. ¿Cómo se calcula (n-2)? Si ... Al final, llegamos a 0 !, que es igual a 1. Por lo tanto, para calcular el factorial podemos usar el valor del factorial para un entero más pequeño. Este cálculo se puede hacer usando Python:

def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

print(factorial(5))        

La situación cuando la función se llama a sí misma se llama recursividad , y dicha función se llama recursiva.

Las funciones recursivas son un mecanismo poderoso en la programación. Desafortunadamente, no siempre son eficaces y a menudo conducen a errores. El error más común es la recursión infinita , cuando la cadena de llamadas a la función nunca termina (bueno, en realidad, termina cuando se queda sin memoria libre en su computadora). Un ejemplo de recursión infinita: <

def f():
    return f()
 Las dos razones más comunes que causan la recursión infinita: 
  1. Condición de detención incorrecta. Por ejemplo, si en el programa de cálculo factorial olvidamos poner el cheque if n == 0 , entonces factorial(0) llamará factorial(-1) , que llamará factorial(-2) , etc.
  2. Llamada recursiva con parámetros incorrectos. Por ejemplo, si la función factorial(n) llama a la función factorial(n) , también obtendremos una cadena infinita de llamadas.

Por lo tanto, al codificar una función recursiva, primero es necesario asegurarse de que llegue a sus condiciones de detención: pensar por qué su recursión terminará alguna vez.

Advertising by Google, may be based on your interests