Julia 鼓勵程式設計師盡可能把程式碼放進可以 JIT 編譯的函式裡,而且依照設計,建立許多小型函式本來就有很好的效能。
這樣一來,往往會留下許多小而簡單的函式,需要把它們組合起來,才能完成不簡單的任務。
一個顯而易見的做法是巢狀呼叫函式。 下面這個例子非常刻意,但足以說明重點。
julia> first.(titlecase.(reverse.(["my", "test", "strings"])))
3-element Vector{Char}:
'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)
這種做法的缺點是,隨著巢狀層級越來越深,可讀性會迅速下降。
我們需要一個更簡單、更有彈性的做法。
這是數學家鍾愛的技巧,而 Julia 沿用了數學的語法。
任意數量的函式都可以用∘運算子composed在一起(輸入\circ後再按 Tab)。
結果可以當成單一個函式來用。
julia> compfunc = first ∘ titlecase ∘ reverse
first ∘ titlecase ∘ reverse
julia> compfunc.(["my", "test", "strings"])
3-element Vector{Char}:
'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)
# alternative syntax, giving the same result
julia> (first ∘ titlecase ∘ reverse).(["my", "test", "strings"])
有幾點值得注意:
另一種做法可以想成是_程式設計師_的路線,而不是_數學家_的。
Pipelines長久以來一直用在 Unix shell 指令碼中,近年也在主流程式語言裡流行起來(F# 有時被認為是率先採用它們的推手)。
基本概念是先有一份資料,然後讓它經過一連串函式,最後得到結果。
管道運算子是|>(和 F# 及近期的 R 版本一樣),不過 Julia 也提供了廣播版本.|>。
julia> ["my", "test", "strings"] .|> reverse .|> titlecase .|> first
3-element Vector{Char}:
'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
'T': ASCII/Unicode U+0054 (category Lu: Letter, uppercase)
'S': ASCII/Unicode U+0053 (category Lu: Letter, uppercase)
執行的方向現在嚴格由左到右,每個函式的輸出會順著箭頭的方向流動,成為下一個函式的輸入。
用來示範合成與管道的函式全都只接受_單一_引數,這絕非巧合。
有些純函式語言會把_第一個_引數送進函式,同時允許附帶其他引數。
相較之下,Julia 在管道裡只接受函式_名稱_(或同等的東西),不能附帶任何額外的引數。
這背後有重要的技術原因(和currying並非語言設計的標準一環有關)。
對柯里化毫無概念的_眾多_人,只要接受這項限制並非粗心大意造成的疏漏,而且未來幾版的 Julia 也不太可能改變就行了。
我們需要只接受單一引數、又能完成各種所需工作的函式。 幸運的是,在 Julia 裡定義新函式很容易。
最簡單的方式是使用anonymous function。
舉例來說,如果我們有一個輸入字串,想用底線把它切開:
julia> "my_test_strings" |> (s -> split(s, '_'))
3-element Vector{SubString{String}}:
"my"
"test"
"strings"
接著就能像之前一樣,把那個向量送進其他函式。
在這個例子裡,把匿名函式用括號包起來是可有可無的,但一般來說,這麼做有助於減少歧義。
同樣地,我們也可以在程式較早的地方建立一個具名函式,需要時再重複使用。
在這個練習中,你將幫助高中情侶在社群媒體上公開示愛,用他們名字的首字母產生一個帶有 unicode 愛心的訊息:
❤ J. + M. ❤
實作 cleanupname 方法。
它應該接受一個名字,移除其中所有的 - 字元,並以空格取代。
同時也應該移除名字開頭和結尾的空白。
julia> cleanupname("Jane-Ann")
"Jane Ann"
實作 firstletter 方法。
它應該接受一個名字,並以字串的形式回傳它的第一個字母。
記得重複使用你在前一步定義的 cleanupname,並將它與其他函式組合起來。
julia> firstletter("Jane")
"J"
實作 initial 方法。
它應該接受一個名字,回傳它的第一個字母,轉為大寫,後面加上一個句點。
記得重複使用你在前一步定義的 firstletter。
initial("Robert")
"R."
實作 couple 方法。
它應該接受兩個名字,回傳兩人的縮寫,並在兩側加上 emoji 愛心。
記得重複使用你在前一步定義的 initial。
couple("Blake Miller", "Riley Lewis")
"❤ B. + R. ❤"