Un paradigma es un modelo de cómo estructurar la computación. El paradigma imperativo describe el cómputo como una secuencia de sentencias que cambian el estado del programa mediante asignaciones; el orden de ejecución es significativo y se apoya en estructuras de control: secuencia, selección e iteración (estilo estructurado). En oposición, el paradigma declarativo especifica QUÉ resultado se desea sin describir el flujo de control paso a paso, y engloba los subparadigmas funcional y lógico.
La programación orientada a objetos (POO) organiza el software en objetos que combinan estado (atributos) y comportamiento (métodos). Sus cuatro pilares son:
El paradigma funcional modela el cómputo como la evaluación de funciones matemáticas; su fundamento teórico es el cálculo lambda (A. Church, 1936). Evita el estado mutable y los efectos secundarios, y favorece la composición. Una función pura produce siempre la misma salida para las mismas entradas y no genera efectos secundarios observables. De ello deriva la transparencia referencial: una expresión puede sustituirse por su valor sin alterar el programa. Las funciones son de primera clase (se asignan, pasan y retornan) y las de orden superior reciben o devuelven otras funciones. La inmutabilidad implica que las 'actualizaciones' generan nuevos valores en vez de mutar los existentes. El paradigma lógico expresa hechos y reglas para que el motor infiera respuestas, y el concurrente coordina tareas simultáneas.
Para trazar código dado en texto: un bucle for/while con variable acumuladora y asignaciones es imperativo; operaciones map, filter y reduce sobre una colección expresan lo mismo en estilo funcional sin mutar estado. En POO se sigue el envío de mensajes (llamadas a métodos) resolviendo el método concreto según la clase del receptor (despacho dinámico). Conviene recordar las estructuras de datos (arreglos, apuntadores, estructuras dinámicas, cadenas) y las operaciones/recorridos de pilas (LIFO), colas (FIFO), árboles y grafos. Existen lenguajes multiparadigma como Python, C++, JavaScript y Scala.
1. ¿Qué característica define fundamentalmente al paradigma de programación imperativo?
El paradigma imperativo se define por sentencias que cambian el estado del programa mediante asignaciones y por la importancia del orden de ejecución; las demás opciones describen los paradigmas lógico, funcional y orientado a objetos. (Robert W. Sebesta, Concepts of Programming Languages, cap. 1 (clasificación de paradigmas))
2. ¿Cuáles son las tres estructuras de control fundamentales de la programación estructurada?
La programación estructurada se basa en secuencia, selección e iteración como estructuras de control básicas; las demás opciones corresponden a la POO, la programación funcional y la concurrencia. (Sebesta, Concepts of Programming Languages, cap. 1)
3. Un desarrollador calcula el promedio de un arreglo de números usando un ciclo for que en cada iteración acumula la suma en una variable y, al terminar el ciclo, divide dicha variable entre el número de elementos. ¿A qué paradigma corresponde este enfoque de solución?
El uso de un ciclo con una variable acumuladora que se reasigna en cada paso es característico del estilo imperativo, no del funcional (que evita mutar estado) ni del lógico u orientado a objetos. (Sebesta, Concepts of Programming Languages, cap. 1)
4. Desde la perspectiva de la programación funcional, ¿cuál de las siguientes acciones realizadas dentro de una función constituye un efecto secundario (side effect)?
Un efecto secundario implica modificar estado observable fuera del ámbito local, como una variable global; las demás acciones no alteran estado externo a la función. (Sebesta, Concepts of Programming Languages (side effects y sentencia de asignación))
5. Según el teorema de la programación estructurada (Böhm-Jacopini), ¿qué conjunto mínimo de estructuras de control basta para expresar cualquier programa que calcule una función computable?
El teorema de Böhm-Jacopini (1966) demuestra que secuencia, selección e iteración son suficientes para expresar cualquier función computable, eliminando la necesidad del goto. (C. Böhm y G. Jacopini, 'Flow diagrams, turing machines and languages with only two formation rules', Communications of the ACM, 1966)
6. ¿Qué crítica formuló Edsger Dijkstra en 1968 contra el uso indiscriminado de la instrucción goto en programas imperativos?
En su carta 'Go To Statement Considered Harmful', Dijkstra argumentó que el goto oscurece el flujo de control y dificulta la verificación y el mantenimiento del código. (E. W. Dijkstra, 'Go To Statement Considered Harmful', Communications of the ACM, 1968)
7. Un ingeniero de software debe programar una rutina que lea el estado de varios sensores en un orden específico, verifique condiciones sobre cada lectura y actualice registros de memoria paso a paso conforme avanza. ¿Qué paradigma resulta más directo para expresar esta lógica?
Una secuencia ordenada de lecturas, verificaciones y actualizaciones de estado paso a paso es el escenario natural del paradigma imperativo estructurado; los otros paradigmas no capturan directamente la dependencia del orden ni el cambio de estado explícito. (Sebesta, Concepts of Programming Languages, cap. 1)
8. ¿Cuál de las siguientes NO se considera una estructura de control fundamental de la programación estructurada?
La programación estructurada excluye deliberadamente el goto, sustituyéndolo por secuencia, selección e iteración como estructuras fundamentales. (Böhm y Jacopini, CACM, 1966; Dijkstra, CACM, 1968)
9. Dentro del paradigma imperativo, ¿cuál es la función principal de una sentencia de asignación?
La sentencia de asignación es el mecanismo central del paradigma imperativo para cambiar el estado del programa; las otras opciones corresponden a declaración de tipos, modularidad y herencia en POO. (Sebesta, Concepts of Programming Languages, cap. 1)
10. ¿Cómo se representa el conocimiento de un problema en el paradigma de programación lógica, como el implementado en Prolog?
En el paradigma lógico, el conocimiento se declara como hechos y reglas (cláusulas), y el motor de inferencia deriva las respuestas; las demás opciones describen los paradigmas imperativo, orientado a objetos y funcional. (Sebesta, Concepts of Programming Languages, cap. 16 (Logic Programming Languages))
11. ¿Qué mecanismo emplea el motor de inferencia de un lenguaje lógico como Prolog para determinar si una consulta (meta) se satisface a partir de los hechos y reglas disponibles?
El motor lógico intenta unificar la meta con hechos o cabezas de reglas y, si falla, retrocede (backtracking) para probar otra alternativa; las demás opciones pertenecen a la gestión de memoria, la POO y la concurrencia. (Sebesta, Concepts of Programming Languages, cap. 16)
12. En el paradigma lógico, ¿qué nombre recibe el proceso mediante el cual el motor de inferencia deshace una decisión previa y explora una alternativa distinta cuando una rama de búsqueda falla?
El backtracking es el mecanismo que permite al motor lógico retroceder y probar unificaciones alternativas cuando una rama de búsqueda no satisface la meta. (Sebesta, Concepts of Programming Languages, cap. 16)
13. ¿Cuál es la diferencia conceptual entre concurrencia y paralelismo en el diseño de programas?
La concurrencia se refiere a tareas cuya ejecución se traslapa lógicamente en el tiempo, mientras que el paralelismo exige ejecución simultánea real sobre múltiples unidades de procesamiento. (Sebesta, Concepts of Programming Languages, cap. 13 (Concurrency))
14. Dos hilos de un mismo proceso incrementan concurrentemente la misma variable compartida en memoria, sin emplear ningún mecanismo de sincronización. ¿Qué problema es más probable que ocurra?
Cuando varios hilos acceden y modifican una variable compartida sin sincronización, el resultado depende del orden de ejecución, lo que constituye una condición de carrera; las demás opciones no se derivan de este escenario. (Sebesta, Concepts of Programming Languages, cap. 13)
15. En un sistema, el proceso A retiene el recurso R1 y espera a que se libere el recurso R2, mientras que el proceso B retiene R2 y espera a que se libere R1; ninguno de los dos cede el recurso que tiene. ¿Qué condición de concurrencia describe esta situación?
La espera circular en la que cada proceso retiene un recurso y espera otro retenido por el otro proceso es la definición de interbloqueo; la exclusión mutua es solo una de las condiciones necesarias para que ocurra, no la situación descrita en sí. (Sebesta, Concepts of Programming Languages, cap. 13)
16. ¿Qué mecanismo de sincronización se utiliza comúnmente para garantizar que solo un hilo a la vez acceda a una sección crítica compartida?
Los semáforos y mutex son los mecanismos clásicos para proteger secciones críticas y garantizar exclusión mutua entre hilos; las demás opciones no cumplen esa función. (Sebesta, Concepts of Programming Languages, cap. 13)
17. ¿Cuál de las siguientes NO forma parte de las cuatro condiciones necesarias de Coffman para que ocurra un interbloqueo (deadlock)?
Las condiciones de Coffman son exclusión mutua, retención y espera, no apropiación (no preemption) y espera circular; la política de planificación round-robin no es una de ellas. (E. G. Coffman, M. Elphick y A. Shoshani, 'System Deadlocks', ACM Computing Surveys, 1971)
18. Considere el siguiente fragmento de pseudocódigo: entero suma <- 0 Para i <- 1 hasta 4 hacer suma <- suma + i Fin Para ¿Cuál es el valor final de la variable suma al terminar el ciclo?
El ciclo acumula 1+2+3+4 = 10; el valor 6 resulta de detener la suma un paso antes (1+2+3) y el valor 15 de sumar un paso de más (1+2+3+4+5). (SICP, cap. 1 (procesos iterativos con acumulador); Sebesta, cap. 1)
19. Considere el siguiente fragmento de pseudocódigo: entero contador <- 0 Para i <- 1 hasta 10 hacer Si i MOD 3 = 0 entonces contador <- contador + 1 Fin Si Fin Para ¿Cuál es el valor final de la variable contador al terminar el ciclo?
Los múltiplos de 3 entre 1 y 10 son 3, 6 y 9, por lo que contador termina en 3; contarlos incorrectamente (incluyendo 10 o excluyendo uno de ellos) produce 4 o 2. (Sebesta, Concepts of Programming Languages, cap. 1 (estructuras de selección e iteración))
20. Considere el siguiente fragmento de pseudocódigo: entero total <- 0 Para i <- 1 hasta 3 hacer Para j <- 1 hasta 2 hacer total <- total + (i * j) Fin Para Fin Para ¿Cuál es el valor final de la variable total al terminar ambos ciclos?
La suma de i·j para i=1..3 y j=1..2 equivale a (1+2+3)×(1+2) = 18; el valor 21 resulta de sumar (i+j) en vez de multiplicar, y 9 o 12 resultan de recorrer solo una parte de los ciclos anidados. (Sebesta, Concepts of Programming Languages, cap. 1 (ciclos anidados))
21. Sobre la lista [1, 2, 3, 4, 5] se aplica primero la operación map con la función 'elevar al cuadrado' y, sobre el resultado, se aplica la operación filter conservando únicamente los valores mayores que 10. ¿Cuál es la lista resultante?
map produce [1, 4, 9, 16, 25] y filter conserva solo los mayores que 10, es decir [16, 25]; la lista vacía resultaría de invertir el orden (filtrar antes de elevar al cuadrado) y las otras opciones surgen de omitir el filtro o usar un umbral incorrecto. (SICP, cap. 2 (map, filter, accumulate); Sebesta, cap. 15)
22. Se definen las clases Animal y Perro, donde Perro hereda de Animal. Ambas implementan el método hacerSonido(): Animal lo define retornando la cadena "..." y Perro lo sobrescribe retornando "Guau". En el código se declara una variable de tipo Animal que referencia un objeto instanciado de la clase Perro, y se invoca miVariable.hacerSonido(). Según el mecanismo de despacho dinámico, ¿qué valor se obtiene?
El despacho dinámico (dynamic binding) resuelve la llamada según la clase real del objeto en tiempo de ejecución (Perro), no según el tipo declarado de la referencia; asignar un Perro a una variable Animal es válido por la relación 'es-un' de la herencia. (Sebesta, Concepts of Programming Languages, cap. 12; Meyer, Object-Oriented Software Construction, 2a ed.)
23. Se aplica la operación reduce sobre la lista [2, 3, 4] con la función acumuladora f(acumulado, elemento) = acumulado * elemento, partiendo de un valor inicial de 2. ¿Cuál es el resultado final de la reducción?
La reducción calcula ((2×2)×3)×4 = 48; el valor 24 resulta de ignorar el valor inicial (2×3×4), 11 de sumar en vez de multiplicar, y 12 de detener la acumulación antes del último elemento. (SICP, cap. 2 (accumulate/reduce); Sebesta, cap. 15 (higher-order functions))
24. Un programador escribe el siguiente ciclo con la intención de sumar los números del 1 al 5, pero al ejecutarlo obtiene un resultado distinto del esperado (15): entero suma <- 0 entero i <- 1 Mientras i < 5 hacer suma <- suma + i i <- i + 1 Fin Mientras ¿Cuál es la causa del resultado incorrecto?
Con la condición i < 5 el ciclo se detiene cuando i llega a 5, dejando de sumar ese valor y obteniendo 10 en vez de 15; la inicialización, la condición inicial y el orden de las sentencias dentro del ciclo son correctos. (Sebesta, Concepts of Programming Languages, cap. 1 (condiciones de iteración))
25. Al depurar un programa mediante una traza de ejecución manual, ¿qué información se registra típicamente en cada paso?
Una traza de ejecución registra, paso a paso, los valores que toman las variables y el punto del código alcanzado, lo que permite localizar dónde el comportamiento se desvía de lo esperado. (Sebesta, Concepts of Programming Languages, cap. 1 (semántica operacional y seguimiento de estado))
26. Se tienen las variables enteras a <- 5 y b <- 8. Se ejecuta la siguiente secuencia de asignaciones: temp <- a a <- b b <- temp ¿Cuáles son los valores finales de a y b al terminar la secuencia?
temp guarda el valor original de a (5); luego a toma el valor de b (8) y b toma el valor guardado en temp (5), intercambiando los valores; las demás opciones surgen de omitir el uso de la variable temporal o de sobrescribir un valor antes de guardarlo. (Sebesta, Concepts of Programming Languages, cap. 1 (sentencia de asignación))
27. En la programación orientada a objetos, el principio que consiste en ocultar los detalles internos de un objeto y exponer únicamente una interfaz pública para interactuar con él se conoce como:
El encapsulamiento oculta el estado interno del objeto y expone solo una interfaz pública, principio derivado del ocultamiento de información propuesto por Parnas en 1972. (D. L. Parnas, On the Criteria To Be Used in Decomposing Systems into Modules, CACM 15(12), 1972.)
28. Cuando una clase 'Vehiculo' define atributos y métodos generales y una clase 'Automovil' los reutiliza y agrega comportamiento propio, la relación entre ambas clases se describe como:
La herencia establece una relación 'es-un' (is-a) entre subclase y superclase, no una relación de composición ('tiene-un') ni de uso. (Robert W. Sebesta, Concepts of Programming Languages, cap. 12.)
29. Caso práctico: un desarrollador tiene una superclase Figura con el método area() y dos subclases, Circulo y Rectangulo, cada una con su propia implementación de area(). En tiempo de ejecución, un arreglo de referencias de tipo Figura contiene objetos de ambas subclases y el programa invoca figura.area() dentro de un ciclo. ¿Qué mecanismo determina cuál implementación de area() se ejecuta para cada elemento?
El polimorfismo se implementa mediante enlace dinámico, que resuelve el método a ejecutar según el tipo real del objeto en tiempo de ejecución, no según el tipo declarado de la referencia ni el orden de declaración. (Sebesta, Concepts of Programming Languages, cap. 12 (Support for Object-Oriented Programming).)
30. De acuerdo con la caracterización clásica de Grady Booch, ¿cuáles son los cuatro pilares esenciales de la programación orientada a objetos?
Booch resume las características esenciales de la POO en cuatro pilares: abstracción, encapsulamiento, herencia y polimorfismo. (Grady Booch, Object-Oriented Analysis and Design with Applications, 3a ed.)
31. ¿Cuál de las siguientes opciones NO corresponde a uno de los cuatro pilares de la programación orientada a objetos según Booch?
La recursividad es una técnica en la que una función se llama a sí misma, no uno de los pilares de la POO, que son abstracción, encapsulamiento, herencia y polimorfismo. (Grady Booch, Object-Oriented Analysis and Design with Applications, 3a ed.)
32. Caso práctico: en una clase, un atributo se declara con el modificador de acceso 'protegido' (protected) en lugar de 'privado' (private). ¿Qué consecuencia tiene esta decisión sobre el encapsulamiento respecto de las subclases?
El modificador protegido relaja el encapsulamiento estricto permitiendo el acceso desde subclases, a diferencia de privado (solo la propia clase) y público (todo el programa). (Sebesta, Concepts of Programming Languages, cap. 11-12 (modificadores de acceso y encapsulamiento).)
33. Caso práctico: un equipo de desarrollo detecta que dos clases, Empleado y Cliente, comparten los atributos nombre y correo, y el método actualizarDatos(), pero no tienen ninguna otra relación conceptual entre sí. ¿Qué acción de diseño resuelve mejor la duplicación de código sin forzar una relación 'es-un' artificial?
Cuando dos clases comparten comportamiento sin relación conceptual 'es-un', la práctica recomendada es extraer una superclase o interfaz común, evitando duplicación sin forzar una jerarquía de herencia artificial. (Grady Booch, Object-Oriented Analysis and Design with Applications; Bertrand Meyer, Object-Oriented Software Construction, 2a ed.)
34. El mecanismo por el cual una llamada a un método se resuelve en tiempo de ejecución según el tipo real del objeto, y no según el tipo declarado de la variable, se conoce como:
El polimorfismo en tiempo de ejecución se apoya en el enlace dinámico, que resuelve el método según el tipo real del objeto receptor, no en tiempo de compilación. (Sebesta, Concepts of Programming Languages, cap. 12.)
35. En programación orientada a objetos se distinguen dos formas de polimorfismo: la sobrecarga (overloading), resuelta en tiempo de compilación según la firma del método, y la sobreescritura (overriding), resuelta en tiempo de ejecución mediante enlace dinámico. Si una subclase redefine con la misma firma un método ya definido en su superclase para especializar su comportamiento, ¿qué forma de polimorfismo se está aplicando?
Redefinir un método con la misma firma en una subclase para especializar su comportamiento es sobreescritura (overriding), resuelta mediante enlace dinámico en ejecución; la sobrecarga implica firmas distintas resueltas en compilación. (Sebesta, Concepts of Programming Languages, cap. 12 (overloading vs. overriding, dynamic binding).)