2009-08-25 9 views
26

Tôi thường xuyên sử dụng shift để giải nén thông số chức năng:Có phải 'thay đổi' điều ác để xử lý các tham số chương trình con Perl không?

sub my_sub { 
    my $self = shift; 
    my $params = shift; 
    .... 
} 

Tuy nhiên, nhiều đồng nghiệp của tôi trên được rao giảng rằng shift thực sự là ác. Bạn có thể giải thích tại sao tôi nên thích

sub my_sub { 
    my ($self, $params) = @_; 
    .... 
} 

đến shift?

+12

Tôi nghĩ bạn nên hỏi các đồng nghiệp của bạn tại sao họ nghĩ rằng sự thay đổi là ác và chia sẻ với chúng tôi những câu trả lời của họ vào đây trong SO – ccheneson

+7

@Bombe, tôi thấy rằng bạn sử dụng PHP khá một chút, nhưng bạn cảm thấy cần phải được xúc phạm về Perl. Tôi hy vọng bạn nhận thức được rằng hầu hết các khiếu nại phổ biến về Perl áp dụng như nhau đối với PHP. Mọi ngôn ngữ đều có những bit xấu xí của nó, nhưng hầu hết đều có thứ gì đó tốt để đưa ra điều đó làm cho nó đáng giá để đối phó với chúng. Để rõ ràng, như Perl, PHP là một công cụ tuyệt vời, rằng chúng tôi may mắn có quyền truy cập miễn phí - tương tự như vậy đối với Java, Python, Ruby và vân vân. Vui lòng không xúc phạm đến việc lựa chọn công cụ của mọi người. Một nụ cười không thay đổi bản chất của bình luận của bạn. Một xúc phạm được gửi đi với một nụ cười vẫn là một sự xúc phạm. – daotoad

+1

Đối với những gì nó có giá trị, rassie, bất cứ ai dogmatically ủng hộ một tính năng của Perl trên khác là thiếu điểm Perl. –

Trả lời

49

Việc sử dụng shift để giải nén đối số không phải là điều xấu. Đó là một quy ước chung và có thể là cách nhanh nhất để xử lý các đối số (tùy thuộc vào số lượng có và cách chúng được truyền). Dưới đây là một ví dụ về một kịch bản khá phổ biến trong trường hợp đó: một người truy cập đơn giản.

use Benchmark qw(cmpthese); 

sub Foo::x_shift { shift->{'a'} } 
sub Foo::x_ref { $_[0]->{'a'} } 
sub Foo::x_copy { my $s = $_[0]; $s->{'a'} } 

our $o = bless {a => 123}, 'Foo'; 

cmpthese(-2, { x_shift => sub { $o->x_shift }, 
       x_ref => sub { $o->x_ref }, 
       x_copy => sub { $o->x_copy }, }); 

Các kết quả trên perl 5.8.8 trên máy tính của tôi:

  Rate x_copy x_ref x_shift 
x_copy 772761/s  -- -12% -19% 
x_ref 877709/s  14%  --  -8% 
x_shift 949792/s  23%  8%  -- 

Không kịch tính, nhưng có nó được. Luôn kiểm tra kịch bản của bạn trên phiên bản perl của bạn trên phần cứng đích để tìm hiểu chắc chắn.

shift cũng hữu ích trong trường hợp bạn muốn thay đổi trình kích hoạt và sau đó gọi phương thức SUPER::, chuyển số còn lại @_.

sub my_method 
{ 
    my $self = shift; 
    ... 
    return $self->SUPER::my_method(@_); 
} 

Nếu tôi có một loạt rất dài my $foo = shift; hoạt động ở phía trên cùng của một hàm, tuy nhiên, tôi có thể xem xét sử dụng một bản sao hàng loạt từ @_ để thay thế. Nhưng nói chung, nếu bạn có hàm hoặc phương thức cần nhiều hơn một số đối số, hãy sử dụng các tham số được đặt tên (nghĩa là, bắt tất cả @_ trong hàm băm %args hoặc mong đợi một đối số tham chiếu băm) là cách tiếp cận tốt hơn nhiều.

