2010-04-23 37 views
8

Tôi đã thừa kế một số mã từ một chàng trai có thời gian yêu thích trong quá khứ là rút ngắn mọi dòng xuống mức tối thiểu tuyệt đối (và đôi khi chỉ để làm cho nó trông thật tuyệt). Mã của anh ấy rất khó hiểu nhưng tôi đã hiểu (và viết lại) phần lớn nó.Mã Perl này chọn hai phần tử khác nhau từ một mảng như thế nào?

Bây giờ tôi đã tình cờ gặp một đoạn mã, dù tôi cố gắng thế nào đi nữa, tôi không thể hiểu được.

my @heads = grep {s/\.txt$//} OSA::Fast::IO::Ls->ls($SysKey,'fo','osr/tiparlo',qr{^\d+\.txt$}) ||(); 
my @selected_heads =(); 
for my $i (0..1) { 
    $selected_heads[$i] = int rand scalar @heads; 
    for my $j ([email protected]) { 
     last if (!grep $j eq $_, @selected_heads[0..$i-1]); 
     $selected_heads[$i] = ($selected_heads[$i] + 1) % @heads; #WTF? 
    } 
    my $head_nr = sprintf "%04d", $i; 
    OSA::Fast::IO::Cp->cp($SysKey,'',"osr/tiparlo/$heads[$selected_heads[$i]].txt","$recdir/heads/$head_nr.txt"); 
    OSA::Fast::IO::Cp->cp($SysKey,'',"osr/tiparlo/$heads[$selected_heads[$i]].cache","$recdir/heads/$head_nr.cache"); 
} 

Từ những gì tôi có thể hiểu, đây được cho là một loại ngẫu nhiên, nhưng tôi chưa bao giờ thấy một cách phức tạp hơn để đạt được sự ngẫu nhiên. Hay giả định của tôi sai? Ít nhất, đó là những gì mã này được cho là phải làm. Chọn 2 tệp ngẫu nhiên và sao chép chúng.

=== GHI CHÚ ===

Khung OSA là khung của chính chúng tôi. Chúng được đặt tên theo các đối tác UNIX của chúng và thực hiện một số thử nghiệm cơ bản để ứng dụng không cần phải bận tâm với điều đó.

+1

Perl :: Tidy là công cụ tuyệt vời để định dạng lại mã. :) –

Trả lời

12

Điều này giống như một số mã C có cú pháp Perl. Đôi khi biết ngôn ngữ mà người đó đang suy nghĩ giúp bạn tìm ra những gì đang diễn ra. Trong trường hợp này, bộ não của người bị nhiễm với các hoạt động bên trong của quản lý bộ nhớ, con trỏ số học, và mối quan tâm ở mức độ thấp khác, vì vậy ông muốn tỉ mỉ kiểm soát tất cả mọi thứ:

my @selected_heads =(); 

# a tricky way to make a two element array 
for my $i (0..1) { 

    # choose a random file 
    $selected_heads[$i] = int rand @heads; 

    # for all the files (could use $#heads instead) 
    for my $j ([email protected]) { 
     # stop if the chosen file is not already in @selected_heads 
     # it's that damned ! in front of the grep that's mind-warping 
     last if (!grep $j eq $_, @selected_heads[0..$i-1]); 

     # if we are this far, the two files we selected are the same 
     # choose a different file if we're this far 
     $selected_heads[$i] = ($selected_heads[$i] + 1) % @heads; #WTF? 
    } 

... 
} 

Đây là rất nhiều công việc vì bản gốc lập trình viên không hiểu băm hoặc không thích chúng.

my %selected_heads; 
until(keys %selected_heads == 2) 
    { 
    my $try = int rand @heads; 
    redo if exists $selected_heads{$try}; 
    $selected_heads{$try}++; 
    } 

my @selected_heads = keys %selected_heads; 

Nếu bạn vẫn ghét băm và có Perl 5.10 hoặc sau đó, bạn có thể sử dụng đối sánh thông minh để kiểm tra xem một giá trị có nằm trong một mảng không:

my @selected_heads; 
until(@selected_heads == 2) 
    { 
    my $try = int rand @heads; 
    redo if $try ~~ @selected_heads; 
    push @selected_heads, $try; 
    } 

Tuy nhiên, bạn có một hạn chế đặc biệt về vấn đề này. Vì bạn biết chỉ có hai phần tử, bạn chỉ cần kiểm tra xem phần tử bạn muốn thêm có phải là phần tử trước hay không. Trong trường hợp đầu tiên nó sẽ không được undef, do đó, việc bổ sung đầu tiên luôn luôn hoạt động. Trong trường hợp thứ hai, nó không thể là phần tử cuối cùng trong mảng:

