← Unidad II
Unidad II · Paradigmas de Programación
Elementos Fundamentales
UABC · FCITEC Cimarrón UABC
Unidad II · Paradigmas de Programación

Elementos Fundamentales de los Lenguajes de Programación

Distinguir los elementos fundamentales de los lenguajes de programación, considerando el entorno de desarrollo, su estructura y sintaxis, para utilizarlos en la creación de programas de cómputo.

DocenteDr. Jose Manuel Muñoz Contreras
FacultadFCITEC — Unidad Valle de las Palmas, UABC
ProgramaIng. en Desarrollo de Software y Tecnologías Emergentes — Plan 2021-2
AsignaturaParadigmas de Programación — Grupo 544, Subgrupo 0
01Introducción

¿Por qué estudiar los elementos fundamentales?

Todo lenguaje de programación —imperativo, orientado a objetos, funcional o lógico— se construye sobre un mismo puñado de decisiones de diseño. Antes de comparar paradigmas (Unidades III-V), necesitamos el vocabulario preciso para describir qué varía entre ellos.

Viene de la Unidad I

Ya vimos sintaxis (qué se puede escribir) y semántica (qué significa). Ahora preguntamos: ¿cómo un nombre llega a tener un significado concreto durante la ejecución?

Prepara las Unidades III-V

Encapsulamiento (POO), inmutabilidad (funcional) y unificación (lógico) son formas distintas de resolver los mismos problemas: nombres, alcance, memoria y tipos.

📌 La pregunta central de la unidad

Cuando un programa ejecuta x = f(y), ¿qué determina de dónde viene el valor de y, dónde vive el resultado en memoria, y qué puede hacer f con lo que recibe? Responder esto con precisión es el objetivo de las 5 secciones siguientes.

02Agenda

Qué vamos a recorrer

Cinco subtemas más una sección de profundización en Python, enmarcados en una clase de 180 minutos. Cada bloque alterna teoría, animación y una corrida de escritorio que vuelve visible el proceso interno. Clic en cada bloque para ir a su diapositiva.

00Activación0–15 min¿Qué determina el significado de x = f(y)?
2.1Nombres y objetos15–30 minBinding, aliasing
2.2Entornos, alcance y memoria30–50 minLEGB · stack vs. heap · animación
☕Receso50–60 minbebida, aire, estira un poco
2.3Expresiones y comandos60–85 minSelección, iteración, recursión · animación · for→recursión
2.4Subprogramas85–110 minPaso de parámetros · animación
☕Receso110–120 minojos al horizonte
2.5Tipos de datos120–140 minSistemas de tipos, inferencia
🐍Python en profundidad140–165 minModelo de objetos, mutabilidad, comparativa con Java y C++
✔Cierre165–180 minMapa conceptual · mini-quiz · tarjeta de salida
Notas del presentador

Objetivo de la sesión (enmarcarlo al inicio): definir con precisión nombres, alcance, memoria y tipos en un paradigma imperativo, usando Python como espejo.

Antes de 2.1: el "bug inicial" — muestra x = f(y) y pregunta: "¿de dónde sale el valor de y, dónde vive el resultado y qué puede hacer f con lo que recibe?". Conduce a la diapositiva 1.

Ritmo: 2 bloques largos de 35 min con animaciones; no rebasar los recesos. La corrida de cierre (curso, 140–165 min) es el momento de integrar los 3 ejes.

2.1Nombres y objetos

Nombre, objeto y binding

Un nombre (identificador) es una etiqueta textual. Un objeto es la entidad real en memoria. La asociación entre ambos se llama binding (ligadura) — y no siempre es de uno a uno.

📌 Definición — Binding time

El momento en que ocurre la ligadura puede ser en el diseño del lenguaje, en compilación, en enlace (linking) o en ejecución. Cuanto más tarde ocurre, mayor flexibilidad pero menor eficiencia (Sebesta, cap. 5).

Java / C — binding temprano

int x; liga el nombre x a un tipo desde la compilación. El binding con un valor concreto ocurre después, pero el tipo ya quedó fijo.

