Perl 배열을 반복하는 가장 좋은 방법


94

Perl 배열을 반복하는 데 가장 적합한 구현 (속도 및 메모리 사용 측면에서)은 무엇입니까? 더 좋은 방법이 있습니까? ( @Array유지할 필요가 없습니다).

구현 1

foreach (@Array)
{
      SubRoutine($_);
}

구현 2

while($Element=shift(@Array))
{
      SubRoutine($Element);
}

구현 3

while(scalar(@Array) !=0)
{
      $Element=shift(@Array);
      SubRoutine($Element);
}

구현 4

for my $i (0 .. $#Array)
{
      SubRoutine($Array[$i]);
}

구현 5

map { SubRoutine($_) } @Array ;

2
왜 "최고"가 될까요? 특히 우리가 서로를 어떻게 측정할지 모른다는 점을 감안할 때 (속도가 메모리 사용보다 더 중요합니까? map그리고 수용 가능한 대답입니까? 등)
Max Lybbert

2
게시 한 3 개 중 2 개는 "WTH ?!" 현명한 대안을 만들기위한 추가적인 주변 맥락이 없다면. 어쨌든이 질문은 " 두 숫자를 더하는 가장 좋은 방법은 무엇입니까? "수준에 있습니다. 대부분의 경우 한 가지 방법 만 있습니다. 그런 다음 다른 방법이 필요한 상황이 있습니다. 마감 투표.
Sinan Ünür

4
@ SinanÜnür 나는 당신의 의견에 공감 하지만 ( 두 개의 숫자를 추가하는 방법은 한 가지뿐입니다), 비유는 무시할 정도로 강하지 않습니다. 분명히 한 가지 이상의 방법이 있으며 OP는 좋은 아이디어와 그렇지 않은 것을 이해하려고합니다.
CodeClown42

2
Programming Perl의 제 3 판 24 장에는 읽기 좋은 효율성에 대한 섹션이 있습니다. 시간, 프로그래머, 관리자와 같은 다양한 유형의 효율성을 다룹니다. 이 섹션은 "시간 최적화는 때때로 공간이나 프로그래머의 효율성을 떨어 뜨릴 수 있습니다 (아래의 힌트가 상충 됨).

1
두 개의 숫자를 더하는 하나의 방법? 당신은 가산기 등 저장 캐리 내다, 캐리을 생각 .... 낮은 수준의 통화 / 구현에 보이지 않는 경우
workwise

답변:


76
  • 속도 측면에서 : # 1 및 # 4이지만 대부분의 경우 그다지 많지는 않습니다.

    확인을 위해 벤치 마크를 작성할 수 있지만 반복 작업이 Perl 대신 C에서 수행되고 배열 요소의 불필요한 복사가 발생하지 않기 때문에 # 1과 # 4가 약간 더 빠르다고 생각합니다. ( # 1의 요소에 별칭$_지정 되지만 # 2 및 # 3은 실제로 배열에서 스칼라를 복사 합니다.)

    # 5는 비슷할 수 있습니다.

  • 메모리 사용량 측면에서 : # 5를 제외하고 모두 동일합니다.

    for (@a)어레이를 평평하게하지 않도록 특수한 경우입니다. 루프는 배열의 인덱스를 반복합니다.

  • 가독성 측면에서 : # 1.

  • 유연성 측면에서 : # 1 / # 4 및 # 5.

    # 2는 거짓 요소를 지원하지 않습니다. # 2와 # 3은 파괴적입니다.


3
와우, 짧고 간단한 문장으로 트럭 정보를 추가했습니다.
jaypal singh

1
# 2는 대기열을 수행 할 때 좋습니다 (예 : 폭 우선 검색) :my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }
ikegami

구현 4는 인덱스의 중간 배열을 생성하지 않아서 많은 양의 메모리를 사용할 수 있습니까? 그렇다면 그 접근 방식을 사용해서는 안되는 것 같습니다. stackoverflow.com/questions/6440723/… rt.cpan.org/Public/Bug/Display.html?id=115863
Thorsten Schöning

@ikegami 챔피언 스타일에 충실-훌륭한 답변 :)
skeetastax

26

의 요소에만 관심이 있다면 다음을 @Array사용하십시오.

for my $el (@Array) {
# ...
}

또는

인덱스가 중요한 경우 다음을 사용하십시오.

for my $i (0 .. $#Array) {
# ...
}

또는 perl5.12.1부터 다음을 사용할 수 있습니다.

while (my ($i, $el) = each @Array) {
# ...
}

루프 본문에 요소와 색인이 모두 필요한 경우 나는 기대할 것이다 사용 each 가장 빠르지 만5.12.1 이전 버전과의 호환성을 포기하게됩니다 perl.

이러한 패턴이 아닌 다른 패턴은 특정 상황에서 적절할 수 있습니다.


each가장 느릴 것으로 예상합니다 . 별명을 제외한 나머지 작업과 목록 할당, 두 개의 스칼라 복사본 및 두 개의 스칼라 지우기를 모두 수행합니다.
ikegami

그리고 내 측정 능력을 최대한 활용하는 것이 맞습니다. 약 45 % 속도로 for반복 할 때 배열 기준 (I 액세스 수행 색인의 20 % 이상 빠른 어레이의 인덱스를 통해 반복하고, $array->[$i]체내에서)를 사용하여 이상 each과 함께 while.
Sinan Ünür

3

