iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 16
1
Software Development

mostly:functional 從零開始的異世界程式觀 --- 函數式程式設計的試煉系列 第 16

mostly:functional 第十五章:失落的計量

是的,我已然熟知各種辯證的法則
騰挪與算計之後
得出人生是牆角一條
很有歷史感的枯藤
最終的弧線折成一道
帶來龐沛記憶的鋒面
憂傷降臨,城市彷彿是
被一場雨狠狠埋葬的夜樹
顏色皆倉皇而退
...

---何雅雯, 秋轉, 抒情考古學


-- 0601

那件事發生已經一個月了。城市崩毀後我待了很久,然後延著長路往下走想找看能不能再次遇到那座城,而舉目所見皆是荒涼。月昇月落,直到再也撐不住找了個樹下靠著睡著。醒來時我趴在書桌上,電腦的終端機開著 vim 寫著許多依稀有點印象的程式碼。

之後我還有進入那世界幾次,目前看來都是在夜裡寫 code 的時候。依然是那條看不見盡頭的路,我應該回頭看看莊園在不在,還是繼續往下走呢?


-- 0726

決定繼續往下走。後來我有再遇到幾個那種浮在光流上的城市,有些題目裡給了一堆大寫逗號分號,有些問我什麼 otp 什麼的,都答不出來所以沒辦法進去。只有一個問了 immutable 迴圈,答對進去了之後,發現跟似乎不是上次那個城市,建築的感覺不太一樣。我試著四處問有沒有人看過那隻小動物,但能跟我互動的各種人跟東西裡沒有知道的。


-- 0902

孩子出生了。臉好嘟啊,應該算可愛吧?在 fb 上打了 IO.puts "hello world"。他長大以後也可以教他寫程式吧。他會想學嗎?


-- 1226

終於比較能睡過夜了,我跟寶寶都是。晚上不太有自己做事的餘裕,所以也沒什麼機會再去那個世界。不過前天有再去一次,往下走的景物開始變得不太一樣了。


-- 1228

我覺得我可能找到那個神領 Haskell 了。

說是可能,因為那邊其實什麼都沒有,但是有一種不太一樣的氛圍,而草地上散佈著大量小小的透明的文字,拿起來沒什麼反應,也看不太清楚寫什麼。不過只要我走到某一個點,其中有很多文字會聚集起來,變成一個透明的城門樣子的東西,門楣上有個符號,有點像是 https://chart.googleapis.com/chart?cht=tx&chl=%5Clambda 但是比較方一點。

去了好幾次,每次都是同一個問題,問我說「五十度的水加熱五十度是幾度?」怎麼想都是一百度吧?但是每次講完門就消失了,然後什麼也沒有發生。這答案有錯嗎?是說這也不太像跟程式有關的題目吧?


-- 0216

「寶寶你還要蘋果嗎?要幾片?…我教你喔,這樣是一片、然後兩片、再來是三片…可以說說看嗎?1……2……然後 3」

「你應該還聽不懂吧?不過我跟你說喔,以前哪…曾經有一隻尾巴膨膨的棕紅色小動物跟我說過…」

是…兩年了嗎?再想到就好像有些情緒湧上來,但是我還是繼續往下說:

「更久更久以前人們學會把單位拿掉,就可以抽象的思考數字跟…」

等等。

單位,那時我們把單位拿掉了…




資料型別宣告:data

我們在乎的已不單單只是計算而已了。數字跟數字當然可以相加,但是許多時候的重點,在於這個計算有沒有意義。我們可以用型別 (Type) 來加以限制,只讓有意義的計算發生。

在 Haskell 裡,用 data 關鍵字來定義一個資料型別。例如我們可以這樣模擬出布林這個只有兩種可能性的型別:

-- Haskell 語法
data MyBool = Yes | No
bool = Yes

