¡Ya estamos en Twitter!

Perl en Español

  1. Home
  2. Tutoriales
  3. Foro
  4. Artículos
  5. Donativos
  6. Publicidad

Jugando con tie y hashes

por Uriel Lizama

Introducción

Es muy común que se subestime el poder que te da Perl, eso es simplemente porque no se comprende muy bien como es que funcionan ciertas funciones o simplemente porque se ignoran por completo.

En este tutorial vamos a jugar un poco con la función tie de perl y las variables de tipo hash. Para esto vamos a crear un ejemplo básico en donde podremos cambiar un poco el funcionamiento básico de nuestros hashes.

Antes de comenzar es recomendable que si no tienen mucho conocimiento de como funcionan los hases lean el siguiente tutorial:
Usando el hash

Un vistazo a tie

Usando el famoso perldoc de Perl, podremos ver que la sintáxis básica de la función tie es:

tie VARIABLE,CLASSNAME,LIST

La función tie creará un objeto nuevo sobre la variable VARIABLE la cual cumplirá con los métodos establecidos en la implementación. CLASSNAME es el nombre de la clase que describe el comportamiento de nuestro objeto.

También existe otro argumento, LIST. Aquí pondremos una lista de cualquier argumento opcional que pasaremos a la función new de nuestro método, esto puede ser TIESCALAR, TIEHANDLE, TIEARRAY, o TIEHASH, dependiendo de la variable.

Muchos piensan que la función tie sirve solamente para amarrar un hash a un archivos DBM, cuando la realidad es otra.

Por ejemplo, tie generalmente se utiliza para la construcción de objetos persistente, es decir, donde sus atributos son almacenados de manera pseudo-permanente. Otro caso es el uso de Tie::Array donde puedes amarrar un array a un archivo.

Así que básicamente cualquier tipo de dato Perl puede ser amarrado a cualquier cosa. Solamente es caso de escribir un objeto que contenga ciertos métodos pre-definidos.


Usando objetos amarrados

La distribución básica de Perl viene acompañada de módulos que hacen mímica de sus datatypes básicos.

Por ejemplo el módulo Tie::StdHash dentro de Tie::Hash, hace mímica de un hash verdadero.

Entonces podríamos amarrar un hash a este módulo de la siguiente manera:

use Tie::Hash;

my %hash;

tie %hash, 'Tie::StdHash';

Para este momento tendríamos nuestra variable %hash enlazada al módulo Tie::StdHash que tiene la implementación completa para esta variable, así que funciona con un verdadero hash. Pero lo divertido apenas comienza.

Como funciona es que debido a que tenemos amarrado el módulo Tie::StdHash al momento que usemos nuestro hash amarrado Perl llamará la funciones necesarias dentro del módulo. Por ejemplo, cuando uno crea una nueva llave, Perl llamará la función STORE, la cual se encuentra dentro de la implementación.

Para poder amarrar un objeto a un hash, necesitamos implementación a los siguientes métodos. Todos los métodos éstan en mayúsculas:

  • TIEHASH El constructor. Es llamado cuando un usuario llama la función tie. Se le pasa el nombre de la clase y la lista de párametros que fueron pasados al llamar la función tie. Debe de regresar una referencia al nuevo objeto amarrado.
     
  • FETCH Este método se llama cuando un usuario accesa un valor del hash. Se le pasa una referencia al objeto y el valor de llave que se quiere accesar. Debe de regresar el valor asociado con la llave, o undefined si la llave no es encontrada.
     
  • STORE Este método se llama cuando un usuario quiere guardar un valor asociado a una llave. Se le pasa una referencia al objeto, junto con la llave y el valor.
     
  • DELETE Este método se llama cuando un usuario llama la función delete para quitar uno de los pares llave/valor del hash amarrado. Se le pasa una referencia al objeto y la llave que el usuario quiere eliminar. Para emular el "verdadero" delete se debe de regresar el valor de la llave que se eliminó.
     
  • CLEAR Este método se llama cuando un usuario quiere limpiar un hash por completo. Se le pasa una referencia al objeto.
     
  • EXISTS Este método es llamado cuando el usuario llama la función exists para checar si una llave existe. Se le pasa una referencia al objeto y la llave a buscar. Se debe de regresar un valor verdadero si la llave es encontrada por lo contrario falso.
     
  • FIRSTKEY Este método es llamado cuando uno de los bucles de hash (keys o each) es llamado por primera vez. Se pasa una referencia al objeto y se debe de regresar la primera llaver del hash.
     
  • NEXTKEY Este método es llamado cuando un bucle es llamado. Se le pasa una referencia al objeto y el nombre de la llave procesada por última vez. Debe de regresar el valor de la siguiente llave o undef si ya no hay más llaves.
     
  • UNTIE Este método es llamado cuando se llama a la función untie. Se le pasa una referencia al objeto.
     
  • DESTROY Este método se llama cuando el tiempo de vida de una variable termina, es decir cuando su conteo de referencias llega a 0. (Cabe destacar que hay que distinguir entre el alcance de una variable y el tiempo de vida de una variable). Se le pasa una referencia al objeto.

