[Raku] 如何將字串轉換成數字
作者:gugod 發佈於: #rakulang在 Raku 語中,如果有個字串 $s
內容裝的都是數目字,而我們要將其傳換為整數值或是有理數數值,大致上來說,就是這三種算式:
$s.Int
$s.parse-base(10)
$s + 0
如果不是十進位而是 $b
進位,就只能靠 $s.parse-base($b)
。
另外 .parse-base
的傳回值的型別可能是 Int
或 Rat
。視其處理對象而定。要取得特定型別的話就再往後加轉型。例如 $s.parse-base(10).Rat
或 $s.parse-base(10).Int
。Rat
轉 Int
基本上是把小數點後直接省略。視需要或可改用 $s.parse-base(10).floor
, $s.parse-base(10).ceiling。
以下分幾種使用情境來說明。
十進位
若有字串內容全是 ASCII 數目字的 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 這十個字符的排列組合,那麼有幾種方式可以將其轉成十進位整數。
比方說用 .Int
:
my Str $s = "42";
say $s.WHAT, $s.raku; #=> (Str)"42"
my Int $n = $s.Int;
say $n.WHAT, $n.raku; #=> (Int)42
或者用 .parse-base(10)
:
my Str $s = "42";
say $s.WHAT, $s.raku; #=> (Str)"42"
my Int $n = $s.parse-base(10);
say $n.WHAT, $n.raku; #=> (Int)42
或者,也是可以利用 +
算符會將算字自動轉型的特性:
my Str $s = "42";
say $s.WHAT, $s.raku;
#=> (Str)"42"
my Int $n = $s + 0;
say $n.WHAT, $n.raku;
#=> (Int)42
... 但這一招在 $s
的內容會被解析成非整數的其他數字型別時,會出問題:
my Str $s = "42.0";
my Int $n = $s + 0;
執行這兩行會得到如下的錯誤訊息:
Type check failed in assignment to $n; expected Int but got Rat (42.0)
這是由於我們宣告 $n
必為 Int
,而 $str + 0
會算得 Rat
型別。但另一方面,在 $s
為 "42.0"
時,$s.Int
能直接將其轉為 Int
型別的 42
這值,而 $s.parse-base(10)
則是會將其轉成 Rat
型別的 42.0
。
為了觀察到這種結果,可在變數宣告處將型別去掉,讓 raku
自動判斷:
my $s = "42.0";
my $n = $s.Int;
say $n.WHAT, $n.raku;
#=> (Int)42
my $m = $s.parse-base(10);
say $m.WHAT, $m.raku;
#=> (Rat)42.0
在前例裡 $s
正好是數值上的整數,但若 $s
是 "42.9"
這種並非數值上的整數的話,$s.Int
所做的轉換則相當於是將去小數點後的數字給捨去。
my $s = "42.9";
my $n = $s.Int;
say $n.WHAT, $n.raku;
#=> (Int)42
$s = "-42.9";
$n = $s.Int;
say $n.WHAT, $n.raku;
#=> (Int)-42
如何轉為十六進位數字
若字串 $s
的內容是十六進位整數,且用的是 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f 這十六個字符,那麼 $s.pasre-base(16)
就可以將 $s
轉換為 Int
型別的數值:
my Str $s = "beef";
my Int $n = $s.parse-base(16);
say $n.WHAT, $n.raku;
#=> (Int)48879
但若是對 "beef"
呼叫 .Int
就只會得到錯誤,因為 .Int
是假設其處理對象是十進位數字。
my $n = "beef".Int;
#=>
Earlier failure:
Cannot convert string to number: base-10 number must begin with valid digits or '.' in '⏏beef' (indicated by ⏏)
也就是說基本上要將十六進位數字字串轉成其數值,基本上就只能靠 .parse-base(16)
了。
這有個好處,就是可以讓解析十六進位有理數表示法:
my Str $s = "a.a";
my Rat $n = $s.parse-base(16);
say $n.WHAT, $n.raku;
#=> (Rat)10.625
附帶一提,由於 Rat
型別能用來處理有有理數,因此也可再換為分數:
my Rat $n = "a.a".parse-base(16);
say $n.numerator, "/", $n.denominator;
#=> 85/8
再附帶一提,通常分子與分母兩數字是要同時取得的,有個名字長度較短的方法可用:.nude
my Rat $n = "a.a".parse-base(16);
say $n.nude.join("/")
#=> 85/8
(這名字取得真... 好?)
八進位
用 .parse-base(8)
可處正數,及帶負號的負數:
my Str $s = "100";
my Int $n = $s.parse-base(8);
say $n.WHAT, $n.raku;
#=> (Int)64
$s = "-25";
$n = $s.parse-base(8);
say $n.WHAT, $n.raku;
#=> (Int)-21
二進位
用 .parse-base(2)
可處正數,及帶負號的負數:
my Str $s = "00010001";
my Int $n = $s.parse-base(2).Int;
say $n.WHAT, $n.raku;
#=> (Int)17
$s = "-00010001";
$n = $s.parse-base(2).Int;
say $n.WHAT, $n.raku;
#=> (Int)-17
如果是要處理以第一位元表示正負號 (0 為正 1 為負) 的那種形式,那就得先事先做一次字串處理了:
my Str $s = "10010001";
$s ~~ s/^1/-/;
say $s;
#=> -0010001
my Int $n = $s.parse-base(2).Int;
say $n.WHAT, $n.raku;
#=> (Int)-17
三十六進位
這是 parse-base
所支援的最高進位。所用的數目字符號是 0
... 9
加上 A
... Z
二十六個英文字母。小寫 a
.... z
也是可以用的,與其大寫字母對應到同樣的數值。
my Int $n = "Raku".parse-base(36)
say $n.WHAT, $n.raku;
#=> (Int)1273422