Python — binding tardío

x = 5 liga el nombre a un objeto en ejecución; el mismo nombre puede religarse luego a un objeto de otro tipo.

2.1Nombres y objetos

Aliasing: varios nombres, un objeto

En lenguajes con semántica de referencia (Python, Java para objetos), una variable almacena una referencia al objeto, no el objeto mismo. Esto permite el aliasing.

a = [1, 2, 3]
b = a  # b y a son dos NOMBRES para el MISMO objeto
b.append(4)
print(a)  # [1, 2, 3, 4] — el objeto cambió, visible desde ambos nombres
print(id(a) == id(b))  # True: misma identidad de objeto
⚠️ Error común

Confundir "reasignar un nombre" con "mutar un objeto". b = [9] rompe el alias; b.append(9) muta el objeto compartido. Volveremos a esta distinción con una animación en 2.4.

2.2Entornos, bloques y alcance

Entorno y bloque

Un bloque es una región textual del programa (cuerpo de función, if, for) que puede introducir nuevos nombres. Un entorno es la función que, en un punto de ejecución dado, mapea cada nombre visible a su binding.

📌 Definición — Alcance (scope)

El alcance de un nombre es la región del programa donde ese nombre es visible y se refiere a un binding específico. Determina, para cada uso de un nombre, a qué entorno hay que preguntarle.

2.2Entornos, bloques y alcance

Alcance estático vs. dinámico

CriterioEstático (léxico)Dinámico
Determinado porEstructura textual del códigoSecuencia de llamadas en ejecución
Momento de resoluciónCompilación (conceptualmente)Ejecución
PredictibilidadAlta — se lee el código y se sabe el bindingBaja — depende de quién llamó
LenguajesPython, Java, C, Haskell, SchemeBash (variables), Common Lisp (special)
⚠️ Importante

El alcance dinámico dificulta el razonamiento local: no basta con leer una función para saber a qué se refiere un nombre libre; hay que rastrear la pila de llamadas completa. Por eso casi todos los lenguajes modernos usan alcance estático.

📌 Python: regla LEGB

Orden de resolución de nombres: Local → Enclosing (funciones anidadas) → Global → Built-in. Es la implementación concreta del alcance estático en Python — la vemos en la siguiente animación.

2.2Animación · Resolución de alcance

🎬 Resolución de alcance en bloques anidados (LEGB)

x = "global" def externa(): x = "externa" def interna(): print(x) interna() externa()
Entorno global
x → "global"
Entorno de externa()
x → "externa"
Entorno de interna()
(sin nombre local "x")
Presiona Iniciar para ver cómo interna() busca a x siguiendo la cadena léxica LEGB.
Python 3 · resolucion.py
📓 Corrida de escritorio: ejecuta el mismo programa en un intérprete simulado y compara con la animación de arriba.
Notas del presentador

Duración: ~8 min. Recorre la animación PASO A PASO (no la corrida directa): localiza primero el entorno correcto antes de dar "Siguiente", y haz que el grupo pronostique en qué entorno se encuentra x.

Punto clave: LEGB se detiene en el primer match — "externa" gana sobre "global". La corrida refuerza que el intérprete no "busca en todas partes".

Pregunta para la clase: ¿qué imprimiría si interna() tuviera su propia x = "interna"? (Respuesta: "interna").

2.2Gestión de memoria

Pila (stack) y montículo (heap)

Stack

Memoria organizada en marcos de activación (uno por cada llamada activa). Se libera automáticamente al retornar (LIFO). Guarda parámetros, variables locales, dirección de retorno.

Heap

Memoria de asignación dinámica, tamaño no conocido de antemano, tiempo de vida no ligado al de la función que la crea. Requiere gestión explícita o automática.

💡 Conexión con la animación 2

Cada llamada recursiva crea un nuevo marco de activación en el stack. En 2.3 veremos esa pila crecer y liberarse paso a paso con factorial(4).

2.2Gestión de memoria

Estrategias de liberación del heap

