2010-10-13 22 views
15

Nếu tôi có một mảng myarray bằng Python, tôi có thể sử dụng các ký hiệu látCó một hoạt động Perl nhỏ gọn để cắt các phần tử thay thế khỏi một mảng không?

myarray[0::2] 

để chỉ chọn các yếu tố thậm chí lập chỉ mục. Ví dụ:

>>> ar = [ "zero", "one", "two", "three", "four", "five", "six" ] 
>>> ar [ 0 : : 2 ] 
['zero', 'two', 'four', 'six'] 

Có cơ sở tương tự trong Perl không?

Cảm ơn.

Trả lời

21

Có mảng lát:

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

Có một cách để tạo danh sách từ $ x $ và $ y:

my @list = $x .. $y 

Có một cách để tạo danh sách mới từ danh sách:

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

Và có e là một cách để có được độ dài của một mảng:

my $len = $#array; 

Đặt nhau:

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

Cấp, không hoàn toàn là tốt đẹp như những trăn tương đương, nhưng nó không được công việc như nhau, và bạn có thể của Tất nhiên đặt nó trong một chương trình con nếu bạn đang sử dụng nó rất nhiều và muốn tiết kiệm cho mình từ một số văn bản.

Cũng có thể có điều gì đó cho phép viết văn bản này theo cách tự nhiên hơn trong List::AllUtils.

+0

Chỉ cần lưu ý rằng nếu bạn có một mảng trống, phương thức này sẽ thêm một mục (trống) vào nó (sao cho mảng mới không còn kích thước 0). – insaner

21

Một Perl mảng miếng là @ ở phía trước của tên mảng, sau đó danh sách các chỉ số bạn muốn:

@array[@indices]; 

Không có một built-in cú pháp để chọn bội, nhưng nó không phải là khó khăn như vậy. Sử dụng grep() để tạo danh sách các chỉ mục mà bạn muốn:

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

Nếu bạn đang sử dụng PDL, có rất nhiều tùy chọn cắt đẹp.

+0

! ưu tiên cao hơn% – ysth

+0

Đúng, sai lầm đó đã lén lút trong đó. Tuy nhiên, bạn đang repped đủ để sửa chữa những loại của sự vật :) –

+0

Tôi muốn cho phép bạn chọn có sử dụng() hoặc == 0. – ysth

9

tôi sẽ làm điều này trong một quá trình hai bước: đầu tiên tạo ra các chỉ số mong muốn, và sau đó sử dụng một hoạt động lát để trích xuất chúng:

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

Step-by-step, thats:

  • tạo ra một danh sách các số nguyên từ 0 đến yếu tố cuối cùng của mảng chia bởi hai
  • nhân mỗi số nguyên bởi hai: Bây giờ chúng tôi có số chẵn từ số không đến chỉ số của phần tử cuối cùng
  • trích lục các yếu tố từ mảng ban đầu
7

Perl 6 sẽ cải thiện mọi thứ đáng kể, nhưng (cho đến nay?Perl 5 có khả năng cắt khá hạn chế: bạn phải chỉ định rõ ràng các chỉ mục bạn muốn và không thể mở được.

Vì vậy, bạn sẽ phải làm:

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

Tôi đã viết các module List::Gen trên CPAN cung cấp một cách khác để làm điều này:

use List::Gen qw/by/; 

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

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

by phân vùng @array thành các nhóm hai phần tử và trả về một mảng các tham chiếu mảng. map sau đó nhận danh sách này, vì vậy mỗi $_ trong bản đồ sẽ là tham chiếu mảng. $$_[0] (cũng có thể được viết $_->[0]) sau đó lấy phần tử đầu tiên của mỗi nhóm mà đã tạo ra by.

Hoặc, sử dụng mapn chức năng mà by sử dụng trong nội bộ:

use List::Gen qw/mapn/; 

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

Hoặc, nếu danh sách nguồn của bạn là rất lớn và bạn có thể chỉ cần một số yếu tố, bạn có thể sử dụng List::Gen 's danh sách lười biếng:

use List::Gen qw/by gen/; 

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

$slicer hiện là danh sách lười (một tham chiếu mảng) sẽ tạo ra các lát theo yêu cầu mà không cần xử lý bất kỳ thứ gì bạn không yêu cầu. $slicer cũng có một loạt các phương thức accessor nếu bạn không muốn sử dụng nó như là một ref mảng.

4

Nếu bạn không quan tâm đến thứ tự, và nếu các yếu tố số lẻ của danh sách là duy nhất, bạn có thể chuyển đổi chính xác của mảng với băm và lấy values:

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

(Không , đây không phải là câu trả lời nghiêm túc)

+0

TMTOWTDI! * phụ lục ký tự * –

5

Một cách để làm cho điều này là đẹp hơn là quấn nó vào một cái gì đó như autobox.

Ví dụ sử dụng 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; 

Đây là cách bạn có thể xác định những phương pháp autobox:

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

Tôi bắt đầu làm việc trên một unmesh mà đã làm một cái gì đó tương tự, nhưng sau đó tôi đã đi để làm cái gì khác. :) –

+0

I * oohed và aahed * trong một vài giờ trước khi tôi nói * sod it * và quyết định rằng một giải pháp 'autobox' sẽ rất thú vị để xem :) – draegtun

+1

++ cho tất cả mọi thứ [' autobox'] (http: //metacpan.org/pod/autobox::Core) .. đặc biệt là 'autobox :: Core'. –

2

Một cách sẽ là bằng cách sử dụng grep:

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

++ cho bitmasks :-) –

1

Nếu bạn không ngại sử dụng tính năng tối nghĩa của $ | bạn có thể làm điều này:

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

hoặc, như 1 liner:

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

Về cơ bản, - $ | flip flops giữa giá trị 0 và 1 (mặc dù - thường giảm giá trị số), vì vậy grep thấy giá trị "true" mỗi lần khác, khiến cho nó trả về mọi mục khác bắt đầu bằng giá trị ban đầu của $ |.Lưu ý rằng bạn phải bắt đầu bằng 0 hoặc 1, không phải một số chỉ số tùy ý.

+0

Sẽ không dễ dàng hơn khi thực hiện {my $ f = 0; @even = grep {++ $ f% 2} @ar; } –

0

Đây là mã đơn giản nhất mà không tạo ra bất kỳ mảng chỉ mục:

sub even { my $f=0; return grep {++$f%2} @_; } 
sub odd { my $f=1; return grep {++$f%2} @_; } 
Các vấn đề liên quan