2010-02-21 8 views
10

Mam tablicę @test. Jaki jest najlepszy sposób sprawdzenia, czy każdy element tablicy jest tym samym ciągiem znaków?Jak mogę sprawdzić, czy wszystkie elementy tablicy są identyczne w Perlu?

Wiem, że mogę to zrobić z pętlą foreach, ale czy jest lepszy sposób to zrobić? Sprawdziłem funkcję mapy, ale nie jestem pewien, czy tego właśnie potrzebuję.

+0

Jakość rozwiązania naprawdę zależy od tego, co robisz w tej pętli foreach. Tak, istnieje wiele sposobów, aby to zrobić, ale dlaczego uważasz, że brakuje Twojego obecnego rozwiązania? –

+1

Dlaczego nie kodujesz i nie publikujesz tego rozwiązania "foreach", abyśmy mogli komentować? – lexu

+2

do wszystkich plakatów zainteresowanych o undef, nie zakładałbym "sprawdź czy każdy element tablicy jest tym samym ciągiem" zawiera możliwość undefs - definiowany jest "ciąg" z definicji :). (choć z pewnością warto się nad tym zastanowić) – ysth

Odpowiedz

11

Jeśli ciąg jest znany, można użyć grep w kontekście skalarnym:

if (@test == grep { $_ eq $string } @test) { 
# all equal 
} 

W przeciwnym razie, należy użyć skrótu:

my %string = map { $_, 1 } @test; 
if (keys %string == 1) { 
# all equal 
} 

lub krótszą wersję:

if (keys %{{ map {$_, 1} @test }} == 1) { 
# all equal 
} 

UWAGA: Wartość domyślna undef zachowuje się jak pusty ciąg znaków (""), gdy jest używany jako ciąg w Perlu. Dlatego sprawdzenia zwrócą wartość true, jeśli tablica zawiera tylko puste ciągi i undef s.

Oto rozwiązanie, które bierze pod uwagę to:

my $is_equal = 0; 
my $string = $test[0]; # the first element 

for my $i (0..$#test) { 
    last unless defined $string == defined $test[$i]; 
    last if defined $test[$i] && $test[$i] ne $string; 
    $is_equal = 1 if $i == $#test; 
} 
+3

Bad. Zawsze będziesz musiał przejść całą tablicę, nawet jeśli w pierwszym elemencie występuje niezgodność. – codeholic

+1

To, co zrobiłem, zanim zobaczyłem tę odpowiedź, to posortowanie tablicy i sprawdzenie, czy pierwszy i ostatni element są takie same. Dzięki za kod z hash. Nadal uczę się poprawnie korzystać z mapy. – somebody

+1

@Quick Joe Smith: To nie jest tak, że brakuje mojego obecnego rozwiązania. Po prostu szukałem różnych sposobów, aby zrobić to samo, i zasadniczo używam najkrótszego kodu, więc wygląda dobrze: P – somebody

2

Używam List::Util::first do wszystkich podobnych celów.

# try #0: $ok = !first { $_ ne $string } @test; 
# try #1: $ok = !first { (defined $_ != defined $string) || !/\A\Q$string\E\z/ } @test; 

# final solution 
use List::Util 'first'; 
my $str = shift @test; 
my $ok = !first { defined $$_ != defined $str || defined $str && $$_ ne $str } map \$_, @test; 

użyłem map \$_, @test tutaj, aby uniknąć problemów z wartości FALSE.

Uwaga. Jak zauważono cjm, użycie map pokonuje przewagę pierwszego zwarcia. Więc przekazuję czapkę Sinanowi za pomocą jego rozwiązania first_index.

+0

Ale co jeśli '@ test' zawiera pusty ciąg (lub 0 lub' undef')? Twój test sprawi, że '$ ok' będzie prawdziwe, jeśli powinno być fałszywe. – cjm

+0

Ok. Poprawię. – codeholic

+0

Ale to nie jest test, który jest błędny. Jest to fakt, że 'pierwszy' zwraca element' @ testu', który przeszedł test. Nie da się odróżnić 'undef' w' @ test' i nie znaleźć dopasowania. – cjm

10

Obie metody w przyjętym stanowisku daje złą odpowiedź, jeśli @test = (undef, ''). Oznacza to, że deklarują niezdefiniowaną wartość równą pustemu łańcuchowi znaków.

To może być dopuszczalne. Ponadto użycie grep przechodzi przez wszystkie elementy tablicy, nawet jeśli wcześniej wykryto niedopasowanie, a użycie skrótu spowoduje dwukrotne zwiększenie liczby pamięci używanej przez elementy tablicy. Żadne z nich nie stanowi problemu, jeśli masz małe tablice. I, grep prawdopodobnie będzie wystarczająco szybki dla rozsądnych rozmiarów list.