+6

Bạn nên hiển thị khi nó thực sự nhanh hơn để không có sự thay đổi sử dụng. Tôi nghĩ rằng với 2 hoặc 3 args nó nhanh hơn để sao chép @_. Như một quy tắc của ngón tay cái tôi sử dụng thay đổi khi có 1 arg và sao chép @_ khi có nhiều hơn 1. – mpeters

0

Nó không phải là bản chất xấu, nhưng sử dụng nó để kéo ra các đối số của một chương trình con một là tương đối chậm và đòi hỏi một số lượng lớn các dòng mã.

+2

-1 cho thông tin không chính xác. Như @ John Siracusa nói, đó là một thành ngữ Perl phổ biến để sử dụng thay đổi để xử lý các tranh luận. –

+3

Tôi không thấy nơi ông tranh cãi rằng nó không phải là một thành ngữ Perl phổ biến. Anh ta nói nó chậm hơn (điều này đúng, vì mảng đối số phải được thao tác thay vì chỉ truy cập). – JoshJordan

+5

Tony là đúng, loại. Dựa trên điểm chuẩn của tôi, sự dịch chuyển nhanh hơn đối với 2 tham số hoặc ít hơn. Danh sách gán nhanh hơn cho 3 hoặc nhiều hơn. Vì bất kỳ thứ gì có 3 hoặc 4 hoặc 10 tham số nên có lẽ không được thực hiện các tham số vị trí theo cách đó, sử dụng kiểu thay đổi là hợp lý. –

20

Nó không phải là ác, nó là một loại hương vị của điều. Bạn thường sẽ thấy những phong cách sử dụng cùng nhau:

sub new { 
    my $class = shift; 
    my %self = @_; 
    return bless \%self, $class; 
} 

tôi có xu hướng sử dụng shift khi có một luận cứ hoặc khi tôi muốn để điều trị một vài đối số đầu tiên khác biệt so với phần còn lại.

8

Gán @_ vào danh sách có thể mang lại một số tính năng bổ sung trợ giúp.

Giúp bạn thêm thông số được đặt tên vào một ngày sau, khi bạn sửa đổi mã của mình Một số người coi tính năng này, tương tự như cách kết thúc danh sách hoặc băm bằng dấu ',' để thêm thành viên trong tương lai.

Nếu bạn có thói quen sử dụng thành ngữ này, thì việc chuyển các đối số có thể có hại, bởi vì nếu bạn chỉnh sửa mã để thêm đối số, bạn có thể kết thúc bằng một lỗi nhỏ, nếu bạn không chú ý.

ví dụ:

sub do_something { 
my ($foo) = @_; 
} 

sau chỉnh sửa để

sub do_something { 
    my ($foo,$bar) = @_; # still works 
} 

tuy nhiên

sub do_another_thing { 
    my $foo = shift; 
} 

Nếu một đồng nghiệp, những người sử dụng các hình thức đầu tiên giáo điều (có lẽ họ nghĩ rằng sự thay đổi là ác) chỉnh sửa tập tin và vắng mặt của bạn -mập nhật thông tin này để đọc

sub do_another_thing { 
    my ($foo, $bar) = shift; # still 'works', but $bar not defined 
} 

và họ có thể đã giới thiệu một lỗi nhỏ.

Chỉ định cho @_ có thể nhỏ gọn và hiệu quả hơn với không gian dọc, khi bạn có một số lượng nhỏ thông số để chỉ định cùng một lúc. Nó cũng cho phép bạn cung cấp các đối số mặc định nếu bạn đang sử dụng kiểu băm của các tham số chức năng được đặt tên

ví dụ:

my (%arguments) = (user=>'defaultuser',password=>'password',@_); 

