viernes, 17 de noviembre de 2017

T2. FUNCIONES RECURSIVAS: PROBANDO, PROBANDO...

HONGOS Y HOJARASCA EN EL SOTOBOSQUE DE LA LAURISILVA DE LOS MONTES DE LOS REALEJOS, NOROESTE DE TENERIFE.

     Imaginémonos la siguiente situación: tomamos nuestro smartphone, lo encendemos y, al cabo de unos segundos, se nos pide en pantalla que introduzcamos nuestro número pin para dejarlo operativo. Sobre el teclado y el campo de introducción de clave se nos muestra, destacado, un mensaje de lo más inquietante: "Dispone usted de tres intentos". Da la casualidad de que la noche anterior estuvimos de celebración, vulgo farra o tenderete, y hoy nos hemos levantado un tanto embotados de los sentidos. Tecleamos los cuatro o cinco numeritos de marras... y nos sale el mensajito de error. ¡Mecachis! "Dispone usted de dos intentos", leemos con resacado estupor en la parte superior de la pantalla. Un par de segundos más tarde, tras rascarnos la desordenada pelambre, sonreímos, condescendientes con nosotros mismos. "Ya sé - pensamos -. El uno iba antes del dos, claro, y por eso metí la pata". Tecleamos nuestro pin de nuevo convencidos y ufanos de nuestra memoria y capacidades deductivas (¡bueno estaría!)...
"Error. Dispone usted de un intento". No nos lo podemos creer. Nos hemos vuelto a equivocar. O eso dice el maldito aparato de Satanás. ¿Por qué? ¿En qué? De la euforia postetílica pasamos al pánico en cuestión de nanosegundos. Se nos viene al caletre que existía un número, un tal puck o algo así, que venía en las instrucciones, o en la cajita,  o en el contrato, o en vaya usted a saber, o en algún sitio de ésos y que servía para desbloquear el móvil. Un número que, como pasa con Santa Bárbara, sólo nos acordamos de él cuando truena. Y ahora mismo está a punto de caernos un tormentón de los gordos. ¿Quién diantre se acuerda del número ése con nombre de duende? ¿Quién es el guapo/a que se lo aprende de memoria? ¡Venga ya, hombre!
Sudor frío, temblor de manos, pupilas dilatadas. Solo se escucha el tic tac del reloj del salón, a kilómetros de distancia. La mañana se ha vuelto gris y los pájaros, en el parque, han enmudecido su cantar.
Tras un largo minuto de indecisión y producto de un colosal esfuerzo de memoria abriéndonos paso en la espesura de una buena noche, llegamos a una conclusión: "¿Sería así? A lo mejor era así... El tres que venía después del uno, el ocho entre el dos y el tres, ... No podemos seguir así. Tomamos una decisión valiente, arriesgada, temeraria. Pedimos auxilio a la divinidad en nuestro fuero interno... y tecleamos el número con húmeda parsimonia. Nos sentimos al cabo de un juicio, cuando nos piden que nos pongamos en pie para escuchar la sentencia del juez.
Tomamos aliento antes de pulsar la tecla de "aceptar" que, de repente, se nos ha trasmutado en boca del infierno.
Cerramos los ojos, los apretamos con fuerza. Notamos que se nos llena la vejiga. Las palpitaciones se multiplican, se nos sube la tensión, se nos baja el azúcar... Y apretamos la dichosa tecla.
La ruedecilla gira. Transcurre un segundo. La ruedecilla gira. Pasa un segundo y medio. Somos presa de un ataque de ansiedad. "Nunca jamás en mi vida vuelvo a salir de noche. Palabra". La ruedecilla gira, y gira,...
¡Música! ¡La música! ¡Por fin ese repulsivo soniquete de m...da que ahora nos suena, mira tú por dónde, a música celestial! El pin es correcto.
La mañana del domingo vuelve a ser maravillosa.
¿Qué es lo que ha provocado todo esto en nosotros? Una función recursiva con su inseparable contador (de intentos).
La función recursiva, llamada recursiva o, simplemente, recursividad es aquella función definida por el usuario que es capaz de llamarse a sí misma a partir de una cierta condición, incluida en su propio bloque de código, en el cuerpo de la función.
Resultan particularmente útiles en determinados casos como el que hemos expuesto en el ejemplo, ingreso de claves o passwords, cálculos cerrados, etc. Python permite la creación de funciones recursivas capaces, como acabamos de decir, de llamarse a sí mismas de modo análogo a como invocaríamos a una función corriente.
Para el caso anterior, podríamos proponer la siguiente función recursiva:




