2009-02-13 29 views
14

Tôi thường có một chương trình con trong Perl lấp đầy một mảng với một số thông tin. Vì tôi cũng đang sử dụng để hack trong C++, tôi thấy mình thường xuyên làm điều đó như thế này trong Perl, sử dụng tài liệu tham khảo:Trả lại toàn bộ mảng từ một chương trình con Perl không hiệu quả?

my @array; 
getInfo(\@array); 

sub getInfo { 
    my ($arrayRef) = @_; 
    push @$arrayRef, "obama"; 
    # ... 
} 

thay vì phiên bản đơn giản hơn:

my @array = getInfo(); 

sub getInfo { 
    my @array; 
    push @array, "obama"; 
    # ... 
    return @array; 
} 

Lý do, trong tất nhiên, là tôi không muốn mảng được tạo cục bộ trong chương trình con và sau đó được sao chép khi trả về.

Đúng không? Hay Perl có tối ưu hóa không?

+0

Có thể bạn có thể giải thích ở mức cao hơn những gì bạn đang cố gắng làm. Có thể có một cách hiện đại hơn, Perlish bằng văn bản những gì bạn muốn mà sẽ tránh được vấn đề của bạn hoàn toàn. Hoặc ít nhất làm cho ý định của bạn rõ ràng hơn. –

+0

@unknown (google): Tôi thực hiện việc này thường xuyên trong các ngữ cảnh khác nhau. Lần cuối cùng là khi tôi muốn đọc một tập tin vào một mảng. Vì vậy, các tập tin đã được mở ra, tôi in thông tin về những tập tin tôi đang mở trên STDERR, sửa đổi các dòng một chút và đẩy chúng vào mảng, sau đó đóng tập tin. – Frank

+0

sửa đổi cách thức? giống như nó có thể được thực hiện trên mỗi dòng, với một tuyên bố bản đồ? tôi tìm thấy đẩy một cái gì đó trên một mảng để được đánh giá cao nghi ngờ, một lần nữa ủng hộ các kỹ thuật khác. đôi khi nó không thể được giúp đỡ, mặc dù –

Trả lời

18

Điều gì về việc trả lại tham chiếu mảng ở địa điểm đầu tiên?

sub getInfo { 
    my $array_ref = []; 
    push @$array_ref, 'foo'; 
    # ... 
    return $array_ref; 
} 

my $a_ref = getInfo(); 
# or if you want the array expanded 
my @array = @{getInfo()}; 

Chỉnh sửa theo ý kiến ​​của dehmann:

Nó cũng có thể sử dụng một mảng bình thường trong chức năng và trả về một tham chiếu đến nó.

sub getInfo { 
    my @array; 
    push @array, 'foo'; 
    # ... 
    return \@array; 
}  
+0

Điều đó nghe có vẻ giống như giải pháp tốt nhất cho tôi! – Powerlord

+0

Trên thực tế, làm thế nào về việc tạo ra một mảng thực trong chức năng, nhưng có nó trở lại một tham chiếu đến nó? Perl sẽ giữ mảng được tạo cục bộ còn sống và trả về một tham chiếu hiệu quả. – Frank

+0

@dehmann: điểm tốt, tôi kết hợp nhận xét của bạn vào câu trả lời của tôi, cảm ơn. – user55400

-4

Tôi không biết gì về Perl nên đây là câu trả lời trung lập về ngôn ngữ.

Đó là, theo nghĩa nào đó, không hiệu quả để sao chép một mảng từ một chương trình con vào chương trình gọi. Sự thiếu hiệu quả phát sinh trong bộ nhớ thừa được sử dụng và thời gian thực hiện để sao chép dữ liệu từ nơi này sang nơi khác. Mặt khác, đối với tất cả nhưng các mảng lớn nhất, bạn có thể không cho một damn, và có thể thích để sao chép mảng ra cho sang trọng, cussedness hoặc bất kỳ lý do nào khác.

Giải pháp hiệu quả là để chương trình con chuyển chương trình gọi địa chỉ của mảng. Như tôi đã nói, tôi không có một đầu mối về hành vi mặc định của Perl trong khía cạnh này. Nhưng một số ngôn ngữ cung cấp cho lập trình viên tùy chọn để chọn phương pháp tiếp cận nào.

+0

