Jaka jest najlepsza implementacja (pod względem szybkości i wykorzystania pamięci) do iteracji po tablicy Perla? Czy jest lepszy sposób? ( @Array
nie trzeba ich zachowywać).
Wdrożenie 1
foreach (@Array)
{
SubRoutine($_);
}
Wdrożenie 2
while($Element=shift(@Array))
{
SubRoutine($Element);
}
Wdrożenie 3
while(scalar(@Array) !=0)
{
$Element=shift(@Array);
SubRoutine($Element);
}
Wdrożenie 4
for my $i (0 .. $#Array)
{
SubRoutine($Array[$i]);
}
Wdrożenie 5
map { SubRoutine($_) } @Array ;
map
i akceptowalna odpowiedź? Itd.)Odpowiedzi:
Pod względem szybkości: # 1 i # 4, ale w większości przypadków niewiele.
Mógłbyś napisać benchmark, aby to potwierdzić, ale podejrzewam, że okaże się, że # 1 i # 4 są nieco szybsze, ponieważ praca iteracyjna jest wykonywana w C zamiast w Perlu i nie ma niepotrzebnego kopiowania elementów tablicy. (
$_
jest aliasowany do elementu w # 1, ale # 2 i # 3 faktycznie kopiują skalary z tablicy).# 5 może być podobny.
Pod względem wykorzystania pamięci: wszystkie są takie same, z wyjątkiem # 5.
for (@a)
ma specjalną wielkość liter, aby uniknąć spłaszczenia tablicy. Pętla wykonuje iterację po indeksach tablicy.Pod względem czytelności: # 1.
Pod względem elastyczności: # 1 / # 4 i # 5.
# 2 nie obsługuje elementów, które są fałszywe. # 2 i # 3 są destrukcyjne.
źródło
my @todo = $root; while (@todo) { my $node = shift; ...; push @todo, ...; ...; }
Jeśli zależy Ci tylko na elementach
@Array
, użyj:for my $el (@Array) { # ... }
lub
Jeśli indeksy mają znaczenie, użyj:
for my $i (0 .. $#Array) { # ... }
Lub, od
perl
5.12.1, możesz użyć:while (my ($i, $el) = each @Array) { # ... }
Jeśli potrzebujesz zarówno elementu, jak i jego indeksu w treści pętli,
oczekiwałbymza pomocąeach
być najszybszym, ale wtedyzrezygnujesz z kompatybilności z poprzednimi wersjami 5.12.1perl
.W pewnych okolicznościach odpowiedni może być inny schemat niż te.
źródło
each
będzie najwolniejszy. Wykonuje całą pracę pozostałych bez aliasu, plus przypisanie listy, dwie kopie skalarne i dwa wyczyszczenia skalarne.for
iteracji po indeksach tablicy i 20% szybciej podczas iteracji po indeksach odwołania do tablicy (mam dostęp$array->[$i]
w treści) w porównaniu z użyciemeach
w połączeniu zwhile
.IMO, implementacja nr 1 jest typowa i krótka i idiomatyczna dla Perla przewyższa inne tylko w tym zakresie. Wzorzec trzech opcji może przynajmniej dać ci wgląd w szybkość.
źródło
1 różni się zasadniczo od 2 i 3, ponieważ pozostawia tablicę w takcie, podczas gdy pozostałe dwie pozostawiają ją pustą.
Powiedziałbym, że numer 3 jest dość zwariowany i prawdopodobnie mniej wydajny, więc zapomnij o tym.
Co daje ci # 1 i # 2, a oni nie robią tego samego, więc jeden nie może być „lepszy” od drugiego. Jeśli tablica jest duża i nie musisz jej utrzymywać, ogólnie zakres się nią zajmie ( ale zobacz UWAGA ), więc ogólnie rzecz biorąc , # 1 jest nadal najbardziej przejrzystą i najprostszą metodą. Wyłączenie każdego elementu niczego nie przyspieszy. Nawet jeśli istnieje potrzeba uwolnienia tablicy od odniesienia, po prostu zrobiłbym:
undef @Array;
kiedy skończysz.
źródło
@Array = ();
nie zwalnia podstawowej tablicy. Nawet wyjście poza zakres by tego nie zrobiło. Gdybyś chciał zwolnić podstawową tablicę, użyłbyśundef @Array;
.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
()
vsundef
, ale jeśli wyjście poza zakres nie zwalnia pamięci używanej przez tablicę lokalną w tym zakresie, czy nie czyni to z Perla wycieku katastrofy? To nie może być prawda.W jednym wierszu, aby wydrukować element lub tablicę.
print $ _ for (@array);
UWAGA: pamiętaj, że $ _ odwołuje się wewnętrznie do elementu @array w pętli. Wszelkie zmiany wprowadzone w $ _ zostaną odzwierciedlone w @array; dawny.
my @array = qw( 1 2 3 ); for (@array) { $_ = $_ *2 ; } print "@array";
wyjście: 2 4 6
źródło
Najlepszy sposób decydowania o takich pytaniach w celu ich porównania:
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, });
I uruchamiając to na perlu 5, wersja 24, subversion 1 (v5.24.1) zbudowanym dla x86_64-linux-gnu-thread-multi
Dostaję:
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)
Zatem „foreach (@Array)” jest około dwa razy szybszy niż pozostałe. Wszystkie inne są bardzo podobne.
@ikegami wskazuje również, że istnieje kilka różnic w tych implementacjach innych niż szybkość.
źródło
$index < $#array
powinno faktycznie odbywać się,$index <= $#array
ponieważ$#array
nie jest to długość tablicy, ale jej ostatni indeks.