2010-02-21 84 views
10

Tôi có một mảng @test. Cách tốt nhất để kiểm tra xem mỗi phần tử của mảng có cùng một chuỗi không?Làm cách nào để kiểm tra xem tất cả các phần tử của một mảng có giống hệt nhau trong Perl không?

Tôi biết tôi có thể làm điều đó với vòng lặp foreach nhưng có cách nào tốt hơn để thực hiện việc này không? Tôi đã kiểm tra chức năng bản đồ nhưng tôi không chắc đó có phải là những gì tôi cần hay không.

+0

Chất lượng của giải pháp của bạn thực sự phụ thuộc vào những gì bạn đang làm ở chỗ vòng lặp foreach. Có, có rất nhiều cách để làm điều đó, nhưng tại sao bạn cảm thấy giải pháp hiện tại của bạn là thiếu? –

+1

Tại sao bạn không mã hóa và đăng giải pháp "foreach" đó, vì vậy chúng tôi có thể nhận xét? – lexu

+2

cho tất cả các áp phích liên quan về undef, tôi sẽ không giả định "kiểm tra xem mỗi phần tử của mảng có cùng chuỗi không" bao gồm khả năng undefs - một "chuỗi" theo định nghĩa :) được xác định. (mặc dù nó chắc chắn là một cái gì đó để xem xét) – ysth

Trả lời

11

Nếu chuỗi được biết đến, bạn có thể sử dụng grep trong bối cảnh vô hướng:

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

Nếu không, sử dụng một hash:

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

hoặc một phiên bản ngắn hơn:

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

LƯU Ý: Giá trị ined undef hoạt động giống như chuỗi rỗng ("") khi được sử dụng như một chuỗi trong Perl. Do đó, các kiểm tra sẽ trả về true nếu mảng chỉ chứa các chuỗi rỗng và undef s.

Dưới đây là một giải pháp mà mất này vào tài khoản:

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

Xấu. Bạn sẽ luôn luôn cần phải đi qua toàn bộ mảng ngay cả khi không khớp trong phần tử đầu tiên. – codeholic

+1

Những gì tôi đã làm trước khi tôi thấy câu trả lời này đã sắp xếp các mảng và kiểm tra xem các yếu tố đầu tiên và cuối cùng là như nhau. Cảm ơn bạn đã nhập mã băm. Tôi vẫn đang học cách sử dụng bản đồ đúng cách. – somebody

+1

@Quick Joe Smith: Không phải là giải pháp hiện tại của tôi thiếu. Tôi chỉ đang tìm những cách khác nhau để làm điều tương tự, và về cơ bản sử dụng mã ngắn nhất để có vẻ tốt: P – somebody

2

Tôi sử dụng List::Util::first cho tất cả các mục đích tương tự.

# 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; 

Tôi đã sử dụng map \$_, @test tại đây để tránh các vấn đề với giá trị đánh giá sai.

Lưu ý. Như cjm chú ý khá, sử dụng map đánh bại lợi thế của việc đoản mạch đầu tiên. Vì vậy, tôi tip mũ của tôi để Sinan với giải pháp first_index của mình.

+0

Nhưng nếu '@ test' chứa chuỗi rỗng (hoặc 0 hoặc' undef') thì sao? Thử nghiệm của bạn sẽ đặt '$ ok' thành true khi nó sai. – cjm

+0

Ok. Tôi sẽ sửa. – codeholic

+0

Nhưng nó không phải là thử nghiệm đó là sai. Thực tế là 'first' trả về phần tử' @ test' đã vượt qua bài kiểm tra. Không có cách nào để phân biệt giữa 'undef' trong' @ test' và không tìm được kết quả phù hợp. – cjm

10

Cả hai phương pháp trong bài chấp nhận cung cấp cho bạn câu trả lời sai nếu @test = (undef, ''). Nghĩa là, họ khai báo một giá trị không xác định bằng với chuỗi rỗng.

Điều đó có thể chấp nhận được. Ngoài ra, việc sử dụng grep đi qua tất cả các phần tử của mảng ngay cả khi không khớp được sớm và sử dụng hàm băm lớn hơn gấp đôi bộ nhớ được sử dụng bởi các phần tử của mảng. Không ai trong số này sẽ là một vấn đề nếu bạn có mảng nhỏ. Và, grep có thể đủ nhanh cho các kích thước danh sách hợp lý.

Tuy nhiên, đây là một sự thay thế mà 1) trả về false cho (undef, '')(undef, 0), 2) không làm tăng bộ nhớ của chương trình của bạn và 3) ngắn mạch càng sớm càng không phù hợp được tìm thấy:

#!/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; 
} 

Tuy nhiên, sử dụng List::MoreUtils::first_index có khả năng là nhanh hơn:

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 Câu trả lời thú vị. Tôi đăng một biến thể trên phương pháp đầu tiên của bạn (nhưng xin vui lòng cho tôi biết nếu tôi bỏ qua một cái gì đó). – FMc

+0

Um, bạn thực sự nên thử mã của bạn trước khi đăng nó. Bạn muốn 'my $ ref = \ @_;' trong tiểu vùng 'của tôi ($ ref) = @_;' đang đặt phần tử đầu tiên hoặc mảng được truyền vào $ ref. – htaccess

+0

@htaccess Chính xác ... bạn phải gọi 'all_the_same' với tham chiếu đến mảng bạn đang kiểm tra làm đối số một và duy nhất. –

4

TIMTOWTDI, và tôi đã được đọc rất nhiều Mark Jason Dominus thời gian gần đây.

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

Tôi đã sử dụng cái đó, hoạt động hoàn hảo. Tuy nhiên nó có một lỗ hổng nhỏ. trả lại trừ khi $ bằng -> ($ v); phải trả về 0 trừ khi $ bằng -> ($ v); – user2050516

+0

@ user2050516 Trong trường hợp ở bàn tay, nó không quan trọng cho dù chúng ta trở về 'undef' hoặc' 0' (cả hai đánh giá sai). – FMc

+0

cho đến hôm qua tôi đã có cùng một ý kiến ​​nhưng chính xác lỗ hổng này không trở lại undef hoặc 0 hơi say lên. Xem dưới đây là hai điều, undef là một giá trị, không có gì là gì cả. trả về undef; trả lại; Nó đóng vai trò ở đâu? Nếu bạn làm điều này: @ array = (all_the_same ([0,1]), all_the_same ([1,1])); in @array, "\ n"; Bất ngờ lớn nó sẽ có một phần tử trong mảng. – user2050516

3

Bạn có thể kiểm tra số lần phần tử trong mảng (@test) được lặp lại bằng cách đếm nó trong một hàm băm (% nhìn thấy). Bạn có thể kiểm tra xem có bao nhiêu khóa ($ size) có trong hash (% được thấy).Nếu có nhiều hơn 1 khóa, bạn biết rằng các phần tử trong mảng không giống nhau.

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

Điều này phù hợp với tôi! Cảm ơn Hemanth! – Vibhuti

2

Tôi nghĩ rằng, chúng ta có thể sử dụng Danh sách :: MoreUtils qw (uniq)

my @uniq_array = uniq @array; 
my $array_length = @uniq_array; 
$array_length == 1 ? return 1 : return 0; 
Các vấn đề liên quan