Inicio / Ruby / Ruby: Lenguaje Elegante y Expresivo / Métodos y Bloques

Métodos y Bloques

Definición de métodos, parámetros, keyword args, bloques, yield, Procs y Lambdas.

Principiante Funciones
🔒 Solo lectura
📖

Estás en modo lectura

Puedes leer toda la lección, pero para marcar progreso, hacer ejercicios y ganar XP necesitas una cuenta Pro.

Desbloquear por $9/mes

Métodos y Bloques en Ruby

Los métodos y bloques son conceptos fundamentales en Ruby. Los bloques son una de las características más distintivas y poderosas del lenguaje.


Métodos

Definición básica

def saludar(nombre)
  "Hola, #{nombre}!"
end

puts saludar("Ana")   # "Hola, Ana!"

# El return es implícito: la última expresión es el valor de retorno
def sumar(a, b)
  a + b   # retorno implícito
end

# Return explícito (para salir temprano)
def dividir(a, b)
  return "Error: división por cero" if b == 0
  a.to_f / b
end

Parámetros

# Valor por defecto
def saludar(nombre = "Mundo")
  "Hola, #{nombre}!"
end
puts saludar       # "Hola, Mundo!"
puts saludar("Ana") # "Hola, Ana!"

# Keyword arguments (nombrados)
def crear_usuario(nombre:, email:, rol: :user)
  { nombre: nombre, email: email, rol: rol }
end
crear_usuario(nombre: "Ana", email: "ana@test.com")
crear_usuario(email: "bob@test.com", nombre: "Bob", rol: :admin)

# Splat (varargs)
def suma(*numeros)
  numeros.reduce(0, :+)
end
puts suma(1, 2, 3, 4)   # 10

# Double splat (keyword varargs)
def config(**options)
  options.each { |k, v| puts "#{k}: #{v}" }
end
config(debug: true, port: 3000, host: "localhost")

Convenciones de métodos

# Métodos booleanos terminan en ?
def mayor_de_edad?(edad)
  edad >= 18
end

# Métodos destructivos terminan en !
numeros = [3, 1, 2]
numeros.sort    # retorna nuevo array [1, 2, 3], original intacto
numeros.sort!   # muta el array original

# Métodos de conversión empiezan con to_
42.to_s        # "42"
"42".to_i      # 42
[[:a, 1]].to_h # {a: 1}

Bloques

Un bloque es un fragmento de código que se pasa a un método. Es la base de la iteración en Ruby.

Sintaxis

# Llaves — para una línea
[1, 2, 3].each { |n| puts n }

# do...end — para múltiples líneas
[1, 2, 3].each do |n|
  resultado = n * 2
  puts resultado
end

yield — ejecutar el bloque recibido

def ejecutar_dos_veces
  yield
  yield
end

ejecutar_dos_veces { puts "¡Hola!" }
# ¡Hola!
# ¡Hola!

# Con argumentos
def con_saludo
  yield("Ana")
  yield("Bob")
end

con_saludo { |nombre| puts "Hola, #{nombre}!" }
# Hola, Ana!
# Hola, Bob!

# block_given? — verificar si se pasó un bloque
def opcional
  if block_given?
    yield
  else
    puts "Sin bloque"
  end
end

opcional                    # "Sin bloque"
opcional { puts "¡Con bloque!" }  # "¡Con bloque!"

Crear tus propios iteradores

def repetir(n)
  i = 0
  while i < n
    yield(i)
    i += 1
  end
end

repetir(3) { |i| puts "Iteración #{i}" }
# Iteración 0
# Iteración 1
# Iteración 2

# Ejemplo práctico: benchmark
def medir
  inicio = Time.now
  resultado = yield
  fin = Time.now
  puts "Tiempo: #{fin - inicio}s"
  resultado
end

medir { (1..1_000_000).sum }

Procs y Lambdas

Los bloques no son objetos. Para guardar un bloque como variable, usamos Proc o Lambda.

Proc

mi_proc = Proc.new { |n| puts n * 2 }
mi_proc.call(5)   # 10
mi_proc.(5)        # 10 (atajo)

# Desde un bloque
cuadrado = proc { |n| n ** 2 }
puts cuadrado.call(4)   # 16

# Pasar proc a un método
def aplicar(array, operacion)
  array.map { |item| operacion.call(item) }
end

doble = proc { |n| n * 2 }
puts aplicar([1, 2, 3], doble).inspect   # [2, 4, 6]

Lambda

mi_lambda = lambda { |n| n * 2 }
mi_lambda = ->(n) { n * 2 }   # sintaxis arrow (preferida)

puts mi_lambda.call(5)   # 10
puts mi_lambda.(5)        # 10

Diferencias Proc vs Lambda

# 1. Aridad: lambda verifica el número de argumentos
mi_proc = proc { |a, b| "#{a}, #{b}" }
mi_proc.call(1)          # "1, " (b = nil, sin error)

mi_lambda = ->(a, b) { "#{a}, #{b}" }
# mi_lambda.call(1)      # ArgumentError!

# 2. Return: en proc sale del método contenedor, en lambda solo del lambda
def test_proc
  mi_proc = proc { return "desde proc" }
  mi_proc.call
  "nunca llega aquí"
end
puts test_proc   # "desde proc"

def test_lambda
  mi_lambda = -> { return "desde lambda" }
  mi_lambda.call
  "sí llega aquí"
end
puts test_lambda   # "sí llega aquí"

Convertir entre bloque y Proc

# & convierte un bloque en Proc (y viceversa)
def capturar_bloque(&bloque)
  puts bloque.class    # Proc
  bloque.call("Ana")
end

capturar_bloque { |n| puts "Hola, #{n}!" }

# Pasar un Proc como bloque con &
doble = ->(n) { n * 2 }
[1, 2, 3].map(&doble)   # [2, 4, 6]

# Symbol#to_proc
[1, 2, 3].map(&:to_s)   # ["1", "2", "3"]
# Equivale a: [1, 2, 3].map { |n| n.to_s }

Closures

Los bloques, procs y lambdas capturan el entorno donde se crean:

def crear_contador
  count = 0
  incrementar = -> { count += 1; count }
  obtener = -> { count }
  [incrementar, obtener]
end

inc, get = crear_contador
puts inc.call   # 1
puts inc.call   # 2
puts inc.call   # 3
puts get.call   # 3

Resumen

Concepto Sintaxis Es objeto Verifica aridad Return
Bloque { } o do...end Sale del método
Proc Proc.new { } Sale del método
Lambda -> { } Sale del lambda
Method method(:nombre) Sale del método
🔒

Ejercicio práctico disponible

Bloques, yield y closures

Desbloquear ejercicios
// Bloques, yield y closures
// Desbloquea Pro para acceder a este ejercicio
// y ganar +50 XP al completarlo

function ejemplo() {
    // Tu código aquí...
}

¿Te gustó esta lección?

Con Pro puedes marcar progreso, hacer ejercicios, tomar quizzes, ganar XP y obtener tu constancia.

Ver planes desde $9/mes