Dominio del software: Lenguajes de programación

En esta pánina, considerarás por qué hay diferentes lenguajes de programación y verás algunas de las formas en que los lenguajes difieren.

JavaScript, Python, Snap!, C++, Java, Scheme, Prolog... ¿Por qué hay tantos lenguajes de programación? ¿Por qué no elegimos el mejor, o diseñamos uno nuevo y mejor, y nos atenemos a él?

Algunos lenguajes tienen propósitos muy limitados; estos se llaman lenguajes con propósito especial. Por ejemplo, Microsoft Word tiene un lenguaje de programación incorporado llamado "macros de Word" que solo sirve para generar datos y dar formato a un documento. Así como, HTML (Lenguaje de marcado de hipertexto) es solo para estructurar páginas web.

Los lenguajes de propósito general no tienen un propósito específico. En cierto sentido, estos idiomas son todos iguales: si un algoritmo puede ser expresado en un idioma, puede ser expresado en todos ellos. Se incluyen varias características básicas en casi todos los idiomas, incluidos los operadores aritméticos (+, -, ×, ÷) y operadores booleanos (y, o, no). Las diferencias entre los idiomas se refieren principalmente a niveles de abstracción.

Lenguajes de alto y bajo nivel

diagrama de lenguajes de programación comunes enumerados en orden de nivel de abstracción; hay una flecha vertical de doble cabeza a la derecha que indica que la primera fila de lenguajes (Snap!, Scheme, Prolog, Ruby, Lisp) son 'lenguajes de alto nivel', la segunda fila (JavaScript, Python, Java, Alice, Scratch) se encuentra en el medio, y la tercera fila (C, C++) son 'lenguajes de bajo nivel'

Un lenguaje de nivel alto (como Snap! o Scheme) incluye muchas abstracciones incorporadas que hacen más fácil centrarse en el problema que se quiere resolver en lugar de en cómo funciona el hardware de la computadora. Un lenguaje de bajo nivel (como C) tiene menos abstracciones, requiriendo que sepas mucho sobre la arquitectura de tu computadora para escribir un programa.

¿Por qué los programadores usan lenguajes de alto nivel?

Los lenguajes de nivel alto pueden producir programas más seguros —los que son menos propensos a tener errores—porque las abstracciones manejan detalles desordenados que pueden hacer fracasar a los programadores.

Los lenguajes de nivel alto reducen los errores en el uso de la memoria. Los lenguajes más antiguos y de bajo nivel requerían que el programador manejara el uso de la memoria de la computadora con instrucciones que dijeran "consígueme un bloque de memoria lo suficientemente grande para contener 100 números" y otras instrucciones que dijeran "bien, ya terminé de usar este bloque de memoria; puede ser asignado para algún otro propósito."

Es una molestia en la que hay que pensar, y a los programadores humanos no se les da bien esto. En los lenguajes de bajo nivel, un error muy común es que una parte del programa diga "He terminado con este bloque de memoria" mientras que otra parte del programa lo sigue usando. Los lenguajes de alto nivel se encargan de esto por nosotros usando una técnica llamada recolección de basura que pone a la computadora a cargo de saber cuándo un bloque de memoria ya no está en uso.

Los lenguajes de alto nivel también pueden hacer la programación mucho más conveniente porque ofrecen más abstracciones. Un ejemplo son las funciones de orden más alto (como mapear, mantener, combinar y para cada), que permiten al programador escribir un código más corto y limpio.

  1. Este código es similar a un procedimiento de orden superior que has aprendido. Habla con tu compañero Identifica el procedimiento que este código imita:
    variables de programa (resultado) (índice); fijar el parámetro (resultado) a (lista); fijar el parámetro (índice) a (0); repetir (longitud de (palabras)){incrementar (índice) en (1); añadir (unir (elemento (índice) de (palabras)) s) a (resultado)}; reportar (resultado)
    para cada (palabra) de (palabra) {reportar ((unir (palabra) (s)))}
    reportar (mantener los elementos donde (unir () (s)) de (palabras))
    reportar (mapear (unir () (s)) sobre (palabras))
    reportar (combinar los elementos de (unir () (s)) con (palabras))

En C, puedes hacer esto de la forma más larga:
variables de programa (resultado) (índice); asignar a resultado el valor (lista); asignar a índice el valor (0); repetir (longitud de (palabras)){ incrementar índice en (1); añadir (unir (elemento (índice) de (palabras)) s) a (resultado)}; reportar (resultado)
pero C no te deja tomar una expresión (como unir () (s) o ((5) × ( )) + (7)) y ponerla en una función de orden superior como mapear:
reportar (mapar (unir () (s)) sobre (palabras))

