學習軌道
/
Julia
Julia
/
練習
/
一乾二淨
一乾二淨

一乾二淨

學習練習

簡介

這是個_龐大_的主題! 光是它就能寫成一本厚書,而且真的有好幾個人這麼做了(在 Amazon 搜尋「unicode book」就能看到一些例子)。

極簡歷史

幾十年前,電腦處理字元要簡單得多,當時的程式設計師假設英文是唯一重要的語言。 於是:26 個字母(含大小寫)、10 個數字、幾個標點符號,再加上一個讓鈴聲響起的碼(0x07),全部塞進 7 個位元裡:這就是 ASCII 字元集。

很自然地,有人開始問那 à、ä 和 Ł 怎麼辦,接著又有人問起 ऄ、ஹ 和 ญ,然後年輕人想要 emoji 😱。 該怎麼辦呢?

長話短說,許多聰明又有耐心的人得在委員會裡待上好幾年,把 Unicode 字元集以及 UTF-8 之類編碼的細節一一敲定,而大量軟體也需要_極_複雜的改寫。 同時,也引入了許多新的 bug。

為了避免_所有東西_都壞掉,Unicode/UTF-8 的設計確保前 127 個編碼與 ASCII 完全相同_(連鈴聲也是)_。

Julia 中的字元

大約 2005 年之後才設計的語言有個很大的優勢:當時已經有還算穩定的 Unicode 標準了。

Julia(2012 年首次發布)可以直接假設一切都會是 Unicode:字元、字串、變數與函式名稱、數學運算子……

引用手冊的說法:「Julia 讓處理純 ASCII 文字變得簡單又有效率,而處理 Unicode 也盡可能地簡單又有效率。」 注意「as possible」這幾個字,它是這段話很重要的一部分。

字元常值是寫在單引號裡,和寫在雙引號裡的字串不同。

對來自 C/C++ 世界的人來說這很明顯,但對 Python 和 Javascript 的程式設計師來說可能很容易搞混。

julia> a = 'a'  # Roman alphabet
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

julia> typeof(a)
Char

julia> jha = 'झ'  # Devanagari alphabet
'झ': Unicode U+091D (category Lo: Letter, other)

julia> typeof(jha)
Char

julia> '❤'  # heart emoji
'❤': Unicode U+2764 (category So: Symbol, other)

從這些例子可以看出型別是 Char,而且 Julia 還進一步提供了字元類別的資訊。

仔細看的話,這些字元似乎可以用 4 個十六進位數字表示。 完整的字元集則需要多達 6 個十六進位數字。

這些數字稱為「碼位」,目前的範圍從 U+0000 到 U+10FFFF。 它們會顯示在 REPL 中,但在程式碼裡要用 codepoints() 取得。

在Char和Int之間轉換很簡單:

julia> Int('a')
97

julia> Char(97)
'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)

編譯器允許對Char做_某些_形式的整數運算:

julia> 'b' - 'a'  # interval, in alphabetic order
1

julia> 'b' + 'a'
ERROR: MethodError: no method matching +(::Char, ::Char)

julia> 'a' + 5
'f': ASCII/Unicode U+0066 (category Ll: Letter, lowercase)

julia> 'f' + ('A' - 'a')  # same as `uppercase('f')`
'F': ASCII/Unicode U+0046 (category Lu: Letter, uppercase)

處理字元的函式

許多處理字串的函式也能用在Char輸入上。

  • 對適用的字母系統,可用uppercase()和lowercase()改變大小寫。
  • 用isuppercase()和islowercase()測試大小寫。
  • 用以下函式測試字元類型:
    • isletter()涵蓋許多字母系統
    • isdigit(),只測試 0:9
    • isnumeric(),比isdigit更廣,所以對 ¾ 和各種非歐洲文字都會得到true
    • isxdigit(),十六進位數字
    • isascii(),Unicode 之前的字元
    • ispunct(),標點符號
    • isspace(),任何空白字元
    • isprint(),可列印字元(相反的是iscntrl())
