2009-04-06 11 views
20

Mam listę możliwych wartości:Jak mogę sprawdzić, czy wartość jest obecna w tablicy (liście) w Perlu?

@a = qw(foo bar baz); 

Jak mogę sprawdzić w zwięzły sposób, że wartość $val jest obecny lub nieobecny w @a?

Oczywistym wdrożeniem jest zapętlenie na liście, ale jestem pewien, TMTOWTDI.


Dziękuję wszystkim, którzy odpowiedzieli! Trzy odpowiedzi, które chciałbym podkreślić, to:

  1. Przyjęta odpowiedź - najbardziej "wbudowany" i zgodny wstecz.

  2. RET's answer jest najczystszy, ale tylko dobry dla Perla 5.10 i późniejszych.

  3. draegtun's answer jest (być może) nieco szybszy, ale wymaga użycia dodatkowego modułu. Nie lubię dodawać zależności, jeśli mogę ich uniknąć, i w tym przypadku nie potrzebuję różnicy wydajności, ale jeśli masz listę z 1 000 000 elementów, możesz spróbować udzielić tej odpowiedzi.

+1

nie jestem pewien, że widzę problemu zależność z listy :: Util. Jest to standard w Perlu i jeśli używasz go z qw/first/(jak zrobił Draegtun), importujesz tylko jeden podprogram. – Telemachus

+0

To nie jest problem per se, to bardziej osobiste preferencje. – MaxVT

+1

Nie ma problemu zależności od odpowiedzi List :: Util. Gdyby to był ja, byłaby to akceptowana odpowiedź. Niechęć do korzystania z podstawowych modułów uderza mnie jako preferencję zakorzenioną w przesądach. W tym przypadku grep {} jest prawie tak dobry. – singingfish

Odpowiedz

20

Perl w grep() jest przeznaczone do tego celu.

@matches = grep(/^MyItem$/, @someArray); 

czy można wstawić dowolny wyraz w dopasowującego

@matches = grep($_ == $val, @a); 
+0

+1 Hej! Prosto i przyjemnie =) Dzięki – Viet

8

Jednym z możliwych sposobów jest użycie funkcji List :: MoreUtils.

use List::MoreUtils qw/any/; 

my @array = qw(foo bar baz); 

print "Exist\n" if any {($_ eq "foo")} @array; 

Aktualizacja: korygowane na podstawie komentarza Zoul użytkownika.

+0

Zdefiniowane ("foo") jest zawsze prawdziwe, czy miałeś na myśli $ _ eq 'foo'? – zoul

+0

-1 przepraszam, za to, co wspomniał Zoul. –

+0

tak, to musi być dowolny {$ _ eq "foo"} lub jakikolwiek {m/^ foo \ z /} ... – jettero

5

ciekawym rozwiązaniem szczególnie do ponownego przeszukiwania: bulit

my %hash; 
map { $hash{$_}++ } @a; 
print $hash{$val}; 
+0

Sugestia Zoul na temat skrótu jest bardzo zbliżona do optymalnej, ale sugerowałbym, aby podczas dodawania i usuwania wartości do tablicy w trakcie programu dodawać i usuwać te wartości w haszowaniu. –

+0

Ponadto, chociaż działa to i jest powszechne, niektórzy ludzie (ja, jak sądzę) będą narzekać na używanie mapy w pustym kontekście. Dlaczego zamiast tego nie ma wartości $ hash {$ _} ++ dla @a? – jettero

+0

Tak, ładniej. – zoul

38

Jeśli masz Perl 5.10, użyj smart-match operator ~~

print "Exist\n" if $var ~~ @array;

To niemal magiczne.

+0

NB. Od 5.10.1 semantyka trochę się zmieniła i musi być: 'if $ var ~~ @ array'. Aby pomóc mi myśleć o '~~' jako 'w". ref: http://perldoc.perl.org/perldelta.html#Smart-match-changes – draegtun

+0

Dzięki - zamieniliśmy odpowiednio zamówienie. – RET

+1

Mój adres URL perldoc nie jest już ważny. Tutaj jest naprawiony: http://search.cpan.org/~dapm/perl-5.10.1/pod/perl5101delta.pod#Smart_match_changes – draegtun

2
$ perl -e '@a = qw(foo bar baz);$val="bar"; 
if (grep{$_ eq $val} @a) { 
    print "found" 
} else { 
    print "not found" 
}' 

znaleźć

$val='baq'; 

Nie znaleziono

15

Użyj funkcji z List::Utilpierwszy który jest standardowo z Perl ....

use List::Util qw/first/; 

my @a = qw(foo bar baz); 
if (first { $_ eq 'bar' } @a) { say "Found bar!" } 

NB.najpierw zwraca pierwszy znaleziony element, więc nie musi przechodzić przez pełną listę (co zrobi grep).

+1