IMO, 구현 # 1은 일반적이며 Perl의 경우 짧고 관용적 인 것이 다른 것보다 우선합니다. 세 가지 선택에 대한 벤치 마크는 최소한 속도에 대한 통찰력을 제공 할 수 있습니다.


2

1은 배열을 그대로두고 나머지 두 개는 비워두기 때문에 2 및 3과 실질적으로 다릅니다.

# 3은 꽤 엉뚱하고 아마도 덜 효율적이라고 말하고 싶습니다.

그러면 # 1과 # 2가 남습니다. 그들은 같은 일을하지 않습니다. 그래서 하나가 다른 것보다 "더 나을"수 없습니다. 배열이 크고 보관할 필요가없는 경우 일반적으로 범위가 처리하므로 ( 참고 참조 ) 일반적으로 # 1이 여전히 가장 명확하고 간단한 방법입니다. 각 요소를 해제해도 속도가 빨라지지는 않습니다. 참조에서 배열을 해제해야하는 경우에도 그냥 가겠습니다.

undef @Array;

완료되면.

  • 참고 : 배열의 범위를 포함하는 서브 루틴은 실제로 배열을 유지하고 다음에 공간을 재사용합니다. 일반적으로 괜찮습니다 (댓글 참조).

@Array = ();기본 배열을 해제하지 않습니다. 범위를 벗어나지 않아도 그렇게 할 수 있습니다. 기본 배열을 해제하려면 undef @Array;.
ikegami

2
데모; perl -MDevel::Peek -e'my @a; Dump(\@a,1); @a=qw( a b c ); Dump(\@a,1); @a=(); Dump(\@a,1); undef @a; Dump(\@a,1);' 2>&1 | grep ARRAY
ikegami

뭐??? 나는 GC의 전체 포인트가 한 번 ref count == 0이라고 생각했고, 관련된 메모리는 재활용 가능해졌습니다.
CodeClown42

@ikegami : ()vs undef에 대한 내용을 봤지만 범위를 벗어난 경우 해당 범위의 로컬 배열에서 사용하는 메모리가 해제되지 않으면 펄이 누수 재앙이되지 않습니까? 그것은 사실 일 수 없습니다 .
CodeClown42

그들은 또한 누출되지 않습니다. 서브는 여전히 그것들을 소유하고 있으며 다음에 서브가 호출 될 때 재사용 할 것입니다. 속도에 최적화되었습니다.
ikegami

1

한 줄로 요소 또는 배열을 인쇄합니다.

(@array에 대해 $ _ 인쇄);

참고 : $ _는 내부적으로 @array in loop의 요소를 참조합니다. $ _에서 변경 한 사항은 @array에 반영됩니다. 전의.

my @array = qw( 1 2 3 );
for (@array) {
        $_ = $_ *2 ;
}
print "@array";

출력 : 24 6


0

벤치마킹하기 위해 이와 같은 질문을 결정하는 가장 좋은 방법 :

use strict;
use warnings;
use Benchmark qw(:all);

our @input_array = (0..1000);

my $a = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    foreach my $element (@array) {
       die unless $index == $element;
       $index++;
    }
};

my $b = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    while (defined(my $element = shift @array)) {
       die unless $index == $element;
       $index++;
    }
};

my $c = sub {
    my @array = @{[ @input_array ]};
    my $index = 0;
    while (scalar(@array) !=0) {
       my $element = shift(@array);
       die unless $index == $element;
       $index++;
    }
};

my $d = sub {
    my @array = @{[ @input_array ]};
    foreach my $index (0.. $#array) {
       my $element = $array[$index];
       die unless $index == $element;
    }
};

my $e = sub {
    my @array = @{[ @input_array ]};
    for (my $index = 0; $index <= $#array; $index++) {
       my $element = $array[$index];
       die unless $index == $element;
    }
};

my $f = sub {
    my @array = @{[ @input_array ]};
    while (my ($index, $element) = each @array) {
       die unless $index == $element;
    }
};

my $count;
timethese($count, {
   '1' => $a,
   '2' => $b,
   '3' => $c,
   '4' => $d,
   '5' => $e,
   '6' => $f,
});

x86_64-linux-gnu-thread-multi 용으로 빌드 된 perl 5, 버전 24, subversion 1 (v5.24.1)에서 실행

나는 얻다:

Benchmark: running 1, 2, 3, 4, 5, 6 for at least 3 CPU seconds...
         1:  3 wallclock secs ( 3.16 usr +  0.00 sys =  3.16 CPU) @ 12560.13/s (n=39690)
         2:  3 wallclock secs ( 3.18 usr +  0.00 sys =  3.18 CPU) @ 7828.30/s (n=24894)
         3:  3 wallclock secs ( 3.23 usr +  0.00 sys =  3.23 CPU) @ 6763.47/s (n=21846)
         4:  4 wallclock secs ( 3.15 usr +  0.00 sys =  3.15 CPU) @ 9596.83/s (n=30230)
         5:  4 wallclock secs ( 3.20 usr +  0.00 sys =  3.20 CPU) @ 6826.88/s (n=21846)
         6:  3 wallclock secs ( 3.12 usr +  0.00 sys =  3.12 CPU) @ 5653.53/s (n=17639)

따라서 'foreach (@Array)'는 다른 것보다 약 2 배 빠릅니다. 다른 모든 것은 매우 유사합니다.

@ikegami는 또한 속도 외에 이러한 구현에 상당한 차이가 있음을 지적합니다.


1
비교는 $index < $#array실제로해야 $index <= $#array하기 때문에 $#array배열의 길이 만의 마지막 인덱스가 없습니다.
josch
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.