Todos los métodos que vayas a usar deben de ser implementados para poder amarrar un hash con un objeto. Pero más adelante vamos a ver como podemos evitar esto heredando de Tie::StdHash, usando un paradigma básico de programación OO.


Nuestro hash personal

Ya que aprendimos un poco de tie y cómo es que funciona y ya que tenemos conocimiento de todos los métodos que debemos de implementar para poder amarrar un hash, es hora de ponernos manos a la obra.

Para este ejemplo, vamos a hacer un hash que siempre guarde los valores en puras minúsculas. Es un ejemplo básico pero nos podrá dar un vistazo al poder de tie.

Para empezar vamos a hacer un nuevo módulo para el objeto. He decidido ponerle "MiHashPersonal":

package MiHashPersonal;

Como solamente quiero cambiar un poco el funcionamiento del método STORE, para poder modificar la manera en que se van a guardar los valores en las llaves, no tiene caso que escriba toda la implementación del hash, para esto vamos a usar el módulo Tie::StdHash como base:

use strict;
use Tie::Hash;
use Carp;

use base('Tie::StdHash');

En la última línea le decimos a nuestro módulo que herede el comportamiento de Tie::StdHash usando el pragma base de Perl.

Ya hasta aquí podríamos amarrar nuestro módulo a un hash:

use strict;
use MiHashPersonal;


my %hash;

tie %hash, 'MiHashPersonal';

Como va a funcionar es que cuando Perl busque un método primero lo va a buscar en 'MiHashPersonal', en caso de que no lo encuentre debido a que heredamos en el módulo de Tie::StdHash entonces Perl buscará ahí el método. De esta manera podremos implementar aquellos métodos que realmente nos interesa modificar.

Ya había dicho que queremos modificar el método STORE, para ello vamos a crear una nueva función STORE en nuestro módulo:

sub STORE{

}

Como habíamos visto el método STORE recibe como argumentos una referencia al objeto amarrado, el nombre de la llave y el valor que queremos crear.

sub STORE{

my ($self, $key, $val) = @_;

}

Teniendo estos argumentos debemos de crear la nueva llave y el nuevo valor, pero antes de hacer eso vamos a convertir el valor a minúsculas usando la función lc():

sub STORE{

my ($self, $key, $val) = @_;

$self->{$key} = lc($val);

}

Ya que guardamos nuestro módulo podremos hacer algo así:

use strict;
use MiHashPersonal;

my %hash;

tie %hash, 'MiHashPersonal';

$hash{'llave1'} = "VALOR1";

print $hash{'llave1'} . "\n";

Vemos como el valor de "llave1" es "valor1". Ya tenemos nuestro propio hash personalizado que guarda todos los valores en minúsculas.


Un poco más de diversión

Vamos a jugar un poquito más con esto. Pues ahora vamos a hacer que nuestro hash aparte de que todos los valores los guarde en minúsculas, vamos a hacer que tenga llaves especiales que nunca podrán ser eliminadas.

