2010-08-04 34 views
12

Mục tiêu của tôi là để có thể sử dụng $obj như thế này:Perl: Cách tạo đối tượng khi đang di chuyển?

print $obj->hello() . $obj->{foo}; 

Và tôi muốn tạo một inline đối tượng, có thể sử dụng một cái gì đó như này:

my $obj = (
    foo => 1, 
    hello => sub { return 'world' } 
); 

nhưng khi tôi cố gắng để sử dụng $obj làm đối tượng, tôi nhận được lỗi nói rằng $ obj chưa được may mắn. Có một số lớp cơ sở (như stdClass trong PHP) Tôi có thể sử dụng để ban phước cho băm để tôi có thể sử dụng nó như là một đối tượng?


Đối với những người biết Javascript, tôi đang cố gắng để làm như sau, nhưng trong Perl:

# JS CODE BELOW 
var obj = { foo: 1, hello: function() { return 'world' } }; 
echo obj.hello() + obj.foo; 
+0

phương pháp Perl không phải là lĩnh vực trong một hash - đây không phải là Java. – Ether

+0

JavaScript cũng không phải là Java. – cjm

Trả lời

14

Perl sẽ yêu cầu trợ giúp một chút để thực hiện việc này. Bởi vì nó không xem xét các tham chiếu mã được lưu trữ trong băm như "các phương thức". Các phương thức được thực hiện như các mục nhập vào một bảng biểu tượng gói.Perl là nhiều hơn theo định hướng lớp học so với JavaScript, tự hào tuyên bố rằng nó là nhiều hơn hướng đối tượng (trên từng đối tượng đối tượng).

Để thực hiện chức năng đó, bạn sẽ phải tạo một lớp đã ánh xạ các tham chiếu theo cách này. Cách lấy các phương thức trong bảng biểu tượng là phương thức AUTOLOAD. Nếu một gói chứa một chương trình con AUTOLOAD, khi một cuộc gọi được thực hiện đến một đối tượng may mắn mà Perl không thể tìm thấy trong chuỗi kế thừa, nó sẽ gọi AUTOLOAD và đặt biến có phạm vi gói (our) $AUTOLOAD sẽ chứa tên đầy đủ của chức năng.

