for con rangos

# =============================================================================
# BUCLES FOR CON RANGE() EN PYTHON
# =============================================================================
# Hasta ahora hemos usado el bucle "for" para recorrer listas y cadenas.
# En este programa aprenderemos a usarlo con range(), que nos permite repetir
# algo un número exacto de veces o recorrer rangos de números.
#
# range() genera una secuencia de números. Sus formas principales son:
#
#   range(n)          → genera: 0, 1, 2, ..., n-1        (n números desde 0)
#   range(a, b)       → genera: a, a+1, ..., b-1         (sin incluir b)
#   range(a, b, paso) → genera: a, a+paso, a+paso*2, ... (de a en a saltos)
#
# En este archivo veremos:
#   1. range() básico para contar y sumar
#   2. range() para recorrer cadenas
#   3. Funciones que usan range() para calcular sumas
#   4. Dibujar figuras con emojis usando bucles
#   5. Árboles de Navidad cada vez más elaborados
# =============================================================================

import random
# Importamos el módulo "random" que usaremos al final para generar números
# aleatorios. Un módulo es una colección de funciones ya escritas que podemos
# usar directamente. "random" viene incluido con Python, no hay que instalarlo.


# =============================================================================
# EJEMPLO 1: range() BÁSICO — CONTAR DEL 0 AL 6
# =============================================================================

for i in range(7):
    # range(7) genera los números: 0, 1, 2, 3, 4, 5, 6
    # OJO: genera 7 números pero el último es 6, no 7. Nunca incluye el límite.
    # En cada vuelta, "i" toma el siguiente valor de la secuencia.
    # "i" es el nombre más habitual para un contador en programación.

    print(i)
    # Imprime el valor actual de i.
    # Salida:
    #   0
    #   1
    #   2
    #   3
    #   4
    #   5
    #   6


# =============================================================================
# EJEMPLO 2: SUMAR TODOS LOS NÚMEROS DEL 0 AL 100
# =============================================================================

suma = 0
# Acumulador: empezamos en 0 y vamos añadiendo números.
# Siempre se inicializa a 0 ANTES del bucle.

for i in range(101):
    # range(101) genera: 0, 1, 2, 3, ..., 100
    # Necesitamos 101 (no 100) porque range() no incluye el número final.
    # PREGUNTA FRECUENTE: ¿por qué no range(100)?
    #   range(100) → llega hasta 99. range(101) → llega hasta 100. ✓

    suma = suma + i
    # Añadimos el valor actual de i a la suma acumulada.
    # ALTERNATIVA más corta: suma += i
    # Paso a paso: suma=0 → 0+0=0 → 0+1=1 → 1+2=3 → 3+3=6 → ... → 5050

print(suma)
# Imprime 5050. Es la suma de los números del 0 al 100.
# Existe una fórmula matemática directa: n*(n+1)/2 → 100*101/2 = 5050


# =============================================================================
# EJEMPLO 3: range() PARA RECORRER UNA CADENA POR POSICIÓN
# =============================================================================

cadena = "hola"

for i in range(len(cadena)):
    # len(cadena) → devuelve 4 (la cadena "hola" tiene 4 caracteres)
    # range(4)    → genera: 0, 1, 2, 3
    # Así recorremos los índices válidos de la cadena.

    print(i, cadena[i])
    # cadena[i] → accede a la letra en la posición i
    # cadena[0] → "h"
    # cadena[1] → "o"
    # cadena[2] → "l"
    # cadena[3] → "a"
    #
    # Salida:
    #   0 h
    #   1 o
    #   2 l
    #   3 a
    #
    # RECUERDA: esta es la "Manera 2" del tema anterior. La manera más pythónica
    # es con enumerate(), pero esta es útil cuando necesitas el índice para
    # acceder a posiciones vecinas (cadena[i-1], cadena[i+1]...).


# =============================================================================
# FUNCIÓN suma_hasta() — SUMAR DEL 0 HASTA UN LÍMITE
# =============================================================================
# Generalizamos el ejemplo 2: en lugar de sumar siempre hasta 100,
# dejamos que el usuario elija el límite.
#
# suma_hasta(10)  → 0+1+2+...+10  = 55
# suma_hasta(100) → 0+1+2+...+100 = 5050
# =============================================================================

def suma_hasta(limite):
    # Parámetro "limite": el número hasta el que queremos sumar.

    suma = 0
    # Acumulador LOCAL a la función. Se reinicia a 0 en cada llamada.

    for i in range(limite + 1):
        # limite + 1 porque range() no incluye el último número.
        # Si limite=10 → range(11) → genera 0, 1, 2, ..., 10 ✓
        # Sin el +1 → range(10) → llegaríamos solo hasta 9 ✗

        suma = suma + i
        # Acumulamos cada número en la suma.

    return suma
    # Devolvemos el resultado al lugar donde se llamó la función.

