struct
recibe como
argumentos una lista que describe la estructura de datos
asociada con un objeto. El efecto es la generación de un método
constructor (que tiene por nombre new
) y métodos de acceso.
La siguiente sesión con el depurador muestra un ejemplo de uso:
lhp@nereida:~/LCLASS_TRAIT/myexample$ perl -MClass::Struct -wde 0 main::(-e:1): 0 DB<1> struct Foo => { a => '@', s => '$', h => '%', c => 'Tutu' } DB<2> struct Tutu => [ nombre => '$', apellidos => '@' ]Estas dos líneas crean los métodos.
La diferencia entre usar la sintáxis de llaves y la de corchetes estriba en que el objeto construido es un hash en el caso de las llaves y un array en el caso de los corchetes:
DB<32> x Tutu->new(nombre=>'Alberto', apellidos=>[qw(Gonzalez Martinez)]) 0 Tutu=ARRAY(0x8704558) 0 'Alberto' 1 ARRAY(0x85a3fe0) 0 'Gonzalez' 1 'Martinez'
Activando $Data::Dumper::Deparse
podemos ver la forma del
constructor proporcionado:
DB<23> use Data::Dumper DB<24> $Data::Dumper::Deparse = 1 DB<25> p Dumper(\&Foo::new) $VAR1 = sub { package Foo; use strict 'refs'; my($class, %init) = @_; $class = 'Foo' unless @_; my($r) = {}; if (defined $init{'c'}) { if (ref $init{'c'} eq 'HASH') { $$r{'Foo::c'} = 'Tutu'->new(%{$init{'c'};}); } elsif (UNIVERSAL::isa($init{'c'}, 'Tutu')) { $$r{'Foo::c'} = $init{'c'}; } else { croak('Initializer for c must be hash or Tutu reference'); } } croak('Initializer for h must be hash reference') if defined $init{'h'} and ref $init{'h'} ne 'HASH'; $$r{'Foo::h'} = defined $init{'h'} ? $init{'h'} : {}; croak('Initializer for a must be array reference') if defined $init{'a'} and ref $init{'a'} ne 'ARRAY'; $$r{'Foo::a'} = defined $init{'a'} ? $init{'a'} : []; $$r{'Foo::s'} = defined $init{'s'} ? $init{'s'} : undef; bless $r, $class; };
Ahora podemos construir objetos de esas clases::
DB<4> x $a = Tutu->new(nombre=>'Juan', apellidos=>[qw(Gonzalez Hernandez)]) 0 Tutu=ARRAY(0x84eb024) 0 'Juan' 1 ARRAY(0x8474e5c) 0 'Gonzalez' 1 'Hernandez' DB<5> x $b = Foo->new(a=>[1..3], s=>'hola', h=>{x=>1, y=>2}, c=>$a) 0 Foo=HASH(0x850524c) 'Foo::a' => ARRAY(0x84eb4e0) 0 1 1 2 2 3 'Foo::c' => Tutu=ARRAY(0x84eb024) 0 'Juan' 1 ARRAY(0x8474e5c) 0 'Gonzalez' 1 'Hernandez' 'Foo::h' => HASH(0x851bed0) 'x' => 1 'y' => 2 'Foo::s' => 'hola'
Los siguientes comandos muestran como acceder a los elementos de un atributo de tipo lista:
DB<6> x $b->a 0 ARRAY(0x84eb4e0) 0 1 1 2 2 3 DB<7> x $b->a(1) 0 2 DB<8> x $b->a(1,7) 0 7 DB<9> x $b->a(1) 0 7 DB<30> $b->a([9..12]) DB<31> x $b->a 0 ARRAY(0x85399f0) 0 9 1 10 2 11 3 12
Mediante Dumper
podemos ver el codigo del
método de acceso. He introducido comentarios
que explican las diferentes partes del método::
DB<26> p Dumper(\&Foo::a) $VAR1 = sub { package Foo; use strict 'refs'; my $r = shift @_; my $i; # Si no quedan args retornamos la ref al array @_ ? ($i = shift @_) : return($$r{'Foo::a'}); # Si $i es ARR y no args modificamos array if (ref $i eq 'ARRAY' and not @_) { $$r{'Foo::a'} = $i; return $r; } croak('Too many args to a') if @_ > 1; # $i definido y no es ARR y no args. Modificamos elemento @_ ? ($$r{'Foo::a'}[$i] = shift @_) : $$r{'Foo::a'}[$i]; };
Mas sencillo aún es acceder a un atributo escalar:
DB<10> x $b->s 0 'hola' DB<11> x $b->s('mundo') 0 'mundo' DB<12> x $b->s 0 'mundo'
Los siguientes comandos muestran accesos al atributo de tipo hash:
DB<13> x $b->h 0 HASH(0x851bed0) 'x' => 1 'y' => 2 DB<14> x $b->h('x') 0 1 DB<15> x $b->h(x => 3) 0 3 DB<16> x $b->h('x') 0 3
Los atributos de un objeto pueden ser de tipo objeto:
DB<17> x $b->c 0 Tutu=ARRAY(0x84eb024) 0 'Juan' 1 ARRAY(0x8474e5c) 0 'Gonzalez' 1 'Hernandez' DB<18> x $b->c->nombre 0 'Juan' DB<19> x $b->c->nombre('Pedro') 0 'Pedro' DB<20>