Comenzamos en 1. declarando una función def a la que ponemos por nombre pin, y a la que pasamos un único argumento, un argumento opcional que se corresponde con una asignación, con la variable intento, a la que proporcionamos un valor inicial y por defecto de tipo numérico, un entero de valor 1.
Ya en 2. creamos el input, la entrada de datos por parte del usuario siguiendo la sintaxis habitual, declarando una variable, resp, a la que asignaremos como valor la respuesta proporcionada por el usuario.
En la siguiente línea de código, en 3., introducimos ya el condicional if donde la condición a valorar por el intérprete de Python será si la respuesta proporcionada por el usuario y que hemos almacenado en la variable resp, es distinta (operador de comparación !=) a la que hemos propuesto nosotros. Fijémonos que hemos pasado las cuatro cifras de nuestro código pin <<correcto>> en modo string, esto es, entrecomillado, a la manera de una cadena de texto o de caracteres literales, dado que, como ya sabemos, toda introducción de datos (input data) asume el tipo de dato string por defecto. de esta forma, caracterizando nuestro pin como "1234" como una cadena de caracteres, evitamos conflictos y código innecesario de tipado. Hasta aquí nada nuevo.
Ahora, en 4., colocamos un segundo condicional if, convenientemente indentado con respecto al condicional anterior (hablamos, pues, de un condicional anidado), del que es subsidiario. En este caso, la condición que establecemos es que el valor de la variable intento sea menor que 3. ¿Por qué 3? porque hemos decidido que el usuario disponga de un número máximo de tres intentos. Si hubiéramos decidido que, por ejemplo, dispusiera de cinco oportunidades, tendríamos que haber puesto el entero 5 a la derecha del operador de comparación.

PATIO TÍPICO CANARIO
A partir de ahora nos encontramos con el bloque de código (el correspondiente al del segundo if), donde le decimos al intérprete de Python lo que tiene que hacer. Lo primero de todo, en 5., es proporcionar un texto en pantalla mediante el recurso a la función print() en el que se nos advierte de que hemos cometido un error, e instándonos a que introduzcamos de nuevo el dichoso número pin.
En 6. tenemos una de las claves del script. Aquí introducimos el 'contacto'. Mediante el operador aritmético += sumamos una cantidad, en este caso, 2, a la variable intento aumentando su valor de 1, al comienzo pues, recordemos, éste era su valor inicial en la zona de argumentos de la función, a 3 (1 + 2).
Justo debajo, a continuación, tal y como señalamos a través del comentario adjunto a la línea de código, implementamos una llamada recursiva. Nuestra primera llamada recursiva, que nos devuelve a la línea uno del script de forma que el intérprete de Python inicie de nuevo el proceso (y de aquí el concepto de recursividad) mediante la llamada o invocación recurrente a la función pin.
Instauramos en 7. un tercer condicional if, (segundo nivel de anidación) debajo de la llamada recursiva (o retrollamada, como se la conoce también en algunas partes) a la función cuya condición es exactamente la misma que propusimos en la línea 4. del código: que intento sea menor que 3.
A renglón seguido, en 8., volvemos a echar mano del contador y repetimos el código que escribimos en 6., esto es, incrementar en 2 mediante el operador += en l valor de la variable intento.
Inmediatamente después, segunda llamada recursiva mediante la instrucción pin(intento) que ya empleamos en la llamada precedente.
Una vez efectuada la llamada, insertamos una cláusula else correspondiente a nuestro tercer if (¡cuidado con las indentaciones! 🔔). Dicha cláusula contendrá dos líneas de código: la primera, 9., se corresponde con un texto que se mostrará al usuario mediante la función print() y en el que le advertimos de que le resta tan sólo un último intento. La segunda línea es nuestra tercera y última llamada recursiva: pin(intento).
Colocamos una segunda cláusula else para cerrar convenientemente nuestro segundo condicional if, 10., y le ponemos como contenido único, la palabra reservada pass. La sentencia pass constituye un tipo específico de sentencia que le indica al intérprete de Python que no es necesario efectuar acción alguna; en resumidas cuentas, 'que no haga nada'. La sentencia pass se utiliza habitualmente cuando no queremos que se haga nada en una sentencia que requiere a su vez de de otra, como por ejemplo:

                                                                                    >>> while True: Pass *