Tôi vẫn coi đó là câu hỏi về phong cách/khẩu vị. Tôi nghĩ điều quan trọng nhất là áp dụng một phong cách này hay phong cách khác với sự nhất quán, tuân theo nguyên tắc ít ngạc nhiên nhất.

+2

Tôi không nghĩ rằng 'my ($ foo, $ bar) = shift;' là một lỗi * tinh tế * . Đó là một viên gạch để đầu, phải không? – Telemachus

+2

Tôi đã nhìn thấy nó xảy ra quá nhiều lần. Sai lầm ngu ngốc tất nhiên, nhưng thật dễ dàng để trở thành 'mã mù' khi bạn đọc rất nhiều. – cms

+0

Thông thường, nếu bạn giải nén bằng ca, thông số bổ sung sẽ được thêm bằng cách thêm một dòng khác. 'my $ foo = shift; my $ bar = shift; 'Tôi chưa bao giờ thấy những gì bạn mô tả – daotoad

1

Tôi không nghĩ rằng shift là điều xấu. Việc sử dụng shift cho thấy sự sẵn sàng của bạn để thực sự đặt tên biến - thay vì sử dụng $_[0].

Cá nhân, tôi sử dụng shift khi chỉ có một tham số cho hàm. Nếu tôi có nhiều tham số, tôi sẽ sử dụng ngữ cảnh danh sách.

my $result = decode($theString); 

sub decode { 
    my $string = shift; 

    ... 
} 

my $otherResult = encode($otherString, $format); 

sub encode { 
    my ($string,$format) = @_; 
    ... 
} 
11

Đây là, như những người khác đã nói, một vấn đề về hương vị.

Tôi thường muốn chuyển các tham số của mình thành từ vựng vì nó cho tôi một vị trí chuẩn để khai báo một nhóm các biến sẽ được sử dụng trong chương trình con. Độ chi tiết thêm cho tôi một nơi tuyệt vời để treo nhận xét. Nó cũng làm cho nó dễ dàng để cung cấp các giá trị mặc định trong một thời trang gọn gàng.

sub foo { 
    my $foo = shift;  # a thing of some sort. 
    my $bar = shift;  # a horse of a different color. 
    my $baz = shift || 23; # a pale horse. 

    # blah 
} 

Nếu bạn đang thực sự lo ngại về tốc độ gọi thói quen của bạn, đừng giải nén đối số của bạn ở tất cả - truy cập chúng trực tiếp sử dụng @_. Hãy cẩn thận, đó là những tham chiếu đến dữ liệu của người gọi mà bạn đang làm việc. Đây là một thành ngữ phổ biến trong POE. POE cung cấp một loạt các hằng số mà bạn sử dụng để có được các thông số vị trí theo tên:

sub some_poe_state_handler { 
    $_[HEAP]{some_data} = 'chicken'; 
    $_[KERNEL]->yield('next_state'); 
} 

Bây giờ lỗi ngu ngốc lớn bạn có thể nhận được nếu bạn có thói quen giải nén params với sự thay đổi này là một:

sub foo { 
    my $foo = shift; 
    my $bar = shift; 
    my @baz = shift; 

    # I should really stop coding and go to bed. I am making dumb errors. 

} 

tôi nghĩ rằng kiểu mã nhất quán quan trọng hơn bất kỳ kiểu dáng cụ thể nào. Nếu tất cả đồng nghiệp của tôi đã sử dụng kiểu gán danh sách, tôi cũng sẽ sử dụng nó.

Nếu đồng nghiệp của tôi cho biết có vấn đề lớn khi sử dụng ca để giải nén, tôi sẽ yêu cầu trình diễn lý do tại sao nó xấu. Nếu vụ án là chắc chắn, thì tôi sẽ học điều gì đó. Nếu vụ việc là không có thật, tôi có thể bác bỏ nó và giúp ngăn chặn sự lây lan của kiến ​​thức. Sau đó, tôi đề nghị rằng chúng tôi xác định một phương pháp tiêu chuẩn và làm theo nó cho mã tương lai.Tôi thậm chí có thể cố gắng thiết lập một Perl :: Chính sách phê bình để kiểm tra quyết định theo tiêu chuẩn.