Como va a funcionar es que cualquier llave que inicie con "i-" (de importante, jeje) nunca podrá ser eliminada.

Para ello debemos de implementar el método DELETE. Entonces creamos una función llamada DELETE en nuestro módulo "MiHashPersonal":

sub DELETE{

}

Como sabemos el método DELETE recibe una referencia al objeto amarrado y el nombre de la llave que el usuario quiere eliminar.

sub DELETE{

my ($self, $key) = @_;

}

Lo primero que vamos a checar es si la llave inicia con "i-" usando una expresión. En caso de que así sea vamos a retornar inmediatamente.

sub DELETE{

my ($self, $key) = @_;

return if $key =~ /^i-/;

}

En caso de que la llave no inicie con "i-" entonces si vamos a eliminar la llave del hash.


sub DELETE{

my ($self, $key) = @_;

return delete $self->{$key} if $key !~ /^i-/;

}

En la línea que acabamos de poner eliminamos la llave solamente en el caso de que el nombre de la llave no inicie con "i-". En caso de que no sea así, retornamos el regreso de la llamada a la función delete() que en este caso sería el valor de la llave que acaba de ser eliminada.

Listo, guardamos nuestro módulo y tenemos nuestro hash con llaves importantes que no pueden ser eliminadas:

use strict;
use MiHashPersonal;


my %hash;

tie %hash, 'MiHashPersonal';

$hash{'i-clave'} = "mi clave importante";

delete $hash{'i-clave'}; #intentamos eliminar la llave

print $hash{'i-clave'} . "\n";

Si ejecutas el código verás que la llave i-clave no fue eliminada.

Código Final

Módulo MiHashPersonal

package MiHashPersonal;

use strict;
use Tie::Hash;
use Carp;

use base('Tie::StdHash');

sub STORE{

my ($self, $key, $val) = @_;

$self->{$key} = lc($val);

}


sub DELETE{

my ($self, $key) = @_;

return delete $self->{$key} if $key !~ /^i-/;

}


1; #retorno verdadero


Script de Ejemplo

#!/usr/bin/perl

use warnings;
use strict;
use MiHashPersonal; #cargamos nuestro módulo


#creamos un nuevo hash y lo amarramos a nuestro módulo
my %hash;
tie %hash, 'MiHashPersonal';

#creamos una nueva llave
$hash{'llave1'} = "VALOR1";

print $hash{'llave1'} . "\n"; #el valor en minúsculas


#creamos una variable 'especial'
$hash{'i-clave'} = "miclave";

#intentamos eliminarla
delete $hash{'i-clave'};

#No se pueden eliminar llaves especiales
print $hash{'i-clave'} . "\n";


Agradecimientos

Quisiera agradecer a Marco A. Manzo por hacer una revisión del tutorial y haber dado recomendaciones en cambios en el código, haciendo del tutorial un tutorial mejor.


Bibliografía

perldoc tie
http://www.perldoc.com/perl5.8.4/pod/func/tie.html

Changing Hash Behaviour with tie
http://www.perl.com/pub/a/2001/09/04/tiedhash.html

Documentación Tie::Hash
http://search.cpan.org/~nwclark/perl-5.8.7/lib/Tie/Hash.pm

¿Quiéres más tutoriales como este? Escribir tutoriales toma una gran cantidad de tiempo y esfuerzo. Si este tutorial te ayudó a aprender o a solucionar algo, por favor considera dejar alguna donación en apoyo a Perl en Español.

Cliquea en el botón de abajo para dejar tu donación por medio de PayPal.

Comparte:
Categorías de Tutoriales:
En Nuestros Foros:

    Software error:

    junk after document element at line 1, column 32, byte 32 at /usr/lib64/perl5/vendor_perl/XML/Parser.pm line 187.
    

    For help, please send mail to the webmaster ([email protected]), giving this error message and the time and date of the error.

  • Entra a los foros »
Socializa:
Síguenos por Twitter

Suscríbete GRATUITAMENTE al Boletín de Perl en Español

Perl en Español es mantenido con Movable Type
Todo el contenido de Perl en Español está bajo una licencia CC:
Creative Commons License