* Esto es, lo que se llama, un bucle infinito. mientras (while) lo que quiera que se esté ejecutando (el análisis) arroje como resultado de la evaluación de la condición True, no se haga o ejecute nada.


      A TÍTULO INFORMATIVO, LA SENTENCIA PASS ES MUY SOCORRIDA CUANDO UN PROGRAMADOR ESTÁ DESARROLLANDO LA ESTRUCTURA BÁSICA DE UN PROGRAMA ANTES DE 'RELLENARLO', PUESTO QUE AÚN NO CONOCE LAS INSTRUCCIONES QUE LE DARÁN SU FORMA FINAL, Y LE CONVIENE MANTENER FUNCIONAL LA ESTRUCTURA PARA HACER TESTEOS. UNA VEZ COMPROBADO QUE LO CODIFICADO HASTA AHORA RESPONDE A SUS EXPECTATIVAS  GRACIAS A QUE LA SENTENCIA PASS LE HA PERMITIDO RECORRER SU ESTRUCTURA SIN NECESIDAD DE INSERTAR TANTO CÓDIGO. MÁS ADELANTE, SUSTITUIRÁ LA SENTENCIA POR LOS TROZOS DE CÓDIGO QUE ESTIME OPORTUNOS.

Colocamos en 11. la última cláusula else para el primero de nuestros condicionales if, y en 12. colocamos el texto que deberá imprimirse en pantalla: "NÚMERO DE PIN CORRECTO".

VISTA DEL VALLE DE AGUERE. TENERIFE.

Aquí mostramos otro ejemplo de de función recursiva más sencillo (y más zen, 👍):



Vamos a estudiar un último ejemplo de recursividad y, con él, dar por concluida esta entrada. Lo haremos de la mano de los profesores Andrés Marral e Isabel Gracia, en su libro Introducción a Python, Universitat Jaume I, apoyándose en una variante más científica: el cálculo recursivo de un factorial.
De nuevo,...¡Que no cunda el pánico! Veremos que resulta más fácil y asequible de lo que pudiéramos pensar.
Primero vamos a ver su formulación matemática:




✽ Donde cualquier número factorial N se representa así: N!, de tal modo que el factorial de 5, por ejemplo, se representa como 5!.
Digamos antes de proseguir que una FACTORIZACIÓN constituye una técnica matemática que nos permite hallar dos o más factores (divisores), cuyo producto sería igual a la expresión matemática que proponemos, un número entero, de acuerdo al máximo común divisor del mismo.
Toca ahora "traducir" la fórmula matemática que acabamos de mostrar a un bloque de código, un script, comprensible para el intérprete de Python. Quedaría algo así:



Donde n es el número entero que queremos factorizar. ¿Dónde está la recursividad? Imaginemos que pasamos el número 5 como argumento de la función factorial. Una vez comprobado que el valor de n no es ni 1 ni 0, en cuyo caso nos devolvería automáticamente el valor 1, el intérprete de Python comienza a calcular el factorial de 5. Pero, claro, cuando llega a la li´nea 5 del código... ¡Ostras! ¿Qué pasa aquí? Que le toca llamar el factorial de n-1, esto es, 5 - 4, luego el factorial de 4 para poder completar la expresión que hemos propuesto (n * factorial(n- 1)). Así que, "vuelta pa'rriba"y a calcular ahora el factorial de 4. El intérprete de Python comprueba que, en efecto, n - 1 no es 0 ni 1, y continúa leyendo el bloque de código. Comprueba en la cuarta línea de código que 4 es mayor que 1, pasa a la quinta... y de nuevo, topetazo: n - 1. Como ahora n era igual a 4, al restarle 1 se nos queda en 3, el nuevo valor que toma n ahora, y vuelta a empezar. Y así sucesivamente hasta que n=1. En este caso, por mor de la segunda y tercera línea del bloque de código, ya puede cerrar ese bucle, llegar a la línea sexta y devolver el resultado pues el intérprete de Python ya lo conoce. Ahora ya puede pasar al número siguiente, 2: ahora que ya sabe que el factorial de 1 es 1, pasa a la quinta línea y ejecuta el algoritmo, n * factorial(n - 1), esto es, 2 (que es el valor que tiene n en ese momento, ¡ojo!) * factorial(2 - 1), que deviene en, 2 * factorial(1). Y como el factorial de 1 es 1, , se nos queda en 2 * 1, que es igual a 2. El 2 será el siguiente número devuelto.

