2017-12-08 22 views
5
sub foo { 
    my @return_value = (1, 2); 
} 
my @receiver = foo(); 

Điều này có được gán giống như bất kỳ sự chỉ định nào khác trong perl không? mảng được nhân bản trong bộ nhớ? Tôi nghi ngờ nguyên nhân của điều này vì mảng được giữ bởi chương trình con là dùng một lần, một sự sao chép hoàn toàn dư thừa. nó có ý nghĩa để chỉ 'liên kết' mảng với @ receiver vì lý do tối ưu.Trong perl, khi gán giá trị trả về của một chương trình con cho một biến, dữ liệu có được sao chép trong bộ nhớ không?

nhân tiện, tôi nhận thấy một câu hỏi tương tự Perl: function returns reference or copy? nhưng không nhận được những gì tôi muốn.

và tôi đang nói về Perl5

ps. bất kỳ sách hoặc tài liệu về các chủ đề như vậy về perl?

Trả lời

3

Chương trình con trả về kết quả của thao tác cuối cùng nếu bạn không chỉ định trả về rõ ràng.

@return_value được tạo riêng biệt từ @receiver và các giá trị được sao chép và bộ nhớ được sử dụng bởi @return_value được phát hành khi nó nằm ngoài phạm vi tại lối ra chương trình con.

Vì vậy, có - bộ nhớ được sử dụng được nhân đôi.

Nếu bạn rất muốn tránh điều này, bạn có thể tạo một mảng vô danh một lần, và 'vượt qua' một tham chiếu đến nó xung quanh:

#!/usr/bin/env perl 
use strict; 
use warnings; 

use Data::Dumper; 

sub foo { 
    my $anon_array_ref = [ 1, 2 ]; 
    return $anon_array_ref; 
} 

my $results_from_foo = foo(); 

print Dumper $results_from_foo; 

này thường sẽ tối ưu hóa quá sớm tuy nhiên, trừ khi bạn biết bạn' đang xử lý các cấu trúc dữ liệu thực sự lớn.

Lưu ý - bạn có thể bao gồm một số return; rõ ràng trong phụ của bạn sau khi chuyển nhượng, vì thực hành tốt để làm rõ những gì bạn đang làm.

+0

* "có lẽ bạn nên bao gồm' return' "*' return' rõ ràng là dành cho ngôn ngữ không phải Perl! Tôi nghĩ rằng nó là rõ ràng hơn (và briefer) chỉ để đánh giá danh sách đó là để được trả lại trong tuyên bố cuối cùng của chương trình con. Trong perls cũ, nó làm chậm mọi thứ xuống để thêm 'return', và chúng tôi rất vui khi chỉ đặt' 1; 'ở cuối mô-đun thay vì' return 1; ' – Borodin

+3

Từ cuốn sách 'Learning perl (6th) p. 74': _Một số lập trình viên muốn sử dụng trả lại mỗi lần có một giá trị trả về, như một phương tiện để ghi lại rằng nó là một giá trị trả về. Bạn không thực sự cần nó, nhưng nó cũng không làm hại gì cả. Tuy nhiên, nhiều lập trình viên của Perl tin rằng đó chỉ là bảy ký tự phụ của việc gõ. _ :) Tôi thích sách perl. :) – jm666

6

Các vô hướng được trả về bởi :lvalue không được sao chép.

Các vô hướng được trả về bởi người đăng ký XS không được sao chép.

Các vô hướng được trả về bởi hàm (toán tử có tên) không được sao chép.

Các vô hướng được trả về bởi các người đăng ký khác được sao chép.

Nhưng đó là trước khi bất kỳ bài tập nào được đưa vào chơi. Nếu bạn gán các giá trị trả về cho một biến, bạn sẽ sao chép chúng (một lần nữa, trong trường hợp của một Perl bình thường).

Điều này có nghĩa là my $y = sub { $x }->(); bản sao $x hai lần!

Nhưng điều đó không thực sự quan trọng vì tối ưu hóa.


Hãy bắt đầu với ví dụ về thời điểm chúng không được sao chép.

$ perl -le' 
    sub f :lvalue { my $x = 123; print \$x; $x } 
    my $r = \f(); 
    print $r; 
' 
SCALAR(0x465eb48) # $x 
SCALAR(0x465eb48) # The scalar on the stack 

Nhưng nếu bạn loại bỏ :lvalue ...

$ perl -le' 
    sub f { my $x = 123; print \$x; $x } 
    my $r = \f(); 
    print $r; 
' 
SCALAR(0x17d0918) # $x 
SCALAR(0x17b1ec0) # The scalar on the stack 

Tệ hơn nữa, người ta thường sau lên bằng cách gán vô hướng đến một biến, do đó, một bản sao thứ hai xảy ra.

$ perl -le' 
    sub f { my $x = 123; print \$x; $x } 
    my $r = \f(); # \ 
    print $r;  # > my $y = f(); 
    my $y = $$r; #/
    print \$y; 