"Địa chỉ của mảng" trong Perl là tham chiếu. Câu hỏi đặt ra là liệu Perl có tối ưu hóa nó hay không. –

13

Truyền tham chiếu hiệu quả hơn, nhưng sự khác biệt không lớn bằng C++. Các giá trị đối số tự (có nghĩa là: các giá trị trong mảng) luôn luôn được truyền bằng tham chiếu (giá trị trả về được sao chép).

Câu hỏi là: có quan trọng không? Hầu hết thời gian, nó không. Nếu bạn đang quay trở lại 5 yếu tố, đừng bận tâm về nó. Nếu bạn đang trả lại/vượt qua 100'000 phần tử, hãy sử dụng tham chiếu. Chỉ tối ưu hóa nó nếu đó là một nút cổ chai.

3

Để trả lời tin đồn cuối cùng, không, Perl không tối ưu hóa điều này. Nó không thể, thực sự, bởi vì trả về một mảng và trả về một vô hướng về cơ bản là khác nhau.

Nếu bạn đang xử lý một lượng lớn dữ liệu hoặc nếu hiệu suất là mối quan tâm lớn, thì thói quen C sẽ giúp bạn vượt qua và trả về tham chiếu đến cấu trúc dữ liệu hơn là cấu trúc để chúng không cần để được sao chép. Nhưng, như Leon Timmermans chỉ ra, phần lớn thời gian, bạn đang đối phó với một lượng nhỏ dữ liệu và hiệu suất không phải là một thỏa thuận lớn, do đó, làm điều đó trong bất cứ cách nào có vẻ dễ đọc nhất.

8

Nếu tôi nhìn vào ví dụ của bạn và suy nghĩ về những gì bạn muốn làm Tôi đang sử dụng để viết nó theo cách này:

sub getInfo { 
    my @array; 
    push @array, 'obama'; 
    # ... 
    return \@array; 
} 

Dường như với tôi như phiên bản đơn giản khi tôi cần trở lớn Số lượng dữ liệu.Không cần phải phân bổ mảng bên ngoài sub như bạn đã viết trong đoạn mã đầu tiên vì my làm điều đó cho bạn. Dù sao, bạn không nên thực hiện tối ưu hóa sớm như Leon Timmermanssuggest.

2

Đây là cách tôi thường trả về mảng.

sub getInfo { 
    my @array; 
    push @array, 'foo'; 
    # ... 
    return @array if wantarray; 
    return \@array; 
} 

Bằng cách này, nó sẽ hoạt động theo cách bạn muốn, trong ngữ cảnh vô hướng hoặc danh sách.

my $array = getInfo; 
my @array = getInfo; 

$array->[0] == $array[0]; 

# same length 
@$array == @array; 

Tôi sẽ không cố tối ưu hóa nó trừ khi bạn biết đó là một phần chậm của mã. Thậm chí sau đó tôi sẽ sử dụng điểm chuẩn để xem chương trình con nào thực sự nhanh hơn.

+0

Sau đó, bạn không thể lấy số đếm bằng cách gán getInfo() cho một giá trị vô hướng. http://perlmonks.org/?node_id=729965 có một cuộc tranh luận thú vị về việc sử dụng mong muốn. – daotoad

+0

Tôi đồng ý, tôi đã từng sử dụng 'điều ước' khoảng ba năm trước. Tôi đã có khó khăn nó là tính năng mát mẻ. Sau nhiều năm kinh nghiệm trong dự án perl lớn với nhiều nhà phát triển có kỹ năng khác nhau, tôi đã đưa ra quyết định rằng mã nhận biết ngữ cảnh là một trong những điều tồi tệ nhất trong Perl. –

+1

@daotoad: Bạn không bao giờ có thể giả định rằng một hàm trả về một danh sách trong ngữ cảnh danh sách sẽ trả về độ dài của nó trong ngữ cảnh vô hướng, vì điều đó chỉ xảy ra khi hàm trả về một mảng. Nếu hàm trả về giá trị danh sách, bạn sẽ nhận được phần tử cuối cùng của danh sách. Tại sao? Bởi vì Perl HATES bạn. :) –

2