我們先宣告了一個型別,名稱叫 MyBool,而它的會是 Yes 或是 No。語法中的 | 代表"或者",在 Haskell 中我們稱這種有"或者"的概念的型別為 Sum type。Sum 是總合,或是的意思,意味著 MyBool 這個型別的值是 Yes 這一種,加上 No 這一種,總共只有這兩種。

接著我們在第二行宣告了一個變數叫 bool,它的值綁定成 Yes。在沒有標註型別的情況下, Haskell 會試著去推導出這個變數的型別 (type inference)。

(註*: Haskell 內建的布林值型別叫 Bool,其值為 TrueFalse )

建構函式

當我們想要定義一種東西叫做溫度,它有可能是攝氏,也有可能是華氏,而它的值會是一個浮點數 (帶有小數點的數) 時,我們會這樣做:

-- Haskell 語法
data Temp = Celsius Float | Fahrenheit Float
  deriving (Show)

temp1 = Celsius 50.0
temp2 = Fahrenheit 50.0

Temp 是這個型別的名字,而 CelsiusFahrenheit 稱為這個型別的建構函式。(不過如果你之前在其它語言聽過這個字的話,請暫時學著失憶一下,這裡跟那個是相當不同的東西。)

這個型別說明了 Temp 的值 有兩類可能性。一類是 Celsius 後面帶著一個浮點數,另一類是 Fahrenheit 後面帶著一個浮點數。而因為浮點數本身有無限多種可能,所以 Temp 也會有無限多種,但不是任意的數字都可以當做 Temp,一定要是浮點數,且前方要帶有 CelsiusFahrenheit 其中一個。

另外為了要讓溫度能夠印出來,我們加上了 deriving (Show) 這個語法,不過暫時就先照抄,之後會更詳細的說明這部份。

然後我們宣告了兩個變數 temp1temp2。分別做成攝氏及華氏的 50 度。

計算用的函式

接著我們要來實做溫度用的計算函式了。我們先規定只有同為華氏或同為攝氏的溫度能夠相加,像是這樣:

-- Haskell 語法
add :: Temp -> Temp -> Temp
add (Celsius t1) (Celsius t2) = Celsius t1 + t2
add (Fahrenheit t1) (Fahrenheit t2) = Fahrenheit t1 + t2

我們宣告了 add 函式。第一行是型別標注,說明 add接收兩個 Temp,回傳一個 Temp 的函式。而第二行跟第三行是兩種實做。第二行用了 pattern matching,將接收到的攝氏溫度 Celsius t1Celsius t2 拆開取出數字的部份 t1t2,相加之後,再用 Celsius 做出一個新的溫度。

加加看

那麼我們就可以試著使用這個函式了:

-- Haskell 語法
temp3 = add temp1 temp1
t3 -- > Celsius 100.0
temp4 = add temp1 temp2
t4 -- > *** Exception: test.hs:(15,1)-(16,58): Non-exhaustive patterns in function add

當兩個溫度都是攝氏時,可以正確的相加。而因為我們沒有實做兩個溫度的單位不同時的加法,所以回傳了錯誤。另外它也告訴你這個函式的實做是 "Non-exhaustive",意思是你沒有考慮到這個函式會接收到的引數的所有可能性,因為我們沒有處理到華氏加攝氏,以及攝氏加華氏的情況。




我走到那個地點,地上的字很快的匯聚成門,問說:「知道了嗎?五十度的水加熱五十度?」

「不知道,因為你沒有說單位。兩個 50 度都是攝氏或都是華氏嗎?」

從城門左右兩邊開始,城牆迅速成形、延展、圍繞。半透明的牆與其上的城垛充滿著古典而嚴謹的氣息。

「歡迎。」城門開啟,我走了進去。

[to be continue]


上一篇
mostly:functional 第十四章:再一次遞迴,然後…
下一篇
mostly:functional 第十六章:函數自身
系列文
mostly:functional 從零開始的異世界程式觀 --- 函數式程式設計的試煉35

尚未有邦友留言

立即登入留言