2010-04-23 96 views
8

我繼承了一些人的代碼,他們最喜歡的過去時間是將每一行縮短到絕對最小值(有時只是爲了讓它看起來很酷)。他的代碼很難理解,但我設法理解(並重寫)了大部分代碼。這個Perl代碼如何從數組中選擇兩個不同的元素?

現在我偶然發現了一段代碼,無論我多麼努力地嘗試,我都無法理解。

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"); 
} 

從我能理解,這應該是某種隨機的,但我從來沒有見過一個更復雜的方式來實現隨機性。或者我的假設錯了?至少,這是代碼應該做的。選擇2個隨機文件並複製它們。

=== NOTES ===

OSA框架是我們自己的框架。它們是以UNIX的對應名稱命名的,並進行一些基本的測試,以便應用程序無需爲此而煩惱。

+1

Perl :: Tidy是重新格式化代碼的好工具。 :) – 2010-04-23 15:38:57

回答

12

這看起來像一些Perl語法的C代碼。有時候知道這個人想要的語言可以幫助你弄清楚發生了什麼。在這種情況下,人的大腦感染了內存管理,指針運算,和其他低級別的關注內部的工作,所以他要精細地控制一切:

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? 
    } 

... 
} 

這是很多,因爲原來的工作程序員要麼不理解哈希,要麼不喜歡它們。

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; 

如果你仍然討厭哈希和Perl 5。10或更高版本,您可以使用智能匹配來檢查值是否在數組中:

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

但是,您對此問題有特殊限制。既然你知道只有兩個元素,你只需要檢查你想添加的元素是否是先前的元素。在第一種情況下,它不會被取消,所以第一個添加總是有效的。在第二種情況下,它不能是陣列中的最後一個元素:

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

呵呵。我不記得上一次我使用until時,它實際上適合問題。 :)

注意,所有這些解決方案都存在,他們可能會導致一個無限循環,如果原始文件的數量問題小於2。我想通過添加一個保護條件更高了,因此沒有和單個文件的情況下一個錯誤,也許這兩個文件的情況下不打擾他們。

,你可以這樣做的另一個方法是重新洗牌(比如說,與List::Util)原始文件的完整列表,只是起飛前兩個文件:

use List::Util qw(shuffle); 

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

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

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

而對於5.10以下的Perl,'List :: Util :: first'是檢查元素是否在列表中的好方法。它有XS綁定,所以比編寫自己的foreach {... last if ...}循環更快。 – Ether 2010-04-23 16:18:58

+0

+1''redo' :) – friedo 2010-04-23 16:19:43

+1

有趣的是,重寫比原來短! – Zaid 2010-04-23 18:31:22

0

這裏是另一種方式來選擇2個獨特的隨機指數:

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

它選擇從@heads隨機元素。

然後,它會添加另一個隨機,但不同於@heads的元素(如果它是先前選擇的元素,它將滾動瀏覽@heads直到找到之前未選中的元素)。

總之,它在@heads數組中選擇N個(在你的情況下N = 2)不同的隨機索引,然後複製對應於這些索引的文件。

我個人不同的看法寫了一下:你打成「WTF」不是那麼令人不安的是,它只是簡單地確保$selected_heads[$i]仍然作爲@head有效標

# ... 
%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

的一部分。真正令人不安的部分是,確保他不選擇相同的文件是一種非常低效的方式。

再次,如果@heads的大小很小,從0..$#heads步進可能比僅生成int rand(2)更有效,並測試它們是否相同。

但基本上它是隨機拷貝兩個文件(爲什麼?)作爲'.txt'文件和'.cache'文件。

1

如何只

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"); 
} 

,除非以後使用@heads@selected_heads

相關問題