Operadores
En esta guía (10)
En esta lección vas a ver cómo se nombran las cosas en Scala y cómo funcionan los operadores. La idea central es simple y muy propia del lenguaje: los operadores son métodos. 5 + 3 es, en realidad, una llamada al método + del objeto 5. Entender eso explica por qué podés usar métodos como operadores y definir operadores propios. Al final vas a aplicar todo en tres cálculos financieros: conversión de moneda, interés simple e interés compuesto.
Identificadores
Los identificadores son los nombres que les das a valores, variables, funciones, clases y demás elementos del código. Las reglas:
- Empiezan con una letra o un guion bajo (
_). - Después pueden tener letras, números o guiones bajos.
- Distinguen mayúsculas de minúsculas:
saldoySaldoson dos nombres distintos.
val nombre = "Juan"
val _variable = 42
val saldo2023 = 1500.0
El símbolo $ también está permitido (val $valor = 10 compila), pero está reservado para nombres que genera el compilador, así que es mejor no usarlo en tus identificadores.
Convenciones de nombres
- Valores, variables y funciones:
camelCase, la primera palabra en minúscula y las siguientes con mayúscula inicial:nombreUsuario,saldoCuenta,calcularInteres. - Clases y traits:
PascalCase, con la primera letra en mayúscula:CuentaBancaria,Identificable. - Constantes: en código que viene de Java se escriben en mayúsculas con las palabras separadas por guion bajo:
VALOR_PI,LIMITE_MAXIMO. La guía de estilo oficial de Scala recomiendaPascalCase(ValorPi,LimiteMaximo). Vas a ver las dos; elegí una y sé consistente. - Paquetes: todo en minúsculas, con una estructura jerárquica:
package com.miempresa.miproyecto.utilidades.
Los operadores son métodos
En Scala no hay operadores "especiales" del lenguaje para la aritmética: +, -, * y / son métodos de Int, Double y compañía. Estas dos líneas son exactamente lo mismo:
val a = 5 + 3
val b = 5.+(3)
println(a == b) // true
Los operadores que más vas a usar (la lista completa está en la documentación oficial de Scala):
| Tipo | Operadores | Ejemplo | Resultado |
|---|---|---|---|
| Aritméticos | + - * / % | 17 % 5 | 2 |
| Relacionales | == != > < >= <= | 10 >= 7 | true |
| Lógicos | && || ! | true && !false | true |
| A nivel de bits | & | ^ ~ << >> | 6 & 3 | 2 |
Un detalle que conviene tener presente desde ahora: la división entre enteros es entera.
println(7 / 2) // 3, división entera
println(7 / 2.0) // 3.5, alcanza con que uno sea Double
println(7 % 2) // 1, resto
println(math.pow(2, 10)) // 1024.0, la potencia devuelve Double
No existe un operador de potencia: se usa math.pow(base, exponente), que siempre devuelve Double.
Operadores infijos
Como los operadores son métodos, también funciona al revés: cualquier método con un solo parámetro se puede usar como operador, escrito entre los dos operandos. A eso se le llama notación infija:
val suma = 5 + 3 // + es un operador infijo
val concat = "Hola" + " Mundo" // + también, sobre String
val rango = 1 to 5 // to es un método de Int: 1.to(5)
val mayor = 10 max 25 // max también: 10.max(25)
println(rango.toList) // List(1, 2, 3, 4, 5)
println(mayor) // 25
1 to 5 se lee casi como en lenguaje natural, y por eso es tan común en los bucles for (lección 4). Usá la notación infija para operadores simbólicos y para métodos que se leen bien así, como to, until o max. Para el resto, la notación con punto es más clara.
En Scala 3, los métodos con nombre alfabético pensados para usarse de forma infija se pueden marcar con el modificador infix (infix def max(...)). Los de la librería estándar, como to y max, se pueden usar así sin problema.
Operadores unarios
Los operadores unarios se aplican a un solo operando y van delante de él: - cambia el signo, ! niega un booleano, ~ invierte los bits.
val numero = 10
val negativo = -numero // -10
val activo = true
val inactivo = !activo // false
Para tus propias clases, podés definir un operador unario con un método llamado unary_ seguido del símbolo. Solo se admiten cuatro: +, -, ! y ~.
class MiNumero(val valor: Int) {
def unary_- : MiNumero = new MiNumero(-valor)
override def toString: String = s"$valor"
}
val numero = new MiNumero(5)
val opuesto = -numero // llama a numero.unary_-
println(opuesto) // -5
Ojo con el espacio entre unary_- y los dos puntos: si escribís unary_-:, el compilador interpreta -: como parte del nombre y da error.
Operadores de asignación
Los operadores de asignación compuesta combinan una operación con la asignación. Solo tienen sentido con var:
var total = 5
total += 3 // equivale a total = total + 3, ahora vale 8
total *= 2 // equivale a total = total * 2, ahora vale 16
total -= 6 // 10
total /= 4 // 2, división entera
total %= 2 // 0
println(total)
Si venís de Java o C++: en Scala no existen ++ ni --. Para incrementar se usa contador += 1.
Precedencia
Cuando combinás operadores, Scala decide el orden según el primer carácter del operador. De mayor a menor prioridad: * / %, después + -, después :, después < >, después = !, después &, ^, |, y por último las letras (como to o max). En la práctica, se comporta como esperarías en matemática:
println(2 + 3 * 4) // 14
println((2 + 3) * 4) // 20
println(1 + 2 to 5) // Range 3 to 5: primero 1 + 2, después to
Ante la duda, usá paréntesis: no cuestan nada y hacen el código más fácil de leer.
Practicá
- Escribí una función que reciba un monto en USD o EUR, el factor de conversión entre las dos monedas y un porcentaje de impuestos. Tiene que devolver el monto en la otra moneda con los impuestos agregados.
- Calculadora de interés simple. Escribí una función que calcule el interés simple de un préstamo. Recibe el capital (monto del préstamo), la tasa de interés anual y el tiempo en años, y devuelve el interés total. La fórmula es
Interés = Capital * Tasa * Tiempo. - Interés compuesto. Escribí una función que calcule el monto total de un depósito a plazo con interés compuesto. Recibe el capital, la tasa anual y la cantidad de años, y devuelve el monto final. La fórmula es
Monto = Capital * (1 + Tasa) ^ Años.
En los ejercicios 2 y 3, la tasa se expresa como fracción: 5% es 0.05.
Soluciones
// Ejercicio 1
def convertirConImpuestos(monto: Double, factorConversion: Double, impuestos: Double): Double = {
val montoEnOtraMoneda = monto * factorConversion
val montoConImpuestos = montoEnOtraMoneda * (1 + impuestos / 100)
montoConImpuestos
}
val montoUSD = 100.0
val factorConversionUSDaEUR = 0.85
val impuestos = 10.0
val montoConImpuestosEUR = convertirConImpuestos(montoUSD, factorConversionUSDaEUR, impuestos)
println(f"Monto en EUR con impuestos: $montoConImpuestosEUR%.2f") // 93.50
// Ejercicio 2
def calcularInteresSimple(principal: Double, tasaDeInteresAnual: Double, tiempoEnAnios: Double): Double = {
principal * tasaDeInteresAnual * tiempoEnAnios
}
val interesSimple = calcularInteresSimple(1000.0, 0.05, 2.0)
println(s"Interés simple: $interesSimple") // 100.0
println(s"Total a devolver: ${1000.0 + interesSimple}") // 1100.0
// Ejercicio 3
def calcularInteresCompuesto(principal: Double, tasaDeInteresAnual: Double, tiempoEnAnios: Double): Double = {
principal * math.pow(1 + tasaDeInteresAnual, tiempoEnAnios)
}
val montoTotalCompuesto = calcularInteresCompuesto(1000.0, 0.05, 2.0)
println(s"Monto total con interés compuesto: $montoTotalCompuesto") // 1102.5
println(calcularInteresCompuesto(10000, 0.05, 3)) // 11576.250000000002
println(f"${calcularInteresCompuesto(10000, 0.05, 3)}%.2f") // 11576.25
Algunos detalles para revisar:
- En el ejercicio 2, la consigna pide el interés, no el total. Es un error común devolver
principal + interes: si querés el total, calculalo aparte, como en el últimoprintln. - Fijate el resultado de
calcularInteresCompuesto(10000, 0.05, 3):Doubleno representa exactamente todos los decimales, y aparecen esos...0002al final. Para mostrar montos, formateá conf"...%.2f"(lo vas a ver en detalle en la lección 10). Ojo:frespeta la configuración regional de tu sistema, así que en una máquina en español puede mostrar93,50en lugar de93.50. Las salidas del curso están con punto decimal. Para cálculos de dinero donde cada centavo importa, en código real se usaBigDecimal. - En el ejercicio 1, si declararas
impuestoscomoInt,impuestos / 100sería una división entera: con10daría0, y el impuesto desaparecería sin aviso. Por eso conviene que los porcentajes seanDouble.
Resumen
- Los identificadores empiezan con letra o
_y distinguen mayúsculas. UsácamelCasepara valores y funciones, yPascalCasepara clases y traits. - En Scala los operadores son métodos:
5 + 3es5.+(3). - Cualquier método de un parámetro se puede usar en notación infija:
1 to 5,10 max 25. - Los unarios se definen con
unary_+,unary_-,unary_!ounary_~. +=,-=,*=,/=y%=combinan operación y asignación; no hay++ni--.- La división entre enteros es entera, y la potencia se calcula con
math.pow.