Este documento es una guía rápida del lenguaje de programación Perl 6. Para los novatos en Perl 6 sería un punto de partida y puesta en marcha.
Algunas partes de este documento hacen referencia a otras partes (más completas y precisas) de la Documentación de Perl 6. Consulta la documentación de Perl 6 si necesitas más información sobre algo concreto.
A lo largo de este documento encontrarás ejemplos de los temas más comentados. Es conveniente que pruebes todos los ejemplos para entenderlos bien.
Este trabajo está bajo la licencia Creative Commons Attribution-ShareAlike 4.0 International License. Puedes encontrar una copia de esta licencia en
Puedes colaborar en este documento en:
Si te gusta este trabajo clica en Star del repositorio de Github.
1. Introducción
1.1. Qué es Perl 6
Perl 6 es un lenguaje de alto nivel, de propósito general y de tipado gradual. Perl 6 es multiparadigma y soporta programación Procedimental, Orientada a Objetos y Funcional.
-
TMTOWTDI (Pronunciado como Tim Toady): There is more than one way to do it (Hay más de una forma para hacer algo).
-
Las cosas fáciles deben permanecer fáciles, las cosas difíciles deberían ser fáciles y las cosas imposibles deberían ser difíciles.
1.2. Jerga
-
Perl 6: Es una especificación con un banco de pruebas. Las implementaciones que pasan el banco de pruebas de la especificación se consideran Perl 6.
-
Rakudo: Es un compilador para Perl 6.
-
Rakudobrew: Es un script de Perl5 para instalar Rakudo desde su código fuente.
-
Zef: Es un instalador de módulos de Perl 6.
-
Rakudo Star: Es un paquete que incluye Rakudo, Zef, una colección de módulos de Perl 6 y documentación.
1.3. Instalación de Perl 6
Instalación de Rakudo Star desde la línea de comandos:
wget https://rakudo.perl6.org/downloads/star/rakudo-star-2019.03.tar.gz
tar xfz rakudo-star-2019.03.tar.gz
cd rakudo-star-2019.03
perl Configure.pl --gen-moar --make-install --prefix ~/rakudo
Tienes más información en http://rakudo.org/how-to-get-rakudo/#Installing-Rakudo-Star-Linux
Tienes cuatro opciones:
-
Sigue los mismos pasos de la instalación para Linux
-
Realizar la instalación con homebrew:
brew install rakudo-star
-
Realizar la instalación con MacPorts:
sudo port install rakudo
-
Descargar el último instalador (archivo con extensión .dmg) desde https://rakudo.org/latest/star/macos
-
Para arquitectura 64-bit: descarga el instalador más reciente (.msi) desde https://rakudo.org/latest/star/win64
Para arquitectura 32-bit: descarga el instalador más reciente (.msi) desde https://rakudo.org/latest/star/win32 -
Finalizada la instalación, comprueba que C:\rakudo\bin figura en el PATH del sistema.
-
Consigue la imagen oficial de Docker
docker pull rakudo-star
-
Y ejecuta un contenedor con la imagen
docker run -it rakudo-star
1.4. Ejecutando código en Perl 6
Puedes ejecutar código Perl 6 mediante REPL (Read-Eval-Print Loop). Para ello, abre un terminal, introduce perl6
y pulsa [Enter]. Aparecerá el prompt >
. Ahora introduce una línea de código, pulsa [Enter] y aparecerá una línea nueva con el resultado. Puedes introducir otra línea o exit
y pulsar [Enter] para salir al sistema.
También puedes escribir tu código de Perl 6 en un archivo de texto, guardarlo y ejecutarlo. Es recomendable que el nombre de este archivo de texto tenga la extensión .p6
. Ejecuta el archivo de esta forma: perl6 nombre-archivo.p6
desde un terminal y pulsa [Enter]. La ejecución suele mostrar el resultado de sentencias como say
para visualizar por la salida estándar contenidos de texto con un salto de línea al final .
REPL normalmente se utiliza para probar trozos pequeños de código, como una línea. En el caso de programas con más de una línea de código es recomendable guardarlos en un archivo y ejecutarlos como hemos visto.
También puedes ejecutar una línea de código de forma "in-line" mediante el parámetro -e de la siguiente forma: perl6 -e 'línea de código Perl 6'
y pulsando [Enter].
Rakudo Star incorpora un editor de líneas con más funcionalidades para REPL. Si instalaste Rakudo en lugar de Rakudo Star es probable que no tengas estas funcionalidades (historial con flechas verticales, edición de la línea con flechas horizontales, autocompletar con TAB, etc.). Para instalar estas funcionalidades utiliza estos comandos:
|
1.5. Editores
Como casi siempre vamos a guardar nuestros programas de Perl 6 en archivos, necesitamos un editor de textos decente que reconozca la sintaxis de Perl 6.
Yo recomiendo y utilizo Atom. Es un editor de textos moderno que reconoce y visualiza bien la sintaxis de Perl 6. Perl 6 FE es un paquete de Atom con una visualización alternativa de la sintaxis de Perl 6. Deriva del paquete original, tiene muchas correcciones y más funcionalidades.
Las últimas versiones de Vim incorporan la visualización de la sintaxis de Perl 6, mientras que Emacs y Padre necesitan instalar paquetes adicionales.
1.6. ¡Hola Mundo!
Comenzamos con El ritual hola mundo
.
say 'hola mundo';
que también puede escribirse como:
'hola mundo'.say;
1.7. Sintaxis general
Perl 6 tiene forma libre: generalmente los espacios en blanco carecen de significado salvo en ciertos casos.
Una Sentencia normalmente es una línea lógica de código que finaliza en punto y coma:
say "Hola" if True;
Las Expresiones son sentencias especiales que devuelven un valor:
1+2
devuelve 3
Las expresiones están formadas por Términos y Operadores.
Los Términos pueden ser:
-
Variables: Un valor que puede manipularse y ser cambiado.
-
Literales: Un valor constante como un número o un texto.
Los Operadores se clasifican en estos tipos:
Tipo |
Significado |
Ejemplo |
Prefijo |
Antes del término |
|
Infijo |
Entre términos |
|
Sufijo |
Después del término |
|
Circumfijo |
Al principio y al final del término |
|
Precircumfijo |
Después del término, al principio y al final de otro |
|
1.7.1. Identificadores
Los identificadores son los nombres que se le dan a los términos cuando los defines.
-
Deben comenzar con un carácter alfabético o un guión bajo.
-
Pueden contener dígitos excepto en el primer carácter.
-
Pueden contener guión medio o apóstrofe seguido de un carácter alfabético, no al final.
Válido |
No válido |
|
|
|
|
|
|
|
|
|
|
-
Notación Camello:
variableNum1
-
Notación Kebab:
variable-num1
-
Notación Serpiente:
variable_num1
Puedes nombrar tus identificadores como quieras, pero es recomendable utilizar una convención consistente.
Utiliza nombres significativos para hacerlo más fácil, a tí y a los demás.
-
var1 = var2 * var3
es correcto pero no tiene un propósito evidente. -
mes-salario = dia-frecuencia * dias-trabajo
es una buena forma de nombrar las variables.
1.7.2. Comentarios
Un comentario es un texto, sirve como anotación y el compilador no lo tiene en cuenta.
Hay 3 tipos de comentarios:
-
De una línea:
# Esto es un comentario de una línea
-
Incrustado:
say #`(Esto es un comentario incrustado) "Hola Mundo."
-
De varias líneas:
=begin comentario Esto es un comentario de varias líneas. Comentario 1 Comentario 2 =end comentario
1.7.3. Comillas
El texto tiene que ir entre comillas dobles o simples.
Utiliza siempre comillas dobles:
-
si el texto contiene un apóstrofe.
-
si el texto necesita visualizar el texto de una variable (interpolación de variable).
say 'Hola Mundo'; # Hola Mundo
say "Hola Mundo"; # Hola Mundo
say "Ven pa'ca cordera"; # Ven pa'ca cordera
my $nombre = 'Juan De Dios';
say 'Hola $nombre'; # Hola $nombre
say "Hola $nombre"; # Hola Juan De Dios
2. Operadores
2.1. Operadores comunes
La siguiente tabla muestra los operadores más utilizados.
Operador | Tipo | Descripción | Ejemplo | Resultado |
---|---|---|---|---|
|
|
Suma |
|
|
|
|
Resta |
|
|
|
|
Multiplicación |
|
|
|
|
Potencia |
|
|
|
|
División |
|
|
|
|
División Entera (redondeo inferior) |
|
|
|
|
Resto |
|
|
|
|
Divisible |
|
|
|
|
|||
|
|
Máximo común divisor |
|
|
|
|
Mínimo común múltiplo |
|
|
|
|
Igual numérico |
|
|
|
|
No igual numérico |
|
|
|
|
Menor que |
|
|
|
|
Mayor que |
|
|
|
|
Menor o igual |
|
|
|
|
Mayor o igual |
|
|
|
|
Texto igual |
|
|
|
|
Texto no igual |
|
|
|
|
Asignación |
|
|
|
|
Texto concatenado |
|
|
|
|
|||
|
|
Texto replicado |
|
|
|
|
|||
|
|
Expresión regular |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Incremento |
|
|
|
Incremento |
|
|
|
|
|
Decremento |
|
|
|
Decremento |
|
|
|
|
|
Fuerza el operando a un valor numérico |
|
|
|
|
|||
|
|
|||
|
|
Fuerza el operando a un valor numérico y devuelve la negación |
|
|
|
|
|||
|
|
|||
|
|
Fuerza el operando a un valor booleano |
|
|
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
Fuerza el operador a un valor booleano y devuelve la negación |
|
|
|
|
Constructor de Rangos |
|
|
|
|
Constructor de Rangos |
|
|
|
|
Constructor de Rangos |
|
|
|
|
Constructor de Rangos |
|
|
|
|
Constructor de Rangos |
|
|
|
|
Constructor de listas perezosas |
|
|
|
|
Aplanamiento |
|
|
|
|
2.2. Intercambio de Operandos
Al agregar R
delante de cualquier operador hace que se intercambien sus operandos.
Operación original | Resultado | Intercambio de operandos | Resultado |
---|---|---|---|
|
|
|
|
|
|
|
|
2.3. Reducción de Operadores
La reducción de operadores se utiliza en listas de valores. Se forman encerrando el operador entre corchetes []
Operación original | Resultado | Reducción de Operadores | Resultado |
---|---|---|---|
|
|
|
|
|
|
|
|
En https://docs.perl6.org/language/operators tienes una lista completa de los operadores, incluyendo su precedencia. |
3. Variables
Las variables en Perl 6 se reparten en tres categorías: Escalares, Arrays y Hashes.
Un sigilo (Signo en Latín) es un carácter utilizado como prefijo para categorizar variables.
-
$
para escalares -
@
para arrays -
%
para hashes
3.1. Escalares
Un escalar contiene un valor o referencia.
# Texto
my $nombre = 'Juan De Dios';
say $nombre;
# Entero
my $edad = 99;
say $edad;
Dependiendo del valor que aloje, se pueden realizar un conjunto específico de operaciones en un escalar.
my $nombre = 'Juan De Dios';
say $nombre.uc;
say $nombre.chars;
say $nombre.flip;
JUAN DE DIOS
12
soiD eD nauJ
Consulta https://docs.perl6.org/type/Str para ver la lista completa de métodos de texto. |
my $edad = 17;
say $edad.is-prime;
True
Consulta https://docs.perl6.org/type/Int para ver la lista completa de métodos de enteros. |
my $edad = 2.3;
say $edad.numerator;
say $edad.denominator;
say $edad.nude;
23
10
(23 10)
Consulta https://docs.perl6.org/type/Rat para ver la lista completa de métodos de números racionales. |
3.2. Arrays
Los Arrays son listas que contienen varios valores.
my @animales = 'camello','llama','búho';
say @animales;
Se pueden realizar muchas operaciones con arrays, como las de este ejemplo:
La tilde ~ se utiliza para concatenar texto.
|
Script
my @animales = 'camello','vicuña','llama';
say "El zoo tiene " ~ @animales.elems ~ " animales";
say "Los animales son: " ~ @animales;
say "He conseguido un búho para el zoo";
@animales.push("búho");
say "Los animales del zoo ahora son: " ~ @animales;
say "El primer animal del zoo es: " ~ @animales[0];
@animales.pop;
say "Desafortunadamente el búho se escapó y los animales que quedan son: " ~ @animales;
say "Vamos a dejar solo una animal en el zoo";
say "Dejamos ir a: " ~ @animales.splice(1,2) ~ " y dejamos en el zoo al " ~ @animales;
Salida
El zoo tiene 3 animales
Los animales son: camello vicuña llama
He conseguido un búho para el zoo
Los animales del zoo ahora son: camello vicuña llama búho
El primer animal del zoo es: camello
Desafortunadamente el búho se escapó y los animales que quedan son: camello vicuña llama
Vamos a dejar solo una animal en el zoo
Dejamos ir a: vicuña llama y dejamos en el zoo al camello
.elems
devuelve el número de elementos de un array.
.push()
añade uno o más elementos a un array.
Podemos acceder a un elemento concreto del array indicando su posición @animales[0]
.
.pop
elimina el último elemento del array y lo devuelve.
.splice(a,b)
elimina b
elementos que comienzan en la posición a
.
3.2.1. Arrays de tamaño fijo
Un array básico se declara así:
my @array;
El array básico puede tener un número indefinido de valores y por eso se denomina auto-extendible.
Un array puede tener cualquier número de valores sin restricciones.
En contraste, también podemos crear arrays de tamaño fijo.
En estos arrays se define un tamaño fijo y no puede crecer más allá de este tamaño.
Para declarar un array de tamaño fijo, especifica el número máximo de elementos entre corchetes justo después de su nombre:
my @array[3];
Este array tendrá un máximo de 3 valores, indexados desde 0 a 2.
my @array[3];
@array[0] = "primer valor";
@array[1] = "segundo valor";
@array[2] = "tercer valor";
No puedes agregar un cuarto valor a este array:
my @array[3];
@array[0] = "primer valor";
@array[1] = "segundo valor";
@array[2] = "tercer valor";
@array[3] = "cuarto valor";
Index 3 for dimension 1 out of range (must be 0..2)
3.2.2. Arrays multidimensionales
Los arrays que hemos visto hasta ahora son de una dimensión.
Con Perl 6, podemos definir arrays de varias dimensiones.
my @tbl[3;2];
Este array es de dos dimensiones. La primera dimensión puede tener un máximo de 3 valores y la segunda dimensión un máximo de 2 valores.
Imagínalo como una tabla de 3x2.
my @tbl[3;2];
@tbl[0;0] = 1;
@tbl[0;1] = "x";
@tbl[1;0] = 2;
@tbl[1;1] = "y";
@tbl[2;0] = 3;
@tbl[2;1] = "z";
say @tbl
[[1 x] [2 y] [3 z]]
[1 x]
[2 y]
[3 z]
Consulta https://docs.perl6.org/type/Array para tener la referencia completa sobre Arrays. |
3.3. Hashes
my %capitales = ('UK','Londres','Alemania','Berlín');
say %capitales;
my %capitales = (UK => 'Londres', Alemania => 'Berlín');
say %capitales;
Algunos de los métodos aplicables a los hashes son:
Script
my %capitales = (UK => 'Londres', Alemania => 'Berlín');
%capitales.push: (Francia => 'París');
say %capitales.kv;
say %capitales.keys;
say %capitales.values;
say "La capital de Francia es: " ~ %capitales<Francia>;
Salida
(Alemania Berlín Francia París UK Londres)
(Alemania Francia UK)
(Berlín París Londres)
La capital de Francia es: París
.push: (Clave => 'Valor')
agrega un nuevo par clave/valor.
.kv
devuelve una lista con todas las claves y valores.
.keys
devuelve una lista con todas las claves.
.values
devuelve una lista con todos los valores.
Podemos acceder a un valor concreto del hash indicando su clave %hash<clave>
Consulta https://docs.perl6.org/type/Hash para una referencia completa sobre hashes. |
3.4. Tipos
En los ejemplos anteriores no hemos especificado el tipo de valor que debería contener cada variable.
.WHAT devuelve el tipo del valor que contiene la variable.
|
my $var = 'Texto';
say $var;
say $var.WHAT;
$var = 123;
say $var;
say $var.WHAT;
Como puedes ver en el ejemplo anterior, el tipo de valor en $var
primero fue texto (Str) y después entero (Int).
Este estilo de programación se caracteriza por ser de tipado dinámico. Dinámico en el sentido de que las variables pueden contener valores de Cualquier tipo.
Ahora intenta ejecutar el siguiente ejemplo:
Fíjate en el Int
indicado antes de la variable.
my Int $var = 'Texto';
say $var;
say $var.WHAT;
Este ejemplo devuelve un error indicando: Type check failed in assignment to $var; expected Int but got Str
Lo que ocurre es que hemos especificado como entero (Int) el tipo de la variable y falla al intentar asignar en ella un texto (Str).
Este estilo de programación se caracteriza por ser de tipado estático. Estático en el sentido de que la variable se define con un tipo determinado antes de asignarla y este tipo no puede cambiarse después.
Perl 6 es un lenguaje de tipado gradual; lo que permite tipado estático y dinámico.
my Int @array = 1,2,3;
say @array;
say @array.WHAT;
my Str @multilengua = "Hello","Salut","Hallo","您好","안녕하세요","こんにちは";
say @multilengua;
say @multilengua.WHAT;
my Str %capitales = (UK => 'London', Alemania => 'Berlín');
say %capitales;
say %capitales.WHAT;
my Int %código-país = (UK => 44, Alemania => 49);
say %código-país;
say %código-país.WHAT;
Es posible que nunca utilices los dos primeros, pero aparecen en la siguiente lista para que sepas que existen.
|
|
|
|
|
|
||
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3.5. Introspección
Introspección es el proceso para adquirir información sobre las propiedades de un objeto, como por ejemplo su tipo.
En uno de los ejemplos anteriores utilizamos .WHAT
para conocer el tipo de una variable.
my Int $var;
say $var.WHAT; # (Int)
my $var2;
say $var2.WHAT; # (Any)
$var2 = 1;
say $var2.WHAT; # (Int)
$var2 = "Hola";
say $var2.WHAT; # (Str)
$var2 = True;
say $var2.WHAT; # (Bool)
$var2 = Nil;
say $var2.WHAT; # (Any)
El tipo de una variable que contiene un valor se corresponde con su valor.
El tipo de una variable declarada de forma estática y sin valor es el tipo con el que se ha declarado.
El tipo de una variable vacía que no ha sido declarada de forma estática es (Any)
.
Asigna Nil
a una variable para eliminar su valor.
3.6. Alcance
Es necesario declarar una variable antes de utilizarla.
Perl 6 dispone de varias formas de declaración, y de momento estamos utilizando my
.
my $var=1;
La forma de declaración my
proporciona a la variable un alcance léxico.
Dicho de otro modo, la variable solo es accesible desde el mismo bloque donde es declarada.
En Perl 6 un bloque está delimitado por { }
.
En caso de no existir bloque, la variable estará disponible en el script entero.
{
my Str $var = 'Texto';
say $var; # accesible
}
say $var; # no accesible, devuelve un error
Como la variable solo es accesible dentro del bloque donde está definida, el mismo nombre de variable puede utilizarse en cualquier otro bloque.
{
my Str $var = 'Texto';
say $var;
}
my Int $var = 123;
say $var;
3.7. Asignación vs. Vinculación
En los ejemplos anteriores hemos visto cómo asignar valores a variables.
La asignación se realiza mediante el operador =
.
my Int $var = 123;
say $var;
Y podemos cambiar el valor asignado a la variable:
my Int $var = 123;
say $var;
$var = 999;
say $var;
Salida
123
999
Por otro lado, no podemos cambiar el valor vinculado de una variable.
La vinculación se realiza mediante el operador :=
.
my Int $var := 123;
say $var;
$var = 999;
say $var;
Salida
123
Cannot assign to an immutable value
my $a;
my $b;
$b := $a;
$a = 7;
say $b;
$b = 8;
say $a;
Salida
7
8
Como has visto, la vinculación de variables es bidireccional.
$a := $b
y $b := $a
tienen el mismo efecto.
En https://docs.perl6.org/language/variables tienes más información sobre variables. |
4. Funciones y mutadores
Es importante diferenciar entre funciones y mutadores.
Las funciones no cambian el estado del objeto donde se aplica.
Los mutadores modifican el estado del objeto.
Script
1
2
3
4
5
6
7
8
9
10
my @números = [7,2,4,9,11,3];
@números.push(99);
say @números; #1
say @números.sort; #2
say @números; #3
@números.=sort;
say @números; #4
Salida
[7 2 4 9 11 3 99] #1
(2 3 4 7 9 11 99) #2
[7 2 4 9 11 3 99] #3
[2 3 4 7 9 11 99] #4
.push
es un mutador porque cambia el estado del array (#1)
.sort
es una función porque devuelve un array ordenado pero no cambia el estado inicial del array:
-
(#2) muestra la devolución de un array ordenado.
-
(#3) muestra que el estado inicial del array no ha cambiado.
Puedes hacer que una función se comporte como un mutador utilizando .=
en lugar de .
(#4) (línea 9 del script)
5. Bucles y condiciones
Perl 6 tiene muchos constructores de bucles y condiciones.
5.1. if
El código se ejecuta solo si se cumple una condición, por ej., una expresión se evalúa como True
.
my $edad = 19;
if $edad > 18 {
say 'Bienvenido'
}
En Perl 6 podemos invertir el código y la condición, y aún así la condición siempre se evalúa primero.
my $edad = 19;
say 'Bienvenido' if $edad > 18;
Si la condición no se cumple, podemos especificar bloques alternativos de ejecución utilizando:
-
else
-
elsif
# ejecuta el mismo código para distintos valores de la variable
my $número-de-asientos = 9;
if $número-de-asientos <= 5 {
say 'Soy un sedan'
} elsif $número-de-asientos <= 7 {
say 'Tengo 6 o 7 asientos'
} else {
say 'Soy un microbus'
}
5.2. unless
La negación de if
es unless
.
El siguiente código:
my $limpiar-zapatos = False;
if not $limpiar-zapatos {
say 'Limpia tus zapatos'
}
puede escribirse como:
my $limpiar-zapatos = False;
unless $limpiar-zapatos {
say 'Limpia tus zapatos'
}
La negación en Perl 6 se realiza con !
o con not
.
unless (condición)
se utiliza en lugar de if not (condición)
.
unless
no puede utilizar la claúsula else
.
5.3. with
with
es como if
pero solo comprueba si la variable está definida.
my Int $var=1;
with $var {
say 'Hola'
}
No ocurre nada si ejecutas el código sin asignar un valor a la variable.
my Int $var;
with $var {
say 'Hola'
}
without
es la negación de with
y es parecido a unless
.
Si la primera condición with
no se cumple, puedes indicar una alternativa mediante orwith
.
with
y orwith
son parecidos a if
y elsif
.
5.4. for
for
itera sobre una serie de valores.
my @array = [1,2,3];
for @array -> $array-item {
say $array-item * 100
}
Observa que en la iteración hemos creado la variable $array-item
para realizar después la operación *100
en cada elemento del array.
5.5. given
En Perl 6 given
viene a ser switch
en otros lenguajes, pero mucho más potente.
my $var = 42;
given $var {
when 0..50 { say 'Menos o igual a 50'}
when Int { say "es un Entero" }
when 42 { say 42 }
default { say "¿ejem?" }
}
Cuando se produce la coincidencia no se evalúan las demás.
Como opción, proceed
continúa la evaluación aunque se produzca la coincidencia.
my $var = 42;
given $var {
when 0..50 { say 'Menos o igual a 50';proceed}
when Int { say "es un Entero";proceed}
when 42 { say 42 }
default { say "¿ejem?" }
}
5.6. loop
loop
es otra forma de escribir un for
.
Actualmente loop
viene a ser el for
utilizado en la familia de lenguajes de C.
Perl 6 pertenece a la familia de lenguajes de C.
loop (my $i = 0; $i < 5; $i++) {
say "El número actual es $i"
}
En https://docs.perl6.org/language/control tienes más información sobre bucles y condiciones |
6. I/O
En Perl 6, las dos interfaces más utilizadas de Entrada/Salida son el Terminal y los Ficheros.
6.1. E/S básica mediante el Terminal
6.1.1. say
say
escribe en la salida estándar agregando al final una línea nueva. En otras palabras, el siguiente código:
say 'Hola Mamá.';
say 'Hola Señor.';
escribirá dos líneas separadas.
6.1.2. print
Por otro lado print
es como say
pero no agrega la línea nueva.
Prueba a utilizar say
en lugar de print
y compara ambos resultados.
6.1.3. get
Para capturar la entrada desde el terminal utiliza get
.
my $nombre;
say "¡Hola!, ¿cual es tu nombre?";
$nombre = get;
say "¿Que tal $nombre?, bienvenido a Perl 6";
Este código hace que el terminal espere la introducción de tu nombre. Hazlo y después pulsa [Enter]. Como resultado te dará la bienvenida.
6.1.4. prompt
prompt
es una combinación entre print
y get
.
El ejemplo anterior puede escribirse de esta otra forma:
my $nombre = prompt "¡Hola!, ¿cual es tu nombre? ";
say "¿Que tal $nombre?, bienvenido a Perl 6";
6.2. Ejecutando Comandos de la Shell
Podemos utilizar dos subrutinas para ejecutar comandos de la shell:
-
run
Ejecuta un comando externo sin la intervención de la shell. -
shell
Ejecuta un comando desde la shell del sistema y dependerá de la plataforma y la shell. Todos los caracteres especiales los interpreta la shell, como pueden ser las tuberías, redirecciones, sustitución de variables de entorno, etc.
my $nombre = 'Neo';
run 'echo', "Hola $nombre";
shell "ls";
shell "dir";
echo
y ls
son palabras clave típicas de la shell de Linux:
echo
visualiza texto en el terminal (es el equivalente a print
en Perl 6)
ls
muestra un listado de todos los archivos y carpetas del directorio actual
dir
en Windows es el equivalente de ls
en Linux.
6.3. E/S de Archivos
6.3.1. slurp
slurp
lee datos de un archivo.
Crea un archivo de texto con el siguiente contenido:
Juan 9
Juanito 7
Juana 8
Juanita 7
my $datos = slurp "datos.txt";
say $datos;
6.3.2. spurt
spurt
escribe datos en un archivo.
my $datos-nuevos = "Nuevas puntuaciones:
Pablo 10
Pablin 9
Paulo 11";
spurt "datos-nuevos.txt", $datos-nuevos;
El código anterior crea un nuevo archivo llamado datos-nuevos.txt conteniendo las nuevas puntuaciones.
6.4. Manipulando archivos y carpetas
Perl 6 puede mostrar el contenido de una carpeta sin recurrir a los comandos de la shell (utilizando ls
por ejemplo).
say dir; # Muestra archivos y carpetas de la carpeta actual
say dir "/Documentos"; # Muestra archivos y carpetas de la carpeta indicada
Además, puedes crear y eliminar carpetas.
mkdir "carpeta-nueva";
rmdir "carpeta-nueva";
mkdir
crea una carpeta nueva.
rmdir
elimina una carpeta vacía y devuelve un error sino está vacía.
También puedes comprobar si una ruta existe y si es un archivo o una carpeta:
Crea una carpeta vacía llamada carpeta123
, un archivo vacío llamado script123.p6
y el siguiente script:
say "script123.p6".IO.e;
say "carpeta123".IO.e;
say "script123.p6".IO.d;
say "carpeta123".IO.d;
say "script123.p6".IO.f;
say "carpeta123".IO.f;
Ejecuta el script.
IO.e
comprueba si existe la carpeta/archivo.
IO.f
comprueba si la ruta es un archivo.
IO.d
comprueba si la ruta es una carpeta.
en Windows puedes utilizar / o \\ para separar carpetas anidadasC:\\rakudo\\bin C:/rakudo/bin |
En https://docs.perl6.org/type/IO tienes más información sobre E/S. |
7. Subrutinas
7.1. Definición
Las Subrutinas (también denominadas subs o funciones) son una forma de empaquetar y reutilizar funcionalidades.
La definición de una subrutina comienza con la palabra clave sub
. Una vez definida puede invocarse mediante su nombre.
Fíjate en el siguiente ejemplo:
sub saludo-alien {
say "Hola terrícolas";
}
saludo-alien;
El ejemplo anterior es una subrutina sin entrada de datos.
7.2. Signatura
Las subrutinas pueden requerir una entrada, y ésta se proporciona mediante argumentos. Una subrutina puede definir ninguno o varios parámetros. La signatura de una subrutina es el número y el tipo de los parámetros que puede definir.
La siguiente subrutina acepta un argumento de tipo string.
sub di-hola (Str $nombre) {
say "¡¡Hola " ~ $nombre ~ "!!"
}
di-hola "Pablo";
di-hola "Paula";
7.3. Sobrecarga
Es posible definir varias subrutinas con el mismo nombre pero con signaturas diferentes.
Cuando se llama a la subrutina, se decidirá qué versión utilizar en tiempo de ejecución dependiendo del número y tipo de argumentos proporcionados.
Este tipo de subrutinas se definen de la misma forma que una subrutina normal pero utilizando la palabra clave multi
en lugar de sub
.
multi saludo($nombre) {
say "Buenos días $nombre";
}
multi saludo($nombre, $título) {
say "Buenos días $título $nombre";
}
saludo "Juanito";
saludo "Laura","Srta.";
7.4. Parámetros por defecto y opcionales
Tendremos un error si se define una subrutina para aceptar un argumento y éste no es proporcionado.
Con Perl 6 podemos definir subrutinas con:
-
Parámetros opcionales
-
Parámetros por defecto
Un parámetro opcional se define añadiendo ?
al nombre del parámetro.
sub di-hola($nombre?) {
with $nombre { say "Hola " ~ $nombre }
else { say "Hola humano" }
}
di-hola;
di-hola("Laura");
Si no es necesario proporcionar un argumento, puede definirse uno por defecto asignándole un valor al parámetro en la definición de la subrutina.
sub di-hola($nombre="Mateo") {
say "Hola " ~ $nombre;
}
di-hola;
di-hola("Laura");
7.5. Valores de retorno
Hemos visto que todas las subrutinas hasta ahora siempre hacen algo: mostrar resultados en la pantalla del terminal.
Sin embargo y a veces, utilizamos una subrutina para que nos devuelva algún valor que podamos utilizar después en el flujo del programa.
Si una función se ejecuta hasta el final de su bloque, el valor de retorno vendrá determinado por la última declaración o expresión.
sub cuadrado ($x) {
$x ** 2;
}
say "7 al cuadrado es igual a " ~ cuadrado(7);
Para dejarlo más claro sería una buena idea especificar de forma explícita qué es lo que queremos devolver.
Esto se realiza mediante la palabra clave return
.
sub cuadrado ($x) {
return $x ** 2;
}
say "7 al cuadrado es igual a " ~ cuadrado(7);
7.5.1. Restricción de valores de retorno
En uno de los ejemplos anteriores vimos cómo restringir el tipo del argumento aceptado. Lo mismo podemos hacer con los valores de retorno.
Para restringir el valor de retorno a un tipo determinado podemos utilizar returns
o la notación de flecha -->
en la signatura.
sub cuadrado ($x) returns Int {
return $x ** 2;
}
say "1.2 al cuadrado es igual a " ~ cuadrado(1.2);
sub cuadrado ($x --> Int) {
return $x ** 2;
}
say "1.2 al cuadrado es igual a " ~ cuadrado(1.2);
Si el tipo del valor devuelto no coincide con el indicado, tendremos un error.
Type check failed for return value; expected Int but got Rat (1.44)
La restricción del tipo del valor de retorno tambień puede controlar si este valor está definido o no. En los ejemplos anteriores especificamos que el valor de retorno debería ser de tipo Además, podemos indicar que el valor de retorno esté obligatoriamente definido o no utilizando las siguientes signaturas: Como hemos visto es una buena costumbre utilizar estas restricciones.
|
En https://docs.perl6.org/language/functions encontrarás más información sobre subrutinas y funciones. |
8. Programación Funcional
En este apartado veremos algunas características que facilitan la Programación Funcional.
8.1. Las Funciones son de primera clase
Las funciones/subrutinas son de primera clase:
-
Pueden pasarse como argumentos
-
Pueden ser devueltas desde otras funciones
-
Pueden asignarse a variables
Un gran ejemplo es la función map
.
map
es una función de orden superior que puede aceptar otra función como argumento.
my @array = <1 2 3 4 5>;
sub cuadrado($x) {
$x ** 2
}
say map(&cuadrado,@array);
(1 4 9 16 25)
Hemos definido la subrutina cuadrado
que toma un argumento y lo multiplica por sí mismo.
Después utilizamos map
, una función de orden superior, que toma dos argumentos: la subrutina cuadrado
y un array.
El resultado es una lista de los cuadrados de cada elemento del array.
Ten en cuenta que cuando pasamos una subrutina como argumento, es necesario utilizar el prefijo &
en el nombre.
8.2. Funciones anónimas
Una función anónima también se denomina lambda.
Una función anónima no está vinculada a un identificador (no tiene nombre).
Escribamos de nuevo el ejemplo de map
pero utilizando una función anónima
my @array = <1 2 3 4 5>;
say map(-> $x {$x ** 2},@array);
Observa que en lugar de declarar la subrutina cuadrado
y pasarla a map
como argumento, la definimos dentro de la función anónima como -> $x {$x ** 2}
.
En la jerga de Perl 6 nos referimos a esta notación como punto de entrada al bloque
my $cuadrado = -> $x {
$x ** 2
}
say $cuadrado(9);
8.3. Encadenamiento
En Perl 6 los métodos pueden encadenarse, de forma que el resultado de un método pasa como argumento a otro método.
Un ejemplo: dado un array, necesitamos los valores únicos de ese array ordenados de mayor a menor.
A continuación una solución sin encadenamiento:
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = reverse(sort(unique(@array)));
say @final-array;
Aquí llamamos unique
sobre @array
, pasamos el resultado como argumento a sort
y pasamos el resultado a reverse
.
En contraste, con métodos encadenados, el ejemplo anterior puede escribirse así:
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array = @array.unique.sort.reverse;
say @final-array;
Como ves, el encadenamiento de métodos es más visual.
8.4. Operador de Alimentación
El operador de alimentación, llamado tubería en algunos lenguajes de programación funcional, ejemplifica el encadenamiento de métodos.
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
@array ==> unique()
==> sort()
==> reverse()
==> my @final-array;
say @final-array;
Comienza con `@array` y devuelve una lista de elementos únicos
después los ordena
después invierte el orden
después guarda el resultado en @final-array
Fíjate en el flujo de las llamadas a los métodos: se produce de arriba hacia abajo, esto es, desde el primero hasta el último paso.
my @array = <7 8 9 0 1 2 4 3 5 6 7 8 9>;
my @final-array-v2 <== reverse()
<== sort()
<== unique()
<== @array;
say @final-array-v2;
La alimentación hacia atrás es parecida a la anterior pero al revés.
El flujo de las llamadas a los métodos es de abajo hacia arriba, esto es, desde el paso final hacia el primero.
8.5. Hiperoperador
El hiperoperador >>.
puede aplicar un método a todos los elementos de una lista y devolver una lista con los resultados.
my @array = <0 1 2 3 4 5 6 7 8 9 10>;
sub es-par($var) { $var %% 2 };
say @array>>.is-prime;
say @array>>.&es-par;
Mediante el hiperoperador podemos utilizar todos los métodos ya definidos en Perl 6, por ej. is-prime
que devuelve si un número es primo o no.
Además, podemos definir funciones nuevas y utilizarlas mediante el hiperoperador agregando el prefijo &
en el nombre del método, por ej. &es-par
.
El uso del hiperoperador es muy práctico pues evita escribir un bucle for
para iterar sobre cada valor.
Perl 6 garantiza que el orden de los resultados sea el mismo que el de la lista original.
Sin embargo, Perl 6 no garantiza la llamada a los métodos en el orden de la lista o en el mismo hilo. Por tanto, ten cuidado con los métodos que tengan efectos secundarios, como say o print .
|
8.6. Ensamblajes
Un ensamblaje es una superposición lógica de valores.
En el siguiente ejemplo 1|2|3
es un ensamblaje.
my $var = 2;
if $var == 1|2|3 {
say "La variable es 1 o 2 o 3"
}
El uso de ensamblajes normalmente produce autothreading para cada elemento del ensamblaje y todos los resultados se combinan y se devuelven en un nuevo ensamblaje.
8.7. Listas perezosas
Una lista perezosa es una lista que se evalúa perezosamente.
La evaluación perezosa demora la evaluación de una expresión hasta que es requerida, guardando mientras los resultados en una tabla de búsqueda para así evitar repetir la evaluación.
Entre los beneficios tenemos:
-
Incremento del rendimiento evitando cálculos innecesarios
-
La habilidad de construir estructuras de datos potencialmente infinitas
-
La habilidad de definir controles de flujo
Podemos definir una lista perezosa utilizando el operador infijo …
Una lista perezosa tiene elemento(s) inicial(es), un generador y un punto final.
my $listaperezosa = (1 ... 10);
say $listaperezosa;
El elemento inicial es 1 y el punto final es 10. Como no hemos definido un generador, por defecto es el sucesor (+1)
Dicho de otra forma, esta lista perezosa puede devolver (si es requerida) los siguientes elementos (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
my $listaperezosa = (1 ... Inf);
say $listaperezosa;
Esta lista puede devolver (si es requerida) cualquier entero entre 1 e infinito, en otras palabras cualquier número entero.
my $listaperezosa = (0,2 ... 10);
say $listaperezosa;
Los elementos iniciales son 0 y 2, y el punto final es 10.
Aunque no hay un generador definido, Perl 6 utiliza los elementos iniciales para deducir que el generador es (+2)
Esta lista puede devolver (si es requerida) los siguientes elementos (0, 2, 4, 6, 8, 10)
my $listaperezosa = (0, { $_ + 3 } ... 12);
say $listaperezosa;
En este ejemplo hemos definido de forma explícita un generador entre llaves { }
Esta lista puede devolver (si es requerida) los siguientes elementos (0, 3, 6, 9, 12)
Al usar un generador de forma explícita el punto final debe ser uno de los valores que el generador pueda devolver. De forma alternativa puedes sustituir Lo siguiente no detiene al generador
Lo siguiente detiene al generador
|
8.8. Clausuras
Todos los objetos de código en Perl 6 son clausuras, lo que significa que se pueden referenciar variables léxicamente definidas desde un ámbito externo.
sub crear-saludo {
my $nombre = "Juan Dios";
sub saludo {
say "Buenos días $nombre";
};
return &saludo;
}
my $saludo-creado = crear-saludo;
$saludo-creado();
Si ejecutas el código anterior verás en el terminal Buenos días Juan Dios
.
El resultado es simple, pero lo interesante es que se devuelve la subrutina interna saludo
desde una subrutina externa antes de ser ejecutada.
$saludo-creado
es una clausura.
Una clausura es una especie de objeto especial que combina dos cosas:
-
Una Subrutina
-
El Entorno donde se creó esa subrutina.
El entorno es cualquier variable local que estaba dentro del alcance en el momento que se creó la clausura.
En este caso, $saludo-creado
es una clausura que incluye a la subrutina saludo
y el texto Juan Dios
que existía cuando se creó la clausura.
Veamos un ejemplo más interesante.
sub crear-saludo($momento) {
return sub ($nombre) {
return "Buenas $momento $nombre"
}
}
my $tardes = crear-saludo("Tardes");
my $noches = crear-saludo("Noches");
say $tardes("Juan");
say $noches("Juana");
En este ejemplo hemos definido la subrutina crear-saludo($momento)
aceptando el argumento $momento
y devuelve una subrutina nueva. La subrutina devuelta acepta el argumento $nombre
y devuelve el saludo.
crear-saludo
es una fábrica de subrutinas, y en este ejemplo hemos creado dos subrutinas nuevas, una para decir Buenas Tardes
y otra para decir Buenas Noches
.
$tardes
y $noches
también son clausuras. Ambas comparten la misma definición del cuerpo de la subrutina pero alojan entornos distintos.
En el entorno de $tardes
el $momento
es Tardes
y en el entorno de $noches
el $momento
es Noches
.
9. Clases y Objetos
En el apartado anterior hemos visto cómo utilizar la Programación Funcional en Perl 6 y en el siguiente apartado veremos cómo utilizar Perl 6 en la Programación Orientada a Objetos.
9.1. Introducción
La programación Orientada a Objetos es uno de los paradigmas de programación más utilizados actualmente.
Un objeto es un conjunto de variables y subrutinas.
Las variables se llaman atributos y las subrutinas se llaman métodos.
Los atributos definen un estado y los métodos definen el comportamiento de un objeto.
Una clase es una plantilla para crear objetos.
Para entender esta relación veamos el siguiente ejemplo:
Hay 4 individuos en una sala |
objetos ⇒ 4 personas |
Los 4 individuos son humanos |
clase ⇒ Humano |
Tienen distintos nombres, edades, sexo y nacionalidad |
atributos ⇒ nombre, edad, sexo, nacionalidad |
En orientación a objetos decimos que los objetos son instancias de una clase.
Veamos el siguiente script:
class Humano {
has $.nombre;
has $.edad;
has $.sexo;
has $.nacionalidad;
}
my $juan = Humano.new(nombre => 'Juan', edad => 23, sexo => 'M', nacionalidad => 'Español');
say $juan;
La palabra clave class
se utiliza para definir una clase.
La palabra clave has
se utiliza para definir los atributos de una clase.
El método .new()
se denomina constructor y crea el objeto como una instancia de la clase a la que ha sido llamada.
En el script anterior, la variable nueva $juan
tiene una referencia a una instancia nueva de "Humano" definida por Humano.new()
.
Los argumentos que se pasan al método .new()
son utilizados para establecer los atributos del objeto en cuestión.
Una clase puede tener un alcance léxico mediante my
:
my class Humano {
}
9.2. Encapsulación
La encapsulación es un concepto de la programación Orientada a Objetos que consiste en empaquetar un conjunto de datos y métodos.
Los datos (atributos) dentro de un objeto deben ser privados, dicho de otro modo, solo son accesibles desde dentro del objeto.
Para acceder a los atributos de un objeto desde fuera de él utilizamos métodos de acceso.
Los siguientes dos scripts dan el mismo resultado.
my $var = 7;
say $var;
my $var = 7;
sub sayvar {
$var;
}
say sayvar;
El método sayvar
es un método de acceso que nos permite acceder al valor de la variable sin acceder directamente a ella.
Perl 6 realiza la encapsulación mediante twigils (sigilos secundarios) y se ubican entre el sigilo y el nombre del atributo.
En las clases se utilizan dos twigils:
-
!
para indicar de forma explícita que el atributo es privado. -
.
para crear automáticamente un método de accceso al atributo.
Por defecto todos los atributos son privados pero es muy recomendable utilizar siempre el twigil !
.
Por lo tanto, podemos reescribir la clase anterior como:
class Humano {
has $!nombre;
has $!edad;
has $!sexo;
has $!nacionalidad;
}
my $juan = Humano.new(nombre => 'Juan', edad => 23, sexo => 'M', nacionalidad => 'Español');
say $juan;
Si añades al script la siguiente sentencia: say $juan.edad;
devolverá el siguiente error: Method 'edad' not found for invocant of class 'Humano'
debido a que $!edad
es un atributo privado y solo puede utilizarse desde dentro del objeto. Como hemos visto, tendremos un error al intentar acceder a este atributo desde fuera del objeto.
Sustituye has $!edad
por has $.edad
y comprueba el resultado de say $juan.edad;
9.3. Parámetros Posicionales vs. Parámetros con nombre
En Perl 6 todas las clases heredan un constructor .new()
por defecto que puede utilizarse para crear objetos proporcionando argumentos.
El constructor por defecto solo acepta argumentos con nombre.
Como puedes ver en el ejemplo que vimos antes, los argumentos que tiene .new()
están definidos con un nombre:
-
nombre => 'Juan'
-
edad => 23
¿Puedo ahorrarme el nombre de cada atributo al crear un objeto? Sí, pero necesito crear otro constructor que acepte argumentos posicionales.
class Humano {
has $.nombre;
has $.edad;
has $.sexo;
has $.nacionalidad;
# nuevo constructor que sustituye el de por defecto.
method new ($nombre,$edad,$sexo,$nacionalidad) {
self.bless(:$nombre,:$edad,:$sexo,:$nacionalidad);
}
}
my $juan = Humano.new('Juan',23,'M','Español');
say $juan;
9.4. Métodos
9.4.1. Introducción
Los métodos son las subrutinas de un objeto.
Al igual que las subrutinas, los métodos pueden empaquetar un conjunto de funcionalidades, aceptar argumentos, tener una signatura y estar sobrecargadas con multi.
Los métodos se definen con la palabra clave method
y normalmente se utilizan para realizar alguna acción sobre los atributos de los objetos, reforzando así el concepto de encapsulación donde los atributos del objeto solo pueden manipularse dentro del objeto mediante sus métodos.
Desde fuera solo podemos acceder a los métodos de los objetos y no hay acceso directo a sus atributos.
class Humano {
has $.nombre;
has $.edad;
has $.sexo;
has $.nacionalidad;
has $.es-adulto;
method evalúa_es-adulto {
if self.edad < 18 {
$!es-adulto = 'No'
} else {
$!es-adulto = 'Sí'
}
}
}
my $juan = Humano.new(nombre => 'Juan', edad => 23, sexo => 'M', nacionalidad => 'Español');
$juan.evalúa_es-adulto;
say $juan.es-adulto;
Una vez definidos los métodos de una clase, pueden invocarse en un objeto mediante la notación de punto:
objeto . método, como en el ejemplo que hemos visto antes: $juan.evalúa_es-adulto
Si en la definición del método necesitamos hacer referencia al objeto en sí para invocar a otro método utilizaremos la palabra clave self
.
Si en la definición del método necesitamos hacer referencia a un atributo utilizaremos !
aunque el atributo esté definido con .
La razón de esto es que el twigil .
declara un atributo con !
y crea automáticamente el método de acceso.
En el ejemplo anterior, if self.edad < 18
y if $!edad < 18
tendrán el mismo efecto, aunque técnicamente son distintos:
-
self.edad
es una llamada al método (de acceso).edad
También puede escribirse como$.edad
-
$!edad
es una llamada directa a la variable
9.4.2. Métodos privados
Puede llamarse a un método normal de un objeto desde fuera de la clase.
Los métodos privados solo pueden llamarse desde dentro de la clase.
Este es el caso donde un método llama a otro para realizar una acción concreta. El método que interactúa con el mundo exterior es público y a la vez llama al otro método que permanece privado. Al declarar el método como privado conseguimos que el usuario no pueda interactuar con él directamente.
Declarar un método privado requiere utilizar el twigil !
antes de su nombre.
Estos métodos privados se llaman mediante !
en lugar de .
method !soyprivado {
# código
}
method soypúblico {
self!soyprivado;
# más código
}
9.5. Atributos de Clase
Los atributos de Clase son atributos que pertenecen a la clase en sí y no a sus objetos.
Pueden inicializarse durante su definición.
Los atributos de Clase se declaran mediante my
en lugar de has
.
Se llaman en la clase en sí en lugar de sus objetos.
class Humano {
has $.nombre;
my $.contador = 0;
method new($nombre) {
Humano.contador++;
self.bless(:$nombre);
}
}
my $a = Humano.new('a');
my $b = Humano.new('b');
say Humano.contador;
9.6. Tipo de Acceso
Todos los ejemplos que hemos visto hasta ahora utilizan métodos de acceso para conseguir la información de los atributos de los objetos.
¿Y si necesitamos modificar el valor de un atributo?
Para ello necesitamos etiquetar ese atributo como lectura/escritura utilizando las palabras clave is rw
class Humano {
has $.nombre;
has $.edad is rw;
}
my $juan = Humano.new(nombre => 'Juan', edad => 21);
say $juan.edad;
$juan.edad = 23;
say $juan.edad;
Todos los atributos se declaran por defecto como solo lectura y también puedes hacerlo de forma explícita mediante is readonly
9.7. Herencia
9.7.1. Introducción
Herencia es otro concepto de la programación Orientada a Objetos.
Cuando definimos clases nos damos cuenta de que algunas veces utilizan los mismos métodos y atributos.
¿Es necesario duplicar código?
¡NO! Hay que utilizar la herencia
Pensemos en definir dos clases, una clase para seres humanos y otra clase para empleados.
Los seres humanos tienen 2 atributos: nombre y edad.
Los empleados tienen 4 atributos: nombre, edad, compañía y salario.
Con prisas, uno definiría las clases así:
class Humano {
has $.nombre;
has $.edad;
}
class Empleado {
has $.nombre;
has $.edad;
has $.compañía;
has $.salario;
}
El código anterior aunque técnicamente es correcto, conceptualmente es pobre.
Hay una forma mejor de escribirlo:
class Humano {
has $.nombre;
has $.edad;
}
class Empleado is Humano {
has $.compañía;
has $.salario;
}
La herencia se define mediante la palabra clave is
.
En orientación a objetos, decimos que Empleado es hijo de Humano, y que Humano es padre de Empleado.
Todas las clases hijas heredan los atributos y métodos de su clase padre, y así ahorramos duplicar su definición.
9.7.2. Anulación de herencia
Las clases heredan todos los atributos y métodos de sus clases padre correspondientes.
Hay casos donde es necesario que un método heredado actúe de forma distinta.
Para conseguirlo, redefinimos el método en cuestión en la clase hija.
Este concepto se llama anulación de herencia.
En el siguiente ejemplo, el método preséntate
se hereda de la clase Empleado.
class Humano {
has $.nombre;
has $.edad;
method preséntate {
say 'Hola, soy un ser humano y mi nombre es ' ~ self.nombre;
}
}
class Empleado is Humano {
has $.compañía;
has $.salario;
}
my $juan = Humano.new(nombre =>'Juan', edad => 23,);
my $juana = Empleado.new(nombre =>'Juana', edad => 25, compañía => 'Acme', salario => 4000);
$juan.preséntate;
$juana.preséntate;
La anulación de herencia funciona así:
class Humano {
has $.nombre;
has $.edad;
method preséntate {
say 'Hola, soy un ser humano y mi nombre es ' ~ self.nombre;
}
}
class Empleado is Humano {
has $.compañía;
has $.salario;
method preséntate {
say 'Hola, soy un empleado, mi nombre es ' ~ self.nombre ~ ' y trabajo en: ' ~ self.compañía;
}
}
my $juan = Humano.new(nombre =>'Juan',edad => 23,);
my $juana = Empleado.new(nombre =>'Juana',edad => 25,compañía => 'Acme',salario => 4000);
$juan.preséntate;
$juana.preséntate;
El método correspondiente será aplicado dependiendo de la clase a la que pertenece el objeto.
9.7.3. Submétodos
Los submétodos son métodos que no se heredan en las clases hijas.
Solo son accesibles desde la clase donde son declarados.
Se definen utilizando la palabra clave submethod
.
9.8. Herencia Múltiple
Perl 6 permite la herencia múltiple. Una clase puede heredar de varias clases.
class graf-barras {
has Int @.valores-barras;
method dibujar {
say @.valores-barras;
}
}
class graf-líneas {
has Int @.valores-líneas;
method dibujar {
say @.valores-líneas;
}
}
class multi-gráfica is graf-barras is graf-líneas {
}
my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);
my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;
Salida
Ventas actuales:
[10 9 11 8 7 10]
Previsión de ventas:
[9 8 10 7 6 9]
Actual vs Previsión:
[10 9 11 8 7 10]
La clase multi-gráfica
debería ser capaz de tener dos series, una para los valores actuales de las barras y otra para los valores de las previsiones de las líneas.
Por esa razón la hemos definido como hija de graf-líneas
y graf-barras
.
Te habrás dado cuenta que al llamar al método dibujar
en multi-gráfica
no tenemos el resultado deseado.
Solo se dibuja una serie.
¿Qué ha ocurrido?
multi-gráfica
hereda de graf-líneas
y de graf-barras
y ambas tienen un método llamado dibujar
.
Cuando llamamos a ese método desde multi-gráfica
Perl 6 trata de resolver internamente el conflicto llamando a uno de los métodos heredados.
Para que funcione correctamente necesitamos anular la herencia del método dibujar
en multi-gráfica
.
class graf-barras {
has Int @.valores-barras;
method dibujar {
say @.valores-barras;
}
}
class graf-líneas {
has Int @.valores-líneas;
method dibujar {
say @.valores-líneas;
}
}
class multi-gráfica is graf-barras is graf-líneas {
method dibujar {
say @.valores-barras;
say @.valores-líneas;
}
}
my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);
my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;
Salida
Ventas actuales:
[10 9 11 8 7 10]
Previsión de ventas:
[9 8 10 7 6 9]
Actual vs Previsión:
[10 9 11 8 7 10]
[9 8 10 7 6 9]
9.9. Roles
Los Roles son similares a las clases en cuanto a que son una colección de atributos y métodos.
Los roles se declaran con la palabra clave role
. Las clases que quieran implementar un rol, pueden hacerlo utilizando la palabra clave does
.
role graf-barras {
has Int @.valores-barras;
method dibujar {
say @.valores-barras;
}
}
role graf-líneas {
has Int @.valores-líneas;
method dibujar {
say @.valores-líneas;
}
}
class multi-gráfica does graf-barras does graf-líneas {
method dibujar {
say @.valores-barras;
say @.valores-líneas;
}
}
my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);
my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;
Verás que el resultado es el mismo que antes sin utilizar roles.
Y ahora te preguntarás: si un rol es como una clase ¿para qué se utilizan?
Para responder la pregunta, modifica el primer script que hemos utilizado para mostrar el caso de la herencia múltiple, en el que olvidamos anular la herencia del método dibujar
.
role graf-barras {
has Int @.valores-barras;
method dibujar {
say @.valores-barras;
}
}
role graf-líneas {
has Int @.valores-líneas;
method dibujar {
say @.valores-líneas;
}
}
class multi-gráfica does graf-barras does graf-líneas {
}
my $ventas-actuales = graf-barras.new(valores-barras => [10,9,11,8,7,10]);
my $previsión-ventas = graf-líneas.new(valores-líneas => [9,8,10,7,6,9]);
my $actual-vs-previsión = multi-gráfica.new(valores-barras => [10,9,11,8,7,10],
valores-líneas => [9,8,10,7,6,9]);
say "Ventas actuales:";
$ventas-actuales.dibujar;
say "Previsión de ventas:";
$previsión-ventas.dibujar;
say "Actual vs Previsión:";
$actual-vs-previsión.dibujar;
Salida
===SORRY!=== Error while compiling
Method 'dibujar' must be resolved by class multi-gráfica because it exists in multiple roles (graf-líneas, graf-barras)
Tendremos un error en tiempo de compilación si aplicamos varios roles a la misma clase si existe un conflicto.
Este enfoque es mucho más seguro que la herencia múltiple, donde los conflictos no se consideran errores y se resuelven simplemente en tiempo de ejecución.
Los roles te avisarán si existe un conflicto.
9.10. Introspección
La Introspección es la forma de conseguir información de un objeto; como su tipo, atributos o métodos.
class Humano {
has Str $.nombre;
has Int $.edad;
method preséntate {
say 'Hola, soy un ser humano y mi nombre es ' ~ self.nombre;
}
}
class Empleado is Humano {
has Str $.compañía;
has Int $.salario;
method preséntate {
say 'Hola, soy un empleado, mi nombre es ' ~ self.nombre ~ ' y trabajo en: ' ~ self.compañía;
}
}
my $juan = Humano.new(nombre =>'Juan',edad => 23,);
my $juana = Empleado.new(nombre =>'Juana',edad => 25,compañía => 'Acme',salario => 4000);
say $juan.WHAT;
say $juana.WHAT;
say $juan.^attributes;
say $juana.^attributes;
say $juan.^methods;
say $juana.^methods;
say $juana.^parents;
if $juana ~~ Humano {say 'Juana es Humana'};
La introspeción proporciona la siguiente información:
-
.WHAT
devuelve la clase a la que pertenece el objeto. -
.^attributes
devuelve todos los atributos del objeto. -
.^methods
devuelve todos los métodos accesibles del objeto. -
.^parents
devuelve todas las clases padre a las que pertenece la clase del objeto. -
~~
es el operador de coincidencia inteligente. Devuelve True si el objeto pertenece a la clase con la que se compara o con cualquier clase heredada.
Consulta: para obtener más información sobre Programación Orientada a Objetos en Perl6. |
10. Control de Excepciones
10.1. Captura de Excepciones
Las excepciones son situaciones especiales que ocurren en tiempo de ejecución cuando algo va mal.
Decimos que las excepciones son lanzadas.
Veamos una ejecución correcta como en el siguiente script:
my Str $nombre;
$nombre = "Juana";
say "Hola " ~ $nombre;
say "¿Qué haces hoy?"
Salida
Hola Juana
¿Qué haces hoy?
Ahora veamos un script que lanza una excepción:
my Str $nombre;
$nombre = 123;
say "Hola " ~ $nombre;
say "¿Qué haces hoy?"
Salida
Type check failed in assignment to $nombre; expected Str but got Int
in block <unit> at exceptions.p6 line 2
Debes tener en cuenta que cuando se produce un error (en este caso, debido a la asignación de un número a una variable de texto) el programa se interrumpirá y no evaluará cualquier otra línea de código.
El Control de excepciones se produce cuando se lanza una excepción y es capturada de forma que el script continúa su ejecución.
my Str $nombre;
try {
$nombre = 123;
say "Hola " ~ $nombre;
CATCH {
default {
say "¿Puedes decirme tu nombre de nuevo? No podemos encontrarlo en el registro.";
}
}
}
say "¿Qué haces hoy?";
Salida
¿Puedes decirme tu nombre de nuevo? No podemos encontrarlo en el registro.
¿Qué haces hoy?
El Control de excepciones se realiza utilizando un bloque try-catch
.
try {
# código
# si algo va mal, el script saltará al bloque CATCH
# si todo es correcto, el script ignorará el bloque CATCH
CATCH {
default {
# aquí se ejecuta código si se lanza una excepción
}
}
}
El bloque CATCH
puede definirse igual que el bloque given
.
Esto significa que podemos capturar y controlar distintos tipos de excepciones.
try {
# código
# si algo va mal, el script saltará al bloque CATCH
# si todo es correcto, el script ignorará el bloque CATCH
CATCH {
when X::AdHoc { # hace algo si se lanza una excepción de tipo X::AdHoc }
when X::IO { # hace algo si se lanza una excepción de tipo X::IO }
when X::OS { # hace algo si se lanza una excepción de tipo X::OS }
default { # hace algo si se lanza una excepción y no está contemplada en los tipos anteriores }
}
}
10.2. Lanzando Excepciones
Perl 6 también te permite lanzar excepciones de forma explícita.
Se pueden lanzar dos tipos de excepciones:
-
Excepciones ad-hoc
-
Excepciones por tipo
my Int $edad = 21;
die "¡Error!";
my Int $edad = 21;
X::AdHoc.new(payload => '¡Error!').throw;
Las excepciones ad-hoc se lanzan utilizando la subrutina die
, seguida del mensaje describiendo la excepción.
Las excepciones por tipo son objetos, y como vemos en el ejemplo anterior utilizan el constructor .new()
.
Todas las excepciones por tipo pertenecen a la clase X
. Estos son algunos ejemplos:
X::AdHoc
es el tipo de excepción más simple
X::IO
errores relacionados con operaciones de E/S
X::OS
errores relacionados con el Sistema Operativo
X::Str::Numeric
errores relacionados con la conversión de una cadena de texto a un valor numérico
Tienes una lista completa de tipos de excepciones y sus métodos asociados en https://docs.perl6.org/type-exceptions.html |
11. Expresiones Regulares
Una expresión regular, o regex es una secuencia de caracteres que se utiliza para encontrar un patrón.
Piensa en ello como un patrón.
if 'iluminación' ~~ m/ ilumina / {
say "iluminación contiene la palabra ilumina";
}
En este ejemplo, el operador inteligente de coincidencia ~~
sirve para comprobar si el texto (iluminación) contiene la palabra (ilumina).
"Iluminación" se compara con la regex m/ ilumina /
11.1. Definición de Regex
Una expresión regular puede definirse así:
-
/ilumina/
-
m/ilumina/
-
rx/ilumina/
A menos que se indique de forma explícita, el espacio en blanco es ignorado, da igual m/ilumina/
que m/ ilumina /
.
11.2. Búsqueda de caracteres
Los caracteres alfanuméricos y el guión bajo _
se escriben tal cual.
El resto de caracteres deben ser escapados utilizando la barra invertida o backslash o ir entre comillas.
if 'Temperatura: 13' ~~ m/ \: / {
say "El texto contiene el caracter dos puntos :";
}
if 'Edad = 13' ~~ m/ '=' / {
say "El texto contiene el caracter igual = ";
}
if 'nombre@empresa.com' ~~ m/ "@" / {
say "Dirección de mail válida porque contiene el caracter @";
}
11.3. Categorías de caracteres
Los caracteres se pueden clasificar en categorías y podemos realizar comparaciones con ellas.
También podemos comparar la inversa de la categoría (todo menos ella):
Categoría |
Regex |
Inversa |
Regex |
Caracter de palabra (letra, dígito o guión bajo) |
\w |
Cualquier caracter menos un caracter de palabra |
\W |
Dígito |
\d |
Cualquier caracter menos un dígito |
\D |
Espacio en blanco |
\s |
Cualquier caracter menos un espacio en blanco |
\S |
Espacio en blanco horizontal |
\h |
Cualquier caracter menos un caracter en blanco horizontal |
\H |
Espacio en blanco vertical |
\v |
Cualquier caracter menos un caracter en blanco horizontal |
\V |
Tabulador |
\t |
Cualquier caracter menos el tabulador |
\T |
Línea nueva |
\n |
Cualquier caracter menos una línea nueva |
\N |
if "Juan123" ~~ / \d / {
say "Nombre no válido, no se permiten números";
} else {
say "Nombre válido"
}
if "Juan-Dios" ~~ / \s / {
say "El texto contiene un espacio en blanco";
} else {
say "El texto no contiene un espacio en blanco"
}
11.4. Propiedades Unicode
Lo normal es comparar categorías de caracteres como hemos visto.
Dicho esto, podemos tener un enfoque más sistemático utilizando propiedades Unicode, de forma que puedas realizar coincidencias con categorías de caracteres dentro y fuera del estandar ASCII.
Las propiedades Unicode se indican entre <: >
if "Números Devanagari १२३" ~~ / <:N> / {
say "Contiene un número";
} else {
say "No contiene un número"
}
if "Привет, Иван." ~~ / <:Lu> / {
say "Contiene una letra en mayúsculas";
} else {
say "No contiene una letra en mayúsculas"
}
if "Juan-Dios" ~~ / <:Pd> / {
say "Contiene un guión";
} else {
say "No contiene un guión"
}
11.5. Comodines
En una regex también se pueden utilizar comodines.
El punto .
significa cualquier caracter.
if 'abc' ~~ m/ a.c / {
say "Coincide";
}
if 'a2c' ~~ m/ a.c / {
say "Coincide";
}
if 'ac' ~~ m/ a.c / {
say "Coincide";
} else {
say "No coincide";
}
11.6. Cuantificadores
Los cuantificadores van después de un caracter y especifican cuantas veces se repite éste.
El interrogante ?
significa que se repite una vez o ninguna.
if 'ac' ~~ m/ a?c / {
say "Coincide";
} else {
say "No coincide";
}
if 'c' ~~ m/ a?c / {
say "Coincide";
} else {
say "No coincide";
}
El asterisco *
significa que se repite una vez o más de una vez o ninguna.
if 'az' ~~ m/ a*z / {
say "Coincide";
} else {
say "No coincide";
if 'aaz' ~~ m/ a*z / {
say "Coincide";
} else {
say "No coincide";
}
if 'aaaaaaaaaaz' ~~ m/ a*z / {
say "Coincide";
} else {
say "No coincide";
}
if 'z' ~~ m/ a*z / {
say "Coincide";
} else {
say "No coincide";
}
El símbolo +
significa que se repite al menos una vez.
if 'az' ~~ m/ a+z / {
say "Coincide";
} else {
say "No coincide";
}
if 'aaz' ~~ m/ a+z / {
say "Coincide";
} else {
say "No coincide";
}
if 'aaaaaaaaaaz' ~~ m/ a+z / {
say "Coincide";
} else {
say "No coincide";
}
if 'z' ~~ m/ a+z / {
say "Coincide";
} else {
say "No coincide";
}
11.7. Extracción de resultados
Cuando se encuentra el patrón buscado, el resultado se guarda en la variable especial $/
if 'Rakudo es el compilador de Perl 6' ~~ m/:s Perl 6/ {
say "El resultado es: " ~ $/;
say "El texto antes del resultado es: " ~ $/.prematch;
say "El texto después del resultado es: " ~ $/.postmatch;
say "La posición de comienzo del resultado es: " ~ $/.from;
say "La posición final del resultado es: " ~ $/.to;
}
El resultado es: Perl 6
El texto antes del resultado es: Rakudo es el compilador de
El texto después del resultado es:
La posición inicial del resultado es: 27
La posición final del resultado es: 33
$/
devuelve un Objeto de Coincidencia (el texto encontrado o resultado de la regex)
El Objeto de Coincidencia tiene los siguientes métodos:
.prematch
devuelve el texto que hay antes del resultado.
.postmatch
devuelve el texto que hay después del resultado.
.from
devuelve la posición inicial del resultado.
.to
devuelve la posición final del resultado.
Por defecto, el espacio en blanco en una regex se ignora. Si queremos tener en cuenta los espacios en blanco en una regex, tenemos que hacerlo de forma explícita. El parámetro :s en la regex m/:s Perl 6/ hace que la regex tenga en cuenta los espacios en blanco.Otra forma de hacerlo sería así: m/ Perl\s6 / donde \s representa el espacio en blanco.Si la regex contiene más de un espacio en blanco, es mejor utilizar :s que utilizar \s para cada espacio en blanco.
|
11.8. Ejemplo
Vamos a comprobar si una dirección de email es correcta o no.
Para este ejemplo asumiremos que una dirección de email correcta tiene este formato:
nombre [punto] apellido [arroba] compañía [punto] (com/org/net)
La regex que utilizaremos en este ejemplo para validar una dirección de email no es muy precisa, y como su propósito es demostrar el funcionamiento de las regex en Perl 6, conviene no utilizarla en producción. |
my $email = 'juan.dios@perl6.org';
my $regex = / <:L>+\.<:L>+\@<:L+:N>+\.<:L>+ /;
if $email ~~ $regex {
say $/ ~ " es un email válido";
} else {
say "No es una email válido";
}
juan.dios@perl6.org es un email válido
<:L>
coincide con una letra
<:L>+
coincide con una letra o más
\.
coincide con un caracter de [punto]
\@
coincide con un caracter de [arroba]
<:L+:N>
coincide con una letra o más de una y un número
<:L+:N>+
coincide con una o más (una o más letras y un número)
La regex se puede descomponer así:
-
nombre
<:L>+
-
[punto]
\.
-
apellido
<:L>+
-
[arroba]
\@
-
nombre de la compañía
<:L+:N>+
-
[punto]
\.
-
com/org/net
<:L>+
my $email = 'juan.dios@perl6.org';
my regex varias-letras { <:L>+ };
my regex punto { \. };
my regex arroba { \@ };
my regex varias-letras-numeros { <:L+:N>+ };
if $email ~~ / <varias-letras> <punto> <varias-letras> <arroba> <varias-letras-numeros> <punto> <varias-letras> / {
say $/ ~ " es un email válido";
} else {
say "No es una email válido";
}
Una regex con nombre se define de la siguiente forma: my regex nombre-regex { definición de la regex }
Para utilizar una regex, la invocamos con su nombre de esta forma: <nombre-regex>
En https://docs.perl6.org/language/regexes tienes más información sobre regex. |
12. Módulos en Perl 6
Perl 6 es un lenguaje de programación de propósito general. Puede utilizarse para llevar a cabo multitud de tareas, incluyendo: manipulación de texto, gráficos, web, bases de datos, protocolos de red, etc.
La reutilización es un concepto muy importante para que los programadores no tengan que reinventar la rueda cada vez que quieran llevar a cabo una nueva tarea.
Perl 6 permite la creación y redistribución de módulos. Cada módulo es un paquete de funcionalidades que, una vez instalado, se puede reutilizar.
Zef es el gestor de módulos que incorpora Rakudo.
Para instalar un módulo concreto, utiliza el siguiente comando en el terminal:
zef install "nombre del módulo"
Puedes encontrar el directorio de módulos de Perl 6 en: https://modules.perl6.org/ |
12.1. Utilizando Módulos
MD5 es una función de cifrado de tipo hash que produce un valor hash de 128-bit.
MD5 tiene muchas aplicaciones, incluyendo el cifrado de contraseñas guardadas en una base de datos.
Cuando se registra un nuevo usuario sus credenciales no se guardan en texto plano, se guardan cifradas, de forma que si un atacante compromete la base de datos, este atacante no podría conocer las contraseñas.
Por suerte, no necesitas implementar el algoritmo MD5, pues ya lo implementa un módulo de Perl 6.
Vamos a instalarlo:
zef install Digest::MD5
Ahora, ejecutemos el siguiente script:
use Digest::MD5;
my $contraseña = "contraseña123";
my $contraseña-cifrada = Digest::MD5.new.md5_hex($contraseña);
say $contraseña-cifrada;
Para utilizar la función md5_hex()
que produce el cifrado necesitaremos antes cargar el módulo requerido.
La palabra clave use
carga el módulo para después utilizarlo en el script.
En la práctica el cifrado MD5 no es suficientemente seguro pues es vulnerable a ataques de diccionario. Debería combinarse con sal https://es.wikipedia.org/wiki/Sal_(criptografía). |
13. Unicode
Unicode es un estándar para codificar y representar texto en la mayoría de los sistemas de escritura del mundo.
UTF-8 es una codificación de caracteres que puede codificar todos los caracteres o números de código en Unicode.
Los caracteres se definen con un:
Grafema: Representación visual.
Número de código: Un número asignado a un caracter.
13.1. Utilizando Unicode
say "a";
say "\x0061";
say "\c[LATIN SMALL LETTER A]";
Las 3 líneas anteriores muestran formas distintas de construir un caracter:
-
Escribir el caracter directamente (grafema)
-
Utilizar
\x
y el número de código -
Utilizar
\c
y el nombre del número de código
say "☺";
say "\x263a";
say "\c[WHITE SMILING FACE]";
say "á";
say "\x00e1";
say "\x0061\x0301";
say "\c[LATIN SMALL LETTER A WITH ACUTE]";
La letra á
puede escribirse:
-
utilizando su número de código único
\x00e1
-
o combinando sus números de código de
a
y la tilde\x0061\x0301
say "á".NFC;
say "á".NFD;
say "á".uniname;
Salida
NFC:0x<00e1>
NFD:0x<0061 0301>
LATIN SMALL LETTER A WITH ACUTE
NFC
devuelve el número de código único.
NFD
descompone el caracter y devuelve el número de código de cada parte.
uniname
devuelve el nombre del número de código.
my $Δ = 1;
$Δ++;
say $Δ;
my $var = 2 + ⅒;
say $var;
14. Paralelismo, Concurrencia y Asincronía
14.1. Paralelismo
En condiciones normales todas las tareas de un programa se ejecutan de forma secuencial.
Esto no suele ser un problema, a menos que tarde demasiado tiempo.
Perl 6 te permite hacer cosas en paralelo.
Llegados aquí, es importante saber que el paralelismo puede tener dos significados:
-
Paralelismo de Tareas: Dos (o más) expresiones independientes ejecutándose en paralelo.
-
Paralelismo de Datos: Una única expresión iterando en una lista de elementos en paralelo.
Comencemos con la última.
14.1.1. Paralelismo de Datos
my @array = (0..50000); # Creación del array
my @resultado = @array.map({ is-prime $_ }); # Llama a is-prime por cada elemento del array
say now - INIT now; # Visualiza el tiempo que toma el script hasta finalizar
Hacemos solo una operación @array.map({ is-prime $_ })
La subrutina is-prime
es llamada por cada elemento del array de forma secuencial:
is-prime @array[0]
después is-prime @array[1]
después is-prime @array[2]
etc.
is-prime
en múltiples elementos del array al mismo tiempo:my @array = (0..50000); # Creación del array
my @resultado = @array.race.map({ is-prime $_ }); # Llama a is-prime por cada elemento del array
say now - INIT now; # Visualiza el tiempo que toma el script hasta finalizar
Fíjate que la expresión utiliza race
.
Este método permite la iteración en paralelo de los elementos del array.
Después de ejecutar ambos ejemplos (con y sin race
), compara los tiempos consumidos de ambos scripts.
race
hyper
Si ejecutas ambos ejemplos verás que uno muestra los resultados ordenados y el otro no. |
14.1.2. Paralelismo de Tareas
my @array1 = (0..49999);
my @array2 = (2..50001);
my @resultado1 = @array1.map( {is-prime($_ + 1)} );
my @resultado2 = @array2.map( {is-prime($_ - 1)} );
say @resultado1 == @resultado2;
say now - INIT now;
-
Definimos 2 arrays
-
Realizamos una operación con cada array y guardamos los resultados
-
Y comprobamos si ambos resultados son iguales
El script espera mientras realiza @array1.map( {is-prime($_ + 1)} )
hasta finalizar
y después realiza @array2.map( {is-prime($_ - 1)} )
Ambas operaciones realizadas en cada array son independientes.
my @array1 = (0..49999);
my @array2 = (2..50001);
my $promesa1 = start @array1.map( {is-prime($_ + 1)} ).eager;
my $promesa2 = start @array2.map( {is-prime($_ - 1)} ).eager;
my @resultado1 = await $promesa1;
my @resultado2 = await $promesa2;
say @resultado1 eqv @resultado2;
say now - INIT now;
La subrutina start
evalúa el código y devuelve un objeto de tipo promesa o una promesa (promise).
Si el código se evalúa correctamente, la promesa se cumple (kept).
Si el código lanza una excepción, la promesa se rompe (broken).
La subrutina await
espera a una promesa.
Si esta se cumple devuelve los valores.
Si esta se rompe devuelve la excepción.
Comprueba lo que tarda cada script en completarse.
El paralelismo siempre añade una sobrecarga multihilo. Si esta sobrecarga no se compensa aumentando la velocidad de cómputo, el script tardará más en completarse. Debido a esto, el uso de race , hyper , start y await en scripts muy simples podría ralentizarlos.
|
14.2. Concurrencia y Asincronía
En https://docs.perl6.org/language/concurrency tienes más información sobre Concurrencia y Programación Asíncrona. |
15. Interfaz de Llamadas Nativas
Perl 6 nos permite utilizar librerías de C mediante la Interfaz de Llamadas Nativas (NativeCall).
NativeCall
es un módulo estandar que viene incluido con Perl 6 y ofrece un conjunto de funcionalidades para facilitar el trabajo con la interfaz entre Perl 6 y C.
15.1. Llamada a una función
Consideremos un programa en C con una función llamada holadesdec
.
Esta función visualiza Hola desde C
en el terminal. No acepta argumentos ni devuelve ningún valor.
#include <stdio.h>
void holadesdec () {
printf("Hola desde C\n");
}
Dependiendo de tu sistema operativo, compila el código de C anterior en una librería de C.
gcc -c -fpic ncitest.c
gcc -shared -o libncitest.so ncitest.o
gcc -c ncitest.c
gcc -shared -o ncitest.dll ncitest.o
gcc -dynamiclib -o libncitest.dylib ncitest.c
En la misma carpeta donde has compilado la librería en C, crea un nuevo archivo de Perl 6 que contenga el siguiente código y ejecútalo.
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub holadesdec() is native(LIBPATH) { * }
holadesdec();
Lo primero es declarar que vamos a utilizar el módulo NativeCall
.
Después creamos la constante LIBPATH que contiene la ruta de la librería de C.
Como puedes ver, $*CWD
contiene la ruta de la carpeta actual.
Después creamos una subrutina de Perl 6 denominada holadesdec()
que se corresponde y tiene el mismo nombre que la función que reside en la librería de C, la cual se encuentra en LIBPATH
.
Todo esto se hace utilizando is native
.
Por último realizamos la llamada a la subrutina de Perl 6.
En esencia, todo se reduce a declarar una subrutina con is native
y el nombre de la biblioteca de C.
15.2. Cambiando el nombre a una función
Hemos visto cómo realizar una llamada a una función simple de C mediante una subrutina de Perl 6 con el mismo nombre y utilizando is native
.
Es posible que en algunos casos necesitemos que el nombre de la función de Perl 6 sea distinto al de la función en C.
Para hacerlo utilizamos is symbol
.
Vamos a modificar el script de Perl 6 que hemos visto de forma que la función de Perl 6 se llame hola
en lugar de holadesdec
.
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hola() is native(LIBPATH) is symbol('holadesdec') { * }
hola();
En caso de que la subrutina de Perl 6 tenga un nombre distinto al de la función de C correspondiente, utilizamos is symbol
con el nombre de la función de C original.
15.3. Pasando Argumentos
Vamos a modificar y compilar la librería de C y el script de Perl 6 de forma que acepten, en C un argumento de tipo char*
y el mismo argumento en Perl 6 pero de tipo Str
.
#include <stdio.h>
void holadesdec (char* nombre) {
printf("¡Hola, %s! ¡Esto es C!\n", nombre);
}
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub hola(Str) is native(LIBPATH) is symbol('holadesdec') { * }
hola('Juana');
15.4. Valores de retorno
Vamos a repetir el proceso una vez más para crear una calculadora simple que toma dos enteros, los suma, devuelve el resultado y lo visualiza.
Compilamos la librería de C y ejecutamos el script de Perl 6.
int suma (int a, int b) {
return (a + b);
}
use NativeCall;
constant LIBPATH = "$*CWD/ncitest";
sub suma(int32,int32) returns int32 is native(LIBPATH) { * }
say suma(2,3);
Como puedes ver, ambas funciones aceptan dos enteros y devuelven uno (int
en C y int32
en Perl 6).
15.5. Tipos
Te preguntarás por qué en el script de Perl 6 utilizamos int32
en lugar de int
.
En Perl 6 no pueden utilizarse algunos tipos como Int
, Rat
, etc. para pasarlos y recibirlos como valores de una función de C.
En Perl 6 hay que utilizar los mismos tipos que en C.
Por fortuna, Perl 6 proporciona muchos tipos que se corresponden con los de C.
Tipo de C | Tipo de Perl 6 |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Arrays: Por ejemplo |
|
En https://docs.perl6.org/language/nativecall tienes más información sobre la Interfaz de Llamadas Nativas. |
16. La Comunidad
-
En el canal de IRC #perl6 encontrarás el lugar principal para hablar de Perl 6. Consulta: https://perl6.org/community/irc si tienes dudas.
-
Resumen de los cambios semanales en Perl 6.
-
Mantente al día con los blogs que tratan de Perl 6 en pl6anet.
-
Suscríbete en el apartado de Perl6 de Reddit clicando en /r/perl6.
-
Alertas de los desarrolladores de Perl 6, para mantenerse al día con los cambios importantes P6lert