Tại sao một số người dùng trích dẫn tên lớp trong Perl?


12

Nhìn vào Type::Tiny, tôi thấy rằng tên lớp trong lệnh gọi Type::Tiny->newđược trích dẫn trong các tài liệu chính thức,

my $NUM = "Type::Tiny"->new(
   name       => "Number",
   constraint => sub { looks_like_number($_) },
   message    => sub { "$_ ain't a number" },
);

Tại sao lại thế này? Đây có phải là phong cách đơn thuần? Có bất kỳ phân nhánh hiệu suất cho thực hành này?

Câu trả lời:


7

Lấy một ví dụ đơn giản hơn

package Foo { sub new { die 7  } };
package Bar { sub new { die 42 } };
sub Foo { "Bar" }
Foo->new();

Trong ví dụ trên, hằng số phân Foogiải thành "Bar", vì vậy điều này gọi là "Bar"->newkhông "Foo"->new. Làm thế nào để bạn ngăn chặn chương trình con giải quyết? Bạn có thể trích dẫn nó.

"Foo"->new();

Đối với hàm ý hiệu suất, mọi thứ không trở nên tồi tệ hơn bằng cách sử dụng một chuỗi chứ không phải là một bareword. Tôi đã xác nhận optree được tạo bởi O=Deparselà như nhau. Vì vậy, theo nguyên tắc chung, có vẻ tốt hơn để trích dẫn các Tên lớp nếu bạn coi trọng tính chính xác.

Điều này được đề cập trong Lập trình Perl, (đáng buồn là trong bối cảnh gọi phương thức gián tiếp )

... Vì vậy, chúng tôi sẽ nói với bạn rằng bạn hầu như luôn có thể thoát khỏi một tên lớp trần, với điều kiện hai điều là đúng. Đầu tiên, không có chương trình con cùng tên với lớp. (Nếu bạn tuân theo quy ước rằng các tên chương trình con như newbắt đầu chữ thường và tên lớp như ElvenRingbắt đầu chữ hoa , thì điều này không bao giờ là vấn đề). Thứ hai, lớp đã được tải với một trong

use ElvenRing;
require ElvenRing;

Một trong những khai báo này đảm bảo rằng Perl biết ElvenRinglà một tên mô-đun, nó buộc bất kỳ tên trần nào giống như newtrước khi tên lớp ElvenRingđược hiểu là một cuộc gọi phương thức, ngay cả khi bạn đã khai báo một newchương trình con của chính bạn trong gói hiện tại.

Và, điều đó có ý nghĩa: sự nhầm lẫn ở đây chỉ có thể xảy ra nếu các chương trình con của bạn (thường là chữ thường) có cùng tên với một lớp (thường là chữ hoa). Điều này chỉ có thể xảy ra nếu bạn vi phạm quy ước đặt tên ở trên.

tldr; có lẽ là một ý tưởng tốt để trích dẫn tên lớp của bạn, nếu bạn biết chúng và bạn đánh giá cao sự đúng đắn trên sự lộn xộn.

Lưu ý bên lề: thay vào đó, bạn có thể dừng độ phân giải của một bareword cho một chức năng bằng cách gắn một ::đến cuối của nó, ví dụ như ở trên Foo::->new.


Cảm ơn Ginnz trên reddit đã chỉ ra điều này cho tôiToby Inkster cho nhận xét (mặc dù nó không có ý nghĩa với tôi trong lần đọc đầu tiên).


2
Hoặc Foo::->new, như tôi đã học được từ ikegami.
mob

@mob yea, cuốn sách Perl cũng đề cập đến điều đó (một cách rõ ràng), không chắc tôi có nên đặt nó ở đó hay không? cười lớn.
Evan Carroll

@mob Tôi cũng nói thêm, như một chú thích. Mặc dù tôi không chắc là tôi thích nó.
Evan Carroll

1
Foo::có lợi thế là nó cảnh báo nếu Fookhông phải là gói hiện có
ikegami

1
Hãy thử :: Tiny là một ví dụ tốt hơn Foo. Trong mã của bạn bằng cách sử dụng Foo->bạn có thể được mong đợi để biết rằng không có chương trình con như vậy trong gói của bạn. Nhưng nếu bạn đang sử dụng Try :: Tiny và một số mô-đun cpan / nguồn bên ngoài khác, ai sẽ nói nếu một trong số họ đang sử dụng gói Dùng thử, và nếu vậy, nếu có một phụ Tiny trong đó?
ysth

0

Trích dẫn một cách rõ ràng tên lớp thay vì sử dụng một bareword (được coi là một chuỗi) là một trong ba cách để tránh sự mơ hồ cú pháp. Phần Gọi phương thức lớp trong tài liệu perlobj giải thích.

Bởi vì Perl cho phép bạn sử dụng barewords cho tên gói và tên chương trình con, đôi khi nó diễn giải ý nghĩa của bareword không chính xác. Ví dụ, cấu trúc Class->new()có thể được hiểu là 'Class'->new()hoặc Class()->new(). Trong tiếng Anh, cách hiểu thứ hai đó đọc là một cách gọi chương trình con có tên Class(), sau đó gọi new()như một phương thức trên giá trị trả về của Class(). Nếu có một chương trình con được đặt tên Class()trong không gian tên hiện tại, Perl sẽ luôn hiểu Class->new()là phương án thứ hai: một cuộc gọi đến new()đối tượng được trả về bởi một cuộc gọi đến Class().

Xem trường hợp kỳ lạ này trong hành động với bản demo dưới đây.

#! /usr/bin/env perl

use strict;
use warnings;

sub Type::Tiny { print "Returning Bogus\n" ; return "Bogus" }

sub Type::Tiny::new { print "Type::Tiny::new\n" }

sub Bogus::new { print "Bogus::new\n" }

my $class = "Type::Tiny";

Type::Tiny->new;
Type::Tiny::->new;
"Type::Tiny"->new;
$class->new;

Đầu ra của nó là

Trở về
Không có gì mới
Loại :: Tiny :: mới
Loại :: Tiny :: mới
Loại :: Tiny :: mới

Phần còn lại của phần tài liệu nói trên cho thấy cách bảo vệ chống lại hành vi đáng ngạc nhiên hoặc lỗi vô ý.

Bạn có thể buộc Perl sử dụng cách hiểu đầu tiên ( nghĩa là , như một cách gọi phương thức trên lớp có tên "Class") theo hai cách. Đầu tiên, bạn có thể nối thêm ::tên lớp:

Class::->new()

Perl sẽ luôn giải thích điều này như một cuộc gọi phương thức.

Ngoài ra, bạn có thể trích dẫn tên lớp:

'Class'->new()

Tất nhiên, nếu tên lớp nằm trong vô hướng thì Perl cũng sẽ làm đúng:

my $class = 'Class';
$class->new();

Áp dụng cho câu hỏi của bạn, tất cả các cuộc gọi dưới đây là tương đương.

Type::Tiny::->new(  );

"Type::Tiny"->new(  );

my $class = "Type::Tiny";
$class->new(  );

Áp dụng ::đến cuối cùng có lợi thế của việc đưa ra một cảnh báo hữu ích. Nói rằng bạn vô tình gõ

Type::Tinny::->new;

Điều đó tạo ra

Bareword "Loại :: Tinny ::" đề cập đến gói không tồn tại ở ./try dòng 15.
Không thể định vị phương thức đối tượng "mới" thông qua gói "Loại :: Tinny" (có lẽ bạn đã quên tải "Loại :: Tinny"?) Tại ./try dòng 15.
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.