Jednak tutaj jest alternatywą, że 1) zwraca false dla (undef, '') i (undef, 0), 2) nie zwiększa zużycie pamięci swojego programu i 3) zwiera, jak tylko rozbieżność zostanie znaleziony:

#!/usr/bin/perl 

use strict; use warnings; 

# Returns true for an empty array as there exist 
# no elements of an empty set that are different 
# than each other (see 
# http://en.wikipedia.org/wiki/Vacuous_truth) 

sub all_the_same { 
    my ($ref) = @_; 
    return 1 unless @$ref; 
    my $cmpv = \ $ref->[-1]; 
    for my $i (0 .. $#$ref - 1) { 
     my $this = \ $ref->[$i]; 
     return unless defined $$cmpv == defined $$this; 
     return if defined $$this 
      and ($$cmpv ne $$this); 
    } 
    return 1; 
} 

jednak użycie List::MoreUtils::first_index prawdopodobnie będzie szybciej:

use List::MoreUtils qw(first_index); 

sub all_the_same { 
    my ($ref) = @_; 
    my $first = \ $ref->[0]; 
    return -1 == first_index { 
     (defined $$first != defined) 
      or (defined and $_ ne $$first) 
    } @$ref; 
} 
+0

+1 Ciekawa odpowiedź. Opublikowaliśmy wariant na twojej pierwszej metodzie (ale daj mi znać, jeśli coś przeoczyłem). – FMc

+0

Um, naprawdę powinieneś wypróbować swój kod przed opublikowaniem go. Chcesz "my $ ref = \ @_;" in vour sub 'my ($ ref) = @_;' wstawia pierwszy element lub przekazaną tablicę do $ ref. – htaccess

+0

@htaccess Dokładnie ... powinieneś nazwać 'all_the_same' z odniesieniem do tablicy, którą sprawdzasz jako jedyny argument. –

4

TIMTOWTDI i Czytałem wiele Mark Jason Dominus ostatnio.

use strict; 
use warnings; 

sub all_the_same { 
    my $ref = shift; 
    return 1 unless @$ref; 
    my $cmp = $ref->[0]; 
    my $equal = defined $cmp ? 
     sub { defined($_[0]) and $_[0] eq $cmp } : 
     sub { not defined $_[0] }; 
    for my $v (@$ref){ 
     return 0 unless $equal->($v); 
    } 
    return 1; 
} 

my @tests = (
    [ qw(foo foo foo) ], 
    [ '', '', ''], 
    [ undef, undef, undef ], 
    [ qw(foo foo bar) ], 
    [ '', undef ], 
    [ undef, '' ] 
); 

for my $i (0 .. $#tests){ 
    print "$i. ", all_the_same($tests[$i]) ? 'equal' : '', "\n"; 
} 
+0

Użyłem tego, działa idealnie. Jednak ma małą wadę. return, chyba że $ equal -> ($ v); powinno być zwrócone 0, chyba że $ equal -> ($ v); – user2050516

+0

@ user2050516 W tym przypadku nie ma znaczenia, czy zwracamy 'undef' lub' 0' (obie wartości są fałszywe). – FMc

+0

do wczoraj miałem tę samą opinię, ale dokładnie ta wada nie wróciła undef lub 0 spieprzone. Zobacz poniżej to dwie rzeczy, undef to wartość, nic nie jest. return undef; return; Gdzie to odgrywa rolę? Jeśli to zrobisz: @ tablica = (all_the_same ([0,1]), all_the_same ([1,1])); print @array, "\ n"; Wielka niespodzianka będzie miała jeden element w tablicy. – user2050516

3

Można sprawdzić, ile razy element w tablicy (@ test) jest powtarzany, licząc go w haszdzie (widoczne%). Możesz sprawdzić, ile kluczy ($ size) znajduje się w haszdzie (widoczne%).Jeśli jest więcej niż 1 klucz, wiesz, że elementy w tablicy nie są identyczne.

sub all_the_same { 
    my @test = @_; 
    my %seen; 
    foreach my $item (@test){ 
     $seen{$item}++ 
    } 
    my $size = keys %seen; 
    if ($size == 1){ 
     return 1; 
    } 
    else{ 
     return 0; 
    } 
} 
+0

To działa dla mnie! Dzięki Hemanth! – Vibhuti

2

myślę, możemy użyć Lista :: MoreUtils qw (uniq)

my @uniq_array = uniq @array; 
my $array_length = @uniq_array; 
$array_length == 1 ? return 1 : return 0; 
Powiązane problemy