Mucha gente considera el lenguaje Pascal, como un lenguaje obsoleto, ya que en internet hay muchos recurso que hablan sobre el lenguaje Pascal, pero hablan sobre el «viejo» Pascal.

Michalis Kamburelis, creador del motor gráfico Castle Engine, escribió una guía muy concisa y sencilla, en la que muestra las características de este lenguaje.

En esta entrada y sucesivas encontrarás la traducción al español de este texto.

El texto original se puede encontrar aquí.

¿Por qué?

Hay muchos libros y recursos sobre Pascal, pero muchos de ellos hablan sobre el antiguo Pascal, sin clases, unidades o genéricos. Así que escribí esta rápida introducción a lo que llamo Object Pascal moderno. La mayoría de los programadores que lo usan realmente no lo llaman «Objeto Pascal moderno», simplemente lo llamamos «nuestro Pascal». Pero al presentar el lenguaje, creo que es importante enfatizar que es un lenguaje moderno y orientado a objetos. Evolucionó mucho desde el viejo (Turbo) Pascal que mucha gente aprendió en las escuelas hace mucho tiempo. En cuanto a las funciones, es bastante similar a C++, Java o C#. Tiene todas las características modernas que espera: clases, unidades, interfaces, genéricos… Está compilado en un código nativo rápido, Es muy tipo seguro, De alto nivel, pero también puede ser de bajo nivel si lo necesita. También tiene un compilador excelente, portátil y de código abierto llamado Free Pascal Compiler, http://freepascal.org/. Y un IDE que lo acompaña (editor, depurador, una biblioteca de componentes visuales, diseñador de formularios) llamado Lazarus http://lazarus.freepascal.org/. Yo mismo, soy el creador de Castle Game Engine, https://castle-engine.io/, que es un motor de juegos 3D y 2D de código abierto que utiliza Pascal moderno para crear juegos en muchas plataformas (Windows, Linux, macOS, Android, iOS, Nintendo Switch; también viene WebGL). Esta introducción está dirigida principalmente a programadores que ya tienen experiencia en otros lenguajes. No cubriremos aquí los significados de algunos conceptos universales, como «qué es una clase», solo mostraremos cómo hacerlo en Pascal.

Básico

Hola Mundo

  • Este es un programa completo que puede compilar y ejecutar. Este es un programa de línea de comandos, por lo que en cualquier caso — simplemente ejecute el ejecutable compilado desde la línea de comandos.
  • Si usa la línea de comandos FPC, simplemente cree un nuevo archivo myprogram.lpr y ejecute fpc myprogram.lpr
  • Si usas Lazarus, cree un nuevo proyecto (menú Proyecto → Nuevo proyecto → Programa Simple). Guárdelo como myprogram y pegue este código fuente como archivo principal. Compilar utilizando el elemento de menú Ejecutar → Compilar.
  • Este es un programa de línea de comandos, por lo que en cualquier caso, simplemente ejecute el ejecutable compilado desde la línea de comandos.

El resto de este artículo habla sobre el lenguaje Object Pascal, así que no esperes ver nada más elegante que la línea de comandos. Si quiere ver algo genial, simplemente cree un nuevo proyecto GUI en Lazarus (Proyecto → Nuevo proyecto → Aplicación). Listo — una aplicación GUI en funcionamiento, multiplataforma, con un aspecto nativo en todas partes, que utiliza una cómoda biblioteca de componentes visuales. El compilador Lazarus y Free Pascal vienen con muchas unidades listas para redes, GUI, base de datos, formatos de archivo (XML, json, imágenes…), subprocesos y todo lo que pueda necesitar. Ya mencioné mi genial Castle Game Engine antes 🙂

Funciones procedimientos y tipos primitivos

Para devolver un valor de una función, asigne algo a la variable de mágica Result. Puede leer y configurar Result libremente, como una variable local.

También puede tratar el nombre de la función (como MyFunction en el ejemplo anterior) como la variable a la que puede asignar datos. Pero lo desaconsejaría en el código nuevo, ya que parece «sospechoso» cuando se usa en el lado derecho de la expresión de asignación. Simplemente use Result siempre cuando quiera leer o configurar el resultado de la función.