EstrategiaCómo funcionaRiesgo / costoEjemplos
ManualEl programador libera explícitamenteMemory leaks, dangling pointersC (malloc/free), C++ (new/delete)
Garbage CollectionEl entorno detecta objetos inalcanzables y liberaPausas de recolección, menos control finoJava, Python, Go, JavaScript
OwnershipEl compilador verifica en tiempo de compilación cuándo un valor pierde dueñoCurva de aprendizaje (borrow checker)Rust
💡 Tip — Python en detalle

Python combina conteo de referencias (libera en cuanto el contador llega a 0) con un recolector cíclico adicional para referencias circulares. sys.getrefcount(obj) muestra el conteo activo. Profundizamos esto en la sección de Python.

2.3Expresiones y comandos

Expresión vs. comando (sentencia)

Expresión

Se evalúa y produce un valor. Ej.: 3 + 4, f(x).

Comando / sentencia

Se ejecuta por su efecto (asignación, impresión, control de flujo); no necesariamente produce un valor utilizable.

💡 Tip

Lisp, Scheme y —en menor medida— Rust tratan casi todo como expresión ("todo devuelve un valor"), difuminando la distinción: un rasgo típico del paradigma funcional que veremos en la Unidad IV.

2.3Estructuras de control

Secuencia y selección

Secuencia

Comandos ejecutados uno tras otro, en orden textual. Es la estructura por defecto del paradigma imperativo.

Selección

Bifurca el flujo según una condición booleana.

def clasificar(n: int) -> str:
    if n < 0:
        return "negativo"
    elif n == 0:
        return "cero"
    else:
        return "positivo"
2.3Estructuras de control

Iteración

Repetición de un bloque mientras se cumpla una condición (while) o sobre una secuencia (for).

for — sobre una secuencia
def suma_iterativa(n: int) -> int:
    acumulado = 0
    for i in range(1, n + 1):
        acumulado += i
    return acumulado
while — por condición
acumulado, i = 0, 1
while i <= n:
    acumulado += i
    i += 1
2.3Estructuras de control

Recursión

Un subprograma que se invoca a sí mismo (directa) o mediante una cadena de llamadas que regresa a él (indirecta/mutua). Requiere caso base y caso recursivo que progrese hacia él.

def factorial(n: int) -> int:
    if n == 0:          # caso base
        return 1
    return n * factorial(n - 1)  # caso recursivo
⚠️ Importante — Python no optimiza la recursión de cola

Scheme, Haskell y Scala garantizan tail-call optimization (TCO): reutilizan el marco de pila en llamadas de cola. Python NO lo hace — cada llamada consume un nuevo marco, sea o no de cola. El límite por defecto son ~1000 llamadas anidadas (sys.getrecursionlimit()).

2.3Corrida de escritorio

💻 Corrida: convertir un for acumulador en recursión

Mismo problema, dos mecanismos: la iterativa guarda el parcial en una variable mutable (acumulado); la recursiva lo distribuye en la pila de llamadas. La recursión aparece "a cambio de" la variable acumuladora.

iterativa — estado mutable
def suma_iterativa(n: int) -> int:
    acumulado = 0
    for i in range(1, n + 1):
        acumulado += i
    return acumulado
recursiva — pila de llamadas
def suma_recursiva(n: int) -> int:
    if n == 0:          # caso base
        return 0
    return n + suma_recursiva(n - 1)  # caso recursivo
Python 3 · suma_1_a_n.py
📓 Corrida: ambos caminos producen el mismo resultado. Sigue el resaltado para notar dónde vive "el parcial" en cada caso.
💡 Mini-reto para la clase

Conviertan el for de abajo a recursión (el caso base es 0). La pista: el "acumulado" de cada nivel es el resultado que devuelve la llamada hija: return n + suma_recursiva(n - 1).

Notas del presentador

Duración: ~7 min. Corre la corrida una vez completa (10), luego pide el pronóstico de suma_recursiva(4) antes de la siguiente ejecución.

Diferencia clave para verbalizar: la iterativa reutiliza una celda de memoria (acumulado); la recursiva crea n marcos de pila, uno por nivel. De aquí sale el "costo de la elegancia".

