Tại sao các nguyên mẫu chức năng của Perl 5 lại tệ?


116

Trong một câu hỏi khác về Stack Overflow, Leon Timmermans khẳng định:

Tôi khuyên bạn không nên sử dụng nguyên mẫu. Chúng có công dụng của chúng, nhưng không phải cho hầu hết các trường hợp và chắc chắn không phải trong trường hợp này.

Tại sao điều này có thể đúng (hoặc khác)? Tôi hầu như luôn cung cấp các nguyên mẫu cho các hàm Perl của mình và trước đây tôi chưa từng thấy ai khác nói xấu về việc sử dụng chúng.


Tôi cũng tò mò. Lần duy nhất tôi không sử dụng chúng là khi tôi đang gọi với số lượng đối số thay đổi.
Paul Tomblin

7
Tôi có thể vui lòng khuyên bạn đọc bài báo, “Các nguyên mẫu Perl được coi là có hại” không?
tchrist

Câu trả lời:


121

Nguyên mẫu không tệ nếu được sử dụng đúng cách. Khó khăn là các nguyên mẫu của Perl không hoạt động theo cách mà mọi người thường mong đợi. Những người có kiến ​​thức nền về các ngôn ngữ lập trình khác có xu hướng mong đợi các nguyên mẫu cung cấp một cơ chế để kiểm tra xem các lệnh gọi hàm có chính xác hay không: nghĩa là chúng có đúng số lượng và loại đối số. Nguyên mẫu của Perl không phù hợp cho nhiệm vụ này. Đó là việc lạm dụng nó là xấu. Các nguyên mẫu của Perl có một mục đích duy nhất và rất khác:

Nguyên mẫu cho phép bạn xác định các hàm hoạt động giống như các hàm dựng sẵn.

  • Dấu ngoặc đơn là tùy chọn.
  • Bối cảnh được áp đặt cho các đối số.

Ví dụ: bạn có thể xác định một hàm như sau:

sub mypush(\@@) { ... }

và gọi nó là

mypush @array, 1, 2, 3;

mà không cần phải viết \tham chiếu đến mảng.

Tóm lại, các nguyên mẫu cho phép bạn tạo ra đường cú pháp của riêng mình. Ví dụ, khung công tác Moose sử dụng chúng để mô phỏng một cú pháp OO điển hình hơn.

Điều này rất hữu ích nhưng các nguyên mẫu rất hạn chế:

  • Chúng phải được hiển thị tại thời điểm biên dịch.
  • Chúng có thể được bỏ qua.
  • Tuyên truyền ngữ cảnh cho các đối số có thể gây ra hành vi không mong muốn.
  • Chúng có thể gây khó khăn khi gọi các hàm bằng bất kỳ thứ gì khác ngoài biểu mẫu được quy định nghiêm ngặt.

Xem Nguyên mẫu trong perlsub để biết tất cả các chi tiết đẫm máu.


2
Tôi đã chấp nhận câu trả lời này bởi vì tôi cảm thấy nó trả lời tốt nhất cho câu hỏi - về bản chất, nguyên mẫu không tệ, chỉ là cách bạn sử dụng chúng.
Alnitak

2
Mặt khác, nguyên mẫu Moose là / awesome / p3rl.org/MooseX::Declare p3rl.org/MooseX::Method::Signatures
Kent Fredric


Vì vậy, họ là một từ nhầm lẫn, sau đó?
Peter Mortensen

69

Vấn đề là các nguyên mẫu hàm của Perl không làm những gì mọi người nghĩ rằng họ làm. Mục đích của chúng là cho phép bạn viết các hàm sẽ được phân tích cú pháp giống như các hàm tích hợp của Perl.

Trước hết, các cuộc gọi phương thức hoàn toàn bỏ qua các nguyên mẫu. Nếu bạn đang lập trình OO, không quan trọng phương pháp của bạn có nguyên mẫu nào. (Vì vậy, họ không nên có bất kỳ nguyên mẫu nào.)

Thứ hai, nguyên mẫu không được thực thi nghiêm ngặt. Nếu bạn gọi một chương trình con với &function(...), nguyên mẫu sẽ bị bỏ qua. Vì vậy, chúng không thực sự cung cấp bất kỳ loại an toàn nào.

Thứ ba, chúng là những pha hành động ma quái ở khoảng cách xa. (Đặc biệt là $nguyên mẫu, khiến tham số tương ứng được đánh giá trong ngữ cảnh vô hướng, thay vì ngữ cảnh danh sách mặc định.)

Đặc biệt, chúng làm cho việc chuyển các tham số từ mảng trở nên khó khăn. Ví dụ:

my @array = qw(a b c);

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);

sub foo ($;$$) { print "@_\n" }

foo(@array);
foo(@array[0..1]);
foo($array[0], $array[1], $array[2]);

bản in:

a b c
a b
a b c
3
b
a b c

cùng với 3 cảnh báo về main::foo() called too early to check prototype(nếu cảnh báo được bật). Vấn đề là một mảng (hoặc lát mảng) được đánh giá trong ngữ cảnh vô hướng trả về độ dài của mảng.