Puede llamar a Exit para finalizar la ejecución del procedimiento o función antes de que llegue al final final;. Si llama a Exit sin parámetros en una función, devolverá lo último que configuró como Result. También puede usar la construcción Exit(X), para establecer el resultado de la función y salir ahora — esto es como devolver la construcción X en lenguajes similares a C.

Tenga en cuenta que el resultado de la función se puede descartar. Cualquier función puede usarse como un procedimiento. Esto tiene sentido si la función tiene algún efecto secundario (por ejemplo, modifica una variable global) además de calcular el resultado. Por ejemplo:

Testing (if)

Use if .. then o if .. then .. else para ejecutar algún código cuando se cumpla alguna condición. A diferencia de los lenguajes tipo C, en Pascal no tienes que envolver la condición entre paréntesis.

El else está emparejado con el último if. Entonces esto funciona como esperas:

Si bien el ejemplo con el si anidado anterior es correcto, a menudo es mejor colocar el si anidado dentro de un bloque de inicio … final en tales casos. Esto hace que el código sea más obvio para el lector y seguirá siendo obvio incluso si estropeas la sangría. La versión mejorada del ejemplo se encuentra a continuación. Cuando agrega o elimina alguna otra cláusula en el código a continuación, es obvio a qué condición se aplicará (a la prueba A o la prueba B), por lo que es menos propenso a errores.

Operadores lógicos, relacionales y bit a bit

Los operadores lógicos se llaman and, or, not, xor. Su significado es probablemente obvio (busque «exclusivo o» si no está seguro de lo que hace xor :)). Toman argumentos booleanos y devuelven un booleano. También pueden actuar como operadores bit a bit cuando ambos argumentos son valores enteros, en cuyo caso devuelven un número entero.

Los operadores relacionales (de comparación) son =, <>, >, <, <=, >=. Si está acostumbrado a lenguajes similares a C, tenga en cuenta que en Pascal compara dos valores (verifique si son iguales) usando un solo carácter de igualdad A = B (a diferencia de C donde usa A == B). El operador de asignación especial en Pascal es :=.

Los operadores lógicos (o bit a bit) tienen mayor precedencia que los operadores relacionales. Es posible que deba usar paréntesis alrededor de algunas expresiones para tener el orden deseado de los cálculos.

Por ejemplo, esto es un error de compilación:

Lo anterior falla al compilar, porque el compilador primero quiere realizar un bit a bit y en el medio de la expresión: (0 y B). Esta es una operación bit a bit que devuelve un valor entero. Luego, el compilador aplica el operador = que produce un valor booleano A = (0 y B). Y finalmente, el error de «tipo no coincidente» surge después de intentar comparar el valor booleano A = (0 y B) y el valor entero 0.

Esto es correcto:

Se utiliza la evaluación de «cortocircuito». Considere esta expresión:

  • Está garantizado que MyFuncion(X) se evaluará primero.
  • Y si MyFunction(x) devuelve falso, entonces la valor de la expresión será falso, y MyOtherFunction(Y) no se ejecutará en absoluto.
  • De manera análoga es para el operador OR. Si se sabe que la expresión es verdadera (porque el primer operador es verdadero), el segundo operador no se evaluará.

Esto es particularmente útil cuando se escriben expresiones como:

La regla análoga es para o expresión. Allí, si se sabe que la expresión es verdadera (porque el primer operando es verdadero), el segundo operando no se evalúa.

Esto funcionará bien, incluso cuando A es nulo. La palabra clave nil es un puntero igual a cero (cuando se representa como un número). Se llama puntero nulo en muchos otros lenguajes de programación.

Prueba de expresión única para valores múltiples (case)

Si se debe ejecutar una acción diferente dependiendo del valor de alguna expresión, entonces la instrucción case .. of .. end es útil.

La cláusula else es opcional (y corresponde a la predeterminada en lenguajes tipo C). Cuando ninguna condición coincide, y no hay más, entonces no pasa nada.

Si proviene de lenguajes similares a C y compara esto con la declaración de cambio en estos lenguajes, notará que no hay fallas automáticas. Esta es una bendición deliberada en Pascal. No es necesario que recuerde colocar las instrucciones de descanso. En cada ejecución, como máximo se ejecuta una rama del caso, eso es todo.

