Supongamos que tengo una biblioteca de utilidad (other
) que contiene una subrutina (sort_it
) que deseo utilizar para devolver datos ordenados arbitrariamente. Es probable que sea más complicado que esto, pero esto ilustra los conceptos clave:
#!/usr/local/bin/perl
use strict;
package other;
sub sort_it {
my($data, $sort_function) = @_;
return([sort $sort_function @$data]);
}
Ahora vamos a utilizar en otro paquete.
package main;
use Data::Dumper;
my($data) = [
{'animal' => 'bird', 'legs' => 2},
{'animal' => 'black widow', 'legs' => 8},
{'animal' => 'dog', 'legs' => 4},
{'animal' => 'grasshopper', 'legs' => 6},
{'animal' => 'human', 'legs' => 2},
{'animal' => 'mosquito', 'legs' => 6},
{'animal' => 'rhino', 'legs' => 4},
{'animal' => 'tarantula', 'legs' => 8},
{'animal' => 'tiger', 'legs' => 4},
],
my($sort_by_legs_then_name) = sub {
return ($a->{'legs'} <=> $b->{'legs'} ||
$a->{'animal'} cmp $b->{'animal'});
};
print Dumper(other::sort_it($data, $sort_by_legs_then_name));
Esto no funciona, debido a un problema sutil. $a
y $b
son paquete globales. Se refieren a $main::a
y $main::b
cuando se envuelve en el cierre.
Podríamos arreglar esto diciendo, en su lugar:
my($sort_by_legs_then_name) = sub {
return ($other::a->{'legs'} <=> $other::b->{'legs'} ||
$other::a->{'animal'} cmp $other::b->{'animal'});
};
Esto funciona, pero nos obliga a codificar el nombre de nuestro paquete de utilidades todas partes. Si eso cambiara, tendríamos que recordar cambiar el código , no solo la declaración use other qw(sort_it);
que probablemente esté presente en el mundo real.
Puede pensar inmediatamente que intente utilizar __PACKAGE__
. Que vientos hasta la evaluación de "principal". Lo mismo ocurre con eval("__PACKAGE__");
.
Hay un truco que funciona usando caller
:
my($sort_by_legs_then_name) = sub {
my($context) = [caller(0)]->[0];
my($a) = eval("\$$context" . "::a");
my($b) = eval("\$$context" . "::b");
return ($a->{'legs'} <=> $b->{'legs'} ||
$a->{'animal'} cmp $b->{'animal'});
};
pero esto es más bien negro-mágico. Parece que debería haber alguna mejor solución para esto. Pero todavía no lo encontré o lo calculé .
Si utiliza la persona que llama de esa manera, no se romperá apenas tanto si el paquete que define el sub y el paquete que llamar a otros :: sort_it son diferentes? – aschepler