Is there a compact Perl operation to slice alternate elements from an array?

后端 未结 10 917
我在风中等你
我在风中等你 2020-12-15 20:37

If I have an array myarray in Python, I can use the slice notation

myarray[0::2]

to select only the even-indexed elements. For

相关标签:
10条回答
  • 2020-12-15 21:16

    If you don't mind using an obscure feature of $| you can do this:

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

    or, as a 1 liner:

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

    Basically, --$| flip flops between a 0 and 1 value (despite the -- which normally decrements a numeric value), so grep sees a "true" value every other time, thus causing it to return every other item starting with the initial value of $|. Note that you must start with 0 or 1, not some arbitrary index.

    0 讨论(0)
  • 2020-12-15 21:20

    I'll do this in a two-step process: first generate the desired indices, and then use a slice operation to extract them:

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

    Step-by-step, thats:

    • generate a list of integers from 0 to the last element of the array divided by two
    • multiply each integer by two: now we have even numbers from zero to the index of the last element
    • extract those elements from the original array
    0 讨论(0)
  • 2020-12-15 21:26

    Perl 6 will improve things dramatically, but (so far?) Perl 5 has pretty limited slicing capability: you have to explicitly specify the indexes you want, and it can't be open-ended.

    So you'd have to do:

    @ar = ( "zero", "one", "two", "three", "four", "five", "six" );
    print @ar[ grep $_ % 2 == 0, 0..$#ar ]
    
    0 讨论(0)
  • 2020-12-15 21:28

    A Perl array slice is the @ in front of the array name, then the list of indices you want:

     @array[@indices];
    

    There's not a built-in syntax to select multiples, but it's not so hard. Use grep to produce a list of indices that you want:

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

    If you are using PDL, there are lots of nice slicing options.

    0 讨论(0)
  • 2020-12-15 21:29

    Here is the simplest code without creating any index arrays:

    sub even { my $f=0; return grep {++$f%2} @_; }
    sub odd { my $f=1; return grep {++$f%2} @_; }
    
    0 讨论(0)
  • 2020-12-15 21:32

    One way to make this prettier is to wrap it in something like autobox.

    For example using 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;
    

    This is how you can define these autobox methods:

    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 讨论(0)
提交回复
热议问题