Cafetera espresso con granos de café entrando por arriba y una taza de café saliendo por el surtidor, como metáfora de una función que recibe datos y devuelve un resultado

Las funciones en programación: parámetros, retorno y scope

· ⏱ 9+ min lectura

Cuando te das cuenta de que repites

En las entregas anteriores de la serie de Fundamentos de programación montamos programas que guardan datos, deciden y repiten. Si has practicado un poco, ya te habrá pasado algo: te encuentras escribiendo el mismo trozo de código en tres sitios distintos. Calcular un IVA aquí, validar un email allá, formatear una fecha en otro lado.

Y eso es un problema. Porque si mañana cambia la forma de calcular el IVA, tienes que acordarte de tocarlo en los tres sitios. Te dejas uno, y tienes un bug. La solución a esto es una de las ideas más importantes de toda la programación: las funciones.

Qué es una función

Una función es un trozo de programa empaquetado, con nombre, que puedes ejecutar siempre que quieras. La escribes una vez y la llamas mil. Piensa en ella como una pequeña máquina: le metes algo por un lado, hace su trabajo dentro, y te devuelve un resultado por el otro.

En la primera entrega ya vimos la idea sin nombrarla: la abstracción, eso de “hacer café” sin pensar cada vez en los diez pasos. Una función es abstracción pura. Empaquetas “hacer café” una vez, le pones nombre, y a partir de ahí solo dices “haz café” y listo.

FUNCION saludar()
  mostrar "Hola, bienvenido"
FIN FUNCION

saludar()    // esto la "llama": ejecuta su contenido
saludar()    // y la puedes llamar las veces que quieras

Definir la función (escribir lo que hace) y llamarla (ejecutarla) son dos cosas distintas. Defines una vez, llamas tantas veces como necesites. Ahí está el ahorro.

Parámetros y argumentos: darle datos a la función

Una función que siempre hace exactamente lo mismo es útil, pero limitada. Lo potente es poder pasarle datos para que trabaje con ellos. Esos datos de entrada son los parámetros:

FUNCION saludar(nombre)
  mostrar "Hola, " + nombre
FIN FUNCION

saludar("Marcos")    // muestra: Hola, Marcos
saludar("Lucia")     // muestra: Hola, Lucia

Aquí conviene una distinción que confunde a todo el mundo y que en realidad es trivial:

  • El parámetro es el hueco que defines en la función: nombre. Es la etiqueta de “aquí va a entrar algo”.
  • El argumento es el valor real que le pasas al llamarla: "Marcos", "Lucia". Es lo que metes de verdad en ese hueco.

Parámetro es el molde, argumento es lo que echas en el molde. Una función puede tener varios parámetros separados por comas, o ninguno.

El valor de retorno: lo que la función te devuelve

Hasta ahora las funciones hacían algo (mostrar un mensaje). Pero muchas veces lo que quieres es que la función calcule un resultado y te lo dé para usarlo después. Eso es el valor de retorno, con la palabra retornar (return):

FUNCION sumar(a, b)
  resultado = a + b
  RETORNAR resultado
FIN FUNCION

total = sumar(3, 4)    // total vale 7
mostrar total

sumar(3, 4) se va, hace su cuenta y devuelve 7, que guardamos en total. Aquí está la metáfora de la máquina completa: a y b entran, dentro se procesan, y un resultado sale. Una función bien hecha suele recibir datos y devolver un resultado, sin liarse en nada más.

Detalle importante: cuando el programa llega a return, la función termina ahí mismo. Lo que haya escrito después no se ejecuta. Esto se aprovecha mucho, por ejemplo en las guard clauses que vimos en los condicionales.

Ámbito (scope): dónde vive cada variable

Aquí entra un concepto que pilla a todo principiante: el ámbito o scope. Resumido: una variable creada dentro de una función solo existe dentro de esa función. Fuera, no existe.

FUNCION calcular()
  x = 10        // x es LOCAL a calcular()
  mostrar x     // funciona: 10
FIN FUNCION