' 
SCALAR(0x1802958) # $x 
SCALAR(0x17e3eb0) # The scalar on the stack 
SCALAR(0x18028f8) # $y 

Về mặt cộng, hãy gán tối ưu hóa để giảm thiểu chi phí sao chép chuỗi.

XS subs và chức năng (nhà khai thác được đặt tên) thường trả về con số tử vong ("TEMP"). Đây là những vô hướng "trên hàng chết". Chúng sẽ tự động bị hủy nếu không có gì bước vào để yêu cầu một tham chiếu đến chúng.

Trong phiên bản cũ hơn của Perl (< 5.20), gán một chuỗi chết cho vô hướng khác sẽ gây ra quyền sở hữu bộ đệm chuỗi được chuyển để tránh phải sao chép bộ đệm chuỗi. Ví dụ: my $y = lc($x); không sao chép chuỗi được tạo bởi lc; chỉ cần con trỏ chuỗi được sao chép.

$ perl -MDevel::Peek -e'my $s = "abc"; Dump($s); $s = lc($s); Dump($s);' 
SV = PV(0x1705840) at 0x1723768 
    REFCNT = 1 
    FLAGS = (PADMY,POK,IsCOW,pPOK) 
    PV = 0x172d4c0 "abc"\0 
    CUR = 3 
    LEN = 10 
    COW_REFCNT = 1 
SV = PV(0x1705840) at 0x1723768 
    REFCNT = 1 
    FLAGS = (PADMY,POK,pPOK) 
    PV = 0x1730070 "abc"\0  <-- Note the change of address from stealing 
    CUR = 3      the buffer from the scalar returned by lc. 
    LEN = 10 

Trong các phiên bản mới hơn của Perl (≥ 5,20), toán tử gán bao giờ [1] bản chuỗi đệm. Thay vào đó, các phiên bản mới hơn của Perl sử dụng cơ chế copy-on-write ("COW").

$ perl -MDevel::Peek -e'my $x = "abc"; my $y = $x; Dump($x); Dump($y);' 
SV = PV(0x26b0530) at 0x26ce230 
    REFCNT = 1 
    FLAGS = (POK,IsCOW,pPOK) 
    PV = 0x26d68a0 "abc"\0   <----+ 
    CUR = 3        | 
    LEN = 10        | 
    COW_REFCNT = 2       +-- Same buffer (0x26d68a0) 
SV = PV(0x26b05c0) at 0x26ce248   | 
    REFCNT = 1        | 
    FLAGS = (POK,IsCOW,pPOK)    | 
    PV = 0x26d68a0 "abc"\0   <----+ 
    CUR = 3 
    LEN = 10 
    COW_REFCNT = 2 

Ok, cho đến nay, tôi đã chỉ nói về vô hướng. Vâng, đó là vì người đăng ký và chức năng chỉ có thể trả lại số lượng lớn [2].

Trong ví dụ của bạn, vô hướng giao cho @return_value sẽ được trả lại [3], sao chép, sau đó sao chép một lần thứ hai vào @receiver bởi sự phân công.

Bạn có thể tránh tất cả điều này bằng cách trả lại tham chiếu đến mảng.

sub f { my @fizbobs = ...; \@fizbobs } 
my $fizbobs = f(); 

Điều duy nhất được sao chép có tham chiếu, vô hướng không xác định đơn giản nhất.


  1. Ok, có lẽ không bao giờ. Tôi nghĩ rằng cần phải có một byte miễn phí trong bộ đệm chuỗi để giữ số COW.

  2. Trong ngữ cảnh danh sách, chúng có thể trả về 0, 1 hoặc nhiều trong số chúng, nhưng chúng chỉ có thể trả về vô hướng.

  3. Toán tử cuối cùng của phụ của bạn là toán tử gán danh sách. Trong ngữ cảnh danh sách, toán tử gán danh sách trả về các vô hướng mà bên trái của nó (LHS) đánh giá. Xem Scalar vs List Assignment Operator để biết thêm thông tin.

+3

Vì vậy, đây là một câu trả lời đảm bảo với tôi rằng tôi không ** biết perl ở tất cả (chưa).:) Hovever, tôi thích rằng đây là những câu trả lời như vậy bởi vì họ có thể giúp tôi hiểu những điều sâu sắc (vài năm sau) :) – kobame

+1

@amon, Ngăn xếp có thể chứa AV (ví dụ: 'push' dự kiến ​​mục đầu tiên trên ngăn xếp là AV), nhưng người đăng ký không thể trả lại. '@ receiver' và' @ return_value' không phải là cùng một mảng, ít nhất không phải không có thêm công việc: 'tính năng perl -e'use qw (tuyên bố trả lời tuyên bố); my \ @a = sub {my @a; nói \ @a; \ @a} ->(); nói \ @a; ''. – ikegami

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