25.1 De una expresión a una función
Hasta ahora escribimos fórmulas dentro de ejemplos concretos. Si una aplicación necesita calcular muchos escenarios, copiar la misma expresión en distintos lugares multiplica las posibilidades de cometer errores.
Una función reúne el cálculo bajo un nombre y permite invocarlo con datos diferentes:
La fórmula se escribe una vez y se utiliza tantas veces como sea necesario.
25.2 El contrato de una función
Antes de programar conviene describir el contrato: qué recibe, qué devuelve y qué significado tienen los datos.
Capital, tasa decimal y cantidad de períodos.
Aplicación de una fórmula financiera determinada.
Un número o un objeto con resultados relacionados.
Por ejemplo, montoCompuesto(100000, 0.05, 4) promete devolver el monto de $100.000 al 5 % por período durante cuatro períodos. El orden y la unidad de cada argumento forman parte del contrato.
25.3 Nombres que explican
Los nombres deben revelar la intención. montoSimple comunica más que calcular, y periodos comunica más que x. También conviene mantener el mismo vocabulario en todo el curso:
| Nombre | Significado | Unidad |
|---|---|---|
| capital | Importe ubicado al comienzo de la operación | Moneda |
| tasa | Tasa correspondiente a un período | Decimal, no porcentaje |
| periodos | Cantidad de unidades temporales de la tasa | Períodos |
| monto | Capital más intereses al final | Moneda |
Usaremos nombres completos aunque las fórmulas matemáticas empleen C, i, n y M.
25.4 Funciones para los montos
Podemos traducir directamente los dos modelos estudiados:
function montoSimple(capital, tasa, periodos) {
return capital * (1 + tasa * periodos);
}
function montoCompuesto(capital, tasa, periodos) {
return capital * (1 + tasa) ** periodos;
}
const simple = montoSimple(100000, 0.10, 5);
const compuesto = montoCompuesto(100000, 0.10, 5);
console.log("Monto simple:", simple.toFixed(2));
console.log("Monto compuesto:", compuesto.toFixed(2));Las llamadas usan una tasa decimal. Pasar 10 en lugar de 0.10 representaría una tasa de 1000 %, no de 10 %.
25.5 Funciones puras
Las funciones anteriores son puras: para los mismos argumentos siempre devuelven el mismo resultado y no modifican datos externos. Tampoco leen campos HTML, muestran alertas ni cambian el documento.
Separar cálculo e interfaz permite reutilizar la misma función desde un formulario, una prueba automática, una API o un proceso por lotes.
25.6 Una responsabilidad por función
Una función pequeña resulta más sencilla de leer y verificar. En lugar de mezclar cálculo, formato y presentación, distribuimos responsabilidades:
function montoCompuesto(capital, tasa, periodos) {
return capital * (1 + tasa) ** periodos;
}
function interesGenerado(capital, monto) {
return monto - capital;
}
function mostrarDinero(valor) {
return new Intl.NumberFormat("es-AR", {
style: "currency",
currency: "ARS"
}).format(valor);
}
const capital = 180000;
const monto = montoCompuesto(capital, 0.025, 8);
const interes = interesGenerado(capital, monto);
console.log("Monto:", mostrarDinero(monto));
console.log("Interés:", mostrarDinero(interes));montoCompuesto calcula, interesGenerado obtiene una diferencia y mostrarDinero se ocupa únicamente de la presentación.
25.7 Componer funciones
Componer consiste en utilizar el resultado de una función como entrada de otra. Esto evita duplicar la fórmula del monto dentro de la función que calcula el interés:
Una función de mayor nivel reutiliza una operación ya definida.
function montoCompuesto(capital, tasa, periodos) {
return capital * (1 + tasa) ** periodos;
}
function interesCompuesto(capital, tasa, periodos) {
return montoCompuesto(capital, tasa, periodos) - capital;
}
const interes = interesCompuesto(500000, 0.012, 10);
console.log("Interés compuesto:", interes.toFixed(2));25.8 Devolver varios resultados
Una función solo ejecuta una instrucción return, pero puede devolver un objeto con varias propiedades. Esto es útil cuando los resultados comparten cálculos intermedios:
function resumenCompuesto(capital, tasa, periodos) {
const factor = (1 + tasa) ** periodos;
const monto = capital * factor;
return {
capital,
factor,
monto,
interes: monto - capital
};
}
const resultado = resumenCompuesto(200000, 0.05, 4);
console.log("Factor:", resultado.factor.toFixed(8));
console.log("Monto:", resultado.monto.toFixed(2));
console.log("Interés:", resultado.interes.toFixed(2));El objeto conserva nombres explícitos. Quien llama a la función puede utilizar solo las propiedades que necesita.
25.9 Elegir el modelo sin duplicar la interfaz
Una interfaz puede ofrecer ambos modelos y seleccionar la función apropiada. El objeto modelos relaciona cada identificador con una implementación:
const modelos = {
simple(capital, tasa, periodos) {
return capital * (1 + tasa * periodos);
},
compuesto(capital, tasa, periodos) {
return capital * (1 + tasa) ** periodos;
}
};
function calcularMonto(modelo, capital, tasa, periodos) {
return modelos[modelo](capital, tasa, periodos);
}
console.log(
"Simple:",
calcularMonto("simple", 100000, 0.10, 5).toFixed(2)
);
console.log(
"Compuesto:",
calcularMonto("compuesto", 100000, 0.10, 5).toFixed(2)
);Este patrón permite que el formulario no contenga las fórmulas. La validación de un nombre de modelo inexistente se incorporará en el tema siguiente.
25.10 Tasa decimal y porcentaje de entrada
La función matemática debería recibir una tasa decimal coherente. Si la interfaz solicita un porcentaje, la conversión se realiza en su límite:
5 % → 5 / 100 → 0,05
Nombrar tasaPorcentaje y tasaDecimal evita confundir representaciones. No conviene crear una función que algunas veces espere 5 y otras 0.05.
Compatibilidad temporal: las funciones suponen que periodos cuenta la misma unidad a la que corresponde tasa. No convierten tasas mensuales en anuales.
25.11 No redondear dentro del cálculo
Una función financiera reutilizable debe devolver el valor con toda la precisión disponible. Si redondea cada resultado intermedio, las llamadas compuestas pueden acumular diferencias.
Conserva el número completo.
Formatea a dos decimales.
Define una política monetaria explícita.
toFixed(2) devuelve texto y pertenece normalmente a la capa de presentación. No debería utilizarse como sustituto de una política de precisión monetaria.
25.12 Probar el contrato
Los casos conocidos permiten comprobar la función sin depender de la interfaz. Debido al punto flotante, comparamos números calculados con una tolerancia:
function montoCompuesto(capital, tasa, periodos) {
return capital * (1 + tasa) ** periodos;
}
function casiIguales(obtenido, esperado, tolerancia = 0.005) {
return Math.abs(obtenido - esperado) <= tolerancia;
}
const pruebas = [
{ datos: [100000, 0.10, 0], esperado: 100000 },
{ datos: [100000, 0.10, 1], esperado: 110000 },
{ datos: [100000, 0.10, 5], esperado: 161051 }
];
pruebas.forEach(({ datos, esperado }, indice) => {
const obtenido = montoCompuesto(...datos);
const estado = casiIguales(obtenido, esperado) ? "correcta" : "falló";
console.log(`Prueba ${indice + 1}: ${estado} (${obtenido.toFixed(2)})`);
});Las pruebas cubren el estado inicial, un período y varios períodos. Una función incorrecta puede acertar un solo ejemplo por casualidad; por eso usamos casos diferentes.
25.13 Banco de pruebas interactivo
Elegí un modelo y modificá los argumentos. El panel muestra la llamada, el objeto devuelto y casos automáticos que reutilizan las mismas funciones.
Funciones financieras en acción
Una interfaz, dos estrategias de cálculo y un contrato común.
Llamada y valor devuelto
| Caso | Argumentos | Esperado | Obtenido | Estado |
|---|
Datos válidos: la tasa decimal y los períodos utilizan un contrato consistente.
25.14 Ejercicio propuesto
Creá una función resumenInteres que reciba modelo, capital, tasa decimal y períodos. Debe devolver un objeto con capital, monto, interés y factor total.
- Implementá los modelos simple y compuesto como funciones separadas.
- Seleccioná el modelo sin duplicar las fórmulas.
- Calculá $300.000 al 2 % mensual durante 12 meses con ambos modelos.
- Mostrá los resultados con dos decimales.
- Comprobá que ambos devuelven el capital cuando
periodos = 0.
Ver solución y código
El modelo simple produce $372.000 y $72.000 de interés. El compuesto produce aproximadamente $380.472,54 y $80.472,54 de interés.
function montoSimple(capital, tasa, periodos) {
return capital * (1 + tasa * periodos);
}
function montoCompuesto(capital, tasa, periodos) {
return capital * (1 + tasa) ** periodos;
}
const modelos = { simple: montoSimple, compuesto: montoCompuesto };
function resumenInteres(modelo, capital, tasa, periodos) {
const monto = modelos[modelo](capital, tasa, periodos);
return {
capital,
monto,
interes: monto - capital,
factor: monto / capital
};
}
for (const modelo of ["simple", "compuesto"]) {
const resultado = resumenInteres(modelo, 300000, 0.02, 12);
const control = resumenInteres(modelo, 300000, 0.02, 0);
console.log(
`${modelo}: monto = ${resultado.monto.toFixed(2)}, ` +
`interés = ${resultado.interes.toFixed(2)}`
);
console.log(
`Control n=0: ${control.monto === 300000 ? "correcto" : "falló"}`
);
}25.15 Ideas para recordar
- Una función reutilizable tiene entradas, salida y significado bien definidos.
- Los nombres expresivos documentan el contrato del cálculo.
- Separar cálculo, formato e interfaz reduce el acoplamiento.
- Las funciones puras facilitan la reutilización y las pruebas.
- Una función puede componer otras funciones o devolver varios valores mediante un objeto.
- La tasa decimal y la unidad de los períodos deben ser consistentes.
- Los cálculos conservan precisión; el redondeo pertenece a la presentación.
- Los casos conocidos y una tolerancia permiten verificar resultados numéricos.
En el próximo tema estudiaremos la validación de datos y el manejo de casos no válidos.