這是某次 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
. 所以最後可以看 $_
的數值, 真值表示輸入的數值可形成三角形, 反之則否.