2009-03-11 30 views
12

Câu hỏi "How can I monkey-patch an instance method in Perl?" khiến tôi suy nghĩ. Tôi có thể xác định lại các phương pháp Perl động không? Giả sử tôi có một lớp học như thế này:Làm thế nào tôi có thể xác định lại các phương thức lớp Perl?

package MyClass; 
sub new { 
    my $class = shift; 
    my $val = shift; 
    my $self = { val=> $val}; 

    bless($self, $class); 
    return $self; 
}; 

sub get_val { 
    my $self = shift; 
    return $self->{val}+10; 
} 
1; 

Và giả sử thêm hai số thực sự đắt tiền.

Tôi muốn sửa đổi lớp để $ val + 10 chỉ được tính lần đầu tiên tôi gọi phương thức trên đối tượng đó. Các cuộc gọi tiếp theo đến phương thức sẽ trả về giá trị được lưu trong bộ nhớ cache.

tôi có thể dễ dàng sửa đổi phương pháp để bao gồm bộ nhớ đệm, nhưng:

  • tôi có một loạt các phương pháp như thế này.
  • Tôi không muốn làm hỏng phương pháp này.

Điều tôi thực sự muốn làm là chỉ định danh sách các phương thức mà tôi biết luôn trả về cùng một giá trị cho một trường hợp cụ thể. Sau đó tôi muốn lấy danh sách này và chuyển nó vào một hàm để thêm hỗ trợ bộ nhớ đệm vào các phương thức đó

Có cách nào hiệu quả để thực hiện việc này không?

Theo dõi. Mã bên dưới hoạt động, nhưng vì sử dụng nghiêm ngặt không cho phép tham chiếu theo chuỗi, tôi không phải là 100% nơi tôi muốn.

sub myfn { 
    printf("computing\n"); 
    return 10; 
} 
sub cache_fn { 
    my $fnref = shift; 

    my $orig = $fnref; 
    my $cacheval; 

    return sub { 
    if (defined($cacheval)) { return $cacheval; } 
    $cacheval = &$orig(); 
    return $cacheval; 
    } 
} 

*{myfn} = cache_fn(\&myfn); 

Làm thế nào để sửa đổi để chỉ làm điều này ?:

cache_fn(&myfn); 
+0

{không có cảnh báo 'Định nghĩa lại'; Hãy xem câu trả lời mới của tôi về cache_fn (&myfn); – draegtun

+0

Bạn có thể muốn xem xét việc ghi nhớ (có một số mô-đun CPAN tốt) hoặc sử dụng trình kích hoạt Moose để lưu vào bộ nhớ cache tốn kém tính toán trong các thuộc tính và tính toán lại chúng khi cần thiết với trigger – Ether

Trả lời

11

Bạn có thể ghi đè lên các phương pháp như get_val từ một gói như thế này:

*{MyClass::get_val} = sub { return $some_cached_value }; 

Nếu bạn có một danh sách các phương pháp tên, bạn có thể làm một cái gì đó như thế này:

my @methods = qw/ foo bar get_val /; 
foreach my $meth (@methods) { 
    my $method_name = 'MyClass::' . $meth; 
    no strict 'refs'; 
    *{$method_name} = sub { return $some_cached_value }; 
} 

Đó có phải là những gì bạn tưởng tượng không?

+0

sử dụng nghiêm ngặt;.. không cho phép tôi để tham khảo các chức năng của chuỗi có cách nào để làm được việc này một cách an toàn – mmccoo

+1

bạn có nghĩa là một cách mà không sử dụng "no 'refs nghiêm ngặt? '" như trong ví dụ thứ hai của tôi? Không mà tôi biết. – innaM

3

Tôi chưa bao giờ thử bằng phương pháp, nhưng Memoize có thể là những gì bạn đang tìm kiếm. Nhưng hãy nhớ đọc số caveats.

+0

nó sẽ làm việc với các phương pháp, nhưng bạn có thể phải sử dụng tùy chọn Normalizer nếu bạn muốn bộ nhớ cache trên tất cả các trường hợp. Nếu bạn muốn bộ nhớ cache mỗi trường hợp, sau đó nó có thể là dễ dàng hơn để chỉ dính vào các giá trị trong đối tượng, ví dụ như return $ tự -> {foo} – runrig

+0

Đó là dễ dàng hơn nếu bạn đang viết thứ -> {foo} nếu $ tự tồn tại e lớp. Câu hỏi ngụ ý anh ta đang cố gắng tự động sửa đổi một lớp mà anh ta không viết. – cjm

2

Không hữu ích trong trường hợp của bạn, nhưng đã lớp học của bạn được viết bằng Moose sau đó bạn có thể tự động ghi đè lên các phương pháp sử dụng Class::MOP nền tảng của nó ....

{ 
    package MyClass; 
    use Moose; 

    has 'val' => (is => 'rw'); 

    sub get_val { 
     my $self = shift; 
     return $self->val + 10; 
    } 

} 

my $A = MyClass->new(val => 100); 
say 'A: before: ', $A->get_val; 

$A->meta->remove_method('get_val'); 
$A->meta->add_method('get_val', sub { $_[0]->val }); 

say 'A: after: ', $A->get_val; 

my $B = MyClass->new(val => 100); 
say 'B: after: ', $B->get_val; 

# gives u... 
# => A: before: 110 
# => A: after: 100 
# => B: after: 100 
2

Làm thế nào để sửa đổi để chỉ làm điều này ?:

cache_fn (\ & myfn);

Vâng dựa trên ví dụ hiện tại của bạn, bạn có thể làm một cái gì đó như thế này ....

sub cache_fn2 { 
    my $fn_name = shift; 
    no strict 'refs'; 
    no warnings 'redefine'; 

    my $cache_value = &{ $fn_name }; 
    *{ $fn_name } = sub { $cache_value }; 
} 

cache_fn2('myfn'); 

Tuy nhiên nhìn vào ví dụ này tôi không thể không nghĩ rằng bạn có thể sử dụng Memoize để thay thế?

5

tôi viết về những điều khác nhau mà bạn có thể muốn làm trong "Dynamic Subroutines" chương của Mastering Perl. Tùy thuộc vào những gì bạn đang làm, bạn có thể muốn gói chương trình con, hoặc xác định lại nó, hoặc phân lớp, hoặc tất cả các loại thứ khác.

Perl là một ngôn ngữ năng động, vì vậy có rất nhiều ma thuật đen mà bạn có thể làm. Sử dụng nó một cách khôn ngoan là lừa.

+1

cảm ơn liên kết. Tôi đọc một vài trang trực tuyến và họ có những gì tôi cần để trả lời câu hỏi này. Có vẻ như một cuốn sách khác nằm trong danh sách mua sắm của tôi. – mmccoo

Các vấn đề liên quan