¿Por qué los programadores usan lenguajes de bajo nivel?

La mejor razón para usar lenguajes de bajo nivel es para escribir sistemas operativos (como Windows, Mac OS X, Android, o iOS).  Aprenderás más acerca de sistemas operativos en la página Dominio de software: Sistemas operativos.

¿Por que otra razón un programador usaría un lenguaje de bajo nivel?

Los programadores de aplicaciones no suelen decidir "Voy a escribir este programa en un lenguaje de bajo nivel". Puede que simplemente no se den cuenta de que es posible alcanzar niveles más altos de abstracción. Por ejemplo, el hardware de una computadora limita el tamaño de los números que su unidad aritmética puede sumar en un solo paso. Cuatro mil millones—unos diez dígitos—es un límite de tamaño común para los números enteros. Los programadores que utilizan Java, JavaScript, Python, C o C++ pueden pensar que este límite es inevitable. Pero los programadores que usan lenguajes de alto nivel, como Scheme o Common Lisp, saben que pueden hacer aritmética con números de millones o miles de millones de dígitos, limitados solo por el tamaño de la memoria de la computadora. Como verás más adelante, Snap! también tiene una librería que te permite hacer esto.

Las personas a menudo dicen que los diferentes lenguajes de programación son buenos para diferentes tipos de programas, pero excepto para el procesamiento de video en 3D, es difícil imaginar una aplicación que se vería perjudicada por cosas como la recolección de basura o funciones de orden superior. Hay solo unos pocos casos en los que las personas diseñan deliberadamente lenguajes con características que podrían no ser deseadas para algunas aplicaciones. Aquí hay un ejemplo: En Snap!, una cadena de texto de solo dígitos se considera un número; puedes hacer aritmética sobre ella. En un lenguaje para estudiantes, el requerir una conversión explícita entre tipos de datos solo hace más difícil empezar a programar. Pero la mayoría de los lenguajes que no son para principiantes mantienen los dos tipos de datos separados.

Los programadores pueden pensar que la abstracción es demasiado lenta. Esto solía ser cierto, y los programadores de videojuegos en 3D todavía necesitan toda la velocidad que puedan conseguir porque sus programas presionan la velocidad de las computadoras modernas. Así que a menudo escriben parte de sus programas, la parte que realmente pone las imágenes en la pantalla, en lenguaje de máquina, solo para la velocidad. Pero la mayoría de los programadores escriben aplicaciones que no fuerzan a las computadoras en absoluto. Cuando envías un correo electrónico o un mensaje de texto, el factor limitante es la velocidad con la que puedes escribir, no la velocidad con la que tu computadora puede ejecutar programas.

Código heredado. Los programadores de la industria casi nunca llegan a escribir un programa desde el principio. Mucho más a menudo, mantienen un programa que alguien escribió hace años, y esa persona puede que ya no trabaje para esa compañía. A largo plazo, podría ser mejor reescribir el programa en un lenguaje más moderno, pero a corto plazo, no hay tiempo para hacerlo, así que terminan modificando el código existente en el lenguaje de programación existente.

¿Qué es el lenguaje de máquina?

Tanto los idiomas de alto como de bajo nivel son utilizados por personas para escribir programas de máquina. El hardware de la computadora entiende una especie de lenguaje de nivel ultra bajo, llamado lenguaje de máquina. Programas especiales llamados compiladores e intérpretes se utilizan para traducir los lenguajes de programación de humanos a lenguaje de máquina para ser ejecutado por la computadora.

Lee más acerca de compiladores e intérpretes.

Un compilador es un programa que toma un programa de lenguaje de alto o bajo nivel (el código de origen) como entrada y produce un programa de lenguaje de máquina (el código de objeto) como su salida. Una vez producido, el programa de lenguaje de máquina puede ser ejecutado repetidamente sin necesidad de ser compilado de nuevo.

Un intérprete es un programa que toma un programa de alto o bajo nivel como entrada y lleva a cabo las instrucciones del lenguaje de la máquina según sea necesario para ejecutar el programa. No produce un programa de lenguaje de máquina independiente como salida y tendrá que repetir el proceso de nuevo la próxima vez.

¿Eso significa que los compiladores son mejores?

Exacto, excepto que el proceso de escribir un programa incluye depuración. Durante la depuración, un intérprete puede ayudar proporcionando información sobre el progreso del programa, como la característica de paso visual en Snap!, y permitiendo pequeños cambios en el programa de origen sin tener que ejecutar un compilador repetidamente. Por ejemplo, en Snap! puedes arrastrar un bloque a un script mientras se está ejecutando, y un compilador no podría permitir eso.

