2008-09-21 6 views
11

Có ai tìm thấy giải pháp tốt cho các danh sách được đánh giá lười biếng trong Perl không? Tôi đã thử một số cách để biến một số thứ nhưCó giải pháp Perl nào cho danh sách lười biếng bên này của Perl 6 không?

for my $item (map { ... } @list) { 
} 

vào đánh giá lười biếng - bằng cách gắn @list chẳng hạn. Tôi đang cố gắng để tránh phá vỡ và viết một bộ lọc nguồn để làm điều đó, bởi vì họ mess với khả năng của bạn để gỡ lỗi mã. Có ai có bất kỳ thành công nào không. Hay bạn chỉ cần phá vỡ và sử dụng một vòng lặp while?

Lưu ý: Tôi đoán rằng tôi nên đề cập đến rằng tôi là loại nối đôi khi dài grep-bản đồ chuỗi cho các danh sách chuyển đổi chức năng. Vì vậy, nó không quá nhiều vòng lặp foreach hoặc vòng lặp while. Đó là biểu thức bản đồ có xu hướng đóng gói nhiều chức năng hơn vào cùng một không gian dọc.

Trả lời

13

Như đã đề cập trước đó, cho (mỗi) là một vòng lặp mong muốn, do đó, nó muốn đánh giá toàn bộ danh sách trước khi bắt đầu.

Để đơn giản, tôi khuyên bạn nên sử dụng đối tượng trình lặp hoặc kết thúc thay vì cố gắng để có mảng được đánh giá lười biếng. Trong khi bạn có thể sử dụng cà vạt để có danh sách vô hạn được đánh giá, bạn có thể gặp rắc rối nếu bạn yêu cầu (trực tiếp hoặc gián tiếp, như trong phần trên) cho toàn bộ danh sách (hoặc thậm chí kích thước của toàn bộ danh sách) .

Nếu không có viết một lớp đầy đủ hoặc sử dụng bất kỳ mô-đun, bạn có thể làm cho một nhà máy iterator đơn giản chỉ bằng cách sử dụng đóng cửa:

sub make_iterator { 
    my ($value, $max, $step) = @_; 

    return sub { 
     return if $value > $max; # Return undef when we overflow max. 

     my $current = $value; 
     $value += $step;   # Increment value for next call. 
     return $current;   # Return current iterator value. 
    }; 
} 

Và sau đó sử dụng nó:

# All the even numbers between 0 - 100. 
my $evens = make_iterator(0, 100, 2); 

while (defined(my $x = $evens->())) { 
    print "$x\n"; 
} 

Ngoài ra còn có các Tie::Array::Lazy mô-đun trên CPAN, cung cấp giao diện phong phú hơn và đầy đủ hơn cho các mảng lười. Tôi đã không sử dụng các mô-đun bản thân mình, vì vậy số dặm của bạn có thể thay đổi.

All the best,

Paul

+1

Nếu bạn muốn tìm hiểu thêm về loại lập trình này, hãy đọc cuốn sách của Mark Jason Dominus là "Thứ tự cao hơn Perl". Rất tốt, IMHO. – moritz

+2

cho/foreach làm * không * nhận toàn bộ danh sách trong trường hợp đặc biệt của toán tử phạm vi. – user11318

2

Nếu tôi nhớ chính xác, cho/foreach có được toàn bộ danh sách trước tiên, vì vậy danh sách được đánh giá lười biếng sẽ được đọc hoàn toàn và sau đó nó sẽ bắt đầu lặp qua các phần tử. Vì vậy, tôi nghĩ rằng không có cách nào khác hơn là sử dụng một vòng lặp while. Nhưng tôi có thể sai.

Ưu điểm của một vòng lặp while là bạn có thể giả cảm giác của một danh sách uể oải đánh giá với một tham chiếu mã:

my $list = sub { return calculate_next_element }; 
while(defined(my $element = &$list)) { 
    ... 
} 

Sau khi tất cả, tôi đoán một cà vạt là càng gần như bạn có thể nhận được trong Perl 5.

+0

Tại sao không chỉ danh sách $ của tôi = \ & calculate_next_element; ? Hoặc bỏ qua tham chiếu mã và gọi calculate_next_element trực tiếp? – cjm

+0

cho/foreach làm * không * nhận toàn bộ danh sách trong trường hợp của toán tử phạm vi. Nếu không thì họ sẽ làm. – user11318

+0

cjm: Đó chỉ là một trình giữ chỗ cho * tính toán-yếu tố tiếp theo của bạn ở đây *, không thực sự là một cuộc gọi hàm. Nếu không, bạn đúng tất nhiên. – jkramer

5

Có ít nhất một trường hợp đặc biệt khi cho và foreach được tối ưu hóa để không tạo toàn bộ danh sách cùng một lúc. Và đó là toán tử phạm vi. Vì vậy, bạn có thể lựa chọn nói:

for my $i (0..$#list) { 
    my $item = some_function($list[$i]); 
    ... 
} 

và điều này sẽ lặp qua mảng, biến đổi tuy nhiên bạn thích, mà không cần tạo một danh sách dài các giá trị lên phía trước.

Nếu bạn muốn trình bày bản đồ của bạn trở về con số biến của các yếu tố, bạn có thể làm điều này thay vì:

for my $i (0..$#array) { 
    for my $item (some_function($array[$i])) { 
    ... 
    } 
} 

Nếu bạn muốn lười biếng phổ biến hơn này, sau đó lựa chọn tốt nhất của bạn là để học cách sử dụng đóng cửa để tạo danh sách lười biếng. Cuốn sách tuyệt vời của MJD Thứ tự cao hơn Perl có thể hướng dẫn bạn qua các kỹ thuật đó. Tuy nhiên, hãy cảnh báo rằng chúng sẽ liên quan đến những thay đổi lớn hơn đối với mã của bạn.

9

[Phụ chú: Hãy nhận biết rằng mỗi bước cá nhân cùng một chuỗi bản đồ/grep là háo hức. Nếu bạn cung cấp cho nó một danh sách lớn cùng một lúc, các sự cố của bạn sẽ bắt đầu sớm hơn nhiều so với số foreach cuối cùng.]

Những gì bạn có thể làm để tránh viết lại hoàn toàn là quấn vòng lặp của bạn với vòng ngoài. Thay vì viết này:

for my $item (map { ... } grep { ... } map { ... } @list) { ... } 

... viết nó như thế này:

while (my $input = calculcate_next_element()) { 
    for my $item (map { ... } grep { ... } map { ... } $input) { ... } 
} 

này giúp bạn khỏi phải viết lại đáng kể mã hiện tại của bạn, và miễn là danh sách không tăng số đơn đặt hàng của độ lớn trong quá trình chuyển đổi, bạn nhận được khá gần như tất cả các lợi ích mà một viết lại cho phong cách lặp sẽ cung cấp.

7

Nếu bạn muốn tạo danh sách lười biếng, bạn sẽ phải viết trình lặp của riêng bạn. Khi bạn có điều đó, bạn có thể sử dụng một cái gì đó như Object::Iterate có phiên bản nhận thức của vòng lặp mapgrep. Hãy xem mã nguồn của module đó: nó khá đơn giản và bạn sẽ thấy cách viết các chương trình con nhận thức của trình vòng lặp của riêng bạn.

Chúc may mắn, :)

3

Tôi hỏi một câu hỏi tương tự tại perlmonks.org, và BrowserUk đã đưa ra một khuôn khổ thực sự tốt in his answer. Về cơ bản, một cách thuận tiện để có được đánh giá lười biếng là để sinh ra các chủ đề cho việc tính toán, ít nhất là miễn là bạn chắc chắn rằng bạn muốn kết quả, Just Not Now. Nếu bạn muốn đánh giá lười biếng không để giảm độ trễ nhưng để tránh tính toán, cách tiếp cận của tôi sẽ không giúp đỡ bởi vì nó dựa trên một mô hình đẩy, không phải là một mô hình kéo. Có thể sử dụng Coro lỗi, bạn có thể biến phương pháp này thành một mô hình kéo (đơn luồng).

Trong khi cân nhắc vấn đề này, tôi cũng điều tra tie-ing một mảng các kết quả thread để làm cho chương trình Perl chảy giống như map, nhưng cho đến nay, tôi thích API của tôi về giới thiệu các "từ khóa" parallel (một nhà xây dựng đối tượng trong ngụy trang) và sau đó gọi phương pháp trên kết quả. Phiên bản được viết nhiều tài liệu hơn sẽ được đăng dưới dạng trả lời cho that thread và có thể được phát hành lên CPAN.

4

Đưa trở lại này lại từ cõi chết kể rằng tôi chỉ viết các module List::Gen trên CPAN mà thực hiện chính xác những gì người đăng đang tìm kiếm:

use List::Gen; 

for my $item (@{gen { ... } \@list}) {...} 

tất cả các tính toán của danh sách là lười biếng, và có bản đồ/grep tương đương cùng với một vài chức năng khác.

mỗi hàm trả về một 'trình tạo' là tham chiếu đến mảng được gắn. bạn có thể sử dụng mảng liên kết trực tiếp, hoặc có một loạt các phương thức truy cập như trình vòng lặp để sử dụng.

+0

Tôi sẽ xem xét nó. Cảm ơn. – Axeman

+0

Không có vấn đề gì, nếu có bất kỳ tính năng nào bạn nghĩ nên ở trong đó, hãy cho tôi biết. –