2011-06-30 25 views
9

Tôi đã luôn luôn phần nào nhầm lẫn về mục đích và cách sử dụng của người đăng ký ẩn danh trong perl. Tôi hiểu khái niệm này, nhưng tìm kiếm các ví dụ và giải thích về giá trị của thực hành này.Khám phá cách sử dụng của các thành viên ẩn danh

Để được rõ ràng:

sub foo { ... } # <--- named sub 
sub { ... }  # <--- anonymous sub 

Ví dụ:

$ perl -e 'print sub { 1 }' 
CODE(0xa4ab6c) 

Cho tôi phụ trả về một giá trị vô hướng. Vì vậy, tôi có thể làm:

$ perl -e '$a = sub { 1 }; print $a' 

Đối với cùng một đầu ra như trên. Điều này tất nhiên là đúng cho tất cả các giá trị vô hướng, vì vậy bạn có thể tải mảng hoặc băm với subs ngầm ẩn danh.

Câu hỏi là, làm cách nào để sử dụng các đăng ký này? Tại sao tôi muốn sử dụng chúng?

Và đối với một ngôi sao vàng, có bất kỳ sự cố nào chỉ có thể được giải quyết với một phụ ẩn danh không?

+2

"Và đối với một vàng sao, có bất kỳ vấn đề nào chỉ có thể được giải quyết với một phụ không xác định không? " Không. Ngôi sao vàng của tôi đâu rồi? : P –

+7

Ví dụ khác hơn là bạn có thể lắc một thanh tại: http://hop.perl.plover.com/ – friedo

+1

@Chris Đó là trong thư. – TLP

Trả lời

9

Các trình con ẩn danh có thể được sử dụng cho tất cả mọi thứ.

  1. Callback cho các hệ thống xử lý sự kiện:

    my $obj = Some::Obj->new; 
    
    $obj->on_event(sub {...}); 
    
  2. Vòng lặp:

    sub stream {my $args = \@_; sub {shift @$args}} 
    
    my $s = stream 1, 2, 3; 
    
    say $s->(); # 1 
    say $s->(); # 2 
    
  3. cao Chức năng tự:

    sub apply (&@) { 
        my $code = shift; 
        $code->() for my @ret = @_; 
        @ret 
    } 
    
    my @clean = apply {s/\W+/_/g} 'some string', 'another string.'; 
    
    say $clean[0]; # 'some_string' 
    
  4. Tạo bí danh mảng ed:

    my $alias = sub {\@_}->(my $x, my $y); 
    
    $alias[0]++; 
    $alias[1] = 5; 
    
    say "$x $y"; # '1 5'' 
    
  5. lập trình năng động với đóng cửa (ví dụ như việc tạo ra một loạt các chương trình con mà chỉ khác nhau bởi một lượng nhỏ):

    for my $name (qw(list of names)) { 
        no strict 'refs'; 
        *$name = sub {... something_with($name) ...}; 
    } 
    

Không có tình huống mà một chương trình con nặc danh có thể làm bất cứ điều gì mà một chương trình con có tên không thể.Các nhà xây dựng my $ref = sub {...} tương đương như sau:

sub throw_away_name {...} 

my $ref = \&throw_away_name; 

mà không cần phải bận tâm với quyết định về một 'throw_away_name' duy nhất cho mỗi phụ.

Sự tương đương cũng đi theo cách khác, với sub name {...} là tương đương với:

BEGIN {*name = sub {...}} 

Vì vậy, ngoại trừ tên, tài liệu tham khảo mã được tạo ra bởi một trong hai phương pháp là như nhau.

Để gọi một tài liệu tham khảo chương trình con, bạn có thể sử dụng bất kỳ những điều sau đây:

$code->();   # calls with no args 
$code->(1, 2, 3); # calls with args (1, 2, 3) 
&$code();   # calls with no args 
&$code;   # calls with whatever @_ currently is 

Bạn thậm chí có thể sử dụng tài liệu tham khảo mã như phương pháp trên vô hướng may mắn hoặc không được ban phước:

my $list = sub {@{ $_[0] }}; 

say for [1 .. 10]->$list # which prints 1 .. 10 
+1

Câu trả lời đầy đủ và rõ ràng, cảm ơn! – TLP

+0

Cảnh báo tôi gặp phải hôm nay dường như chỉ ra rằng _is_ một cái gì đó mà một chương trình con ẩn danh có thể xử lý một tên được đặt tên sẽ không luôn làm đúng: 'Biến"% s "sẽ không được chia sẻ tại ...' – Izkata

3

Chúng thường được sử dụng khi bạn muốn chuyển một phụ tới một bit mã khác. Thường thì đây là trường hợp "Khi X xảy ra (trong mã của bên thứ ba) làm Y".

Ví dụ: Khi xác định một thuộc tính trong Moose, bạn có thể chỉ định giá trị mặc định của thuộc tính đó bằng cách sử dụng một phụ. Cho một lớp mà có, như là một phần của định nghĩa của nó:

has 'size' => (
     is => 'ro', 
     default => 
      sub { ('small', 'medium', 'large')[ int(rand 3) ] }, 
     predicate => 'has_size', 
); 

Bất cứ khi nào một thể hiện của lớp đó được tạo ra mà không có một kích thước rõ ràng được thông qua, các tiểu sẽ được gọi và giá trị trả về sẽ là kích thước cho đối tượng đó .