Puente con 2.4: la recursividad se apoyará en el paso de parámetros y en la pila que veremos en la siguiente animación con factorial(4).

2.3Animación · Pila de llamadas

🎬 Crecimiento y liberación de la pila: factorial(4)

def factorial(n): if n == 0: return 1 return n * factorial(n - 1)
Presiona Iniciar para ver cómo se apila un marco por cada llamada y cómo se libera al desenrollar la recursión.
Notas del presentador

Duración: ~8 min. Camina los 10 pasos; enfatiza los momentos de push (crece abajo hacia arriba) y pop retornando (LIFO).

Verbaliza: cada marco guarda n, la dirección de retorno y el estado local — por eso la pila "recuerda" multiplicar n por el resultado hijo al desenrollar.

Antes de tocar la pila con un while: pregunta cuántas llamadas se apilan para factorial(4) (5: 4..0). Conecta con el límite recursionlimit ~1000 de la diapositiva 13.

2.4Subprogramas

Funciones, procedimientos y sobrecarga

Un subprograma es una unidad de código nombrada, parametrizable e invocable desde distintos puntos del programa.

Función

Retorna un valor y, idealmente, no produce efectos secundarios (relevante al comparar con el paradigma funcional).

Procedimiento

Invocado por su efecto, sin valor de retorno significativo (Pascal). Python solo tiene funciones — pueden devolver None.

💡 Sobrecarga (overloading)

