2015-01-21 24 views
5

Sto cercando di rendere disponibile una funzione di ordinamento in uno dei miei pacchetti (orientati agli oggetti) che accetta un blocco e rende disponibili $ ae $ b come il Perl standard sort.Includi la funzione di ordinamento di perl in un oggetto

In primo luogo, una versione semplificata di quello che sto cercando di fare nel pacchetto che contiene la funzione avvolto tipo:

# 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 
} 

E poi un esempio di un cliente che passa un blocco che definisce il comparitor a correre per l'ordinamento:

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 }); 

esiste un modo per fare quello che sto cercando di fare pur essendo in grado di utilizzare Perl sort?

risposta

8

Principalmente.

Per utilizzare la sintassi di blocco parziale come subroutine, è necessario utilizzare &prototype. Generalmente si dovrebbero evitare i prototipi, ma passare una subroutine come blocco nudo è una delle poche volte accettabile. Sfortunatamente, poiché devono essere determinati e applicati in fase di compilazione, i prototipi non funzionano sui metodi. Quindi è necessario utilizzare la sintassi subroutine anonima completa, sub { ... }.

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

$a e $b sono globali del pacchetto subroutine tipo è stato dichiarato nel (diciamo questo è Some::Caller). Se eseguito all'interno della classe, sort imposterà $My::Object::a e $My::Object::b ma la subroutine cercherà $Some::Caller::a e $Some::Caller::b. Puoi ovviare al problema con il loro $a e $b$a e $b.

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 esegue una copia temporanea di un globale per la durata del blocco e questo include altre subroutine chiamate, quindi questo effettueranno l'ordinamento. Quindi, quando il metodo viene eseguito, il valore tornerà a quello che era.

Perl globali sono memorizzati in typeglobs che contengono tutte le variabili globali con lo stesso nome. *a contiene $a e @a e %a. Copiando il typeglob del chiamante, quando $My::Object::a cambia, anche il $a del chiamante cambia. Sono alias.

La sintassi *{...} consente di ottenere una variabile globale per nome utilizzando un'altra variabile o espressione. Questo è chiamato symbolic reference. Ora possiamo chiamare il numero *a del chiamante. L'uso di questa sintassi è di solito un errore, quindi devi disattivare strict altrimenti Perl non ti permetterà di farlo.

+2

Apprezzo i tuoi post su SO. –

+0

'La sintassi * {...} fa riferimento a una variabile globale con il suo nome. Sembra un modo divertente di descrivere quella sintassi, specialmente dopo aver spiegato che cos'è * typeglob *. Questa sintassi è un altro modo per scrivere un * typeglob *, dove un * typeglob * rappresenta tutte le variabili globali con il nome specificato ..... – 7stud

+0

Assegnando tutte le variabili globali 'a' del pacchetto corrente, * typeglob * '* a', per tutte le variabili globali 'a' del chiamante, il * typeglob * '* {caller. ':: a'}' fa diventare le variabili globali 'a' del pacchetto corrente come alias per le variabili globali 'a' del pacchetto chiamante .Ciò significa che quando cambi una variabile globale 'a' nel pacchetto corrente, cambia la variabile globale 'a' nel pacchetto del chiamante. * – 7stud