2010-07-15 14 views
5

Ho bisogno di camminare su un array e di eliminare gli elementi in modo condizionale, in Perl. Conosco lo slice, ma non sono sicuro di come utilizzarlo in un contesto foreach.Perl equivalente di Ruby's `reject!`?

In Ruby, c'è reject!:

foo = [2, 3, 6, 7] 
foo.reject! { |x| x > 3 } 
p foo # outputs "[2, 3]" 

Esiste un equivalente Perl?

risposta

22
@foo = (2, 3, 6, 7); 
@foo = grep { $_ <= 3 } @foo; 
print @foo; # 23 
+0

Woah, impressionante. Pensavo che stavo facendo una domanda completa per Perl per principianti, ma la tua risposta è la tua terza più quotata. : o –

+0

@ Shtééf: le domande per principianti sono spesso domande utili. –

+0

@knittl potrebbe espandere la tua soluzione per sostituire <= con un'espressione regolare? – Bnjmn

8

Come le altre risposte suggeriscono, grep è in genere tutto ciò che serve.

Tuttavia, è possibile con Perl prototypes codificare una funzione che, come di rubino Array#reject!:

  • accetta un blocco
  • può modificare la sua matrice di argomento in luogo

L'utilizzo è:

@foo = (2, 3, 6, 7);   # Void context - modify @foo in place 
reject { $_ > 3 } @foo;   # @foo is now (2, 3) 

@foo = (2, 3, 6, 7);   # Scalar context - modify @foo in place 
$n = reject { $_ > 3 } @foo; # @foo is now (2, 3), $n is length of modified @foo 

@foo = (2, 3, 6, 7);   # Array context - return a modified copy of @foo 
@cpy = reject { $_ > 3 } @foo; # @cpy is (2, 3), @foo is (2, 3, 6, 7) 

Implementazione:

sub reject(&\@) { 
    my ($block, $ary) = @_; 

    # Return a copy in an array context 
    return grep {! $block->() } @$ary if wantarray; 

    # Otherwise modify in place. Similar to, but not 
    # quite, how rb_ary_reject_bang() does it. 
    my $i = 0; 
    for (@$ary) { 
      next if $block->(); 
      ($ary->[$i], $_) = ($_, $ary->[$i]); # swap idiom to avoid copying 
      $i++;        # possibly huge scalar 
    } 
    $#$ary = $i - 1; # Shorten the input array 

    # We differ from Array#reject! in that we return the number of 
    # elements in the modified array, rather than an undef value if 
    # no rejections were made 
    return scalar(@$ary) if defined(wantarray); 
} 
+0

+1 Una perla Perl. – Konerak

+0

Grazie, @ Konerak. Come molti altri, tendo a sfuggire ai prototipi/alla sintassi dei blocchi, ma questo mi è sembrato intuitivo e probabilmente utile. :) – pilcrow

+0

Elenco CPAN: UtilsBy ha una funzione extract_by() che è come rifiutare sopra, ma restituisce gli elementi rifiutati (qui chiamati elementi estratti) invece di una copia di un array che hai già (e che hai chiaramente voluto lavorare con comunque, altrimenti perché preoccuparsi di modificarlo). Penso che la funzione di Leonerd sia un valore di ritorno più utile. Utilizza anche un \ @ proto. – masonk

Problemi correlati