Ahora, el intérprete de Python, sabe que el factorial de 2 es 2, lo multiplicará por 3, que es el siguiente valor que adopta n, en virtud de la expresión que muestra la quinta línea de código, tal cual hemos visto con anterioridad. En consecuencia, devuelve el valor 6.
Ya tenemos que el valor de factorial 3 es 6. Lo multiplicará por 4, que es el valor de n que "toca" ahora, y nos devolverá 24 al ejecutar la sexta línea de script.
Y, por fin, llegamos ya al valor original de n, 5. Pues nada: el mismo procedimiento que se ha llevado con todos los enteros anteriores hasta el 1 (4, 3, 2, 1) y a consignar el resultado. En este caso, será 24 lo que multiplicará por 5. El valor de esta multiplicación es 120. Esta es la respuesta a factorial(5).
Veamos:




Con la inclusión de una simple función print() podemos visualizar impresa en pantalla la secuencia completa de factoriales parciales:


Mostramos a continuación un "árbol de llamadas" para clarificar aún más el proceso:



Podemos, sin embargo, resolver el cálculo de 5! factorial apoyándonos en un simple bucle for/in, esto es, sobre un iterador:



Con esto queremos decir que a toda función recursiva es posible "oponer" al menos, otra función iterativa que, al menos, independientemente de su legibilidad (recordemos, la claridad del código) y/o elegancia, suele mostrarse más eficiente que en sus versiones recursivas ahorrando en tiempo y uso de memoria.
Y ya que hemos tomado carrerilla, la propina final, apoyándonos en el texto y los autores que hemos mencionado con anterioridad. Nos proponen calcular el máximo común divisor (mcd) de los números 'n' y 'm' recurriendo para ello en el Algoritmo de Euclides.


     SEGÚN LO HE ESTUDIADO YO EN LA ACADÉMICA PERRUNA, EL ALGORITMO DE EUCLIDES VIENE A DECIR LO SIGUIENTE: "CALCULA EL RESTO DE DIVIDIR EL MAYOR DE LOS DOS NÚMEROS POR EL MENOR DE ELLOS. SI EL RESTO ES 0, ENTONCES EL MÁXIMO COMÚN DIVISOR (MCD) ES EL MENOR DE AMBOS NÚMEROS. SI EL RESTO ES DISTINTO DE 0, EL MÁXIMO COMÚN DIVISOR (MCD) DE n Y m ES EL MÁXIMO COMÚN DIVISOR DE OTRO PAR DE NÚMEROS: EL FORMADO POR EL MENOR DE n Y m Y POR DICHO RESTO". 




Los autores nos invitan a continuación a enfrentar el problema primero "a lápiz", con los dos números dados n=500 y m=218, y después, tratar su traducción al código de Python. Vamos allá:

  • Empezamos calculando el resto (%) de dividir 500 entre 218, el mayor entre el menor.El resto deviene en 64. Como el resto no es 0, aún no hemos terminado, tenemos que calcular el máximo común divisor de 218, el menor entre ambos números, y 64, el resto que acabamos de obtener.
  • Empezamos dividiendo 500 entre 218. Ahora toca dividir 218 entre 64. El resto de esta nueva división es 26, que no es 0, evidentemente. En consecuencia, debemos seguir calculando el máximo común divisor. Esta vez será entre 64 y el nuevo resto devuelto: 26.
  • El resto de dividir 64 entre 26 es 12, que tampoco, ¡vaya por Dios!, es 0. Pues nada: tenemos que seguir calculando el máximo común divisor entre 26 y 12.
  • Procedemos, y el resto de dividir ahora 26 entre 12 es 2. Tampoco es 0... y la punta del lápiz está ya casi gastada. Bueno, seguimos a ver: toca ahora dividir 12 entre 2.
  • El resto de dividir 12 entre 2 es... ¡0! ¡Por fin!¡Ya hemos llegado a 0! Ahora podemos asegurar henchidos de orgullo y gloria que el máximo común divisor de 500 y 218 es 2.


