自由的數字帶來了什麼呢?當人們可以無視單位,專注在數字上之後,我們可以開始討論數字本身的性質,及數字與數字之間的關係。
--- taiansu, mostly:functional 第二章:泥板
將 pattern matching 運用在函式的定義裡時,會長得像這樣:
# Elixir 語法
def foo(0), do: "I don't like zero"
def foo(num) do
num + 1
end
「等等,你那個第一行的括號裡是個…數字?可以直接寫在那?」
是的。這代表當魔法師呼叫這個函式時,傳進來的參數正好是 0 時,就會執行這個區塊。如果不是的話,那麼就會繼續往下找同名的函式,看看有沒有能夠比對成功的函式定義。一旦能夠成功比對就執行該區塊。
「那如果一路往下都找不到呢?」
那就會跳出 FunctionClauseError。由於單個變數 match 任何數值都會成功綁定,所以一般而言函式定義的最後會寫成怎麼樣都能接到的型式。
因此若函式接受的參數是陣列或 Map 時,你可以在定義的部份就把它們原地拆開並且檢查:
# Elixir 語法
def parse(%{head: %{title: title, meta: _}, body: body}) do
IO.inspect("the title is #{title}")
{:ok, body}
end
def parse(_), do: {:error, "格式錯誤"}
這個函式會接收一個 Map,檢查裡面的 head 是不是包含 meta 的鍵值對,而我們不在乎 meta
的值是什麼。接著拆出 title
跟 body
,印一段話,然後回傳 {:ok, body}
。
在數學上,有一種數列,叫做費波那契數列。長得像這樣:
其定義是從 0 跟 1 開始,接下來的數字,是前面兩個數字的合。如果我們用數學的記號來寫的話,會是這樣子:
你看最後一行,就是一個遞迴。
而在 Elixir 裡面用上 pattern-matching,且不考慮效能問題的話,可以寫成這樣:
# Elixir 語法
def fib(0), do: 0
def fib(1), do: 1
def fib(n), do: fib(n - 1) + fib(n - 2)
有沒有長得很像。這也是函數式國度裡,想讓它們的函式貼近數學的許多手段之一。
小動物若有所思,碎唸著「前一個加上前前一個……昨天的加上前天的…啊!上次莊園裡那個湯!」
上次莊園裡那個祖傳的湯,就是把昨天的湯加上前天的湯…這麼說來,你們世界裡那個祖傳湯頭牛肉麵跟鰻魚醬汁,就是費波那契牛肉湯跟費波那契醬汁囉!
像是領悟了巨大發現一般,小動物看起來非常開心的樣子。
你有沒有想過,雖然我們一直都在操作「函式」,但是你們那個世界為什麼把 functional programming 稱為「函數式程式設計」,而不是「函式式程式設計」嗎?
因為雖然英文是同一個字,所謂程式裡的函式,跟數學上的函數,還是有差異的。只是在每個國度中的差別有大有小,在這些稱之為函數式的國度裡,他們會儘量減少函式跟函數的差距,儘量讓函式有良好的數學性質,例如一等公民、不可變動性 (immutability )、樣式比對等等。
在我們剛踏上旅程時說過,當我們將數字本身抽象出來之後,我們可以開始討論數字本身的性質,及數字與數字之間的關係。
那麼當我們有了貼近數學函數性質的函式之後,我們也可以開始討論函數本身的性質,及函數與函數間的關係了。而把已經得證或是可推導的數學概念,運用在魔法的操作上,依這些組合出我們需要的運算方式,這個,就是函數式程式設計的本質。
「你的意思是說,用思考數學的函數以及它的性質與關係的方式,來思考程式的運作…這樣嗎?」
啊,是的。這樣講好多了。
當然每個國度的函式與數學函數的貼近程度,是有差異的。愈接近數學本質的,愈能運用到更多已知的數學概念與成果。等我們到了神領 Haskell 之後,會遇到很多在其它地方根本沒有人在提的字詞,而之所以沒有人提,是有其原因的。等那天到了,記得不要被名詞所迷惑,而是理解「為什麼要這樣看待事物」的根本…
但是當待在其它國度的時候,就儘量嘗試著把魔法裡可以貼近數學的部份儘量做好,就是之前所提過一樣的參數會有一致回傳的函式,利用函式的一等公民特性,做出接收與回傳函式的函式,並確實隔離出來,接著善用那個國度裡優秀的部份,讓兩者好好合作,那麼你的魔法就能提升到另一個層次了。
那麼有了這些概念之後,我們就可以來看看這座城的特色了。它之所以叫做"不沉的堡壘",並非是建築,甚或一座城市本身不會崩毀。而是因為它分散容錯的特性,所以即使遇到重大的損傷,也能儘可能的自行恢復至可運作的狀態。就算不是在同一個地點也一樣,例如我們現在,也只是待在這座城市眾多節點裡的其中一個而己。
而這個分散的本質,其實也是一種在特定條件下的遞迴,讓我來寫個範例給你看吧:
當小動物正專心的在桌上寫出程式碼示範時,我瞥見遠方有一個稍微刺眼的閃光,當下沒有太過留意。但沒多久,稍遠處比較高的建築樓頂,出現了大片的透明紅色。接著如傳染一般,紅色的區塊開始蔓延,擴散。
我正要提醒小動物注意這個狀況時,它終於抬起頭來說:"好了,你看這個由遞迴呼叫…",接著它也注意到四周的不對勁了。那紅色已經幾乎佔領了我們所在的區域,而建築正開始崩落,殘塊在空中分解成齏粉…
而它手上的程式,依然高速運轉著,正伸出兩條管線往我們最近的告示牌銜接。
小動物說:"這真不是個好時機"。
那些最高的建築開始併出炫目的光芒然後光芒變成一條條的弧線,急速射上天空,跨往彼端遙遠遙遠未知的地方。然後建築自身大片大片崩解、消融。
手上程式的管線接觸到已是紅色的告示牌也跟著變紅,然後一路傳遞回來。
小動物看著我,眼神露出非常惋惜的表情,我向前衝要抱住它,但正要抓到的時候卻落空了…
因為小動物的手跟身體跟尾巴,逐漸變成由字母跟數字跟符號所構成的,資料包著資料包著資料資料包著資料的樣子。然後有如風吹過那樣緩慢消散…
我對上它的眼神,它眨了一下眼睛,看起來安祥的、微微帶著笑意,說"只可惜…
回過神,環顧四周,除了長路之外,空無一物。
[to be continue]