1. Referències & Subrutines
Jordi Delgado
Curs BCN Perl Mongers
5 Novembre 2011
remember, remember the fifth of November...
sábado 12 de noviembre de 2011
2. Els Array i els Hash poden
contenir només escalars...
Com podem construir estructures
de dades complexes?
sábado 12 de noviembre de 2011
3. doncs com sempre... amb punters!
referències = punters
sábado 12 de noviembre de 2011
4. Operador referència:
Context escalar: referència
Context llista: llista de
referències
Una referència és un escalar
sábado 12 de noviembre de 2011
5. exemples:
my $nom = ‘anonymous’;
my $nom_ref = $nom; # referència a escalar
my @llista = qw< anon1 anon2 anon3 >;
my $llista_ref = @llista # referència a array
my $aliased_llista = $llista_ref;
my %diccionari = (
op1 => ‘fail’,
op2 => ‘success’,
op3 => ‘unknown’,
op4 => ‘unknown’
);
my $diccionari_ref = %diccionari; # referència a hash
my $aliased_diccionari = $diccionari_ref;
sábado 12 de noviembre de 2011
6. Des-referenciar: afegir un sigil més que
dependrà d’allò referenciat,
utilitzant claus envoltant la referència
my @llista = qw< anon1 anon2 anon3 >;
my $llista_ref = @llista # referència a array
my $aliased_llista = $llista_ref;
print "@{ $llista_ref } n";
my %diccionari = (
op1 => ‘fail’,
op2 => ‘success’,
op3 => ‘unknown’,
op4 => ‘unknown’
);
my $diccionari_ref = %diccionari; # referència a hash
my $aliased_diccionari = $diccionari_ref;
print keys %{ $diccionari_ref } , "n";
print values %{ $diccionari_ref } , "n";
sábado 12 de noviembre de 2011
7. Però, si el que volem des-referenciar
és només una variable escalar
no fan falta les claus {}
@{ $llista_ref } = @$llista_ref
${ $llista_ref }[0] = $$llista_ref[0]
print $$llista_ref[$_], "n" foreach (0..$#$llista_ref)
%{ $diccionari_ref } = %$diccionari_ref
${ $diccionari_ref }{'op2'} = $$diccionari_ref{'op2'}
print $$diccionari_ref{$_}, "n" foreach keys %$diccionari_ref;
farem servir les claus {} només per
desambiguar, quan calgui
sábado 12 de noviembre de 2011
8. Ara ja podem construir estructures de
dades complexes:
my %ops_from_anon1 = (
op1 => ‘fail’,
op2 => ‘success’,
op3 => ‘unknown’,
);
my %ops_from_anon2 = (
op4 => 'unknown',
);
my @primer_grup_ops = ('anon1', %ops_from_anon1);
my @segon_grup_ops = ('anon2', %ops_from_anon2);
my @ops = (@primer_grup_ops, @segon_grup_ops);
foreach my $op (@ops) {
print "Origen: $$op[0] n";
print "t Nom de la op: $_, estat: ${ $$op[1] }{$_} n"
foreach keys %{ $$op[1] };
}
sábado 12 de noviembre de 2011
9. Notació alternativa:
the dereferencing arrow
my %ops_from_anon1 = (
op1 => ‘fail’,
op2 => ‘success’,
op3 => ‘unknown’,
);
my %ops_from_anon2 = (
op4 => 'unknown',
);
my @primer_grup_ops = ('anon1', %ops_from_anon1);
my @segon_grup_ops = ('anon2', %ops_from_anon2);
my @ops = (@primer_grup_ops, @segon_grup_ops);
foreach my $op (@ops) {
print "Origen: $op->[0] n";
print "t Nom de la op: $_, estat: $op->[1]->{$_} n"
foreach keys %{$op->[1]};
}
sábado 12 de noviembre de 2011
10. Array i Hash anònims
Array: [ ... ]
Hash: { ... }
my @ops = (
['anon1', { op1 => 'fail',
! ! op2 => 'success',
! ! op3 => 'unknown', }],
['anon2', { op4 => 'unknown', }]
);
foreach my $op (@ops) {
print "Origen: $op->[0] n";
print "t Nom de la op: $_, estat: $op->[1]->{$_} n"
foreach keys %{$op->[1]};
}
sábado 12 de noviembre de 2011
11. Però s’ha d’anar amb una mica de compte:
my @llista = qw< anon1 anon2 anon3 >;
my $llista1_ref = @llista;
my $llista2_ref = @llista;
push @llista, 'anon4';
...tant @$llista1_ref com @$llista2_ref tenen un servidor nou, però...
my @llista = qw< anon1 anon2 anon3 >;
my $llista1_ref = [ @llista ];
my $llista2_ref = [ @llista ];
push @llista, 'anon4';
...ni @$llista1_ref ni @$llista2_ref tenen cap servidor nou afegit
sábado 12 de noviembre de 2011
12. Autovivificació
La idea és que si des-referenciem una variable
escalar -que conté undef- com si fos una
referència a un array, se li assignarà una
referència a un array anònim buit i es procedirà
amb l’execució del codi.
El mateix passarà amb un hash, si ens trobem una
variable de valor undef que es des-referenciada
com si fos una referència a un hash, s’assigna un
hash anònim buit i l’execució continuarà.
sábado 12 de noviembre de 2011
13. Autovivificació
my $ops;
$ops->[0]->[0] = 'anon1';
$ops->[1]->[0] = 'anon2';
$ops->[0]->[1]->{'op1'} = 'fail';
$ops->[0]->[1]->{'op2'} = 'success';
$ops->[0]->[1]->{'op3'} = 'unknown';
$ops->[1]->[1]->{'op4'} = 'unknown';
foreach my $op (@$ops) {
print "Origen: $op->[0] n";
print "t Nom de la op: $_, estat: $op->[1]->{$_} n"
foreach keys %{$op->[1]};
}
sábado 12 de noviembre de 2011
14. if the arrow ends up between "subscripty kinds of
things" such as square brackets, we can also drop
the arrow.
my $ops;
$ops->[0][0] = 'anon1';
$ops->[1][0] = 'anon2';
$ops->[0][1]{'op1'} = 'fail';
$ops->[0][1]{'op2'} = 'success';
$ops->[0][1]{'op3'} = 'unknown';
$ops->[1][1]{'op4'} = 'unknown';
foreach my $op (@$ops) {
print "Origen: $op->[0] n";
print "t Nom de la op: $_, estat: $op->[1]{$_} n"
foreach keys %{$op->[1]};
}
sábado 12 de noviembre de 2011
15. Problemes:
Aliasing
(as usual)
Comptatge de referències
(no garbage collection)
sábado 12 de noviembre de 2011
16. SUB-RU-TI-NES
(no funcions, ni procediments, ni mètodes, ni...)
sábado 12 de noviembre de 2011
17. SUB-RU-TI-NES
(no funcions, ni procediments, ni mètodes, ni...)
Spock: Cadet Kirk, you somehow managed to install and activate a subroutine in the
programming code, thereby changing the conditions of the test.
James T. Kirk: Your point being?
Admiral Richard Barnett: In academic vernacular, you cheated.
Star Trek (2009)
sábado 12 de noviembre de 2011
18. SUB-RU-TI-NES
(no funcions, ni procediments, ni mètodes, ni...)
Spock: Cadet Kirk, you somehow managed to install and activate a subroutine in the
programming code, thereby changing the conditions of the test.
James T. Kirk: Your point being?
Admiral Richard Barnett: In academic vernacular, you cheated.
Star Trek (2009)
Kim: She's not a member of the crew. She's a character on the holodeck.
Tuvok: You're in love with a computer subroutine?
Kim: That's the problem.
Tuvok: Interesting.
Star Trek Voyager, ‘Alter Ego’ S03E14 (1997)
sábado 12 de noviembre de 2011
19. SUB-RU-TI-NES
(no funcions, ni procediments, ni mètodes, ni...)
Spock: Cadet Kirk, you somehow managed to install and activate a subroutine in the
programming code, thereby changing the conditions of the test.
James T. Kirk: Your point being?
Admiral Richard Barnett: In academic vernacular, you cheated.
Star Trek (2009)
Kim: She's not a member of the crew. She's a character on the holodeck.
Tuvok: You're in love with a computer subroutine?
Kim: That's the problem.
Tuvok: Interesting.
Star Trek Voyager, ‘Alter Ego’ S03E14 (1997)
Lt. Cmdr. Data: You are much more than that, Jenna. I have written a subroutine specifically
for you - a program within the program. I have devoted a considerable share of my internal
resources to its development.
Lt. Jenna D'Sora: Data... that's the nicest thing anybody's ever said to me.
Star Trek The Next Generation, ‘In Theory’ S04E25 (1991)
sábado 12 de noviembre de 2011
20. Subrutines
Declaració:
sub nom_de_la_subrutina { . . . }
Invocació:
nom_de_la_subrutina ( ... )
# de vegades podem prescindir dels parèntesi, però millor
# que no (som moderns :-))
#
# de vegades cal posar un ampersand (&) davant del nom, però
# si no anomenem cap subrutina igual que una subrutina de perl
# no ha d’haver cap problema
sábado 12 de noviembre de 2011
21. Subrutines
Pas de paràmetres:
nom_de_la_subrutina ( ... )
# en el moment de la crida, tots els paràmetres es disposen
# en un array d’escalars (flattening)
# que es veu des de dins de la subrutina com l’array @_
exemple:
# hanoi(N, start, end, extra)
# Solve Tower of Hanoi problem for a tower of N disks,
# of which the largest is disk #N. Move the entire tower from
# peg 'start' to peg 'end', using peg 'extra' as a work space
sub hanoi {
my ($n, $start, $end, $extra) = @_;
if ($n == 1) {
print "Move disk #1 from $start to $end.n"; # Step 1
} else {
hanoi($n-1, $start, $extra, $end); # Step 2
print "Move disk #$n from $start to $end.n"; # Step 3
hanoi($n-1, $extra, $end, $start); # Step 4
} }
(from Higher-Order Perl by Mark Dominus, published by Morgan Kaufmann Publishers, Copyright 2005 by Elsevier Inc)
sábado 12 de noviembre de 2011
22. Pas de paràmetres:
nom_de_la_subrutina ( ... )
# pas de paràmetres per valor? per referència?
# En realitat @_ conté alias dels paràmetres originals, així,
# si modifiquem $_[i] estarem modificant el paràmetre i-èssim
# real. En canvi, en assignar-los, els paràmetres reals es
# copien
exemple:
sub modifica_nom1 { sub modifica_nom2 {
$_[0] = reverse($_[0]); my $temp = shift;
} $temp = reverse $temp;
}
my $nom = 'Jordi'; my $nom = 'Jordi';
modifica_nom1( $nom ); modifica_nom2( $nom );
print $nom, “n”; print $nom, “n”;
idroJ Jordi
sábado 12 de noviembre de 2011
23. Accés als paràmetres:
sub hanoi {
my ($n, $start, $end, $extra) = @_;
if ($n == 1) {
print "Move disk #1 from $start to $end.n"; # Step 1
} else ( . . . )
The dominant practice seems to be to use shift only when your
function must access a single parameter and list assignment when
accessing multiple parameters.
Què retorna una subrutina?
Allò que s’ha calculat tot just abans d’acabar
o bé
allò que és explícitament retornat amb return
sábado 12 de noviembre de 2011
24. Lexicals:
El 'my' que hem vist davant de les variables
quan les feiem servir per primera vegada li
diu a perl que aquelles variables són locals
al bloc més proper que conté el 'my'...
... i això és perque ...
totes les variables són globals, per defecte.
sábado 12 de noviembre de 2011
25. * Retorn de valors no escalars:
No hi ha cap diferència. Es poden retornar
elements que no siguin escalars exactament de
la mateixa manera. També podem retornar
referències.
* Context?
Una subrutina pot conèixer el context en el
que ha estat cridada gràcies a wantarray:
wantarray() -> undef si el context és void
wantarray() -> true si el context és llista
wantarray() -> false si el context és escalar
sábado 12 de noviembre de 2011
26. Referències a subrutines (coderefs):
my $ref = &nom_de_subrutina;
# ampersand obligatori! també, només volem el nom
...
$ref->(p1,p2,...)
# equivalent a nom_de_subrutina(p1,p2,...)
# fixem-nos en la dereferencing arrow
A partir del moment en que puc manipular
referències a funcions, ja puc emmagatzemar-les,
retornar-les, passar-les com a paràmetre... són
ciutadans de primera classe (en realitat no, però
si).
sábado 12 de noviembre de 2011
27. Subrutines anònimes
my $ref_a_subrutina = sub { . . . }
# el cos de la subrutina no té res d’especial
# el resultat és una referència a una subrutina
Closures (tancaments lexics):
sub simple_demo {
my $privatecounter = 0;
return sub { $privatecounter++ };
}
my $demo1 = simple_demo;
my $demo2 = simple_demo;
foreach (0..5) {
print $demo1->(), “n”;
print $demo2->(), “n”;
}
sábado 12 de noviembre de 2011
29. Problema: Memoització
Fer una funció memoize on donada una ref. a una funció f, retorni
una ref. a una funció g que calculi el mateix que f, però
emmagatzemi allò que calcula, de manera que no ho calculi
explícitament mai dues vegades.
Solució:
sub memoize {
my ($func) = @_;
my %cache;
return sub {
my $key = join ‘,’ , @_;
$cache{$key} = $func->(@_)
unless exists $cache{$key};
return $cache{$key};
};
}
sábado 12 de noviembre de 2011
30. Problema: map
Si llista és (l1, l2, l3,...) fer una funció map tal que
map (&f, llista) -> (f(l1), f(l2), f(l3),...)
Solució:
sub map {
my $func = shift;
my @result = ();
foreach (@_) {
push @result, $func->($_);
}
return @result;
}
sábado 12 de noviembre de 2011
31. Problema: grep
Si llista és (l1, l2, l3,...) i f és una funció amb resultat
booleà (diguem, {0,1}) fer una funció grep tal que
grep (&f, llista) -> (li, lj, lk,...)
on els li, lj, lk,... són aquells elements de la llista tals que
quan se’ls aplica f el resultat és 1.
Solució:
sub grep {
my $func = shift;
my @result = ();
foreach (@_) {
push(@result, $_) if $func->($_);
}
return @result;
}
sábado 12 de noviembre de 2011
32. Problema: inject
Si llista és (l1, l2, l3,...) fer una funció inject tal que
inject (&f, escalar, llista) ->
f(... f(f(f(escalar,l1),l2),l3) ...)
Solució:
sub inject {
my $func = shift;
my $escalar = shift;
my $result = $escalar;
foreach (@_) {
$result = $func->($result,$_);
}
return $result;
}
sábado 12 de noviembre de 2011