calcular()
mostrar x        // ERROR: aquí fuera, x no existe

A la variable x se le llama local: nace cuando entra la función y muere cuando la función termina. Esto, lejos de ser un fastidio, es una bendición: significa que cada función es una caja aislada, y puedes usar una variable x en veinte funciones distintas sin que se pisen entre ellas.

Lo contrario es una variable global, declarada fuera de toda función, que sí se ve desde cualquier parte. Suena cómodo, pero abusar de las globales es fuente de bugs endiablados: cualquier función puede cambiarlas y cuesta saber quién las tocó. Regla de oro para empezar: usa locales siempre que puedas, y globales lo mínimo imprescindible.

Por valor o por referencia

Cuando pasas un dato a una función, hay dos formas en que puede viajar, y entenderlas evita errores muy desconcertantes:

  • Por valor: la función recibe una copia del dato. Si la cambia dentro, el original de fuera no se entera. Suele pasar con los tipos simples (números, booleanos).
  • Por referencia: la función recibe el dato original (en realidad, una pista de dónde está). Si lo cambia dentro, el de fuera también cambia. Suele pasar con las colecciones, como las listas y diccionarios que veremos en la próxima entrega.

No te agobies con esto ahora. Solo quédate con la alarma: si una función cambia algo y notas que “por arte de magia” ha cambiado también fuera, casi seguro es que ese dato iba por referencia. Es uno de los “¿pero por qué pasa esto?” más típicos al empezar.

Efectos secundarios

Un efecto secundario es cuando una función, además de devolver su resultado, toca algo de fuera: cambia una variable global, escribe en un archivo, modifica un dato por referencia. No es malo en sí (escribir en pantalla es un efecto secundario y hace falta), pero las funciones más fáciles de entender, probar y reutilizar son las que no tienen efectos secundarios: reciben sus datos, devuelven un resultado y no enredan con nada más. Cuanto más predecible es una función, menos sustos te da.

DRY: el porqué de todo esto

Todo lo de hoy se resume en un principio con nombre propio: DRY, de Don’t Repeat Yourself, no te repitas. La idea: cada pieza de lógica debe vivir en un solo sitio. Si calculas el IVA en una función calcularIVA(), y mañana cambia el porcentaje, lo tocas en un único lugar y todo el programa se actualiza solo.

Lo contrario, copiar y pegar el mismo cálculo por veinte rincones, se llama con sorna código WET (“write everything twice”, escríbelo todo dos veces), y es una fábrica de bugs. Encapsular en funciones es la primera y más importante forma de no repetirte. Si te llevas una sola idea de esta entrega, que sea esta.

Un adelanto: la recursión

Hay una capacidad curiosa de las funciones que solo voy a dejar caer: una función puede llamarse a sí misma. Se llama recursión, y sirve para resolver problemas que se definen en términos de sí mismos (recorrer un árbol de carpetas, por ejemplo). Es potente y al principio marea un poco, así que no la fuerces todavía. La veremos con calma en la serie de Algoritmos, donde luce de verdad.

Lo siguiente: cuando un dato suelto no basta

Ya sabes empaquetar lógica en funciones. Pero hasta ahora nuestras variables guardaban una sola cosa: un número, un texto. ¿Y si necesitas guardar la lista de la compra entera, o los datos de un usuario completo?

Para eso están las estructuras de datos, y de las dos más importantes va la próxima entrega: las listas y los diccionarios, las colecciones que vas a usar literalmente cada día durante el resto de tu vida programando.

Preguntas frecuentes

¿Cuál es la diferencia entre un parámetro y un argumento?

El parámetro es el hueco con nombre que defines en la función (por ejemplo, nombre en “función saludar(nombre)”). El argumento es el valor concreto que le pasas al llamarla (por ejemplo, “Marcos” en “saludar(‘Marcos’)”). En resumen: el parámetro es el molde de entrada, el argumento es lo que metes de verdad en ese molde al ejecutar la función.

¿Qué es el ámbito o scope de una variable?