Para programadores profesionales, el mejor arreglo es tener ambos un intérprete y un compilador para el mismo lenguaje. El programador escribe y depura el programa usando un intérprete, y una vez que están seguros de que funciona, lo compilan. Entonces, el compilador puede funcionar lentamente, poniendo mucho esfuerzo en optimizar el código de lenguaje de máquina, para que obtengan el programa compilado lo más rápido posible.

  1. Estas preguntas son similares a las que verán en el examen AP CSP.
    ¿Cuáles de las siguientes afirmaciones son correctas sobre un lenguaje de programación de bajo nivel en comparación con un lenguaje de programación de alto nivel?
    1. Los programas de lenguaje de bajo nivel son generalmente más difíciles de entender para las personas, que los programas escritos en un lenguaje de alto nivel.
    2. Un lenguaje de bajo nivel proporciona a los programadores más abstracciones que un lenguaje de alto nivel.
    3. Los programas de lenguaje de bajo nivel son generalmente más difíciles de depurar que los programas escritos en un lenguaje de alto nivel.
    I solamente.
    I y III solamente.
    II y III solamente.
    I, II, y III.
    Un programa está escrito en un lenguaje de programación de alto nivel. Identifica la declaración correcta sobre el programa.
    El programa también puede ser escrito en lenguaje de máquina usando código binario, pero entonces será menos fácil de entender por la personas.
    El programa también puede ser escrito en lenguaje de máquina usando código binario, lo que disminuirá la posibilidad de errores.
    El programa no puede ser escrito en código binario, ya que solo los datos pueden ser representados utilizando la notación binaria.
    Partes simples del programa pueden ser escritas en código binario, pero los elementos de control como los condicionales, los bucles deben ser expresados en un lenguaje de programación de alto nivel.

Legibilidad del código

Una de las características que te da Snap! es que puedes poner el texto del título en el medio de un bloque.

Escribiste polígono en Unidad 1: Gráficos y arte.
polígono, ramas: (30) logitud de la rama: (15)
En comparación con otros lenguajes en los que la función tiene un nombre al principio y luego todas las entradas, este aumenta la claridad y la legibilidad de su función.

polígono(30, 15)

Además, en un lenguaje basado en texto, cuando ves algo como 3 × 5 + 4, tienes que haber memorizado que la multiplicación viene antes de la suma (así que la respuesta es 19). Si lo quieres de otra manera, tienes que usar paréntesis: 3 × (5 + 4) para obtener 27. En un lenguaje basado en bloques, los bloques muestran lo que se pretendía: 3 × (5 + 4). Has aprendido el orden de las operaciones en la clase de matemáticas para +, –, ×, y ÷, pero probablemente no has aprendido el orden de las operaciones para una expresión como esta:
x && y << z
. ¿Cómo sabes cuál viene primero?
&&
o
<<
?
¿Cómo lo sabes?

Observa el ejemplo, C Precedencia del operador.

Paralelismo

5.2.1H A process may execute on one or several CPUs.

Una de las razones para crear nuevos lenguajes de programación es facilitar la escritura de programas paralelos —programas que pueden usar más de un procesador al mismo tiempo. Hoy en 2017, las computadoras y los teléfonos inteligentes tienen chips de procesador multinúcleo que pueden incluir 2, 4, u 8 procesadores todos ejecutándose en código al mismo tiempo. (El número de procesadores aumentará aún más con el tiempo). Las grandes compañías como Google usan el paralelismo aún más; tienen grupos de miles de computadoras, todas trabajando en el mismo programa.

4.1.2D Different languages are better suited for expressing different algorithms.
4.1.2E Some programming languages are designed for specific domains and are better for expressing algorithms in those domains.

Lenguajes de programación funcional (lenguajes en los que los programadores nunca cambian el valor de una variable) son particularmente adecuados para el paralelismo porque no hay peligro de que un procesador cambie el valor de una variable que otro procesador está usando. A lo largo de este curso te hemos introducido en técnicas de programación funcional siempre que ha sido posible, incluyendo la redacción de reporteros y el uso de funciones de orden superior (mapear, mantener, y combinar).

Snap! no es un lenguaje de programación funcional, pero lo sería si los programadores de Snap! eliminaran solo unos pocos procedimientos, incluyendo conjunto (en su lugar, utilizarías variables de entrada de funciones recursivas) y estos cuatro comandos de lista: añadir, borrar, insertar, y reemplazar (en lugar de eso, usarías en frente de, elemento 1 de y todo menos el primero de para reportar una nueva lista con diferentes valores en lugar de cambiar la lista anterior).