Firma 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
Última actualización: 25 de agosto de 2026
Agenda

Qué vamos a recorrer

Cinco subtemas, de los nombres que usa un programa hasta los tipos de datos que puede manipular. Tres procesos internos —difíciles de imaginar solo con texto— se muestran aquí con animaciones paso a paso.

2.1 Nombres y objetos

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 uno a uno.

📌 Definición

Varios nombres pueden ligarse al mismo objeto — esto se llama aliasing. En lenguajes de semántica de referencia (Python, Java para objetos), una variable guarda una referencia, no el objeto mismo.

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 apunta a un objeto nuevo); b.append(9) muta el objeto compartido. Esta distinción es la base de la animación de paso de parámetros en 2.4.

2.2 Entornos, bloques, alcance (scope) y gestión de memoria

Un entorno mapea, en un punto dado de la ejecución, cada nombre visible a su binding. El alcance de un nombre es la región del programa donde ese nombre es visible.

Alcance estático (léxico)

Determinado por la estructura textual del código. Python, Java, C, Haskell lo usan. Se resuelve siguiendo la regla LEGB: Local → Enclosing → Global → Built-in.

Alcance dinámico

Determinado por la secuencia de llamadas en ejecución. Usado en Lisp clásico, variables de Bash, special de Common Lisp. Dificulta razonar sobre el código leyéndolo solamente.

CriterioEstáticoDinámico
Determinado porEstructura textualSecuencia de llamadas
PredictibilidadAltaBaja
LenguajesPython, Java, C, HaskellBash, Common Lisp (special)
🎬 Animación 1 — Resolución de alcance en bloques anidados (regla 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.
2.3 Expresiones y comandos: secuencia, selección, iteración, recursión

Una expresión se evalúa y produce un valor. Un comando (sentencia) se ejecuta por su efecto. Sobre esa base se construyen las cuatro estructuras de control fundamentales.

Secuencia

Comandos ejecutados uno tras otro, en orden textual.

Selección

if n < 0:
    return "negativo"
elif n == 0:
    return "cero"
else:
    return "positivo"

Iteración

for i in range(1, n + 1):
    acumulado += i

Recursión

Un subprograma que se invoca a sí mismo. Requiere caso base y caso recursivo que progrese hacia él.

⚠️ Importante

Python no optimiza la recursión de cola (sin TCO): cada llamada consume un nuevo marco de pila, incluso si es "de cola". El límite por defecto son ~1000 llamadas anidadas (sys.getrecursionlimit()).

🎬 Animación 2 — Crecimiento y liberación de la pila de llamadas: 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.
2.4 Subprogramas: funciones, procedimientos y paso de parámetros

Un subprograma es una unidad de código nombrada e invocable. La forma en que se comunican argumento y parámetro define su mecanismo de paso.

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

Python usa "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.

🎬 Animación 3 — 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.
2.5 Tipos de datos

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.

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.

x = 5
x = "ahora soy texto"   # válido: dinámico

"5" + 3   # TypeError — Python es FUERTE, no coerciona
Cierre de unidad

Resumen, actividad y reflexión

Resumen

  • Nombres ≠ objetos; el binding los liga.
  • El alcance estático se resuelve por la estructura del código (LEGB en Python).
  • Stack para llamadas, heap para memoria dinámica; GC automatiza su liberación.
  • Toda recursión necesita caso base + caso recursivo.
  • Python pasa "por referencia de objeto": mutar se ve fuera, reasignar no.
  • Estático/dinámico y fuerte/débil son ejes independientes del sistema de tipos.

Actividad en clase

  1. Escribe una función anidada que ilustre LEGB.
  2. Escribe una función recursiva con 2 casos base.
  3. Escribe una función que reciba una lista y la modifique por mutación y por reasignación; imprime el resultado en ambos casos.

Preguntas de reflexión

  1. ¿Qué problemas causa el alcance dinámico en programas grandes?
  2. ¿Por qué la recursión es central en el paradigma funcional?
  3. ¿Qué riesgos trae la inferencia de tipos frente a la anotación explícita?
  4. ¿Por qué incrementar(x) nunca modifica x si x = 5?

Lecturas recomendadas

  • 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.