El EGEL Plus Ciencias Computacionales clasifica los lenguajes por paradigma. Los cuatro paradigmas fundamentales son el imperativo (procedimental), el orientado a objetos (herencia, polimorfismo y encapsulamiento), el funcional y el lógico. Los reactivos entregan el código en un bloque de texto y exigen identificar el paradigma y trazar el comportamiento paso a paso.
Al trazar expresiones aritméticas, los operadores multiplicativos (* / %) tienen mayor precedencia que los aditivos (+ -). En C y Java la división entre enteros es entera y trunca hacia cero (7/2 = 3 y -7/2 = -3), y el residuo % toma el signo del dividendo (-7 % 3 = -1). En Python el comportamiento difiere: % toma el signo del divisor (-7 % 3 = 2), // (división entera) redondea hacia menos infinito (-7 // 2 = -4) y / siempre devuelve float en Python 3 (7 / 2 = 3.5). La aritmética double sigue IEEE 754 (64 bits), por eso 0.1 + 0.2 = 0.30000000000000004.
En ámbito y paso de parámetros, Java es siempre paso por valor: con objetos se copia el valor de la referencia, así el método puede mutar el objeto apuntado pero no reasignar la referencia original del llamador.
Para los paradigmas no imperativos: el funcional puro se caracteriza por la transparencia referencial y la ausencia de efectos secundarios (misma entrada, mismo resultado, sustituible por su valor). El lógico, representado por Prolog, se basa en la unificación y el retroceso (backtracking) para resolver consultas de forma declarativa a partir de hechos y reglas.
1. En C, C++, Java y Python, al evaluar una expresión aritmética sin paréntesis, ¿qué jerarquía de operadores aplican el compilador o el intérprete?
Según ISO/IEC 9899 y la especificación de Java, los operadores * / % tienen mayor precedencia que + -, por lo que se resuelven primero al no haber paréntesis; asignar la misma precedencia a todos es incorrecto. (ISO/IEC 9899:2018 (C) §6.5; Java Language Specification, Java SE, §15.17-15.18)
2. Se ejecuta la siguiente instrucción en un lenguaje imperativo como C o Java: resultado = 10 - 4 * 2 + 6 / 2; ¿qué valor almacena la variable resultado al aplicar la precedencia estándar de operadores?
Aplicando la precedencia estándar, 4*2=8 y 6/2=3, por lo que 10-8+3=5; las demás opciones surgen de ignorar la precedencia o de agrupar mal los operadores + y -. (ISO/IEC 9899:2018 (C) §6.5; Java Language Specification §15.17-15.18)
3. En Java se ejecuta la instrucción int x = -7 / 2; Considerando que la división entre enteros trunca hacia cero, ¿qué valor almacena x?
En Java y C la división entera trunca hacia cero, por lo que -7/2 = -3 (no -4, que sería el resultado de redondear hacia menos infinito como en Python). (Java Language Specification §15.17.2; ISO/IEC 9899:2018 §6.5.5)
4. Un programador migra a Python un fragmento de código escrito originalmente en Java sin modificar la expresión -7 % 3, que en Java produce -1. Al ejecutar la misma expresión en Python 3, ¿qué valor se obtiene y por qué difiere del resultado en Java?
El operador % en Python toma el signo del divisor, por lo que -7 % 3 = 2, a diferencia de Java, donde el mismo operador toma el signo del dividendo y da -1. (The Python Language Reference §6.7 'Binary arithmetic operations'; Java Language Specification §15.17.3)
5. En Python 3 se ejecuta la instrucción print(-7 // 2). Considerando el comportamiento del operador de división entera //, ¿qué se imprime?
El operador // en Python redondea hacia menos infinito (piso), por lo que -7 // 2 = -4, y no -3 como ocurriría con un truncamiento hacia cero. (The Python Language Reference §6.7 'Binary arithmetic operations')
6. En Python 3 se ejecuta la instrucción print(7 / 2). ¿Qué valor se imprime, considerando que el operador / realiza siempre división de punto flotante?
En Python 3 el operador / siempre realiza división de punto flotante (true division), incluso entre dos enteros, por lo que 7/2 = 3.5 (no 3, que correspondería a la división entera con //). (The Python Language Reference §6.7; PEP 238)
7. En Java se evalúa la condición if (lista != null && lista.size() > 0) sobre una variable lista cuyo valor es null. Debido a la evaluación de cortocircuito del operador &&, ¿qué ocurre al ejecutar esta condición?
El operador && usa evaluación de cortocircuito y se detiene en el primer operando falso, por lo que nunca evalúa lista.size() si lista es null, evitando la excepción. (Java Language Specification §15.23-15.24; ISO/IEC 9899:2018 §6.5.13-6.5.14)
8. En Java un programador declara double a = 0.1, b = 0.2; y evalúa la condición if (a + b == 0.3). Considerando la representación en punto flotante IEEE 754 del tipo double, ¿cuál es el resultado de esta comparación y por qué?
Los valores double siguen IEEE 754 y no representan de forma exacta decimales como 0.1 o 0.2, por lo que su suma da 0.30000000000000004 y la comparación con 0.3 resulta false. (IEEE 754-2019 (Standard for Floating-Point Arithmetic); Java Language Specification §4.2.3)
9. En Java se ejecuta la instrucción System.out.println(1 + 2 + "x"); Considerando que el operador + es asociativo por la izquierda y que uno de los operandos finales es de tipo String, ¿qué se imprime en la consola?
El operador + es asociativo por la izquierda, así que 1+2 se suma aritméticamente antes de concatenarse con la cadena, dando como resultado 3x. (Java Language Specification §15.18.1)
10. Se ejecuta el siguiente fragmento en Java: int i = 5; int a = i++; int b = ++i; Al finalizar, ¿cuáles son los valores de a, b e i?
El postfijo (i++) devuelve el valor antes de incrementar (a=5) y el prefijo (++i) devuelve el valor después de incrementar (b=7), quedando i=7 al final. (Java Language Specification §15.14.2 (posfijo) y §15.15.1 (prefijo))
11. En C se declara int arreglo[8]; y se recorre con el ciclo for (int i = 0; i <= 8; i++) { arreglo[i] = i; } ¿Qué error de gestión de índices contiene este ciclo al trazar su ejecución?
Los índices válidos de un arreglo de 8 elementos van de 0 a 7; al permitir i=8 el ciclo accede a arreglo[8], que está fuera de rango. (ISO/IEC 9899:2018 (C) §6.5.2.1 (subíndices de arreglo); Java Language Specification §10.4 (array access))
12. El paradigma lógico, representado por lenguajes como Prolog, resuelve las consultas a partir de una base de hechos y reglas mediante dos mecanismos fundamentales. ¿Cuáles son?
El paradigma lógico se basa en la unificación de términos y el retroceso (backtracking), a diferencia de los mecanismos propios de los paradigmas orientado a objetos, imperativo o funcional. (ISO/IEC 13211-1:1995 (Prolog); Sebesta, R., Concepts of Programming Languages (cap. Logic Programming))
13. Considera la siguiente base de conocimiento en Prolog: padre(juan, maria). padre(juan, pedro). padre(pedro, ana). abuelo(X, Y) :- padre(X, Z), padre(Z, Y). Al ejecutar la consulta ?- abuelo(juan, ana)., ¿qué ocurre al trazar la unificación y el posible retroceso?
Prolog intenta unificar Z con maria, falla al comprobar padre(maria, ana), retrocede y prueba Z=pedro, con lo que la consulta tiene éxito gracias al backtracking. (ISO/IEC 13211-1:1995 (Prolog), mecanismo de unificación y retroceso (backtracking))
14. En Prolog, cuando una submeta dentro del cuerpo de una regla falla durante la resolución de una consulta, ¿qué hace el motor de inferencia antes de declarar el fallo de la consulta completa?
Cuando una submeta falla, el motor de inferencia de Prolog retrocede (backtracking) hacia la submeta anterior para probar una unificación alternativa antes de declarar el fallo total. (ISO/IEC 13211-1:1995 (Prolog); Sebesta, R., Concepts of Programming Languages (cap. Logic Programming))
15. En un programa Prolog, una cláusula como padre(juan, maria)., escrita sin el operador :- y sin cuerpo, se denomina:
Una cláusula sin cuerpo que simplemente afirma una relación, como padre(juan, maria)., se denomina hecho en Prolog; una regla, en cambio, incluye el operador :- y un cuerpo. (ISO/IEC 13211-1:1995 (Prolog); Sebesta, R., Concepts of Programming Languages)
16. Considera las siguientes cláusulas en Prolog: mayor(X, Y) :- X > Y, !. mayor(X, Y) :- X =< Y. Al ejecutar la consulta ?- mayor(3, 5)., ¿qué ocurre al trazar su resolución?
El corte (!) solo impide el retroceso una vez alcanzado; como 3 > 5 falla antes de llegar al corte, este nunca se ejecuta, Prolog prueba la segunda cláusula y la consulta tiene éxito. (ISO/IEC 13211-1:1995 (Prolog), operador de corte (cut))
17. Considera la siguiente definición en Prolog: suma_lista([], 0). suma_lista([H|T], S) :- suma_lista(T, S1), S is H + S1. Al ejecutar la consulta ?- suma_lista([3, 4, 5], S)., ¿qué valor obtiene S al trazar la recursión hasta el caso base y su posterior desenrollado?
La recursión llega al caso base suma_lista([],0) y se desenrolla sumando cada cabeza: 5+0=5, 4+5=9, 3+9=12, por lo que S=12. (ISO/IEC 13211-1:1995 (Prolog), recursión y unificación de listas)
18. En Prolog, si un hecho no aparece en ninguna parte de la base de conocimiento, una consulta ?- not(hecho_no_declarado). tiene éxito (true). ¿Qué principio explica este comportamiento?
La negación por fallo establece que si un hecho no puede probarse verdadero con la base de conocimiento disponible, se asume falso, por lo que la consulta tiene éxito. (ISO/IEC 13211-1:1995 (Prolog), negación por fallo (negation as failure))
19. A diferencia del paradigma imperativo, donde el programador especifica una secuencia de instrucciones y asignaciones para modificar el estado, el paradigma lógico exige que el programador especifique:
El paradigma lógico exige declarar hechos y reglas sobre relaciones, dejando que el motor de inferencia determine el procedimiento de solución, a diferencia de los demás paradigmas. (CENEVAL, Guía para el sustentante EGEL Plus Ciencias Computacionales (área Lenguajes de programación); Sebesta, R., Concepts of Programming Languages)
20. En C, p es un apuntador que almacena la dirección de una variable entera x. ¿Qué operador se utiliza para acceder o modificar el valor de x a través de p?
En C, el operador de desreferencia * permite acceder o modificar el valor almacenado en la dirección que contiene un apuntador, como en *p = 10;; el operador & obtiene la dirección, no el valor. (ISO/IEC 9899:2018 (C) §6.5.3.2 (operadores de apuntadores * y &))
21. Se ejecuta el siguiente código en C: void swap(int *a, int *b) { int temp = *a; *a = *b; *b = temp; } int main() { int x = 3, y = 7; swap(&x, &y); } Al finalizar la llamada swap(&x, &y);, ¿cuáles son los valores de x e y?
Al pasar las direcciones &x y &y, la función swap modifica directamente las variables originales a través de los apuntadores, intercambiando sus valores a 7 y 3. (ISO/IEC 9899:2018 (C) §6.5.2.2 y §6.5.3.2 (paso de argumentos y desreferencia de apuntadores))
22. Se ejecuta el siguiente código en Java: void modificar(int[] arr) { arr[0] = 99; arr = new int[]{1, 2, 3}; } int[] datos = {5, 6, 7}; modificar(datos); Después de llamar a modificar(datos);, ¿qué valor tiene datos[0]?
Java pasa los objetos por valor de referencia: el método puede mutar el arreglo original (arr[0]=99), pero reasignar arr a un nuevo arreglo solo cambia la copia local de la referencia, sin afectar a datos. (Java Language Specification §8.4.1 y §15.12.4.4 (invocación de método))
23. En C, ¿qué función de la biblioteca estándar reserva un bloque de memoria dinámica en el montículo (heap) durante la ejecución del programa?
malloc() es la función de la biblioteca estándar de C que reserva un bloque de memoria dinámica en el heap; free() libera memoria y sizeof es un operador que calcula tamaños en tiempo de compilación. (ISO/IEC 9899:2018 (C) §7.22.3 (stdlib.h, gestión de memoria dinámica))
24. Un programa en C ejecuta int *p = malloc(sizeof(int) * 100);, utiliza el bloque reservado y después la variable p sale de su alcance (scope) sin que se haya llamado a free(p). ¿Qué problema de gestión de memoria se produce?
Cuando el apuntador que referencia un bloque reservado con malloc sale de alcance sin llamar a free(), ese bloque queda inaccesible pero reservado, lo que constituye una fuga de memoria; no es un apuntador colgante, pues la memoria nunca se liberó. (ISO/IEC 9899:2018 (C) §7.22.3 (gestión de memoria dinámica malloc/free))
25. Se ejecuta el siguiente código en C: int *p = malloc(sizeof(int)); *p = 5; free(p); printf("%d", *p); ¿Qué problema de gestión de memoria contiene la instrucción printf al trazar su ejecución?
Acceder a *p después de free(p) desreferencia un apuntador colgante, ya que la memoria fue liberada y su contenido y comportamiento quedan indefinidos; no es una fuga, pues la memoria sí se liberó. (ISO/IEC 9899:2018 (C) §7.22.3.3 (comportamiento indefinido tras free))
26. A diferencia de C, donde el programador debe liberar explícitamente la memoria dinámica con free(), en Java la memoria ocupada por objetos sin referencias activas se libera mediante:
A diferencia de C, donde free() libera la memoria manualmente, en Java el recolector de basura libera automáticamente la memoria de los objetos sin referencias activas; Java no tiene delete() ni el operador free. (Java Language Specification §12.6 (finalización) y JVM Specification (gestión automática de memoria))
27. En la programación orientada a objetos, el principio que consiste en ocultar los detalles internos de una clase y exponer su funcionalidad únicamente a través de métodos públicos definidos en su interfaz se conoce como:
El encapsulamiento oculta el estado interno de un objeto y restringe el acceso directo a sus atributos, exponiendo solo la interfaz pública; la herencia y el polimorfismo son principios distintos de la POO. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (encapsulation).)
28. Cuando una clase Perro se define a partir de una clase Animal y adquiere automáticamente sus atributos y métodos, pudiendo además agregar o modificar comportamiento propio, el mecanismo de la POO que se está aplicando es:
La herencia permite que una clase derivada reutilice y extienda los atributos y métodos de una clase base, estableciendo una relación es-un; el encapsulamiento y la sobrecarga son conceptos distintos. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (inheritance).)
29. Se tiene el siguiente bloque de código en Java: class Figura { public String nombre() { return "Figura"; } } class Circulo extends Figura { public String nombre() { return "Circulo"; } } public class Main { public static void main(String[] args) { Figura f = new Circulo(); System.out.println(f.nombre()); } } ¿Qué se imprime al ejecutar este programa y qué principio de la POO lo explica?
En Java el enlace de métodos de instancia es dinámico: la JVM invoca el método sobrescrito en la clase del objeto real (Circulo), no en el tipo declarado de la referencia (Figura), lo cual es la esencia del polimorfismo. (Java Language Specification §15.12.4 (invocación dinámica de métodos de instancia).)
30. Una clase define dos métodos con el mismo nombre calcular: uno recibe dos parámetros de tipo entero y el otro recibe un solo parámetro de tipo flotante; el compilador decide en tiempo de compilación cuál método invocar según la lista de parámetros. ¿Cómo se denomina este mecanismo?
La sobrecarga (overloading) permite varios métodos con el mismo nombre pero distinta firma, resuelta en tiempo de compilación; la sobrescritura (overriding) redefine un método heredado con la misma firma y se resuelve en tiempo de ejecución. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (overloading vs. overriding).)
31. Se tiene el siguiente bloque de código en Java: class Base { int valor = 10; public int obtener() { return valor; } } class Derivada extends Base { int valor = 20; public int obtener() { return valor; } } public class Main { public static void main(String[] args) { Base b = new Derivada(); System.out.println(b.valor + " " + b.obtener()); } } ¿Qué se imprime al ejecutar este programa?
Los campos se enlazan de forma estática según el tipo declarado de la referencia (Base), por lo que b.valor da 10, mientras que los métodos se enlazan dinámicamente según el objeto real (Derivada), por lo que b.obtener() da 20. (Java Language Specification §15.11 (acceso a campos resuelto por tipo estático) y §15.12.4 (invocación dinámica de métodos).)
32. En un lenguaje orientado a objetos como Java, un atributo declarado con el modificador de acceso private puede ser accedido directamente desde:
El modificador private restringe el acceso al atributo exclusivamente a los métodos de la propia clase, lo que refuerza el encapsulamiento; ni las subclases ni las clases del mismo paquete tienen acceso directo a miembros private. (Java Language Specification §6.6.1 (Determining Accessibility).)
33. Un equipo de desarrollo diseña una clase CuentaBancaria y quiere impedir que el saldo se modifique directamente desde fuera de la clase, permitiendo que se actualice solo mediante métodos depositar y retirar que validan reglas de negocio. ¿Qué práctica de diseño orientado a objetos deben aplicar?
El caso describe encapsulamiento aplicado correctamente: el atributo se protege con un modificador privado y se manipula solo a través de métodos que garantizan las invariantes de la clase. (Sebesta, R., Concepts of Programming Languages, cap. Object-Oriented Programming Languages (encapsulation e information hiding).)
34. En un lenguaje de tipado estático como Java o C, la verificación de tipos de las variables y expresiones se realiza principalmente:
En los lenguajes de tipado estático, el compilador determina y verifica los tipos de las variables antes de la ejecución, lo que permite detectar errores de tipo en tiempo de compilación. (Sebesta, R., Concepts of Programming Languages, cap. Names, Bindings, and Type Checking.)
35. Se dice que un lenguaje de programación tiene un sistema de tipos fuerte cuando:
El tipado fuerte impide o restringe estrictamente las operaciones entre tipos incompatibles, exigiendo conversiones explícitas o reglas de coerción bien definidas, a diferencia del tipado débil. (Sebesta, R., Concepts of Programming Languages, cap. Data Types (strong vs. weak typing).)