Tipos y conjuntos enumerados, ordinales y matrices de longitud constante

El tipo enumerado en Pascal es un tipo muy agradable y opaco. Probablemente lo usará con mucha más frecuencia que las enumeraciones en otros idiomas 🙂

La convención es prefijar los nombres de enumeración con un atajo de dos letras de nombre de tipo, por lo tanto, ak = atajo para «Animal Kind». Esta es una convención útil, ya que los nombres de enumeración están en el espacio de nombres de la unidad (global). Entonces, al anteponerles el prefijo ak, minimiza las posibilidades de colisiones con otros identificadores.

Las coincidencias en los nombres no son un problema. Es correcto que diferentes unidades definan el mismo identificador. Pero es una buena idea tratar de evitar las coincidencias de todos modos, para mantener el código simple de entender.

Puede evitar colocar nombres de enumeración en el espacio de nombres global mediante la directiva del compilador {$scopedenums on}. Esto significa que tendrá que acceder a ellos calificados por un nombre de tipo, como TAnimalKind.akDuck. La necesidad del prefijo ak desaparece en esta situación, y probablemente llamarás a las enumeraciones Pato, Gato, Perro. Esto es similar a las enumeraciones de C#.

El hecho de que el tipo enumerado sea opaco significa que no se puede asignar simplemente a un número entero. Sin embargo, para un uso especial, puede usar Ord(MyAnimalKind) para convertir forzosamente enum a int, o typecast TAnimalKind(MyInteger) para convertir forzosamente int en enum. En el último caso, asegúrese de comprobar primero si MyInteger está dentro del rango (0 a Ord(High(TAnimalKind)

Los tipos enumerados y ordinales se pueden usar como índices de matriz:

También se pueden usar para crear conjuntos (un campo de bits internamente):

Bucles (for, while, repeat, for .. in)

Sobre los bucles repeat y while:

Hay dos diferencias entre estos tipos de bucle:

  1. La condición de bucle tiene un significado opuesto. En mientras.. le dices cuando continuar, pero en repetir.. hasta que le dices cuando parar.
  2. En caso de repetición, la condición no se comprueba al principio. Entonces, el ciclo de repetición siempre se ejecuta al menos una vez.

Sobre el bucle for I := …​:

El for I := .. to .. do …​ lo construye de manera similar al bucle for tipo C. Sin embargo, está más restringido, ya que no puede especificar acciones/pruebas arbitrarias para controlar la iteración del bucle. Esto es estrictamente para iterar sobre números consecutivos (u otros tipos ordinales). La única flexibilidad que tiene es que puede usar downto en lugar de to para hacer que los números vayan hacia abajo.

A cambio, se ve limpio y está muy optimizado en ejecución. En particular, las expresiones para el límite inferior y superior solo se calculan una vez, antes de que comience el bucle.

Tenga en cuenta que el valor de la variable del contador del ciclo (I en este ejemplo) debe considerarse indefinido después de que finalice el ciclo, debido a posibles optimizaciones. Acceder al valor de I después del bucle puede provocar una advertencia del compilador. A menos que salga del ciclo prematuramente con Break o Exit: en tal caso, se garantiza que la variable contador conservará el último valor.

Sobre el buvle for I in …​

El for I en .. do .. es similar a la construcción foreach en muchos lenguajes modernos. Funciona de forma inteligente en muchos tipos integrados:

  • Puede iterar sobre todos los valores de la matriz (ejemplo anterior).
  • Puede iterar sobre todos los valores posibles de un tipo enumerado:
  • Puede iterar sobre todos los elementos incluidos en el conjunto:
  • Y funciona en tipos de listas personalizadas, genéricas o no, como TObjectList o TFPGObjectList.

Todavía no explicamos el concepto de clases, por lo que el último ejemplo puede no ser obvio para usted todavía — solo continúe, tendrá sentido más adelante 🙂

Salida, Logging

Para simplemente generar cadenas en Pascal, use la rutina Write o WriteLn. Este último agrega automáticamente una nueva línea al final.

Esta es una rutina «mágica» en Pascal. Toma un número variable de argumentos y pueden ser de cualquier tipo. Todos se convierten en cadenas cuando se muestran, con una sintaxis especial para especificar el relleno y la precisión numérica.

Para usar explícitamente una nueva línea en la cadena, use la constante LineEnding (de FPC RTL). (El Castle Game Engine define también una constante NL más corta.) Las cadenas de Pascal no interpretan ninguna secuencia especial de barra invertida, por lo que escribir

no funciona como algunos de ustedes pensarían. Esto funcionará:ç

o solo esto:

/

Tenga en cuenta que esto solo funcionará en aplicaciones de consola. Asegúrese de tener {$apptype CONSOLE} (y no {$apptype GUI}) definido en su archivo de programa principal. En algunos sistemas operativos en realidad no importa y funcionará siempre (Unix), pero en algunos sistemas operativos intentar escribir algo desde una aplicación GUI es un error (Windows).

En Castle Game Engine: use WriteLnLog o WriteLnWarning, nunca WriteLn, para imprimir información de depuración. Siempre se dirigirán a alguna salida útil. En Unix, salida estándar. En la aplicación de GUI de Windows, archivo de registro. En Android, la función de registro de Android (visible cuando usa adb logcat). El uso de WriteLn debe limitarse a los casos en los que escribe una aplicación de línea de comandos (como un convertidor/generador de modelos 3D) y sabe que la salida estándar está disponible.

Convirtiendo a cadena

Para convertir un número arbitrario de argumentos en una cadena (en lugar de generarlos directamente), tiene un par de opciones.

Puede convertir tipos particulares en cadenas usando funciones especializadas como IntToStr y FloatToStr. Además, puedes concatenar cadenas en Pascal simplemente agregándolas. Entonces puedes crear una cadena como esta: Mi número int es ‘ + IntToStr(MyInt) + ‘, y el valor de Pi es ‘ + FloatToStr(Pi).

  • Ventaja: Absolutamente flexible. Hay muchas versiones sobrecargadas de XxxToStr y amigos (como FormatFloat), que cubren muchos tipos. La mayoría de ellos están en la unidad SysUtils.
  • Otra ventaja: Consistente con las funciones inversas. Para convertir una cadena (por ejemplo, la entrada del usuario) de nuevo en un número entero o flotante, use StrToInt, StrToFloat y amigos (como StrToIntDef).
  • Desventaja: una concatenación larga de muchas llamadas y cadenas XxxToStr no se ve bien.

La función Format, utilizada como Format(‘%d %f %s’, [MyInt, MyFloat, MyString]). Esto es como la función sprintf en los lenguajes tipo C. Inserta los argumentos en los marcadores de posición en el patrón. Los marcadores de posición pueden usar una sintaxis especial para influir en el formato, p. %.4f da como resultado un formato de punto flotante con 4 dígitos después del punto decimal.

  • Ventaja: la separación de la cadena de patrón de los argumentos parece limpia. Si necesita cambiar la cadena del patrón sin tocar los argumentos (por ejemplo, al traducir), puede hacerlo fácilmente.
  • Otra ventaja: no hay magia de compilación. Puede usar la misma sintaxis para pasar cualquier cantidad de argumentos de un tipo arbitrario en sus propias rutinas (declare el parámetro como una matriz de const). Luego puede pasar estos argumentos hacia abajo a Formato, o deconstruir la lista de parámetros y hacer lo que quiera con ellos.
  • Desventaja: el compilador no verifica si el patrón coincide con los argumentos. El uso de un tipo de marcador de posición incorrecto dará como resultado una excepción en tiempo de ejecución (excepción EConvertError, nada desagradable como un error de segmentación).

La rutina WriteStr(TargetString, …​) se comporta de manera similar a Write(…​), excepto que el resultado se guarda en TargetString.

  • Ventaja: admite todas las características de Write, incluida la sintaxis especial para formatear como Pi: 1: 4.
  • Desventaja: la sintaxis especial para formatear es una «magia del compilador», implementada específicamente para rutinas como esta. Esto a veces es problemático, p. no puede crear su propia rutina MyStringFormatter (…) que también permitiría la sintaxis especial como Pi: 1: 4. Por esta razón (y también porque no se implementó durante mucho tiempo en los principales compiladores de Pascal), esta construcción no es muy popular.