islowercase('A')  # false
uppercase('γ')  # 'Γ': Unicode U+0393 (category Lu: Letter, uppercase)
ispunct('@')  # true
isdigit('A')  # false
isxdigit('A')  # true

Char 向量與字串的互相轉換

要從字串轉成 Char 向量,可以用collect()。

要從 Char 向量轉成字串,則有String()建構式。

julia> s = "abcde"
"abcde"

julia> cv = collect(s)
5-element Vector{Char}:
 'a': ASCII/Unicode U+0061 (category Ll: Letter, lowercase)
 'b': ASCII/Unicode U+0062 (category Ll: Letter, lowercase)
 'c': ASCII/Unicode U+0063 (category Ll: Letter, lowercase)
 'd': ASCII/Unicode U+0064 (category Ll: Letter, lowercase)
 'e': ASCII/Unicode U+0065 (category Ll: Letter, lowercase)

julia> String(cv)
"abcde"

這對任何字元都適用,不限於 ASCII。

julia> collect("❤,😱")
3-element Vector{Char}:
 '❤': Unicode U+2764 (category So: Symbol, other)
 ',': ASCII/Unicode U+002C (category Po: Punctuation, other)
 '😱': Unicode U+1F631 (category So: Symbol, other)

注意,String()建構式是作用在 Vector 上。 要把單一個Char_轉型_成單一字元的字串,該用的函式是string(),開頭是小寫的s。

julia> string('a')
"a"

儲存

到目前為止,這份文件裡的內容看起來都相對簡單,所以真的沒什麼好擔心的嗎?

很可惜,這想法太樂觀了!

其中一個麻煩來自每個碼位「最多」需要 6 個十六進位數字。 這代表以 UTF-8 編碼時,不同字元在記憶體中需要的大小也不同。

一個位元組只能儲存到 255 的(無號)數字,也就是兩個十六進位數字,所以 UTF-8 用可變的位元組數(1 到 4 個)來儲存一個Char。 這些單位稱為「碼元」,而ncodeunits()函式會回傳某個字元所需的數量。

julia> codepoint(jha)  # jha 'झ' is defined in an earlier example
0x0000091d

julia> ncodeunits(jha)
3

julia> ncodeunits('a')  # ASCII character
1

julia> ncodeunits('😱')  # emoji
4

此外,並非所有能在螢幕上顯示的東西都有自己專屬的碼位。 有些外觀上彼此不同的字元被視為由其他字元衍生而來,因此 Unicode 把它們當成一個母字元加上一個修飾符。

這個問題會影響字串,為索引帶來挑戰。

說明

在這道練習中,你將實作一組實用常式中的一部分,用來協助開發者清理識別字名稱。

在這 6 個任務中,你將逐步建構transform函式來轉換單一字元,以及clean函式來轉換字串。

合法的識別字包含零個或多個字母、底線、連字號、問號和表情符號。

如果傳遞空字串給clean函式,應該回傳空字串。

1. 將遇到的連字號取代為底線

實作transform函式,將任何連字號取代為底線。

julia> transform('-')
"_"

2. 移除所有空白

移除所有空白字元。 這會包含開頭和結尾的空白。

julia> transform(' ')
""

3. 將 camelCase 轉換為 kebab-case

修改transform函式,將 camelCase 轉換為 kebab-case

julia> transform('D')
"-d"

4. 略過數字字元

修改transform函式,略過任何數值字元。

julia> transform('7')
""

5. 將希臘小寫字母取代為問號

修改transform函式,取代介於 'α' 到 'ω' 範圍內的所有希臘字母。

julia> transform('β')
"?"

6. 合併這些操作以處理字串

實作clean函式,將這些操作套用到整個字串。

不符合規則的字元應該原樣通過。

julia> clean("  a2b Cd-ω😀  ")
"ab-cd_?😀"
透過 GitHub 編輯 連結會在新視窗或分頁中開啟
Julia Exercism

準備好開始 一乾二淨 了嗎?

註冊 Exercism,透過 35 個概念128 個練習 和真人引導來學習並精通 Julia,全部免費。