2010-10-13 2 views

Antwort

21

Es gibt Array Scheiben jene Elemente des ursprünglichen Arrays extrahieren:

my @slice = @array[1,42,23,0]; 

Es gibt eine Möglichkeit, Listen zwischen $ x und $ y zu generieren:

my @list = $x .. $y 

Es gibt eine Möglichkeit, Erstellen neuer Listen aus Listen:

my @new = map { $_ * 2 } @list; 

Und ther e ist eine Möglichkeit, die Länge eines Arrays zu erhalten: zusammen

my $len = $#array; 

Put:

my @even_indexed_elements = @array[map { $_ * 2 } 0 .. int($#array/2)]; 

Zugegeben, nicht ganz so schön wie der Python-Äquivalent, aber es macht die gleiche Arbeit, und Sie können von Natürlich stellen Sie das in eine Subroutine, wenn Sie es viel benutzen und sich vor etwas Schreiben bewahren wollen.

Auch gibt es wahrscheinlich etwas, das würde es auf natürlichere Weise in List::AllUtils Schreiben ermöglichen.

+0

Wenn Sie ein leeres Array haben, fügt diese Methode ein (leeres) Element hinzu (so dass das neue Array nicht mehr die Größe 0 hat). – insaner

21

Ein Perl-Array-Slice ist die @ vor dem Array-Namen, dann die Liste des Indizes Sie wollen:

@array[@indices]; 

Es gibt keine eingebaute Syntax-Multiples zu wählen, aber es ist nicht so schwer. Verwenden Sie grep() eine Liste von Indizes zu erzeugen, die Sie wollen:

my @array = qw(zero one two three four five six); 
my @evens = @array[ grep { ! ($_ % 2) } 0 .. $#array ]; 

Wenn Sie PDL verwenden, gibt es viele schöne Schnittoptionen.

+0

! höhere Priorität als% – ysth

+0

Ja, dieser Fehler schlich sich dort hinein. Allerdings sind Sie reppped genug, um diese Art von Dingen zu beheben :) –

+0

Ich würde lieber lassen Sie wählen, ob() oder == 0 zu verwenden. – ysth

9

Ich werde dies in einem zweistufigen Verfahren: zunächst die gewünschten Indizes erzeugen, und dann eine Scheibe Operation verwenden, sie zu extrahieren:

@indices = map { $_ * 2 } (0 .. int($#array/2)); 
my @extracted = @array[@indices]; 

Schritt-für-Schritt, das ist:

  • erzeugen eine Liste von ganzen Zahlen von 0 bis zum letzten Elemente des durch zwei
  • unterteilt Array multiplizieren jede ganze Zahl von zwei: wir jetzt von Null auf den Index des letzten Elements gerade Nummern haben
7

Perl 6 wird Dinge dramatisch verbessern, aber (bis jetzt?) Perl 5 hat ziemlich begrenzte Slicing-Fähigkeit: Sie müssen explizit die gewünschten Indizes angeben, und es kann nicht offen sein.

So würden Sie tun müssen:

@ar = ("zero", "one", "two", "three", "four", "five", "six"); 
print @ar[ grep $_ % 2 == 0, 0..$#ar ] 
11

ich das Modul List::Gen auf CPAN geschrieben haben, die eine alternative Möglichkeit bietet, dies zu tun:

use List::Gen qw/by/; 

my @array = qw/zero one two three four five six/; 

my @slice = map {$$_[0]} by 2 => @array; 

by Partitionen @array in Gruppen von zwei Elemente und gibt ein Array von Array-Referenzen zurück. map erhält dann diese Liste, so dass jeder $_ in der Karte eine Array-Referenz sein wird. $$_[0] (was auch geschrieben werden könnte $_->[0]) dann ergreift das erste Element jeder Gruppe, die by erstellt.

Oder mit der mapn Funktion, die by intern verwendet:

use List::Gen qw/mapn/; 

my @slice = mapn {$_[0]} 2 => @array; 

Oder, wenn Ihre Quelle Liste ist riesig und man kann nur noch bestimmte Elemente, können Sie List::Gen ‚s faul Listen verwenden:

use List::Gen qw/by gen/; 

my $slicer = gen {$$_[0]} by 2 => @array; 

$slicer ist jetzt eine faule Liste (ein Array ref), die es der Scheiben auf Nachfrage generieren, ohne etwas zu verarbeiten, die Sie nicht gebeten haben. $slicer verfügt auch über eine Reihe von Accessor-Methoden, wenn Sie sie nicht als Array-Referenz verwenden möchten.

4

Wenn Sie über den Auftrag nicht kümmern, und wenn die ungeraden Elemente der Liste eindeutig sind, können Sie kurz das Array auf einen Hash konvertieren und nehmen die values:

@even_elements = values %{{@array}}; 
@odd_elements = keys %{{@array}}; 

(No Dies ist keine ernsthafte Antwort)

+0

TMTOWTDI! * Füllerzeichen * –

5

Eine Möglichkeit, dies zu machen schöner ist es in etwas wie autobox wickeln.

Zum Beispiel mit autobox::Core:

use autobox::Core; 
my @ar = qw/zero one two three four five six/; 

# you could do this 
@ar->slice_while(sub{ not $_ % 2 }); 

# and this 
@ar->slice_by(2); 

# or even this 
@ar->evens; 

Dies ist, wie Sie diese autobox Methoden definieren:

sub autobox::Core::ARRAY::slice_while { 
    my ($self, $code) = @_; 
    my @array; 

    for (my $i = 0; $i <= $#{ $self }; $i++) { 
     local $_ = $i; 
     push @array, $self->[ $i ] if $code->(); 
    } 

    return wantarray ? @array : \@array; 
} 

sub autobox::Core::ARRAY::slice_by { 
    my ($self, $by) = @_; 
    my @array = @$self[ map { $_ * $by } 0 .. int($#{$self}/$by)]; 
    return wantarray ? @array : \@array; 
} 

sub autobox::Core::ARRAY::evens { 
    my $self = shift; 
    my @array = $self->slice_by(2); 
    return wantarray ? @array : \@array; 
} 

/I3az/

+0

Ich fing an, an einem Unmesh zu arbeiten, der etwas Ähnliches tat, aber dann ging ich weg, um etwas anderes zu tun. :) –

+0

Ich * oohed und aahed * für ein paar Stunden, bevor ich sagte * sod it * und entschied, dass eine "Autobox" -Lösung wäre schön zu sehen :) – draegtun

+1

++ für alle Dinge ['Autobox'] (http: //metacpan.org/pod/autobox::Core) .. besonders 'autobox :: Core'. –

2

Ein weiterer Weggrep unter Verwendung wäre:

my @array = qw(zero one two three four five six); 

print map { "$_ " } @array[grep { !($_ & 1) } 0 .. $#array]; #even 
Output:zero two four six 

print map { "$_ " } @array[grep { ($_ & 1) } 0 .. $#array]; #odd 
Output:one three five 
+0

++ für Bitmasken :-) –

1

Wenn es Ihnen nichts ausmacht, eine obskure Funktion von $ | zu verwenden Sie können dies tun:

{ 
    local $|; # don't mess with global $| 
    @ar = ("zero", "one", "two", "three", "four", "five", "six"); 
    $| = 0; 
    @even = grep --$|, @ar; 
    $| = 1; 
    @odd = grep --$|, @ar; 
    print "even: @even\\n"; 
    # even: zero two four six 
    print "odd: @odd\\n"; 
    # odd: one three five 
} 

oder als 1-Liner:

{ local $|=0; @even = grep --$|, @ar; } 

Grundsätzlich - $ | Flip Flops zwischen einem 0 und 1 Wert (trotz der - die normalerweise einen numerischen Wert dekrementiert), so grep sieht jedes Mal einen "wahren" Wert und verursacht somit, dass es jedes andere Element zurückgibt, beginnend mit dem Anfangswert von $ |.Beachten Sie, dass Sie mit 0 oder 1 beginnen müssen, nicht mit einem beliebigen Index.

+0

Wäre es nicht einfacher {my $ f = 0; @even = grep {++ $ f% 2} @ar; } –

0

Hier ist die einfachste Code ohne Index-Arrays zu erstellen:

sub even { my $f=0; return grep {++$f%2} @_; } 
sub odd { my $f=1; return grep {++$f%2} @_; } 
Verwandte Themen