per foolishbrat, jeśli korzystasz z importowanego sub, wolałbym List :: MoreUtil :: any(), ponieważ pojęcie ("Zwraca wartość true, jeśli jakikolwiek element w LISTIE spełnia kryterium") jest semantycznie lepiej pasuje do pytania niż first() ("zwraca pierwszy element, w którym wynik z BLOCK jest wartością rzeczywistą. ") – nohat

+0

Lista :: Util :: first() ma tę zaletę, że jest modułem podstawowym (tj. Wszechobecny). Gdybym szukał opcji CPAN, poważnie rozważałbym Perl6 :: Junction :: any ... if (any (@a) eq 'baz') {} – draegtun

18

Odpowiedź na to pytanie brzmi: perlfaq4 - odpowiedź na "How can I tell whether a certain element is contained in a list or array?".

Aby przeszukać perlfaq, możesz przeszukać listę wszystkich pytań pod numerem perlfaq używając swojej ulubionej przeglądarki.

Z wiersza poleceń można użyć przełącznika -q, aby wyszukać słowa kluczowe. byś znalazł odpowiedź szukając „liście”:

perldoc -q list 

(fragmenty tej odpowiedzi wniesionego przez Anno Siegela i Brian d Foy)

Słysząc słowo „w” jest wskazanie Prawdopodobnie powinieneś użyć hasha, a nie listy lub tablicy do przechowywania twoich danych. Hashe zostały zaprojektowane, aby szybko i skutecznie odpowiedzieć na to pytanie. Tablice nie są.

W związku z powyższym istnieje kilka sposobów podejścia. W Perl 5.10 i później, można użyć operatora mecz inteligentnej, aby sprawdzić, że dany element jest zawarty w tablicy lub hash:

use 5.010; 

if($item ~~ @array) 
    { 
    say "The array contains $item" 
    } 

if($item ~~ %hash) 
    { 
    say "The hash contains $item" 
    } 

z wcześniejszymi wersjami Perl, trzeba zrobić trochę więcej pracy. Jeśli masz zamiar dokonać tego zapytania wielokrotnie dowolnych ciągów znaków, najszybszym sposobem jest prawdopodobnie odwrócić oryginalną tablicę i utrzymać hash którego klucze są wartościami pierwszej tablicy za:

@blues = qw/azure cerulean teal turquoise lapis-lazuli/; 
%is_blue =(); 
for (@blues) { $is_blue{$_} = 1 } 

Teraz można sprawdzić czy $ is_blue {$ some_color}. Być może dobrym pomysłem byłoby przede wszystkim utrzymanie bluesa w haszyszu.

Jeśli wartościami są wszystkie małe liczby całkowite, można użyć prostej tablicy indeksowanej. Ten rodzaj tablicy zajmie mniej miejsca:

@primes = (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31); 
@is_tiny_prime =(); 
for (@primes) { $is_tiny_prime[$_] = 1 } 
# or simply @istiny_prime[@primes] = (1) x @primes; 

teraz sprawdzić, czy $ is_tiny_prime [$ some_number].

Jeśli wartości w pytaniu są liczbami całkowitymi zamiast strun, można zaoszczędzić sporo przestrzeni za pomocą bitowe ciągi zamiast:

@articles = (1..10, 150..2000, 2017); 
undef $read; 
for (@articles) { vec($read,$_,1) = 1 } 

Teraz sprawdź czy vec ($ czytać, $ n, 1) jest prawda dla niektórych $ n.

Metody te gwarantują szybkie indywidualne testy, ale wymagają reorganizacji oryginalnej listy lub tablicy. Opłacają się tylko wtedy, gdy musisz przetestować wiele wartości w tej samej tablicy.

Jeśli testujesz tylko raz, standardowy moduł Lista :: Util eksportuje najpierw funkcję do tego celu. Działa poprzez zatrzymanie, gdy znajdzie element. Jest napisany w C dla szybkości, a jego odpowiednik Perl wygląda to podprogram:

sub first (&@) { 
    my $code = shift; 
    foreach (@_) { 
     return $_ if &{$code}(); 
    } 
    undef; 
} 

Jeśli prędkość jest mało troski, wspólnej idiom używa grep w kontekście skalarnym (zwraca liczbę elementów, które przeszły jej stan) przejść przez całą listę. Ma to zaletę informowania o liczbie znalezionych pasujących elementów.

my $is_there = grep $_ eq $whatever, @array; 

Jeśli chcesz wyodrębnić pasujące elementy, po prostu użyj grep w kontekście listy.

my @matches = grep $_ eq $whatever, @array; 
1

Jeśli nie podoba niepotrzebnych zależność, wdrożyć any lub first sobie

sub first (&@) { 
    my $code = shift; 
    $code->() and return $_ foreach @_; 
    undef 
} 

sub any (&@) { 
    my $code = shift; 
    $code->() and return 1 foreach @_; 
    undef 
}