2004-11-02 22:16:18

$_^=y/ /+/<s/./$&*2>eval/eg

這是某次 Perl Golf 的解答, 題目在這裡:

http://terje2.perlgolf.org/~pgas/score.pl?func=rules&hole=66&season=1

輸入三個以一個空白隔開的 1 至 6 的數字, 測試這三個數字是否可形成一個三角形. 奪得冠軍的解答如下:

$_^=y/ /+/<s/./$&*2>eval/eg

其中輸入的字串在 $_ 之中.

寫成一串大概看得懂的人沒幾個, 把它拆開來.

  $_ ^= ( y/ /+/ ) < ( s/./ $&*2 > eval /eg )

第一個括號很容易, 就是把其中的空白代換成 '+'. 假設輸入的數值是 "6 6 4", 經過第一個括號之後, 它會變成 "6+6+4". 別忘了 y/// 的傳回值是代換成功的次數.

第二個括號看起來也不難了. 它就是把 $_ 裡的每一個字元挑出來, 執行 $&*2 > eval, 並將結果代換回去. $& 代表的是被比對到的字元, 而 eval 的預設變數是 $_, 也就是 "6+6+4".

讓我們先複習一下三角形的條件. 要形成一個三角形, 它必須任兩邊和大於第三邊, 也就是

a + b > c

兩邊都加入 c:

a + b + c > 2*c

沒錯, s/// 的第二個部份就是在執行這樣的檢查. 作者是以反向的條件來檢查, 也就是只要符合這樣的條件, 它會傳回 false. 在這裡, false 就是 '', 也就是空字串. 如果符合三角形條件, 在 $_ 中對應的數字就會被消去. 反之, 如果有不符合的, 就會在對應的數字留下 '1'.

這樣有另一個好處, 就算拿 '+' 來比的話, 這個條件也一定是 false, 所以 '+' 也會被消去. 所以 s/// 的第一個部份就直接以 . 來比對, 也就是把所有的字元都拿來比對. 如果全都符合的話, $_ 會變成空字串. 如果有不符合的數值, 那麼至少會有一個 '1'.

然後 s/// 的傳回值是比對並代換的次數, 所以它一定是 5.

我們已經把右邊都跑完了, 所以上面這個程式跑到最後會變成

$_ ^= 2 < 5

也就是

$_ ^= 1

如果數值都符合三角形條件, 那麼 $_ 會是空字串, 意即 $_ = 0 ^ 1 => $_ = 1. 反之則為 $_ = 1 ^ 1 = 0. 所以最後可以看 $_ 的數值, 真值表示輸入的數值可形成三角形, 反之則否.

由 plasma 於 10:16 PM 所發表 | 迴響 (9)