El ámbito (scope) define dónde existe y se puede usar una variable. Una variable local, creada dentro de una función, solo existe dentro de ella y desaparece cuando la función termina. Una variable global, declarada fuera, se ve desde todo el programa. Conviene usar locales siempre que se pueda: aíslan cada función y evitan que unas partes del código pisen las variables de otras.

¿Qué significa el principio DRY?

DRY viene de “Don’t Repeat Yourself” (no te repitas). Es la idea de que cada pieza de lógica debe estar en un único lugar del código, normalmente encapsulada en una función. Así, si algo cambia, lo modificas en un solo sitio y todo el programa se actualiza. Lo contrario, copiar y pegar el mismo código por todas partes, multiplica los errores y dificulta el mantenimiento.

El libro para esta entrega

Las funciones son la puerta de entrada al código bien hecho, y el libro de referencia sobre cómo escribirlas (cortas, claras, con una sola responsabilidad y sin repetirte) es Código limpio, de Robert C. Martin. No es para tu primer día, pero en cuanto te sientas suelto con funciones, su capítulo sobre ellas te cambia la forma de programar.

Este artículo contiene enlaces de afiliado.

Compártelo si te ha resultado útil.

¿Cuál fue la primera función que escribiste que te hizo decir “ah, ahora lo pillo”? Cuéntame.

Y… en la próxima entrega, a guardar muchas cosas a la vez.

Artículos relacionados

Vista aérea de una rotonda al anochecer con las estelas de luz de los coches formando un círculo continuo, como metáfora de un bucle

Bucles en programación: for, while y do-while desde cero

Si tuvieras que saludar a cien usuarios, ¿escribirías cien veces la misma línea? Claro que no. Para repetir trabajo sin copiar y pegar existen los bucles, y son una de las herramientas que más usarás en tu vida programando. En esta quinta entrega de la serie de Fundamentos te explico los tres bucles básicos (for, while y do-while), cuándo usar cada uno, el for-each para recorrer colecciones cómodamente, el break y el continue para controlar la repetición por dentro, y el clásico susto del principiante: el bucle infinito que cuelga el programa, por qué ocurre y cómo evitarlo. Todo con pseudocódigo y ejemplos cortos para que lo veas claro.

16:00 8 min Marcos Ramírez Lucía
Bifurcación de un camino en dos direcciones señalizadas, vista cenital, como metáfora de una decisión en un programa

Condicionales en programación: if, else y switch desde cero

Hasta ahora nuestros programas iban en línea recta, una instrucción detrás de otra. Pero un programa útil tiene que decidir: hacer una cosa si se cumple algo y otra distinta si no. De eso van los condicionales, y son lo que de verdad le da inteligencia a tu código. En esta cuarta entrega de la serie de Fundamentos te explico el if, el else y el else if, el switch para cuando hay muchos caminos, el operador ternario como condicional exprés, cómo combinar varias condiciones, el peligro de anidar demasiado y un truco que te cambiará la vida: las guard clauses (cláusulas de guarda) para escribir condicionales que se leen sin marearte. Todo con pseudocódigo y ejemplos cortos.

16:00 8 min Marcos Ramírez Lucía
Teclado en primer plano con las teclas de símbolos matemáticos y de comparación iluminadas sobre un fondo oscuro

Operadores en programación: aritméticos, lógicos y de bits

Ya sabes guardar datos en variables. Ahora toca hacer cosas con ellos, y para eso están los operadores: los símbolos con los que un programa suma, compara, combina y decide. En esta tercera entrega de la serie de Fundamentos los repaso todos sin dejarme ninguno: aritméticos, de asignación, de comparación, lógicos, a nivel de bits, el operador ternario y los modernos de coalescencia nula y encadenamiento opcional. Le dedico atención especial al error que comete absolutamente todo el mundo al empezar: confundir el = de asignar con el == de comparar, y por qué en muchos lenguajes hay además un === todavía más estricto. También verás la precedencia, es decir, en qué orden se resuelve todo y cuándo poner paréntesis para no liarla.

16:00 8 min Marcos Ramírez Lucía