Nếu chúng ta chuyển sang ngôn ngữ khác để đưa ra một ví dụ khác, bạn sẽ tìm thấy một khái niệm tương tự trong JavaScript.

var b = document.getElementById('my_button'). 
b.addEventListener('click', function (e) { alert('clicked!'); }); 
4

Vâng tôi đã viết trình phân tích SAX cho perl là sự kiện được điều khiển. Bạn có thể chuyển các người đăng ký ẩn danh tới các sự kiện bắt đầu/kết thúc trên một phần tử.

my $str = "<xml><row><data></data></row></xml>": 

my $parser = SAXParser->new(); 

$parser->when('row')->begin(sub { 
    my ($element) = @_; 
    push(@rows, $row); 
}); 

$parser->when('row')->end(sub { 
    ## do something like serialize it or whatever 
}); 

$parser->parse($str); 
8

Bạn có thể sử dụng nó để tạo trình lặp.

use strict; 
use warnings; 

use 5.012; 

sub fib_it { 
    my ($m, $n) = (0, 0); 

    return sub { 
    my $val = ($m + $n); 
    $val = 1 unless $val; 
    ($m, $n) = ($n, $val); 
    return $val; 
    } 
} 

my $fibber = fib_it; 
say $fibber->() for (1..3); ### 1 1 2 

my $fibber2 = fib_it; 
say $fibber2->() for (1..5); ### 1 1 2 3 5 
say $fibber->() for (1..3); #### 3 5 8 
+0

+1 Tốt, nhưng hơi khó để nắm được lời giải thích và ví dụ. :) Cảm ơn bạn. – TLP

7

Dưới đây là một cái gì đó tương tự bạn có thể đã từng thấy trước đây:

@new_list = map { $_ + 1 } @old_list; 

Và cũng:

@sorted = sort { $a <=> $b } @unsorted; 

Không những là tàu ngầm ẩn danh, nhưng hành vi của họ có thể được bắt chước trong các chức năng của bạn với ẩn danh. Họ không cần từ khóa sub vì các hàm (về cơ bản) được tạo kiểu mẫu để có đối số đầu tiên của chúng là một chương trình con, và Perl nhận ra rằng đây là trường hợp đặc biệt, trong đó sub có thể bị bỏ đi. (Các hàm cũng đặt các biến cần thiết cho các giá trị có ý nghĩa trước khi gọi các chương trình con mà bạn cung cấp để đơn giản hóa việc chuyển đối số, nhưng điều đó không liên quan.)

Bạn có thể viết riêng map -like chức năng của bạn:

sub mapgrep (&@) { # make changes and also filter based on defined-ness 
    my ($func, @list) = @_; 
    my @new; 
    for my $i (@list) { 
    my $j = $func->($i); 
    push @new, $j if defined $j; 
    } 
} 

Sự kỳ diệu để làm cho nó làm việc với $ _ là một chút nhiều để viết ở đây - phiên bản trên chỉ hoạt động cho tàu ngầm rằng một vài tham số.

+0

nếu ví dụ đầu tiên sẽ giống như một phụ ẩn danh, việc thêm 'return' sẽ không kết thúc hàm, nhưng chỉ có' ​​map'. Hãy thử sub foo { @old_list = (1 .. 3) của tôi; @new_list của tôi = bản đồ {return $ _ + 1} @old_list; in "end of foo \ n"; } foo(); in "kết thúc \ n"; – hexcoder

+0

"Cả hai đều là những người đăng ký ẩn danh". Không, họ không. Xem bình luận của hexcoder. Chúng là các khối mã ẩn danh được 'eval'ed, không được gọi. –

+0

Tôi nghĩ trong bối cảnh đó (bản đồ/sắp xếp) chúng được gọi là các khối. Tôi đã thử sử dụng từ khóa phụ trong ngữ cảnh đó và đó là lỗi cú pháp. – TLP

8

Các trình con ẩn danh có thể được sử dụng để tạo các bao đóng.

Đóng cửa là khái niệm của thế giới Lisp nói rằng nếu bạn định nghĩa một hàm ẩn danh trong một ngữ cảnh đặc biệt, nó giả vờ chạy trong ngữ cảnh đó ngay cả khi nó được gọi bên ngoài ngữ cảnh.

+0

Bạn có thể cung cấp một ví dụ không? – TLP

+1

một nit nhỏ, thường trình con được đặt tên cũng có thể được đóng: '{my $ x; đếm phụ {$ x ++}} '. –

2

Trong ví dụ của bạn, bạn chưa thực sự gọi tạo chương trình con. Cuộc gọi được thực hiện với cú pháp & $ a hoặc $ a ->(). Những gì bạn đã làm là bạn đã lưu trữ một tài liệu tham khảo vào chương trình con trong $ a, sau đó đã xâu chuỗi nó và in kết quả. So sánh:

my $a = sub {1}; 
my $b = sub {1}; 
print join("\n", $a, $a->(), $b, $b->()); 
+0

+1 giải thích rất ngắn gọn, cảm ơn bạn. – TLP

2

Đây là những người đăng ký cho lập trình viên lười biếng. Bạn có thể sử dụng chúng cho các chức năng ném đi cục bộ và có thể lưu một số thao tác gõ. Thay vì

sub x { ... } 
my $function_ptr = \&x; 

bây giờ bạn có thể sử dụng

my $function_ptr = sub { ... }; 

Các chức năng vô danh cũng là tư nhân, và chỉ có thể được truy cập thông qua $function_ptr, vì vậy họ không có một mục trong bảng biểu tượng.

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