25. Funciones reutilizables para cálculos de interés

Una fórmula se vuelve una herramienta de software cuando definimos con claridad sus entradas, su resultado y sus responsabilidades. Así evitamos repetir cálculos y podemos probarlos de manera independiente.

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:

datos de entrada → función financiera → resultado

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.

Entradas

Capital, tasa decimal y cantidad de períodos.

Proceso

Aplicación de una fórmula financiera determinada.

Salida

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:

NombreSignificadoUnidad
capitalImporte ubicado al comienzo de la operaciónMoneda
tasaTasa correspondiente a un períodoDecimal, no porcentaje
periodosCantidad de unidades temporales de la tasaPeríodos
montoCapital más intereses al finalMoneda

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.

Recibirargumentos
Calcularsin efectos externos
Devolverun valor
Usarel resultado afuera

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:

interesCompuesto(C, i, n) = montoCompuesto(C, i, n) − C

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:

tasa decimal = porcentaje / 100
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.

Cálculo

Conserva el número completo.

Presentación

Formatea a dos decimales.

Persistencia

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.

Modelo activoCompuesto
Monto$243.101,25
Interés$43.101,25
Factor total1,21550625

Llamada y valor devuelto

resumen("compuesto", 200000, 0.05, 4)
capital200000
tasa0,05
periodos4
monto243101,25
Pruebas de la función de monto compuesto.
CasoArgumentosEsperadoObtenidoEstado
La función compuesta produjo un resultado coherente y superó sus casos de control.

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.

  1. Implementá los modelos simple y compuesto como funciones separadas.
  2. Seleccioná el modelo sin duplicar las fórmulas.
  3. Calculá $300.000 al 2 % mensual durante 12 meses con ambos modelos.
  4. Mostrá los resultados con dos decimales.
  5. 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.