Qué es una función

Enterate del próximo post
Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.

Un mail cuando publico algo nuevo. Sin spam y te podés dar de baja cuando quieras.
Antes de arrancar un lavarropas, alcanza con elegir un programa y cargar la ropa. Adentro pasan varias cosas -el tambor gira, el agua se calienta a la temperatura que pide el programa elegido, el detergente se disuelve y circula- que no hace falta conocer para terminar con la ropa limpia. Alcanza con saber qué hace falta poner adentro y qué se va a obtener al final; cómo se logra ese resultado queda completamente oculto detrás de la puerta.
Una función, en el sentido que interesa acá, funciona igual: recibe algo, hace algo con eso que no hace falta ver desde afuera, y entrega un resultado.
Desde el primer post de la serie, cada bloque de pseudocódigo arranca con la palabra algoritmo seguida de un nombre y, entre paréntesis, los datos que necesita para trabajar. Ese molde -nombre, entrada, un cuerpo de pasos, y casi siempre un devolver al final- es exactamente lo que se llama una función: una porción de un algoritmo con nombre propio, que recibe cero o más valores (los parámetros), hace algo con ellos siguiendo sus propios pasos internos, y entrega un resultado.
Lo que separa a una función de una simple lista de pasos sueltos es que queda encapsulada: usarla solo requiere conocer dos cosas -qué hay que darle (los parámetros) y qué va a devolver (el resultado)-, sin necesidad de leer ni entender ningún paso de su interior. La descomposición (post 3 de esta serie) ya usaba esta idea sin nombrarla: dividir un problema grande en sub-algoritmos más chicos y resolver cada uno por separado con llamar a ... con ... para obtener ... funciona precisamente porque cada sub-algoritmo puede tratarse como una caja cerrada.
Los parámetros se comportan como las variables del post 8 (variables y estado): son nombres cuyo valor puede cambiar, con una diferencia puntual, el valor inicial no lo fija ningún paso adentro de la función, sino cada llamada en el momento en que ocurre. Cada llamada puede pasarles valores distintos a los mismos parámetros, y cualquier variable que la función cree en su interior existe solo mientras dura esa llamada: cuando la función termina, esa variable desaparece, y la próxima llamada arranca de cero, sin memoria de la anterior.
Esa combinación -una entrada declarada, un interior oculto, una salida definida- es lo que permite reusar una función: se escribe una sola vez y se llama todas las veces que haga falta, cada vez con datos distintos, sin volver a escribir ni un paso.
Una función que calcula el área de un rectángulo recibe dos medidas y devuelve un número:
algoritmo area_rectangulo(base, altura):
multiplicar base por altura
devolver el resultadoLa misma función sirve para cualquier rectángulo, sin tocarle una sola línea:
llamar a area_rectangulo con 4 y 3 para obtener area_living
# area_living: 12
llamar a area_rectangulo con 10 y 2 para obtener area_cocina
# area_cocina: 20Las dos llamadas ejecutan exactamente los mismos dos pasos internos, con base y altura tomando un valor distinto cada vez. Nada de lo que pasó en la primera llamada (los valores 4 y 3, o el resultado 12) sobrevive a la segunda: area_rectangulo no recuerda nada entre una llamada y otra.
El siguiente ejemplo usa además una variable propia, creada adentro de la función:
algoritmo precio_con_descuento(precio, porcentaje):
multiplicar precio por porcentaje
asignar el resultado a descuento
restar descuento a precio
devolver el resultadollamar a precio_con_descuento con 1000 y 0.1 para obtener precio_final da 900: primero calcula que el 10% de 1000 son 100 (descuento), y después resta esos 100 al precio original. descuento solo existe durante esa llamada puntual: no queda ningún rastro de ella disponible para la próxima vez que se llame a la función, ni con el mismo precio ni con uno distinto. Llamar a precio_con_descuento no requiere saber que existe una variable intermedia llamada descuento: alcanza con saber que, dado un precio y un porcentaje, la función devuelve el precio ya descontado.
Definir una función perimetro_rectangulo que reciba el largo y el ancho de un terreno rectangular y devuelva su perímetro (la suma de los cuatro lados).
Pensar primero qué necesita recibir la función para poder calcular el perímetro, y qué tiene que entregar al terminar, antes de escribir ningún paso interno.
algoritmo perimetro_rectangulo(largo, ancho):
sumar largo y ancho
multiplicar el resultado por 2
devolver el resultadollamar a perimetro_rectangulo con 8 y 5 para obtener perimetro_terreno da 26: la suma de los dos lados (13) multiplicada por dos. Igual que area_rectangulo, esta función queda lista para usarse con cualquier largo y ancho, sin ninguna referencia al terreno puntual de este ejemplo escrita en su interior.
Un negocio necesita saber cuánto va a costar pintar una pared rectangular, conociendo sus medidas y el precio de la pintura por metro cuadrado.
Pensar si hace falta calcular el área de la pared paso por paso otra vez, o si ya existe algo que resuelve exactamente esa parte del problema.
algoritmo costo_pintura(base_pared, altura_pared, precio_por_metro):
llamar a area_rectangulo con base_pared y altura_pared para obtener area
multiplicar area por precio_por_metro
devolver el resultadollamar a costo_pintura con 4, 2.5 y 1500 para obtener costo_living da 15000: area_rectangulo calcula los 10 metros cuadrados de la pared, y costo_pintura los multiplica por el precio del metro. costo_pintura nunca vuelve a escribir "multiplicar base por altura": llama a la función que ya resuelve esa parte y sigue trabajando con el resultado. Leer costo_pintura tampoco requiere mirar adentro de area_rectangulo para confiar en el número que devuelve: alcanza con el contrato ya establecido en el ejemplo anterior, dos medidas entran, un área sale.
Una función es un contrato: qué necesita recibir y qué promete devolver, sin comprometerse con cómo llega de una cosa a la otra. Ese contrato es lo que permite usarla sin releer su interior, volver a llamarla con datos distintos las veces que haga falta, y construir problemas más grandes llamando a otras funciones ya resueltas.
El próximo post de la serie vuelve sobre esta misma idea de función para hacerle una pregunta distinta: además de devolver un resultado, ¿puede una función cambiar algo por fuera de sí misma sin que se note desde donde se la llama? Esa distinción, entre una función que solo calcula y entrega un valor y otra que además modifica algo del mundo a su alrededor, es la que separa una función pura de una que no lo es.