print(suma_hasta(10))   # → 55
print(suma_hasta(100))  # → 5050


# =============================================================================
# FUNCIÓN suma_entre() — SUMAR ENTRE DOS NÚMEROS
# =============================================================================
# Ahora también elegimos desde dónde empezamos a sumar.
# Usamos la forma range(inicio, final) que empieza en "inicio".
#
# suma_entre(10, 20) → 10+11+12+...+20 = 165
# suma_entre(2, 5)   → 2+3+4+5         = 14
# =============================================================================

def suma_entre(inicio, final):
    # Dos parámetros: "inicio" (desde dónde) y "final" (hasta dónde).

    suma = 0

    for i in range(inicio, final + 1):
        # range(inicio, final+1) genera: inicio, inicio+1, ..., final
        # El +1 sigue siendo necesario para incluir "final".
        # Ejemplo: range(10, 21) → 10, 11, 12, ..., 20 ✓
        # Sin +1: range(10, 20) → 10, 11, ..., 19 — nos falta el 20 ✗

        suma = suma + i

    return suma

print(suma_entre(10, 20))  # → 165
print(suma_entre(2, 5))    # → 14


# =============================================================================
# TRIÁNGULO DE EMOJIS
# =============================================================================
# Usamos range() para controlar cuántos emojis imprimimos en cada línea.
# En la línea 0 → 0 emojis, línea 1 → 1 emoji, ..., línea n → n emojis.
# =============================================================================

tamanyo = 6

for i in range(tamanyo + 1):
    # range(tamanyo+1) → range(7) → genera 0, 1, 2, 3, 4, 5, 6
    # Con tamanyo+1 incluimos la última fila completa.

    print("🟢" * i)
    # "🟢" * i repite el emoji i veces.
    # "🟢" * 0 → ""      (línea vacía)
    # "🟢" * 1 → "🟢"
    # "🟢" * 2 → "🟢🟢"
    # "🟢" * 6 → "🟢🟢🟢🟢🟢🟢"
    # Salida:
    #
    # 🟢
    # 🟢🟢
    # 🟢🟢🟢
    # 🟢🟢🟢🟢
    # 🟢🟢🟢🟢🟢
    # 🟢🟢🟢🟢🟢🟢


# =============================================================================
# FUNCIÓN arbolito() — TRIÁNGULO COMO FUNCIÓN REUTILIZABLE
# =============================================================================

def arbolito(tamanyo):
    # Exactamente el mismo código que arriba, pero dentro de una función.
    # Así podemos llamarla con cualquier tamaño sin reescribir el bucle.

    for i in range(tamanyo + 1):
        print("🟢" * i)

arbolito(10)   # Dibuja un triángulo de 10 filas
# RETO: ¿qué pasa si llamas a arbolito(0)? ¿Y a arbolito(1)?


# =============================================================================
# FUNCIÓN arbolito_puro() — DEVUELVE EL ÁRBOL COMO TEXTO (SIN IMPRIMIR)
# =============================================================================
# Diferencia clave respecto a arbolito():
#   arbolito()      → imprime directamente (no devuelve nada útil)
#   arbolito_puro() → construye el texto y lo devuelve con return
#
# ¿Por qué es mejor devolver el texto?
# Porque podemos guardarlo, modificarlo, enviarlo por email, guardarlo
# en un archivo... En cambio, lo que se imprime con print() se pierde.
# =============================================================================

def arbolito_puro(tamanyo):
    resultado = ""
    # Cadena vacía donde iremos construyendo el árbol línea a línea.

    for i in range(tamanyo + 1):
        resultado += "🟢" * i + "\n"
        # Añadimos los emojis de esta fila MÁS "\n" (salto de línea).
        # "\n" es el carácter especial que representa "pulsar Enter".
        # Sin "\n" todo saldría en una sola línea.
        # ALTERNATIVA: resultado = resultado + "🟢" * i + "\n"

    return resultado
    # Devolvemos todo el árbol como una sola cadena con saltos de línea.

print(arbolito_puro(4))
# Al hacer print() de una cadena con "\n", cada "\n" se convierte en
# un salto de línea visible en pantalla.
# Salida:
#
# 🟢
# 🟢🟢
# 🟢🟢🟢
# 🟢🟢🟢🟢


# =============================================================================
# FUNCIÓN arbol_guay() — ÁRBOL CENTRADO CON FORMA DE TRIÁNGULO INVERTIDO
# =============================================================================
# Ahora mejoramos el árbol:
#   - Empieza en 1 (no en 0) para que la primera fila tenga 1 bola
#   - La primera fila tiene una bola roja (la estrella de la cima)
#   - Cada fila tiene un número IMPAR de bolas: 1, 3, 5, 7...
#   - Añadimos espacios a la izquierda para que quede centrado
#
# Con tamanyo=4, fila por fila:
#   i=1: espacios=3, bolas=1  →  "      🔴"
#   i=2: espacios=2, bolas=3  →  "    🟢🟢🟢"
#   i=3: espacios=1, bolas=5  →  "  🟢🟢🟢🟢🟢"
#   i=4: espacios=0, bolas=7  →  "🟢🟢🟢🟢🟢🟢🟢"
# =============================================================================