my @selected_heads; 
until(@selected_heads == 2) 
    { 
    my $try = int rand @heads; 
    redo if $try eq $selected_heads[-1]; 
    push @selected_heads, $try; 
    } 

Huh. Tôi không thể nhớ lần cuối cùng tôi sử dụng until khi nó thực sự phù hợp với vấn đề. Lưu ý rằng tất cả các giải pháp này đều có vấn đề là chúng có thể gây ra vòng lặp vô hạn nếu số lượng tệp gốc nhỏ hơn 2. Tôi sẽ thêm điều kiện bảo vệ cao hơn để trường hợp tệp không và duy nhất thông qua một lỗi và có lẽ cả hai trường hợp tập tin không bận tâm để đặt hàng chúng.

Một cách khác bạn có thể làm điều này là để shuffle (nói, với List::Util) toàn bộ danh sách các tập tin ban đầu và chỉ cất cánh hai tập tin đầu tiên:

use List::Util qw(shuffle); 

my @input = 'a' .. 'z'; 

my @two = (shuffle(@input))[0,1]; 

print "selected: @two\n"; 
+0

Và đối với Perls dưới 5.10, 'Danh sách :: Util :: first' là một cách tuyệt vời để kiểm tra xem một phần tử có nằm trong danh sách hay không. Nó có các ràng buộc XS vì vậy nó nhanh hơn việc viết vòng lặp 'foreach {... last if ...}' của riêng bạn. – Ether

+0

+1 cho 'redo' :) – friedo

+1

Vui cách viết lại ngắn hơn bản gốc! – Zaid

0

Dưới đây là một cách khác để chọn 2 chỉ số ngẫu nhiên độc đáo:

my @selected_heads =(); 
my @indices = 0..$#heads; 
for my $i (0..1) { 
    my $j = int rand (@heads - $i); 
    push @selected_heads, $indices[$j]; 
    $indices[$j] = $indices[@heads - $i - 1]; 
} 
2

Nó chọn một yếu tố ngẫu nhiên từ @heads.

Sau đó, nó thêm vào một ngẫu nhiên nhưng khác nhau phần tử từ @heads (nếu nó là phần tử được chọn trước đó, nó cuộn qua @heads cho đến khi tìm thấy phần tử không được chọn trước đó).

Tóm lại, nó chọn N (trong trường hợp của bạn N = 2) các chỉ mục ngẫu nhiên khác nhau trong mảng @heads và sau đó sao chép các tệp tương ứng với các chỉ mục đó.

Cá nhân tôi sẽ viết nó một chút khác nhau:

# ... 
%selected_previously =(); 
foreach my $i (0..$N) { # Generalize for N random files instead of 2 
    my $random_head_index = int rand scalar @heads; 
    while ($selected_previously[$random_head_index]++) { 
     $random_head_index = $random_head_index + 1) % @heads; # Cache me!!! 
    } 
    # NOTE: "++" in the while() might be considered a bit of a hack 
    # More readable version: $selected_previously[$random_head_index]=1; here. 
1

Phần bạn có nhãn "WTF" không phải là quá rắc rối, nó chỉ đơn giản là đảm bảo rằng $selected_heads[$i] vẫn là một subscript hợp lệ @head. Phần thực sự đáng lo ngại là nó là một cách khá hiệu quả để đảm bảo rằng anh ta không chọn cùng một tệp.

Sau đó, một lần nữa, nếu kích thước @heads là nhỏ, bước từ 0..$#heads có lẽ hiệu quả hơn là chỉ tạo ra int rand(2) và thử nghiệm nếu chúng giống nhau.

Nhưng về cơ bản, nó sao chép hai tệp một cách ngẫu nhiên (tại sao?) Dưới dạng tệp '.txt' và tệp '.cache'.

1

Làm thế nào về chỉ

for my $i (0..1) { 
    my $selected = splice(@heads, rand @heads, 1); 
    my $head_nr = sprintf "%04d", $i; 
    OSA::Fast::IO::Cp->cp($SysKey,'',"osr/tiparlo/$selected.txt","$recdir/heads/$head_nr.txt"); 
    OSA::Fast::IO::Cp->cp($SysKey,'',"osr/tiparlo/$selected.cache","$recdir/heads/$head_nr.cache"); 
} 

trừ khi @heads hoặc @selected_heads được sử dụng sau.

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