[Raku] 以自定型別來解 Fizz Buzz
作者:gugod 發佈於: #rakulang雖然維基百科上說 Fizz Buzz 是會出現在程式設計師的面試考題,但現在應該沒有甚麼公司真的還在用這個題目來做篩選了吧 😎
姑且還是先把題目定義一下:
請提供一個函式,能將數字 1 至 100 逐一印出,但若碰到 3 的倍數則改印出 Fizz
,若碰到 5 的倍數則改印 Buzz
,碰到同時爲 3 與 5 的倍數時則改印 Fizz Buzz
。
直觀解之一就是依序檢查數字是否爲 15、5、3 的倍數,然後做將數字換成不同的字串。檢查順序會影響結果,15 要先檢查,但 3 和 5 兩者的檢查順序則不影響結果:
for 1..100 -> $n {
# [3]
say do given ($n) {
when $_ %% 15 { "Fizz Buzz" }
when $_ %% 3 { "Fizz" }
when $_ %% 5 { "Buzz" }
default { "$_" }
}
}
在此提供一個重構模式。那就是把「3 的倍數」、「5 的倍數」視爲兩種型別,而「15 的倍數」則可以由前兩者合成.
# [1]
subset Fizzer of Int where { $_ %% 3 }
subset Buzzer of Int where { $_ %% 5 }
# [2]
subset FizzBuzzer of Fizzer where Buzzer;
for 1..100 -> $n {
say do given ($n) {
when FizzBuzzer { "Fizz Buzz" }
when Fizzer { "Fizz" }
when Buzzer { "Buzz" }
default { "$_" }
}
}
[1] 處是定義兩個 Int
的子集合,Fizzer
爲 3 的倍數,Buzzer
爲 5 的倍數。
[2] 處定義的 FizzBuzzer
是「Fizzer
的子集合中,同時又是 Buzzer
者」。也就是同時爲 3 與 5 的倍數的 Int
。
[3] 處的 given...when
的這段,則是改用方才定義出的三種新的 Int
子集合來改寫。順序一樣是要注意的。
或者,可以不必使用 FizzBuzzer
,而是將 when FizzBuzzer
的改爲 when Fizzer & Buzzer
:
for 1..100 -> $n {
say do given ($n) {
when Fizzer & Buzzer { "Fizz Buzz" }
when Fizzer { "Fizz" }
when Buzzer { "Buzz" }
default { "$_" }
}
}
此處的 &
爲 Junction 算符。表示「前後兩方的條件都要滿足」之意。when Fizzer & Buzzer
就相當於 when ($_ ~~ Fizzer && $_ ~~ Buzzer)
,也就是 $_
必須滿足 Fizzer
的條件,同時必須滿足 Buzzer
的條件。
顯然以解 Fizz Buzz 這個練習題來說,任何重構都算是多餘的工程技術了。除了練習以外,沒有別的目的。這種自定型別用在檢查使用者輸入的時候非常的合用。尤其當條件稍微複雜一些的時候,若能將判別式拆解成「幾個集合的交集」這種形式,顯然能在讓程式碼更加容易閱讀。