Nếu bạn cần viết một hàm hoạt động giống như một hàm tích hợp, hãy sử dụng một nguyên mẫu. Nếu không, đừng sử dụng nguyên mẫu.

Lưu ý: Perl 6 sẽ có các nguyên mẫu được tân trang lại hoàn toàn và rất hữu ích. Câu trả lời này chỉ áp dụng cho Perl 5.


Nhưng chúng vẫn cung cấp một kiểm tra hữu ích rằng người gọi của bạn và người phụ đang sử dụng cùng một số đối số, vậy điều đó có gì sai?
Paul Tomblin

2
Không; sự đồng thuận chung là các nguyên mẫu hàm Perl về cơ bản không mang lại lợi ích gì. Bạn cũng có thể không bận tâm đến chúng, ít nhất là trong Perl 5. Perl 6 có thể là một câu chuyện khác (tốt hơn).
Jonathan Leffler

5
Có những cách tốt hơn để xác thực các đối số, chẳng hạn như mô-đun Params :: Validate: search.cpan.org/~drolsky/Params-Validate-0.91/lib/Params/…
Friedo

10
Sửa: việc cắt mảng trả về một danh sách , do đó, một lát cắt mảng trong ngữ cảnh vô hướng trả về phần tử cuối cùng của danh sách. Lời gọi thứ hai đến cuối cùng của foo()bản in 2 vì đó là phần tử cuối cùng trong lát hai phần tử của bạn. Thay đổi thành my @array = qw(foo bar baz)và bạn sẽ thấy sự khác biệt. (Ngoài ra, đây là lý do tại sao tôi không khởi tạo mảng / danh sách thành chuỗi số dựa trên 0 hoặc 1 trong mã trình diễn, loại bỏ. Sự nhầm lẫn giữa các chỉ số, số lượng và phần tử trong ngữ cảnh đã khiến tôi nhiều hơn một lần. Ngớ ngẩn nhưng có thật.)
pilcrow

2
@pilcrow: Tôi đã chỉnh sửa câu trả lời để sử dụng a b cđể làm rõ hơn quan điểm của bạn.
Flimm,

30

Tôi đồng ý với hai áp phích trên. Nói chung, $nên tránh sử dụng . Nguyên mẫu là chỉ hữu ích khi sử dụng đối số khối ( &), những đống ( *), hoặc mẫu tài liệu tham khảo ( \@, \$, \%, \*)


Nói chung, có lẽ, nhưng tôi muốn đề cập đến hai trường hợp ngoại lệ: Thứ nhất, ($)nguyên mẫu tạo ra một toán tử một ngôi được đặt tên, có thể hữu ích (chắc chắn Perl thấy chúng hữu ích; thỉnh thoảng tôi cũng vậy). Thứ hai, khi ghi đè các tích hợp sẵn (cho dù thông qua nhập hay sử dụng CORE :: GLOBAL: :), nói chung, bạn nên bám vào bất kỳ nguyên mẫu nào mà tích hợp sẵn có, ngay cả khi điều đó bao gồm một $hoặc bạn có thể làm cho lập trình viên ngạc nhiên (chính bạn, thậm chí) với ngữ cảnh danh sách trong đó phần cài sẵn sẽ cung cấp ngữ cảnh vô hướng.
The Sidhekin

4

Một số người, khi nhìn vào nguyên mẫu chương trình con Perl, nghĩ rằng nó có nghĩa là gì đó mà nó không:

sub some_sub ($$) { ... }

Đối với Perl, điều đó có nghĩa là trình phân tích cú pháp mong đợi hai đối số. Đó là cách của Perl cho phép bạn tạo các chương trình con hoạt động giống như các chương trình cài sẵn, tất cả đều biết điều gì sẽ xảy ra từ đoạn mã tiếp theo. Bạn có thể đọc về các nguyên mẫu trong perlsub

Nếu không đọc tài liệu, mọi người đoán rằng các nguyên mẫu đề cập đến việc kiểm tra đối số thời gian chạy hoặc một cái gì đó tương tự mà họ đã thấy trong các ngôn ngữ khác. Như với hầu hết những điều mọi người đoán về Perl, hóa ra chúng đã sai.

Tuy nhiên, bắt đầu với Perl v5.20, Perl có một tính năng, thử nghiệm khi tôi viết bài này, cung cấp một cái gì đó giống như những gì người dùng mong đợi và những gì. Chữ ký chương trình con của Perl thực hiện đếm đối số thời gian, gán biến và cài đặt mặc định:

use v5.20;
use feature qw(signatures);
no warnings qw(experimental::signatures);

animals( 'Buster', 'Nikki', 'Godzilla' );

sub animals ($cat, $dog, $lizard = 'Default reptile') { 
    say "The cat is $cat";
    say "The dog is $dog";
    say "The lizard is $lizard";
    }

Đây là tính năng bạn có thể muốn nếu bạn đang xem xét các nguyên mẫu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.