+1

+1 nhưng lưu ý rằng nếu các đối số cần nhận xét (và có nhiều hơn hai trong số chúng), tốt hơn là sử dụng các đối số được đặt tên và cung cấp giá trị mặc định băm. –

+0

Tôi thường đặt giới hạn ở mức 3, nhưng tuyệt đối. – daotoad

1

Perl :: Phê bình là bạn của bạn ở đây. Nó tuân theo "tiêu chuẩn" được thiết lập trong cuốn sách Perl Best Practices của Damian Conway. Chạy nó với --verbose 11 cho bạn một lời giải thích về lý do tại sao mọi thứ đều xấu. Không giải nén @_ đầu tiên trong các đăng ký của bạn là mức độ nghiêm trọng 4 (trong số 5). Ví dụ:

echo 'package foo; use warnings; use strict; sub quux { my foo= shift; my (bar,baz) = @_;};1;' | perlcritic -4 --verbose 11 

Always unpack @_ first at line 1, near 'sub quux { my foo= shift; my (bar,baz) = @_;}'. 
    Subroutines::RequireArgUnpacking (Severity: 4) 
    Subroutines that use `@_' directly instead of unpacking the arguments to 
    local variables first have two major problems. First, they are very hard 
    to read. If you're going to refer to your variables by number instead of 
    by name, you may as well be writing assembler code! Second, `@_' 
    contains aliases to the original variables! If you modify the contents 
    of a `@_' entry, then you are modifying the variable outside of your 
    subroutine. For example: 

     sub print_local_var_plus_one { 
      my ($var) = @_; 
      print ++$var; 
     } 
     sub print_var_plus_one { 
      print ++$_[0]; 
     } 

     my $x = 2; 
     print_local_var_plus_one($x); # prints "3", $x is still 2 
     print_var_plus_one($x);  # prints "3", $x is now 3 ! 
     print $x;      # prints "3" 

    This is spooky action-at-a-distance and is very hard to debug if it's 
    not intentional and well-documented (like `chop' or `chomp'). 

    An exception is made for the usual delegation idiom 
    `$object->SUPER::something(@_)'. Only `SUPER::' and `NEXT::' are 
    recognized (though this is configurable) and the argument list for the 
    delegate must consist only of `(@_)'. 
+3

Điều này có vẻ nhầm lẫn khi phân bổ một trường hợp sử dụng @_ trực tiếp, thay vì lấy các bản sao cục bộ. Điều này thực sự, hầu như luôn luôn là điều sai trái để làm. Tuy nhiên, không chuyển, cũng không gán @_ cho một danh sách bị các vấn đề được mô tả trong đầu ra. Tuy nhiên, việc vận chuyển hàng hóa xung quanh đầu ra của tiện ích này có thể giải thích tại sao mọi người nghĩ rằng các nhà khai thác như dịch chuyển là xấu xa, tôi cho là vậy. – cms

+1

Đừng đặt quá nhiều niềm tin vào thực tiễn tốt nhất của Perl. Có quá nhiều tình huống để áp dụng một quy tắc duy nhất cho tất cả chúng. –

1

Có tối ưu hóa cho việc chỉ định danh sách.

Tham chiếu duy nhất tôi có thể tìm thấy là this one.

5.10.0 vô tình vô hiệu hóa một tối ưu hóa, khiến một giọt đo lường hiệu suất trong danh sách chuyển nhượng, như thường được dùng để thông số chức năng assign từ @_. Tối ưu hóa đã được khôi phục lại, và hồi quy hiệu suất đã được sửa.

Đây là ví dụ về hồi quy hiệu suất bị ảnh hưởng.

sub example{ 
    my($arg1,$arg2,$arg3) = @_; 
} 
+0

Nếu có ai có tài liệu tham khảo tốt hơn, vui lòng để lại nhận xét. –