2015-01-21 9 views
5

Ich versuche, eine Sortierfunktion in einem meiner (objektorientiert) Pakete zu machen, die einen Block akzeptiert und stellt $ a und $ b wie der Standard Perl sort.Wrap Perl Sortierfunktion in einem Objekt

Zunächst wird eine vereinfachte Version von dem, was ich versuche, im Paket zu tun, die die verpackte Sortierfunktion enthält:

# In package My::Object 
sub sort { 
    my $self = shift; 
    my $block = \&{shift @_}; 

    return sort $block @{$self->{arrayRef}}; # I want to use the passed in block with array data contained in this object 
} 

Und dann ein Beispiel für einen Client einen Block vorbei, die den Komparator definiert laufen für die Art:

my $obj = My::Object->new([3, 1, 5, 6, 2, 4]); # As an example, these values will be come arrayRef from above 
my @sortedVals = $obj->sort({ $a < $b }); 

gibt es eine Möglichkeit zu tun, was ich versuche, während sie noch in Perl zu verwenden, in der Lage zu tun sort?

Antwort

8

Meistens.

Um die Block-als-Unterroutinen-Syntax zu verwenden, müssen Sie &prototype verwenden. Im Allgemeinen sollten Sie Prototypen vermeiden, aber eine Unterroutine als bloßen Block zu übergeben ist eine der wenigen Male, die dies akzeptabel ist. Da sie zur Kompilierzeit bestimmt und angewendet werden müssen, arbeiten Prototypen leider nicht mit Methoden. Sie müssen also die vollständige Syntax der anonymen Subroutine sub { ... } verwenden.

my @sortedVals = $obj->sort(sub { $a <=> $b }); 

$a und $b sind Globals des Pakets die Sortierroutine in deklariert wurde (wir sagen, das ist Some::Caller). Wenn in Ihrer Klasse ausführt, wird eingestellt Art $My::Object::a und $My::Object::b aber das Unterprogramm wird für $Some::Caller::a und $Some::Caller::b suchen. Sie können durch Aliasing ihre $a und $b auf Ihre $a und $b dieses Problem umgehen.

sub sort { 
    my $self = shift; 
    my $block = shift; 

    no strict 'refs'; 
    local *{caller.'::a'} = *a; 
    local *{caller.'::b'} = *b; 

    return sort $block @{$self->{arrayRef}}; 
} 

local macht eine temporäre Kopie eines globalen für die Dauer des Blocks und dazu gehört auch andere Subroutinen genannt, so wird dies die Art bewirken. Dann, wenn die Methode fertig ist, wird der Wert zu dem zurückkehren, was er war.

Globals Perl werden in typeglobs gespeichert, die alle globalen Variablen mit dem gleichen Namen enthalten. *a enthält $a und @a und %a. Wenn $My::Object::a geändert wird, ändert sich die $a des Anrufers, indem der Typeglob des Anrufers kopiert wird. Sie sind Aliase.

Die *{...} Syntax können Sie auf eine globale Variable mit Namen bekommen eine andere Variable oder Ausdruck. Dies nennt man symbolic reference. Jetzt können wir die *a des Anrufers bekommen. Die Verwendung dieser Syntax ist normalerweise ein Fehler, daher müssen Sie strict ausschalten, sonst lässt Perl Sie nicht.

+2

Schätzen Sie Ihre Beiträge auf SO. –

+0

'Die * {...} -Syntax verweist auf eine globale Variable nach ihrem Namen.' Das scheint eine lustige Art zu sein, diese Syntax zu beschreiben - vor allem nach der Erklärung, was ein * typeglob * ist. Das Syntax ist ein weiterer Weg, um eine * Typeglob * zu schreiben, wobei a * Typeglob * alle globalen Variablen mit dem angegebenen Namen darstellt ..... – 7stud

+0

alle aktuellen Pakets global ‚a‘ Variablen zuweisen, die * Typeglob * '* a ', für alle globalen' a'-Variablen des Aufrufers bewirkt der * typeglob * '* {caller.' :: a'} ', dass die globalen 'a'-Variablen des aktuellen Pakets Aliase für die globalen' a'-Variablen des aufrufenden Pakets werden .Das bedeutet, wenn Sie eine globale Variable 'a' im aktuellen Paket ändern, ändert sich die globale Variable 'a' im Paket des Aufrufers. * – 7stud