Java/C++ resuelven en compilación cuál versión de un subprograma usar según tipo/cantidad de parámetros. Python no soporta sobrecarga real por firma: la última definición sobrescribe a las anteriores; se simula con valores por defecto, *args/**kwargs o functools.singledispatch.

2.4Subprogramas

Mecanismos de paso de parámetros

MecanismoQué se copia/ligaEfecto sobre el originalDónde aparece
Por valorEl valor del argumentoModificar el parámetro NO afecta al originalC (primitivos), Java (primitivos)
Por referenciaLa misma localización/variableModificar el parámetro SÍ afecta al originalC++ (&), Pascal (var)
Por referencia de objetoCopia de la referencia al objetoMutar SÍ se refleja; reasignar NOPython, Java (objetos), JavaScript
📌 El caso de Python: "pass by object reference"

El parámetro recibe una copia de la referencia. Si el objeto es mutable y se muta con un método, el cambio es visible fuera. Si el parámetro se reasigna, solo cambia el binding local. Lo vemos paso a paso en la siguiente animación.

2.4Animación · Entorno de memoria

🎬 Mutar vs. reasignar dentro de un subprograma

Entorno del llamador

original→[1, 2, 3]
↔

Entorno de la función

lista→[1, 2, 3]
Elige un modo arriba y presiona Iniciar para ver cómo cambia (o no) el objeto compartido.
Python 3 · mutar_vs_reasignar.py
📓 Corrida: observa la diferencia en el print de salida. Solo una de las dos operaciones dejó huella en original.
Notas del presentador

Duración: ~8 min. Usa las pestañas mutar/reasignar primero en la animación; la corrida sintetiza ambos casos en un solo script.

Punto clave: Python "pasa por referencia de objeto": mutar (lista.append) se ve fuera; reasignar (lista = ...) no. Es la misma idea de la diapositiva 4 (aliasing).

Pregunta trampa: ¿qué pasa si el parámetro se llama original y se escribe original = original.copy() al inicio? (Respuesta: rompe el alias deliberadamente).

2.5Tipos de datos

Primitivos, compuestos y abstractos

Un tipo de dato define un conjunto de valores posibles y las operaciones válidas sobre ellos.

Primitivos

int, float, bool, char — no descomponibles.

Compuestos

Arreglos, registros, listas, tuplas, diccionarios — construidos a partir de otros tipos.

Tipos abstractos (TAD)

Definidos por su comportamiento, no por su representación interna — base conceptual de las clases en POO (Unidad III).

2.5Tipos de datos

Sistemas de tipos: dos ejes independientes

CriterioEstáticoDinámicoFuerteDébil
Cuándo se verificaCompilaciónEjecución—
Coerción implícita—No permitidaPermitida
EjemplosJava, C, HaskellPython, JSPython, JavaC, JavaScript
⚠️ Error común

Confundir "estático/dinámico" (cuándo se verifica) con "fuerte/débil" (qué tan estrictas son las conversiones). Python es dinámico y fuerte: "5" + 3 lanza TypeError, no concatena silenciosamente como haría JavaScript.

x = 5
x = "ahora soy texto"   # válido: dinámico
"5" + 3   # TypeError — Python es FUERTE, no coerciona
2.5Tipos de datos

Inferencia de tipos

Mecanismo por el cual el compilador deduce el tipo de una expresión sin anotación explícita, preservando las garantías del tipado estático con la brevedad del dinámico.

Python — type hints opcionales
def area_circulo(radio: float) -> float:
    return 3.14159 * radio ** 2

Las anotaciones no se verifican en ejecución; herramientas externas como mypy las validan estáticamente.

Otros lenguajes con inferencia fuerte
  • Haskell: sistema Hindley-Milner, infiere tipos completos sin anotaciones.
  • Rust: let x = 5; infiere i32.
  • Java: var desde Java 10.
  • C++: auto.
🔗 Conexión

Este tema conecta con los tipos algebraicos de la Unidad IV (tipos suma/producto en Haskell) y con el tipado estructural vs. nominal relevante al polimorfismo de la Unidad III.

🐍Python en profundidad

¿Por qué Python como lenguaje de referencia?

A lo largo de la unidad usamos Python para ejemplificar cada concepto. Antes de la comparativa final, formalicemos por qué se comporta como se comporta.

Filosofía de diseño

Prioriza legibilidad y rapidez de desarrollo sobre el control de bajo nivel. "Explicit is better than implicit" (Zen de Python) — pero eso no significa verboso: significa sin magia oculta.

Clasificación formal

Tipado dinámico (se verifica en ejecución) y fuerte (no coerciona entre tipos incompatibles). Interpretado (CPython compila a bytecode e interpreta ese bytecode).

💡 Por qué importa para esta unidad

Python ilustra con claridad los 5 subtemas porque hace visibles decisiones que otros lenguajes ocultan en tiempo de compilación: el binding tardío de nombres, el paso por referencia de objeto, y un sistema de tipos totalmente dinámico.

🐍Python en profundidad

Modelo de objetos: "todo es un objeto"

En Python, enteros, funciones, clases, módulos e incluso tipos son objetos con identidad (id()), tipo (type()) y valor. No hay "tipos primitivos" en el sentido de C — todo pasa por el mismo modelo de referencias.

def saludar(): pass

print(type(5))        # <class 'int'>
print(type(saludar))  # <class 'function'> — la función también es un objeto
print(id(5))          # dirección/identidad del objeto entero 5
📌 Gestión de memoria en CPython

Cada objeto lleva un contador de referencias. Cuando llega a 0, se libera inmediatamente (determinístico, a diferencia del GC de Java). Un recolector cíclico adicional se ejecuta periódicamente para detectar referencias circulares que el conteo por sí solo no puede romper (ej. dos objetos que se referencian mutuamente).

🐍Python en profundidad

Mutabilidad: la clave para entender el paso de parámetros

La animación de la sección 2.4 solo tiene sentido si distinguimos qué tipos de Python son mutables y cuáles no.

InmutablesMutables
int, float, boollist
strdict
tupleset
frozensetInstancias de clases definidas por el usuario (por defecto)
x = 5
def incrementar(n):
    n = n + 1   # int es inmutable: SIEMPRE crea un nuevo objeto y religa localmente
incrementar(x)
print(x)   # 5 — nunca cambia, sea cual sea el tipo, si solo se reasigna
⚠️ Regla general

Si el tipo es inmutable, ninguna operación dentro de una función puede afectar al objeto del llamador — como máximo se religa el nombre local. Si es mutable, los métodos que mutan in situ (.append, .update, [i] =) sí se ven reflejados fuera.

🐍Python en profundidad

Comparativa: Python vs. Java vs. C++

AspectoPythonJavaC++
AlcanceEstático (LEGB)Estático (por bloque)Estático (por bloque)
Sistema de tiposDinámico, fuerteEstático, fuerteEstático, moderadamente débil
Gestión de memoriaRefcounting + GC cíclicoGC generacional (JVM)Manual (new/delete) o RAII
Paso de parámetrosPor referencia de objeto (todo)Por valor (primitivos); por referencia de objeto (objetos)Por valor por defecto; por referencia explícita con &
Sobrecarga de funcionesNo soportada nativamenteSí, resuelta en compilaciónSí, resuelta en compilación
Recursión de cola (TCO)No optimizadaNo optimizadaDepende del compilador (no garantizado por el estándar)
🐍Corrida de escritorio · Bloque final

💻 Corrida comparativa: el mismo programa en 3 lenguajes

Una función que primero muta la lista y luego reasigna el parámetro. Ejecuta la corrida en cada pestaña y fíjate cómo cambia (o no) el resultado según la semántica de paso de parámetros de cada lenguaje.

Python
def arreglar(lista):
    lista.append(99)
    lista = [7, 7]   # religa el nombre local

original = [1, 2, 3]
arreglar(original)
print(original)
Java
C++
Terminal · lenguaje seleccionado
📓 Corrida: ejecuta el código de la pestaña seleccionada. Pronostica el print antes de presionar el botón.
📌 Lectura del resultado

Python y Java imprimen [1, 2, 3, 99]: la mutación append/add tocó el objeto compartido y la reasignación local se perdió. C++ imprime [1, 2, 3]: al pasar por valor se copió el vector y ninguna de las dos operaciones afectó al original — el contraste que define, en una línea, las tres semánticas.

Notas del presentador

Duración: ~15 min (Bloque 3). Es LA corrida de integración. Pide pronóstico antes de cada tab (3 veces) antes de revelar la salida.

Encaje conceptual: usa la tabla comparativa (diapositiva anterior) como respuesta de la razón: Python (referencia de objeto), Java (referencia de valor de objeto), C++ (valor, salvo &). Para C++ menciona que con & el resultado sería como Python.

Al terminar: regresa a la pregunta central de la unidad y salta al mapa conceptual.

25Síntesis

Errores comunes de la unidad

⚠️ Alcance

Asumir que un nombre libre en una función anidada se resuelve "buscando en todas partes" en vez de seguir estrictamente la cadena LEGB, deteniéndose en el primer match.

⚠️ Memoria

Confundir "el nombre cambió de valor" con "el objeto se mutó". Son eventos distintos que solo coinciden cuando el tipo es mutable y se muta in situ.

⚠️ Recursión

Olvidar el caso base, o escribir un caso recursivo que no progresa hacia él — produce recursión infinita y agota la pila (RecursionError en Python).

⚠️ Paso de parámetros

Esperar que def f(n): n = n + 1 modifique la variable externa en Python — nunca ocurre, sin importar el tipo, porque es una reasignación, no una mutación.

⚠️ Tipos

Tratar "dinámico" y "débil" como sinónimos. Python es dinámico y fuerte a la vez; JavaScript es dinámico y débil — son ejes distintos.

⚠️ Sobrecarga

Intentar definir dos funciones con el mismo nombre en Python esperando comportamiento tipo Java/C++ — la segunda definición sobrescribe silenciosamente a la primera.

Notas del presentador

Uso: diapositiva de "parcheo". Antes de iniciar el bloque de Python, pide a la clase votar cuál error les ha pasado alguna vez.

Diagnóstico rápido: las tarjetas de error son el checklist del examen. Los dos errores con más incidencia real en los años anteriores: paso de parámetros y dinámico/débil.

26Cierre

Mapa conceptual de la unidad

Nombre→Binding→Objeto
Entorno→Alcance (LEGB)→Stack / Heap
Expresión / Comando→Control de flujo→Recursión = Pila
Subprograma→Paso de parámetros→Mutar vs. reasignar
Tipo de dato→Estático/Dinámico→Fuerte/Débil

Estos cinco ejes reaparecerán, con otro nombre, en cada paradigma de las Unidades III-V.

27Actividad en clase

Actividad práctica

Implementa en Python un programa que contenga:

  1. Una función anidada que ilustre alcance estático (LEGB).
  2. Una función recursiva con al menos 2 casos base.
  3. Una función que reciba una lista y la modifique tanto por mutación como por reasignación, imprimiendo el resultado en ambos casos para contrastar el efecto observado desde el llamador.

Documenta con comentarios qué mecanismo de scope/paso de parámetros se está observando en cada bloque.

28Preguntas de reflexión

Para discutir

1.

¿Qué problemas puede causar el alcance dinámico frente al estático en programas grandes?

2.

¿Por qué la recursión es central en el paradigma funcional pero opcional en el imperativo?

3.

¿Qué ventajas y riesgos trae un sistema de tipos con inferencia frente a uno con anotación explícita?

4.

Si Python "pasa por referencia de objeto", ¿por qué incrementar(x) con x = 5 nunca modifica x fuera de la función?

31Evaluación formativa · Cierre

📝 Mini-quiz: 5 preguntas de la unidad

Marcar una opción por pregunta y presionar Calificar. El sistema resalta aciertos y errores con retroalimentación. No cuenta puntos: es para saber qué reforzar antes del examen.

1 · Alcance. Dado def f(): x = 7, x = 5, f(), print(x) — ¿qué imprime el programa?

2 · Memoria / aliasing. Si a = [1, 2] y b = a, tras b.append(3) con print(a) obtenemos…

3 · Control / recursión. Una función recursiva sin caso base que pueda alcanzarse provoca…

4 · Parámetros. def f(n): n = n + 1, x = 5, f(x), print(x) — ¿qué imprime?

5 · Tipos. En Python, "5" + 3 produce…

Notas del presentador

Duración: ~8 min individual, en silencio. Después, 5 min de socialización: cada pregunta la lee un estudiante; usa las explicaciones del quiz para cerrar el debate.

Función: datos para ti (qué reforzar) y para ellos (metacognición). Preguntas con mayor caída histórica: la 3 (recursión) y la 5 (fuerte vs. dinámico).

Opción: si el grupo está trabado, abre la animación correspondiente (7 LEGB, 18 mutar/reasignar) antes de revelar la respuesta correcta.

32Cierre

Lecturas recomendadas · Tarjeta de salida

  • Sebesta, R. (2015). Concepts of Programming Languages (11th ed.), cap. 5, 6, 9-10. Pearson.
  • Pratt, T., & Zelkowitz, M. (2001). Programming Languages: Design and Implementation (4th ed.), cap. 3-5. Prentice Hall.
  • Scott, M. (2015). Programming Language Pragmatics (4th ed.), cap. 3. Morgan Kaufmann.
🎫 Tarjeta de salida — entrégala (papel o chat) antes de irte

3 ideas

Anota tres conceptos que hoy quedaron claros.

2 dudas

Dos puntos que quieras repasar o ver más despacio.

1 conexión

Una idea que conecte la clase con tu forma de programar o tu proyecto.

Dr. Jose Manuel Muñoz Contreras
Facultad de Ciencias de la Ingeniería y Tecnología (FCITEC) — Unidad Valle de las Palmas, UABC
Ingeniería en Desarrollo de Software y Tecnologías Emergentes — Plan de Estudios 2021-2
Paradigmas de Programación · Grupo 544, Subgrupo 0

Notas del presentador

Cierre (spa 165–180): primero el mapa conceptual (slide 28) para integrar, luego el mini-quiz (31), y al final la tarjeta de salida. La tarjeta es la evidencia formativa de la sesión: úsala para decidir qué repasar en la próxima.

Las lecturas de Sebesta/Pratt/Scott son las referencias oficiales de la asignatura; señalarlas aquí cierra el ciclo guion→presentación→bibliografía.

01 / 01