[Raku] 如何定義函式
作者:gugod 發佈於: ,更新於: #rakulang定義函式的關鍵字是 sub
,基本上的語法結構是要帶個函式名、特徵宣告、及本文。以函式名 example
為例:
sub example ( 特徵宣告 ) {
本文
}
特徵宣告就是參數串列及傳回值,以 -->
分隔,寫在小括號裡面。
參數串列 --> 傳回值
以這種語法定義出的函式,在使用時的語法如:
變數 = example( 參數串列 );
以下以幾個不同的範例來說明各項細節。
例:華氏溫度換攝氏溫度
以華氏溫度轉攝氏溫度為例,其公式為:
C = (F - 32) / 1.8
其中 F 代表華氏溫度度數,而 C 代表攝式溫度度數。
這運算需要一個參數 $f
,代表華氏溫度度數,算完後會有一個傳回值,也就是攝式溫度度數。兩種數值的型別都為有理數 Rat
。因此特徵宣告的部份是 Rat $f --> Rat
。全文如下:
sub celsius-from-farenheit (Rat $f --> Rat) {
return ($f - 32) / 1.8;
}
攝氏溫度轉華氏溫度也是類似:
sub farenheit-from-celsius (Rat $c --> Rat) {
return 1.8 * $c + 32;
}
例:BMI 值計算
BMI (身體質量指數) 的計算公式為:
BMI = 體重 / 身高²
其體重的單位需為公斤,而身高的單位需為公尺。
也就是說在特徵宣告的部份是有兩個參數與一個傳回值,像是這樣:
(Rat $weight, Rat $height --> Rat)
若先假設 $weight
代表以公斤為單位的體重數值,$height
代表以公尺為單位的身高數值,也就是不必進行任何單位換算,這函式可定義如下:
sub bmi (Rat $weight, Rat $height --> Rat) {
return $weight / $height ** 2;
}
例:空氣品質指標數字轉口語敘述
依環保暑空氣品質監測網上的表格,空氣品質指標是個整數數值,範圍在 0 到 500 之間。並且由小到大,分為能用以下這幾個不同的口語敘述來形容:良好、普通、對敏感族群不健康、對所有族群不健康、非常不健康、危害。
試以此表格為規格來設計一個函式,將空氣品質指標轉為口語敘述吧。
粗略來看,這函式的特徵宣告應為 (Int $x --> Str)
,也就是其參數為一個代表空氣品質指標的變數 $x
,而傳回值為一字串。
sub airquality-in-zh-Hant-TW (Int $x --> Str) {
本文
}
但看來表格只定義在整數數值的 0..500 這範圍內,參數若是落在這範圍之外,就不應處理。那麼,可在函式的特徵宣告部份加上限制。寫法如下:
(Int $x where { 0 <= $x <= 500 } --> Str)
本文部份,可依表格上所定義的各段邊界,以 if...elsif...
來定義:
sub airquality-in-zh-Hant-TW (Int $x where { 0 <= $x <= 500 } --> Str) {
return do {
if 0 <= $x <= 50 { "良好" }
elsif 51 <= $x <= 100 { "普通" }
elsif 101 <= $x <= 150 { "對敏感族群不健康" }
elsif 151 <= $x <= 200 { "對所有族群不健康" }
elsif 201 <= $x <= 300 { "非常不健康" }
elsif 301 <= $x <= 500 { "危害" }
};
}
或是以 given ... when...
來定義。
sub airquality-in-zh-Hant-TW (Int $x where { 0 <= $x <= 500 } --> Str) {
return do {
given $x {
when 0..50 { "良好" }
when 51..100 { "普通" }
when 101..150 { "對敏感族群不健康" }
when 151..200 { "對所有族群不健康" }
when 201..300 { "非常不健康" }
when 300..500 { "危害" }
}
};
}
又或者,可利用這規格表以 50 為範圍單位的這項特性,建出翻譯表來:
sub airquality-in-zh-Hant-TW (Int $x where { 0 <= $x <= 500 } --> Str) {
my constant @translations = [
"良好",
"普通",
"對敏感族群不健康",
"對所有族群不健康",
"非常不健康",
"非常不健康",
"危害",
"危害",
"危害",
"危害",
];
return @translations[ $x div 50 ];
}
這個以查表方式來實做的版本由於運算次數較少,所花費的執行時間也應較少,但由於不易與原規格表直接兩相對照,所以在可讀性方面算是有點扣分。優劣各有,不失為一種選擇。
例:同時取得商數及餘數的整數除法
取整數除法的商數的算符是 div
,而除餘數的算符是 %
,若能有一函式能讓人同時取得商數及餘數,或許在某些情境之下會比較方便。
特徵宣告的部份,可定為:
(Int $dividend, Int $divisor --> Array[Int])
$divident
為被除數,$divisor
為除數。傳回值為兩個整數,分別代表商數及餘數。在此先簡單以 Array[Int]
做為傳回值的型別。
函式全文如下:
sub divmod (Int $dividend, Int $divisor --> Array[Int]) {
my $quotient = $dividend div $divisor;
my $remainder = $dividend % $divisor;
return Array[Int].new( $quotient, $remainder );
}
使用起來如下:
my ($q, $r) = divmod(14, 3);
say $q; #=> 4
say $r; #=> 2
雖然傳回值型別是個陣列,但若有賦值算式右方為陣列 (Array
) 而左方為串列 (List
),那麼 raku 會將陣列內容逐一對應到串列裡去。因此 $q
對應到陣列第零個元素,而 $r
對應到陣列第一個元素。
附帶一提如果將此函式名為 infix:<divmod>
,那就相當於是在將 divmod
這個關鍵字定義成一個新的中綴算符,在使用時要放在兩算子中間:
sub infix:<divmod> (Int $dividend, Int $divisor --> Array[Int]) {
my $quotient = $dividend div $divisor;
my $remainder = $dividend % $divisor;
return Array[Int].new( $quotient, $remainder );
}
my ($q, $r) = 14 divmod 3;
say $q; #=> 4
say $r; #=> 2