Có hai cân nhắc. Điều hiển nhiên là mảng của bạn sẽ lớn đến cỡ nào? Nếu nó nhỏ hơn vài chục phần tử, thì kích thước không phải là một yếu tố (trừ khi bạn tối ưu hóa vi mô cho một số hàm được gọi nhanh, nhưng bạn phải làm một số thông tin bộ nhớ để chứng minh rằng đầu tiên).

Đó là phần dễ dàng. Việc xem xét thứ hai bị bỏ qua là giao diện. Mảng trả về sẽ được sử dụng như thế nào? Điều này là quan trọng bởi vì toàn bộ mảng dereferencing là kinda khủng khiếp trong Perl. Ví dụ:

for my $info (@{ getInfo($some, $args) }) { 
    ... 
} 

Thật xấu xí. Thế này tốt hơn.

for my $info (getInfo($some, $args)) { 
    ... 
} 

Nó cũng tự vay để lập bản đồ và grepping.

my @info = grep { ... } getInfo($some, $args); 

Nhưng trả lại một ref mảng có thể hữu ích nếu bạn đang đi để chọn ra các yếu tố cá nhân:

my $address = getInfo($some, $args)->[2]; 

Đó là đơn giản hơn:

my $address = (getInfo($some, $args))[2]; 

Hoặc:

my @info = getInfo($some, $args); 
my $address = $info[2]; 

Nhưng tại thời điểm đó, bạn sho câu hỏi uld cho dù @info thực sự là một danh sách hay một băm.

my $address = getInfo($some, $args)->{address}; 

Điều bạn không nên làm là có getInfo() trả về tham chiếu mảng trong ngữ cảnh vô hướng và một mảng trong ngữ cảnh danh sách. Điều này làm lộn xộn việc sử dụng truyền thống ngữ cảnh vô hướng như chiều dài mảng sẽ làm người dùng ngạc nhiên.

Cuối cùng, tôi sẽ cắm mô-đun của riêng mình, Method::Signatures, vì nó cung cấp sự thỏa hiệp để chuyển các tham chiếu mảng mà không phải sử dụng cú pháp ref của mảng.

use Method::Signatures; 

method foo(\@args) { 
    print "@args";  # @args is not a copy 
    push @args, 42; # this alters the caller array 
} 

my @nums = (1,2,3); 
Class->foo(\@nums); # prints 1 2 3 
print "@nums";  # prints 1 2 3 42 

Điều này được thực hiện thông qua sự kỳ diệu của Data::Alias.

+0

Bạn không bao giờ có thể giả định rằng một hàm trả về một danh sách trong ngữ cảnh danh sách sẽ trả về độ dài của nó trong ngữ cảnh vô hướng, vì điều đó chỉ xảy ra khi hàm trả về một mảng.Nếu hàm trả về giá trị danh sách không phải mảng, bạn sẽ nhận được phần tử cuối cùng của danh sách thay vì kích thước của nó. –

+0

Sau đó, không trả lại danh sách! Nếu tay cầm búa của bạn cung cấp cho bạn mảnh vụn, không đeo găng tay, cát nó mịn! Toàn bộ "danh sách vs mảng" điều trong Perl 5 là một cái bẫy khổng lồ, gaping gấu ngay giữa sân chơi. – Schwern

+0

Tôi hoàn toàn đồng ý với câu cuối cùng của bạn. Tôi sẽ thêm rằng hầu hết những đứa trẻ trong sân chơi, và thậm chí có thể là những nhà thiết kế sân chơi, không biết về cái bẫy gấu này. :) –

0

3 cải tiến hiệu suất có khả năng LỚN khác nếu bạn đang đọc một toàn bộ, tập tin largish và slicing nó vào một mảng:

  1. Tắt ĐANG TẢI với sysread() thay vì read() (thủ cảnh báo về trộn)
  2. Pre-mở rộng mảng bằng cách xác định giá trị các yếu tố cuối cùng - tiết kiệm cấp phát bộ nhớ
  3. Sử dụng Unpack() để nhanh chóng chia dữ liệu như dữ liệu kênh đồ họa uint16_t

Việc chuyển mảng vào hàm cho phép chương trình chính xử lý một mảng đơn giản trong khi chức năng công nhân ghi một lần và quên sử dụng truy cập "$ @" và mũi tên -> [$ II] phức tạp hơn các hình thức. Là khá C'ish, nó có khả năng được nhanh chóng!

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