La selección de un lenguaje depende de tres ejes técnicos —tipado, ejecución y gestión de memoria— y de los requisitos del escenario. El tipado estático verifica los tipos en tiempo de compilación, antes de ejecutar el programa (C, C++, Java, C# y Rust); el tipado dinámico los verifica en tiempo de ejecución (Python, JavaScript, Ruby y PHP). Este eje es ortogonal al de la fuerza: el tipado fuerte no permite operaciones entre tipos incompatibles sin conversión explícita y reporta siempre los errores de tipo, mientras que el tipado débil admite coerciones implícitas que pueden ocultar errores. Existen así las cuatro combinaciones: estático-fuerte (Java), estático-débil (C), dinámico-fuerte (Python) y dinámico-débil (JavaScript).
En cuanto a la ejecución, los lenguajes compilados traducen el código fuente a código máquina antes de correr, con mayor rendimiento; los interpretados traducen y ejecutan instrucción por instrucción en tiempo de ejecución, con mayor portabilidad y flexibilidad pero menor velocidad. Muchos usan bytecode intermedio: Java compila a bytecode (.class) que ejecuta la JVM (portabilidad, write once run anywhere); CPython compila a bytecode (.pyc) que interpreta la PVM.
Excepciones y traza: ante un error, el lenguaje lanza una excepción que se captura y trata (por ejemplo con try/catch), preservando la seguridad de la ejecución. Para trazar un código dado, sigue el flujo línea por línea anotando el valor de cada variable, recordando que en tipado dinámico una misma variable puede cambiar de tipo. Al seleccionar el lenguaje, contrasta los requisitos: rendimiento nativo y control de memoria favorecen a C, C++ o Rust; portabilidad y productividad favorecen a Java, C# o lenguajes dinámicos como Python.
1. En el diseño de lenguajes de programación, ¿en qué momento se realiza la verificación de tipos cuando un lenguaje utiliza tipado estático?
El tipado estático verifica los tipos en tiempo de compilación, antes de que el programa se ejecute, como en C, C++, Java, C# y Rust; el enlazado y la recolección de basura son procesos distintos que no verifican tipos. (Sebesta, Concepts of Programming Languages, cap. 6; Pierce, Types and Programming Languages, cap. 1)
2. ¿En qué momento realiza la verificación de tipos un lenguaje de tipado dinámico, como Python o JavaScript?
Python, JavaScript, Ruby y PHP verifican los tipos en tiempo de ejecución, a diferencia del tipado estático, que verifica en compilación. (Sebesta, Concepts of Programming Languages, cap. 5-6; Scott, Programming Language Pragmatics, cap. 7)
3. ¿Qué característica define a un lenguaje fuertemente tipado (strong typing)?
El tipado fuerte evita que se apliquen operaciones a tipos incompatibles sin conversión explícita; verificar el tipo 'al compilar' describe el eje estático/dinámico, que es independiente de fuerte/débil. (Sebesta, Concepts of Programming Languages, cap. 6, sección 'Strong Typing')
4. ¿Qué caracteriza al tipado débil (weak typing) de un lenguaje de programación?
El tipado débil permite conversiones implícitas que pueden ocultar errores, como ocurre en C y JavaScript; prohibir toda conversión automática describiría, en cambio, un tipado fuerte. (Sebesta, Concepts of Programming Languages, cap. 6; ECMA-262 (ECMAScript), sección Type Conversion)
5. Un equipo de desarrollo analiza un lenguaje que verifica los tipos de las variables antes de ejecutar el programa, pero que permite sumar directamente un carácter con un entero sin exigir una conversión explícita. ¿Cómo se clasifica este lenguaje según los dos ejes de tipado?
Verificar tipos antes de ejecutar corresponde al eje estático, y permitir sumar un char con un int sin conversión explícita corresponde al eje débil; C es el ejemplo típico de esta combinación. (Scott, Programming Language Pragmatics, cap. 7 (Type Systems); ISO/IEC 9899, cláusulas 6.2-6.3)
6. ¿Qué sucede con el código fuente de un programa escrito en Java antes de que pueda ejecutarse?
Java compila el código fuente a bytecode (.class) que ejecuta la JVM, lo que le da portabilidad ('write once, run anywhere'); a diferencia de C, no se traduce directamente a código máquina nativo. (The Java Language Specification, cap. 4; The Java Virtual Machine Specification)
7. Una desarrolladora utiliza CPython, la implementación de referencia de Python, para ejecutar su programa. ¿Qué describe correctamente lo que ocurre internamente antes de que las instrucciones se ejecuten?
CPython compila el código fuente a bytecode (.pyc) que después interpreta la máquina virtual de Python (PVM), sin traducirlo directamente a código máquina nativo. (The Python Language Reference (docs.python.org), sección Execution model)
8. ¿Cuál de las siguientes combinaciones describe correctamente las características del lenguaje C?
C es de tipado estático (verifica en compilación) y débil (permite coerciones implícitas), se compila a código máquina nativo y requiere manejo manual de memoria con malloc/free, sin recolector de basura. (ISO/IEC 9899 (Programming languages - C), cláusulas 6.2 y 6.3)
9. ¿Cuál de los siguientes lenguajes NO utiliza recolección automática de basura (garbage collection) para administrar su memoria?
C++ requiere gestión manual de memoria, igual que C; Java, Python y Go incorporan recolección automática de basura para reducir fugas de memoria. (Sebesta, Concepts of Programming Languages, cap. 6.11 (Pointer and Reference Types / heap management))
10. Un equipo debe elegir la estrategia de traducción de un lenguaje para desarrollar un sistema donde el rendimiento en tiempo de ejecución es la prioridad principal, aceptando un mayor tiempo previo de preparación. ¿Qué estrategia conviene según las características de traducción de los lenguajes?
Los lenguajes compilados traducen el código fuente completo a código máquina antes de ejecutarlo, lo que ofrece mayor rendimiento en ejecución a cambio de un proceso previo de compilación; los interpretados priorizan portabilidad sobre velocidad. (Aho et al., Compilers: Principles, Techniques and Tools, cap. 1)
11. ¿Qué algoritmo permite que un lenguaje de tipado estático, como ML o Haskell, deduzca automáticamente el tipo de una variable sin que el programador lo declare explícitamente?
El algoritmo Hindley-Milner es el mecanismo clásico de inferencia de tipos usado en lenguajes como ML y Haskell, y permite mantener el tipado estático sin declaración explícita de tipos. (Pierce, Types and Programming Languages, cap. 22 (Type Reconstruction))
12. En Python, una función recibe un objeto y le invoca el método volar() sin comprobar antes de qué clase proviene dicho objeto; la función funciona correctamente con cualquier objeto que tenga ese método implementado. ¿Qué característica del tipado dinámico ilustra este comportamiento?
El 'duck typing' es propio de lenguajes dinámicos como Python: la aptitud de un objeto para una operación depende de que posea los métodos requeridos, no de su clase declarada ni de una jerarquía de herencia. (The Python Language Reference / Python Glossary (docs.python.org), término 'duck-typing')
13. ¿Qué garantiza un sistema de tipos que cumple la propiedad de seguridad de tipos (type safety)?
La seguridad de tipos (type safety) garantiza la ausencia de comportamientos indefinidos por errores de tipo no detectados en ejecución; esta propiedad es independiente de que el tipado sea estático o dinámico. (Pierce, Types and Programming Languages, cap. 8 (teorema de soundness: progreso + preservación))
14. Un equipo requiere un lenguaje de tipado estático y fuerte que garantice la seguridad de memoria en tiempo de compilación, sin depender de un recolector de basura en tiempo de ejecución. ¿Qué mecanismo de un lenguaje como Rust cumple este requerimiento?
Rust garantiza la seguridad de memoria en tiempo de compilación mediante su sistema de ownership y borrowing, sin necesidad de un recolector de basura en tiempo de ejecución. (The Rust Programming Language (doc.rust-lang.org), cap. 4 'Understanding Ownership')
15. En Java, dos tipos se consideran compatibles solo si comparten el mismo nombre de clase o una relación de herencia declarada explícitamente. ¿Qué modelo de equivalencia de tipos ejemplifica este comportamiento?
En el tipado nominal (Java, C++) la compatibilidad de tipos depende del nombre o la declaración explícita, a diferencia del tipado estructural (OCaml, TypeScript), donde depende de la forma del tipo. (Scott, Programming Language Pragmatics, cap. 7 (Type Equivalence: name vs structural))
16. ¿Cuáles son los tres pilares fundamentales que caracterizan al paradigma de programación orientado a objetos?
El paradigma orientado a objetos, presente en lenguajes como Java, se caracteriza por el encapsulamiento, la herencia y el polimorfismo; la recursión y la inmutabilidad son propias del paradigma funcional. (Sebesta, Concepts of Programming Languages, cap. sobre Object-Oriented Programming)
17. ¿Qué característica distingue principalmente al paradigma de programación funcional, presente en lenguajes como Haskell?
El paradigma funcional se basa en funciones puras, sin efectos colaterales, e inmutabilidad de los datos, como ocurre en Haskell; declarar reglas y hechos corresponde, en cambio, al paradigma lógico. (Sebesta, Concepts of Programming Languages, cap. 'Functional Programming Languages')
18. Un lenguaje describe la solución de un problema mediante una secuencia explícita de instrucciones que modifican el estado de variables paso a paso, como ocurre en C. ¿A qué paradigma de programación corresponde este enfoque?
El paradigma imperativo (procedural), ejemplificado por C, describe la solución mediante una secuencia de instrucciones que modifican el estado del programa; el paradigma orientado a objetos organiza además ese estado en objetos con comportamiento propio. (Sebesta, Concepts of Programming Languages, cap. sobre Imperative Programming Languages)
19. Un equipo necesita consultar y transformar información de una base de datos relacional describiendo qué resultado desea obtener, sin especificar el algoritmo paso a paso para calcularlo, como ocurre al escribir una consulta SQL. ¿Qué paradigma de programación ejemplifica mejor este enfoque?
El paradigma declarativo describe el 'qué' se desea obtener en lugar del 'cómo' obtenerlo paso a paso, como en una consulta SQL; el paradigma imperativo, en cambio, exige especificar la secuencia de instrucciones. (Sebesta, Concepts of Programming Languages, cap. sobre Logic Programming Languages / programación declarativa)
20. Un lenguaje permite definir clases con herencia y polimorfismo, además de tratar funciones como valores de primera clase que pueden pasarse como argumento, sin restringir al programador a un único estilo de solución. ¿Cómo se clasifica un lenguaje con estas características?
Un lenguaje multiparadigma, como Python, Rust o C++, combina elementos de distintos paradigmas, por ejemplo orientado a objetos y funcional, sin restringir al programador a un único estilo de solución. (Sebesta, Concepts of Programming Languages, cap. introductorio sobre paradigmas de programación)
21. ¿Qué elementos utiliza principalmente el paradigma de programación lógica, ejemplificado por Prolog, para resolver un problema?
El paradigma lógico, ejemplificado por Prolog, se basa en declarar hechos y reglas que un motor de inferencia resuelve mediante resolución lógica, a diferencia del paradigma imperativo, que ejecuta instrucciones secuenciales. (Sebesta, Concepts of Programming Languages, cap. 'Logic Programming Languages')
22. Dado el siguiente fragmento de código en JavaScript: var resultado = "5" + 3; console.log(resultado); ¿Qué imprime este código y qué caracteriza este comportamiento?
JavaScript es de tipado débil: al usar + con una cadena y un número, convierte el número en cadena y concatena, produciendo "53" como cadena; el operador + no fuerza aquí una conversión a número. (ECMA-262 (ECMAScript), sección Type Conversion / Abstract Operations)
23. Dado el siguiente fragmento de código en Python: resultado = "5" + 3 print(resultado) ¿Qué ocurre al ejecutar este código?
Python es de tipado fuerte, aunque dinámico: no permite operar entre str e int sin conversión explícita, por lo que arroja un TypeError en tiempo de ejecución, ya que al ser interpretado el error surge al ejecutar, no al compilar. (The Python Language Reference, sección Data model; Sebesta, cap. 6, 'Strong Typing')
24. Dado el siguiente fragmento de código en C: char letra = 'A'; int numero = letra + 1; printf("%d", numero); ¿Qué imprime este programa?
'A' equivale al valor entero 65 en el código ASCII; al ser C un lenguaje de tipado débil, convierte implícitamente el char en int, por lo que la suma 65+1 resulta en 66. (ISO/IEC 9899 (Programming languages - C), cláusula 6.3 (Conversions))
25. Dado el siguiente fragmento de código en C++: auto contador = 10; contador = "texto"; ¿Qué ocurre al intentar compilar este código?
auto, incorporado en C++11, infiere el tipo en tiempo de compilación a partir del valor inicial, pero el lenguaje sigue siendo de tipado estático: una vez fijado el tipo entero, no puede reasignarse una cadena. (ISO/IEC 14882 (C++11), especificación de la palabra clave auto)
26. Dado el siguiente fragmento de código en Rust: let s1 = String::from("hola"); let s2 = s1; println!("{}", s1); ¿Qué ocurre al intentar compilar este código?
El sistema de ownership de Rust transfiere la propiedad del valor de s1 a s2; usar s1 después de ese movimiento es un error detectado en tiempo de compilación, no en ejecución. (The Rust Programming Language (doc.rust-lang.org), cap. 4 'Understanding Ownership')
27. Un programa se traduce por completo a lenguaje máquina antes de ejecutarse, generando un archivo ejecutable independiente del código fuente original. ¿Qué modelo de traducción de lenguajes describe este proceso?
La compilación traduce todo el código fuente a código máquina antes de la ejecución, a diferencia de la interpretación, que traduce y ejecuta instrucción por instrucción. (A. V. Aho et al., Compilers: Principles, Techniques and Tools, cap. 1)
28. En un lenguaje interpretado, ¿en qué momento se traduce y ejecuta cada instrucción del programa?
En la interpretación, cada instrucción se traduce y ejecuta en tiempo de ejecución, a diferencia de la compilación, que traduce todo el programa previamente. (A. V. Aho et al., Compilers: Principles, Techniques and Tools, cap. 1)
29. Un equipo de desarrollo necesita máximo rendimiento en tiempo de ejecución para un sistema embebido con recursos muy limitados, y puede aceptar un mayor tiempo de traducción previo a la ejecución. ¿Qué modelo de ejecución conviene priorizar?
La compilación anticipada a código máquina nativo ofrece el mayor rendimiento en ejecución y el menor consumo de recursos; la interpretación y los modelos con máquina virtual añaden sobrecarga en tiempo de ejecución. (A. V. Aho et al., Compilers: Principles, Techniques and Tools, cap. 1)
30. Un equipo prioriza poder ejecutar su aplicación en múltiples plataformas sin recompilar el código para cada una, aceptando a cambio una menor velocidad de ejecución. ¿Qué modelo de traducción resulta más adecuado para este requerimiento?
Los lenguajes interpretados ejecutan el mismo código fuente en cualquier plataforma que tenga el intérprete, ofreciendo portabilidad a costa de velocidad; las demás opciones generan código nativo dependiente de la plataforma y exigen recompilar. (A. V. Aho et al., Compilers: Principles, Techniques and Tools, cap. 1)
31. Un programador compila un archivo fuente de Java y obtiene un archivo con extensión .class, el cual después ejecuta sin recompilar en distintos sistemas operativos. ¿Qué componente hace posible esta portabilidad?
El código Java se compila a bytecode independiente de la plataforma, el cual es ejecutado por la JVM en cualquier sistema donde esta esté instalada, logrando 'write once, run anywhere'. (The Java Language Specification, cap. 4; The Java Virtual Machine Specification)
32. Cuando se compila un programa escrito en Java, ¿a qué se traduce el código fuente antes de poder ejecutarse?
El compilador de Java genera bytecode en archivos .class, que la Máquina Virtual de Java (JVM) ejecuta, proporcionando portabilidad entre plataformas. (The Java Virtual Machine Specification (Oracle), 'The class File Format'; The Java Language Specification, cap. 4)
33. La implementación de referencia de Python, conocida como CPython, ¿qué realiza con el código fuente antes de que la Máquina Virtual de Python (PVM) lo ejecute?
CPython compila el código fuente a bytecode almacenado en archivos .pyc, el cual es interpretado por la Máquina Virtual de Python (PVM). (The Python Language Reference, sección Execution model)
34. A diferencia de Java, cuyo compilador genera bytecode ejecutado por la JVM, el compilador de C traduce el código fuente directamente a:
Una implementación típica de C compila el código fuente a código máquina nativo, sin una máquina virtual intermedia como la JVM. (A. V. Aho et al., Compilers: Principles, Techniques and Tools, cap. 1)
35. ¿En qué momento se verifica la compatibilidad de tipos de variables y expresiones en un lenguaje de tipado estático como Java o C#?
El tipado estático verifica la compatibilidad de tipos en tiempo de compilación, antes de ejecutar el programa. (R. W. Sebesta, Concepts of Programming Languages, cap. 6; B. C. Pierce, Types and Programming Languages, cap. 1)