def arbol_guay(tamanyo):

    for i in range(1, tamanyo + 1):
        # range(1, tamanyo+1) → empieza en 1, no en 0.
        # Con tamanyo=6 → genera: 1, 2, 3, 4, 5, 6
        # Empezamos en 1 para que la primera fila tenga al menos 1 bola.

        bola = "🟢"
        # Por defecto, todas las bolas son verdes.

        if i == 1:
            bola = "🔴"
            # La primera fila (i=1) es la cima del árbol → bola roja (estrella).

        espacios = tamanyo - i
        # Cuántos espacios dobles ponemos a la izquierda para centrar.
        # Cuanto más arriba (i pequeño), más espacios → la cima queda centrada.
        # i=1 → espacios=5 (muchos, está en la cima)
        # i=6 → espacios=0 (ninguno, es la base)

        bolas = 2 * i - 1
        # Fórmula para obtener números impares: 1, 3, 5, 7, 9, 11...
        # i=1 → 2*1-1=1   i=2 → 2*2-1=3   i=3 → 2*3-1=5
        # Los árboles de Navidad tienen filas con número impar de bolas
        # para que queden simétricas.

        print("  " * espacios + bola * bolas)
        # "  " * espacios → espacios dobles para el centrado (2 espacios cada uno)
        # bola * bolas    → repite la bola el número de veces calculado
        # Los concatenamos con + para formar la línea completa.


# =============================================================================
# FUNCIÓN arbol_decorado() — ÁRBOL CON DECORACIONES ALEATORIAS
# =============================================================================
# La versión más completa. Añade decoraciones aleatorias entre las bolas verdes.
# Novedad: usa un BUCLE ANIDADO (un for dentro de otro for) para pintar
# cada bola de la fila una a una, pudiendo cambiar el emoji en cada posición.
# =============================================================================

def arbol_decorado(tamanyo):
    decoracion = "🟠🟡🔵🎄🎅🤶"
    # Cadena con los emojis de decoración disponibles.
    # decoracion[0] → "🟠"
    # decoracion[1] → "🟡"
    # decoracion[5] → "🤶"
    # len(decoracion) → 6

    for i in range(1, tamanyo + 1):
        # Bucle EXTERIOR: recorre las filas del árbol (igual que arbol_guay).

        bola = "🟢"
        # Bola por defecto para esta fila.

        if i == 1:
            bola = "🔴"
            # La cima sigue siendo roja.

        espacios = tamanyo - i
        bolas = 2 * i - 1
        # Mismas fórmulas de centrado que en arbol_guay().

        print("  " * espacios, end="")
        # Imprimimos los espacios de centrado.
        # end="" evita el salto de línea automático del print().
        # Sin end="" cada print() saltaría de línea y el árbol quedaría roto.

        for j in range(bolas):
            # Bucle INTERIOR: recorre cada posición de bola en esta fila.
            # "j" va de 0 a bolas-1. En cada posición decidimos qué emoji poner.

            bola = "🟢"
            # Reiniciamos la bola a verde para cada posición.

            if random.randint(0, 3) == 0:
                # random.randint(0, 3) genera un número aleatorio: 0, 1, 2 o 3.
                # Solo si sale 0 (probabilidad 1 de cada 4 = 25%), ponemos decoración.
                # Así la mayoría de bolas son verdes y las decoraciones son escasas.

                bola = decoracion[random.randint(0, len(decoracion) - 1)]
                # Elegimos un emoji aleatorio de la cadena "decoracion".
                # random.randint(0, 5) → número entre 0 y 5 (los 6 índices válidos).
                # len(decoracion)-1 = 5, así nunca nos salimos de rango.
                # ALTERNATIVA más corta: bola = random.choice(decoracion)

            print(bola, end="")
            # Imprimimos la bola (verde o decoración) SIN salto de línea,
            # para que todas las bolas de la misma fila queden juntas.

        print()
        # print() sin argumentos imprime solo un salto de línea.
        # Lo necesitamos al final de cada fila para pasar a la siguiente.
        # Es el "Enter" que falta porque usamos end="" en los print anteriores.

arbol_guay(6)
# Llamamos a arbol_guay para probarlo con tamaño 6.
# RETO: llama también a arbol_decorado(10) y observa que cada ejecución
# produce un árbol diferente gracias a random.

Publicado por

Juan Pablo Fuentes

Formador de programación y bases de datos