LAURISILVA  ABIERTA EN LOS MONTES DE GÜÍMAR, CENTRO SUR DE TENERIFE.

Con el ejemplo que acabamos de estudiar se hace explícitamente que una y otra vez resolvemos el mismo problema (he aquí la esencia misma de la recursividad: ejecutar una y otra vez un mismo algoritmo en factores diferentes hasta cumplir una determinada condición que provoca la deseada bifurcación), sólo que con datos diferentes. Si analizamos el algoritmo en términos de recursión encontramos que el caso base es aquél en el que el resto (%) de la división en 0 y, el caso general, cualquier otro.
Para construir el programa debemos saber primero cuál es el número mayor (máximo) y cuál es el menor (mínimo), por lo que nos vendrá bien definir antes funciones que efectúen ese cálculo:



Y completamos el programa con la función que calcula el Algoritmo de Euclides:



Como ya hemos aprendido, toda función recursiva puede ser traducida (y viceversa) a una iteración.

DIQUE Y CRESTA COMO CONSECUENCIA DE LA EROSIÓN SOBRE UNA EXTRUSIÓN VOLCÁNICA, SEPARANDO BARRANCOS EN LAS CUMBRES DE TAGANANA,  MACIZO DE ANAGA,  NORESTE DE TENERIFE.


jueves, 17 de agosto de 2017

T1. FUNCIÓN EVAL(). UNA ALIADA PARA CALCULAR SOBRE LA MARCHA.:

COSTA NORTE DE TENERIFE DESEE LAS ALTURAS DE SAN JOSÉ DE LOS LLANOS, CERCA DE EL TANQUE, CENTRO NORTE DE TENERIFE.

      La función eval() guarda ciertas similitudes con la función int(), que convierte una string, una cadena de caracteres literales, en un número entero, siempre que sea posible. En este aspecto su comportamiento es muy similar si pasamos un número entero (en base 10) como una cadena de caracteres, esto es, entrecomillados, tanto en un caso como en el otro:
Sin embargo, donde la función int() no puede devolver un entero cuando pasamos como literal un número decimal, por ejemplo, "16.61", la función eval() sí que puede hacerlo, por lo que actúa de manera análoga a como lo haría la función float(), que sí convierte a decimal un número de punto flotante que le pasemos como literal.
Observaremos en el ejemplo que mostramos a continuación que la función int() aplicada sobre un número decimal, devuelve solo la parte entera del número, obviando la decimal.



         OBSERVEMOS EL COMPORTAMIENTO PECULIAR DE int() Y eval() CUANDO LES PASAMOS CEROS POR EL LADO IZQUIERDO: EN EL PRIMER CASO, SE NOS DEVUELVE LA PARTE ENTERA; EN EL SEGUNDO, SE LANZA UN ERROR. COMPARTEN, SIN EMBARGO, UN COMPORTAMIENTO ANÁLOGO CUANDO TENEMOS CARACTERES LITERALES VACÍOS O CUANDO ESTOS SON "REALES". VEÁMOSLO:



Como sugiere su nombre, estamos ante una función evaluadora, que evalúa, en esta ocasión, strings (cadenas de texto) que pueden contener cualquier tipo de dato válido en Python (listas, tuplas, conjuntos, etc.), incluidas expresiones aritméticas o algoritmos, devolviéndonos un resultado que mostrará el tipo de dato que podríamos obtener si eliminásemos las comillas. Veamos un ejemplo:


Como ya hemos visto en ejemplos anteriores, cuando eval() no es capaz de determinar el tipo de dato que se le pasa como argumento, se mostrará un error.
Su mayor y más palmaria utilidad pasa por emplear la función eval() inclusa en funciones que suelen ingresar o devolver cadenas, como la función input():

                                                                   x = int(eval(input("Introduce un número entero: ")))

Observemos estas propuestas de uso de la función eval():



Lo que sucede es que el intérprete de Python es capaz de "entender" la expresión o algoritmo que pasamos como argumento como tipo de dato string, manejando incluso elementos externos, como observamos en la ´lista' del ejemplo.
Terminamos con un ejemplo más elaborado. No importa que aún no entendamos la sintaxis def que se muestran: quedémonos por ahora con el resultado:




viernes, 11 de agosto de 2017

T2. LAS FUNCIONES LAMBDA: ELOGIO DE LA SENCILLEZ.

PINAR Y MONTEVERDE EN LAS LADERAS ALTAS DE LOS SILOS, NOROESTE DE TENERIFE, CON LA ISLA DE LA PALMA ASOMANDO AL FONDO, POR ENCIMA DEL MAR DE NUBES.

      Las funciones lambda constituyen un tipo especial de función dentro del género de funciones predefinidas en Python. A parte de una sintaxis exclusiva, las funciones lambda  se caracterizan por una función anónima cuando se le asigna una variable concreta.
Dichas funciones ejecutan una expresión y devuelve el resultado de la misma. Pueden llevar o no parámetros, igual que las funciones def que ya conocemos, y cuando los lleva suelen ser casi siempre nombres de variables (recordemos: referencias a espacios de memoria que almacenan objetos. Y prácticamente todo, incluidas las propias funciones, son objetos en Python:  variables, conjuntos, tuplas, números, bytes, listas, etc.) separados por comas: señalemos que en el momento en que separamos por comas unas variables de otras, éstas se convierten automáticamente en "argumentos posicionales", lo que nos obliga a respetar su orden para cumplir con aquéllo de "cada oveja con su pareja", "cada variable con su dato".
Se les puede aplicar toda la estructura de argumentos que soportan las funciones def y que hemos visto con anterioridad dotándola de una alta operatividad. Sin embargo, cuenta con restricciones importantes como que la expresión no puede contener bucles (for/in y while) aunque sí permite el uso del condicional if. Tampoco utiliza la sentencia return que, recordemos, constituye el pie de las funciones definidas por el usuario.
Dicho esto, veamos ahora la estructura sintáctica de las funciones lambda:


Las funciones lambda, más que una sentencia, constituye en sí misma una expresión, casi un simple algoritmo como cualquier fórmula física que se nos ocurra, una velocidad = espacio/tiempo, para entendernos, donde anteponemos la palabra reservada lambda, coma para que se note, los valores (o variables que almacenan esos mismos valores), dos puntos (no hay indentado) y la expresión (algoritmo) a ejecutar.
La siguiente tabla nos puede resultar particularmente esclarecedora:




La utilidad de las funciones lambda estriba en que nos permite definir una función dentro del propio código, es decir, de manera implícita al mismo (inline) sin más aditamentos sintácticos, por lo que en comparación con las funciones def que ya conocemos, desaparecen, desde el punto de vista de la estructura sintáctica, el cuerpo y el pie de la función (no existe sentencia return que valga) y, en consecuencia, la indentación o sangrado. Por esta razón, nos vienen de perilla para resolver de manera ágil cierto tipo de situaciones, por ejemplo, cuando elaboramos una lista sobre la que queremos ejecutar distintas operaciones y decidirnos por una u otra según nuestra conveniencia; también cuando nos toca efectuar una serie de cálculos concretos con variables similares durante la elaboración de un programa; cuando queremos crear un diccionario por defecto, etc...
Vayamos viendo ejemplos: supongamos que se nos presenta la opción de diseñar dos funciones def para obtener dos resultados con los que operar más tarde y obtener el volumen de una pirámide. Para ello, necesitamos conocer el área de la base (cuadrada) y el área de una de las cuatro caras de la pirámide (triángulo isósceles). Como sólo nos interesa el resultado, podríamos proceder de la siguiente forma:



En 1. generamos una lista de variable homónima (lista) con sendas listas (sublistas) anidadas a nivel 1 en su interior. Cada una de ellas contiene una función lambda con su estructura prototipo: la palabra reservada lambda; el parámetro correspondiente; los dos puntos y la expresión correspondiente a lo que queramos calcular, en el primer caso, lado por lado para hallar el área de la base; y en el segundo, lado por altura entre dos para hacer lo propio con el triángulo.
A continuación, en 2. y 3. creamos dos nuevas variables x e y, para llamar a cada una de estas listas anidadas por su índice, [0] para la primera y [1] para la segunda.
En 4., mediante la asignación abreviada, establecemos los valores correspondientes a cada parámetro: lado y altura.
En 5. y 6. construimos  un bucle for/in, por supuesto fuera de la expresión lambda que, como ya sabemos, igual que sucede con el bucle while, no los admite, para cada una de las funciones lambda. Notemos que a la hora de llamar a la función print(), junta a la palabra  'item' que señala a los elementos de la función lambda pasamos el argumento correspondiente entre paréntesis.
Obviamente, puestos a poner ejemplos, aquí mostramos un par más, en esta ocasión, todo un canto a la sencillez. La mayor parte de las veces, cuando estemos programando y se nos ocurra recurrir a una función lambda para solventar una situación, será para introducir códigos tan sencillos como éstos:


PAREIDOLIA DE UN ROSTRO EN LA PARED EN LOS TAJOS EN EL MONTEVERDE DE VALLE BROSQUE, PARTE ALTA DEL BARRANCO, SUR DE ANAGA.
Las funciones lambda pueden ser empleadas sin ningún tipo de objeción cuando se las requiere desde otra función, incluso desde una función definida por el usuario tratándolas como simples expresiones. Veamos un par de casos:



Como vemos, todo resulta muy sencillo. Y aún más asociando directamente la expresión lambda a la sentencia return por aquello de la elegancia y el ahorro de código.

   
      Una función lambda actúa u opera sobre un único elemento, x, en base a la sintaxis que ya conocemos: lambda x: expresión_x.¿Guau?..., digo...¿Vale?
Para que una función lambda recorra los ítems de un iterable (x es ahora cada uno de los ítems de la secuencia), nos viene bien echarle la pata..., esto..la mano a la función built-in de Python map(), que le confiere una capacidad o potencial iterador sobre una secuencia que pasamos como segundo argumento de la función:

                                       map(función lambda, secuencia)


      

      LA FUNCIÓN map():

   
      Podemos recurrir a la función integrada map() para pasar como parámetros la función lambda a otra función de corte más convencional. La función map(), como podemos ver en el esquema inferior, recibe dos parámetros: el primero, 'func', es una función, en nuestro caso, la función lambda con toda su sintaxis; el segundo, la variable que almacena todos los elementos (items) a los que se le va a aplicar la función, (*iterables). Por ello, normalmente la aplicaremos sobre secuencias de datos, como listas, por ejemplo.
Hemos enmarcado en rojo la literatura que realmente nos interesa de toda la información que nos proporciona el recurso a la función de ayuda help() sobre la clase map (v. type(), al final) y que, traducido, viene a decir lo siguiente: construye un iterador que computa la función lambda aplicándolo a los argumentos de cada iterable (lista, conjunto,...). Concluye cuando se ha iterado por todos los ítems del iterable.



Veamos un ejemplo de su funcionamiento:



En 1. construimos un iterable, en este caso, una lista asociada a la variable 'decenas' que contiene 5 ítemes, todos ellos números enteros (int). En 2. declaramos una variable para poder invocarla más adelante, a la que llamamos en el colmo de la originalidad, lista_decenas y a la que le asignamos la función map(). Como ya sabemos, la función map() llevará dos argumentos: una función lambda, con su sintaxis propietaria y, tras la coma de rigor que separa los argumentos, el nombre de la variable que almacena el iterable que construimos en 1., 'decenas',que es el la lista que contiene los distintos ítems sobre los que queremos aplicar la función lambda. Ya en 3. instauramos el consiguiente bucle for/in para que actúe sobre la nueva variable que creamos justo en la línea de código anterior, lista_decenas. Finalmente, solicitamos un print() para comprobar que todo ha ido bien.
No parece que sea muy complicado, ¿verdad?
Mostraremos un par de ejemplos más. En ambos casos, el iterable será una tupla.