Chúng tôi lấy tên của phương thức được gọi, bằng cách nhận nút cuối cùng (sau 'cuối cùng' :: ') của tên phụ đủ điều kiện. Chúng tôi tìm xem liệu có một coderef tại vị trí đó không và nếu có, chúng tôi có thể trả lại.

package AutoObject; 

use strict; 
use warnings; 
use Carp; 
use Params::Util qw<_CODE>; 
our $AUTOLOAD; 

sub AUTOLOAD { 
    my $method_name = substr($AUTOLOAD, index($AUTOLOAD, '::') + 2); 
    my ($self) = @_; 
    my $meth  = _CODE($self->{$method_name}); 
    unless ($meth) { 
     Carp::croak("object does not support method='$method_name'!"); 
    } 
    goto &$meth; 
} 


1; 

Sau đó, bạn sẽ chúc phúc cho các đối tượng vào lớp rằng:

package main; 

my $obj 
    = bless { foo => 1 
     , hello => sub { return 'world' } 
     }, 'AutoObject'; 

print $obj->hello(); 

Thông thường, trong một AUTOLOAD phụ tôi "xi măng" hành vi. Tức là, tôi tạo các mục nhập vào bảng biểu tượng gói để tránh AUTOLOAD vào lần tiếp theo. Nhưng đó thường là cho một hành vi lớp học được xác định hợp lý.

Tôi cũng thiết kế một gói QuickClass để tạo gói cho từng đối tượng được khai báo, nhưng có chứa rất nhiều biểu tượng bàn cãi rằng giờ đây có thể thực hiện tốt hơn với Class::MOP.


Đưa ra đề xuất của Eric Strom, bạn có thể thêm mã sau đây vào gói AutoObject. Phụ đề import sẽ được gọi bất cứ lúc nào ai đó use -d AutoObject (với thông số 'object').

# Definition: 
sub object ($) { return bless $_[0], __PACKAGE__; }; 

sub import { # gets called when Perl reads 'use AutoObject;' 
    shift; # my name 
    return unless $_[0] eq 'object'; # object is it's only export 
    use Symbol; 
    *{ Symbol::qualify_to_reference('object', scalar caller()) } 
     = \&object 
     ; 
} 

Và sau đó, khi bạn muốn tạo ra một "đối tượng theo nghĩa đen", bạn có thể chỉ cần làm:

use AutoObject qw<object>; 

Và biểu thức sẽ là:

object { foo => 1, hello => sub { return 'world' } }; 

Bạn thậm chí có thể làm :

object { name => 'World' 
     , hello => sub { return "Hello, $_[0]->{name}"; } 
     }->hello() 
     ; 

Và bạn có một "đối tượng chữ "biểu hiện". Có lẽ mô-đun sẽ tốt hơn được gọi là Object::Literal.

+0

Đây thực sự là một mẹo hữu dụng. Tôi có thể đặt điều này để sử dụng tốt, nếu tôi đã từng sử dụng OOP trong Perl. –

+0

Bạn có thể làm cho mã gọi một chút sạch hơn bằng cách ẩn các phước lành trong một phụ: 'phụ đối tượng {ban phước cho $ _ [0] => 'AutoObject'}' và sau đó 'my $ obj = object {...};' –

+0

@Eric Strom: có ý nghĩa tốt. Nhưng đây là suy nghĩ của tôi: Thành ngữ JavaScript về cơ bản là một "đối tượng theo nghĩa đen". Trong Perl, "đối tượng theo nghĩa đen" là (thường) 'ban phước {...}, 'MyClass''. * Tất nhiên *, vì 'ban phước 'chỉ là một func, không có lý do gì mà' đối tượng {...} 'không phải là một chữ tốt hơn. Tôi sẽ thay đổi mã của tôi. – Axeman

2

$obj sẽ là một đại lượng vô hướng, vì vậy bất cứ bạn gán cho nó có phải là một đại lượng vô hướng như tốt. Bạn có thể có thể nói

my %obj = (foo => 1, hello => sub { return 'world' }); 

hoặc

my $obj = { foo => 1, hello => sub { return 'world' }}; 

Sau đó, với dấu ngoặc nhọn, tạo ra một tài liệu tham khảo băm (mà là một đại lượng vô hướng, vì vậy nó có thể đi vào $obj). Để có được những thứ bên trong một tham chiếu băm, bạn phải sử dụng toán tử mũi tên. Một cái gì đó như $obj->{foo} hoặc &{$obj->{hello}}.

Trừ khi bạn cần có danh sách băm hoặc thứ gì đó tương tự, tốt hơn hết là nên sử dụng phương pháp đầu tiên.

Dù bằng cách nào, bạn sẽ không thể nói $obj->hello(). Perl sử dụng cú pháp đó cho hương vị riêng của OOP, có chức năng hello trong một gói riêng biệt mà tham chiếu của bạn đã được bless chỉnh sửa. Giống như:

package example; 
sub new {} { my $result = {}; return bless $result, 'example' } 
sub hello { return 'world' } 

package main; 
my $obj = example->new(); 

Như bạn có thể thấy, các phương pháp bạn có thể gọi đã được xác định và không tầm thường để thêm nhiều hơn. Có những phương pháp kỳ diệu mà bạn có thể sử dụng để làm một điều như vậy, nhưng thực sự, nó không đáng giá. &{$obj{hello}} (hoặc &{$obj->{hello}} để tham khảo) là ít nỗ lực hơn so với cố gắng để làm cho Perl làm việc như Javascript.

+0

Vâng, điều này mang lại cho tôi tài liệu đọc cho phần còn lại trong ngày ... – Tom

0

Phương thức trong Perl không phải là thuộc tính của đối tượng như trong Python. Các phương thức là các hàm chức năng thông thường trong một gói được liên kết với đối tượng. Các hàm thông thường lấy một đối số bổ sung cho tham chiếu tự.

Bạn không thể có các hàm được tạo động dưới dạng phương thức.

Dưới đây là một trích dẫn từ perldoc perlobj:

1. An object is simply a reference that happens to know which class it 
     belongs to. 

    2. A class is simply a package that happens to provide methods to deal 
     with object references. 

    3. A method is simply a subroutine that expects an object reference 
     (or a package name, for class methods) as the first argument. 

Oh, và ban phước cho() là cách bạn thiết lập kết nối giữa các tài liệu tham khảo và các gói.

+0

Không chắc chắn những gì bạn có trong tâm trí bằng cách nói rằng * "Bạn không thể có chức năng tạo động như các phương pháp." * Một có thể tạo ra các chức năng tự động và chèn chúng vào một gói. Nhiều mô-đun CPAN làm điều này. Ví dụ khá đơn giản, xem Getopt :: Long :: Descriptive. Đối với mỗi trình phân tích cú pháp tùy chọn được người dùng yêu cầu, nó tạo ra một tên lớp mới. Vào gói đó, nó cài đặt một getter cho mỗi tùy chọn dòng lệnh do người dùng định nghĩa. – FMc

+0

Tôi có nghĩa là bạn không thể thêm phương thức theo cách anh ấy muốn. Chèn chức năng động là khó tính hơn nhiều ("không có ref nghiêm ngặt; * {" $ package \ :: function "} = sub {...};", bất cứ ai? Gah!) Và không nên được thực hiện nhẹ nhàng. – Arkadiy

1

Trong bất kỳ chức năng nào bạn đang tạo đối tượng, bạn cần gọi bless trên đối tượng của mình để bật tính năng gọi phương thức.

Ví dụ:

package MyClass; 

sub new 
{ 
    my $obj = { 
    foo => 1 
    }; 

    return bless($obj, "MyClass"); 
} 

sub hello 
{ 
    my $self = shift; 
    # Do stuff, including shifting off other arguments if needed 
} 

package main; 
my $obj = MyClass::new(); 

print "Foo: " . $obj->{foo} . "\n"; 
$obj->hello(); 

EDIT: Nếu bạn muốn để có thể sử dụng tài liệu tham khảo chương trình con để cung cấp chức năng động cho các đối tượng của bạn ...

Thứ nhất, bạn có thể tạo tài liệu tham khảo mã của bạn như vậy (trong vòng băm này ví dụ nhà xây dựng):

my $obj = { 
    foo => 1, 
    hello => sub { print "Hello\n"; }, 
} 

Sau đó bạn có thể gọi nó như thế này:

my $obj = MyClass::new(); # or whatever 
$obj->{hello}->(@myArguments); 

Một chút rườm rà, nhưng nó công trinh. (Bạn có thể thậm chí không cần mũi tên thứ hai, nhưng tôi không chắc chắn.)

2

Nó đánh vần một chút khác nhau trong Perl:

my $obj = { foo => 1, hello => sub { return "world" } }; 
print $obj->{hello}() . $obj->{foo}; 

Nhưng code đang lúng túng. Cảnh báo bạn thấy về tham chiếu không được ban phước cho bạn biết rằng đối tượng của bạn không được triển khai trong the way Perl expects. Toán tử bless đánh dấu một đối tượng có gói để bắt đầu tìm kiếm các phương thức của nó.

Hãy cho chúng tôi biết bạn muốn làm gì về miền vấn đề của mình và chúng tôi có thể đưa ra đề xuất để có cách tiếp cận tự nhiên hơn trong Perl.

4

Cách tiếp cận Perlish hơn là tạo một không gian tên riêng cho các phương thức mong muốn của đối tượng của bạn và bless đối tượng để làm cho các phương thức đó khả dụng cho đối tượng của bạn. Các mã để làm điều này vẫn có thể được khá succint.

my $obj = bless { foo => 1 }, "bar"; 
sub bar::hello { return 'world' }; 

Như gbacon gợi ý, nếu bạn sẵn sàng để viết $obj->{hello}->() thay vì $obj->hello(), bạn có thể bỏ qua các hoạt động ban phước.

my $obj = { foo => 1, hello => sub { return 'world' } }; 
0

Tôi khuyên bạn nên sử dụng Class :: Struct như được giải thích trong trang người dùng perltoot.

Thay vì diễn giải tài liệu, hãy để tôi trích dẫn nó như là nó đã giải thích như thế nào làm việc này:

"Những gì nó là cung cấp cho bạn một cách để 'tuyên bố' một lớp là có đối tượng có các trường là của một cụ thể Vì hàm cấu trúc hoặc bản ghi không phải là kiểu cơ sở trong Perl, mỗi khi bạn muốn tạo một lớp để cung cấp một đối tượng dữ liệu giống như bản ghi, bạn phải tự mình định nghĩa một phương thức new(), cộng với các phương thức truy cập dữ liệu riêng biệt cho mỗi trường của bản ghi đó, bạn sẽ nhanh chóng trở nên chán với quá trình này: Hàm Class :: Struct :: struct() làm giảm bớt tedium này. "

Tuy trích dẫn từ doc là một ví dụ cách làm thế nào để thực hiện nó:

use Class::Struct qw(struct); 
use Jobbie; # user-defined; see below 
struct 'Fred' => { 
    one  => '$', 
    many  => '@', 
    profession => 'Jobbie', # does not call Jobbie->new() 
}; 
$ob = Fred->new(profession => Jobbie->new()); 
$ob->one("hmmmm"); 
$ob->many(0, "here"); 
$ob->many(1, "you"); 
$ob->many(2, "go"); 
print "Just set: ", $ob->many(2), "\n"; 
$ob->profession->salary(10_000); 
Các vấn đề liên quan