Para finalizar, en el ejemplo que sigue cambiamos una función lambda por una función definida por el usuario. Comprobamos su operatividad, aunque su utilidad es muy limitada.



   Algunas cosillas nos quedan por saber sobre la función map() de Python. No tiene que ver con la función lambda que da título a este apartado, por lo que hemos decidido dedicarle una entrada propia, entre otras funciones conectadas.

EL RARO TAJINASTE AZUL EN PLENA FLORACIÓN. PARQUE NACIONAL DE LAS CAÑADAS DE EL TEIDE.

      LA INCLUSIÓN DEL CONDICIONAL if:

      
      No es muy habitual, aunque sí resulta factible, incluir una condición dentro de una función lambda, aunque su devolución no destacará por su finura, precisamente.




      EL MÉTODO sort() DE LAS LISTAS Y LA FUNCIÓN lambda. LA FUNCIÓN sorted():

      
      Las funciones lambda se suelen emplear, como expresiones que son al fin y al cabo, como funciones clave para la función integrada sorted() e, incluso, para el método sort() de las listas. Veamos un ejemplo: imaginemos que tenemos una lista, que llamaremos citysport, con los nombres y dorsales de varios jugadores del equipo de fútbol "City Sport", almacenados dentro de un tipo de dato tupla (2-tupla):

                                                   citysport = [(4, "Raúl"), (1, "Javier"), (7, "Gonzalo"), (2, "Saúl")]

Esta lista citysport puede ordenarse por su propio método:


Tal y como podemos ver se produce un ordenamiento de acuerdo al orden numérico, de menor a mayor, y no por el orden alfabético.
La función sorted() puede, por su parte, ella solita, proporcionarnos una función clave para cambiar el orden. Para ello podríamos definir una función que ejerza como tal, pero la solución más común pasa por recurrir a una función lambda:



El procedimiento es harto simple: como vemos en 1., llamamos al método sort() de las listas y le pasamos como argumento una variable que llamamos 'key' y a la que le asignamos toda una función lambda hecha y derecha con su sintaxis prototipo. Se hacen imprescindibles los paréntesis cuando la expresión sea una tupla, por lo que insertamos entre ellos las llamadas a los índices, separados por comas, al solicitar la lista con la nueva ordenación, se nos mostrará un resultado similar al ejemplo anterior. Si queremos ordenar los ítems de acuerdo al alfabeto nos basta con tan sólo invertir el orden de los índices, tal y como hemos hecho en 2. y... ¡Conseguido!
Podemos, incluso, por qué no, con tuplas que contengan todos los elementos que queramos. Ponemos un ejemplo:



También, si podemos trabajar con índices, podemos hacerlo igualmente con rebanadas (slices), tan poco que nos acordamos de ellas y tanto que nos pueden resolver. De muestra, un botón:



Terminamos haciendo una referencia a la función integrada sorted() de Python. Esta función realiza la misma función, valga la redundancia, que el método sort() de las listas, pero sin necesidad de invocar al método, es decir, realiza el ordenamiento de manera automática con sólo pasar la secuencia que queramos ordenar como argumento de la misma. Aquí tenemos un ejemplo:




Teniendo en cuenta que la función lambda es una función de nivel superior, esto es, que lleva como argumento una función, podemos construir un código alternativo a una cascada de condicionales if/elif/else, muy elegante, por cierto, y con un punto de complejidad, del siguiente modo:


Así, a través del método get() de los diccionarios, podemos seleccionar una operación aritmética entre las cuatro posibles y pasarle sus respectivos valores.

TABAIBAS Y ALMENDROS EN EL PAISAJE DE VALLE DE SANTIAGO, OESTE DE TENERIFE.

Rematamos la faena proponiendo el visionado a continuación, en la siguiente entrada, de las funciones recursivas: T2. LAS FUNCIONES RECURSIVAS: PROBANDO, PROBANDO...


sábado, 15 de julio de 2017