前言
函數式程式設計 (functional programming) 是一種程式設計的模範 (paradigm),主要見於 Lisp 和 ML 家族語言。由於函數式程式易於平行化處理,近年來許多主流語言也吸收了一些函數式程式的概念。一些大數據框架,像是 Hadoop 和 Spark 等,也用到函數式程式的概念。本文介紹以 Raku 撰寫函數式程式。
比起傳統的指令式程式,函數式程式一開始比較難理解,如果讀者覺得過於困難,可以先略過這個章節。
函式物件
函數式程式的前提在於函式為第一級物件 (first-class object),簡單地說,函式可以做為值。由於函式可以做為值,函式可以做為其他函式的參數,也可以做為回傳值。
以下是實例:
或者是將現有的函式的參考傳入:
也可以用以下較為簡略的寫法,算是一種語法糖:
閉包
閉包 (closure) 是一種帶有狀態的 (stateful) 副程式。以下是實例:
以下例子使用閉包生成費伯那西數列:
以下實例用閉包生成質數數列:
以下例子將雜湊包在閉包中,做為簡易的快取:
高階函式
高階函式 (higher-order function) 是指使用函式做為參數或用函式做為回傳值的函式。我們先前的閉包也是一種高階函式。以下是一個以副程式為參數的實例:
此 filter
副程式以一個陣列和一個副程式為參數,回傳符合該副程式的新陣列。
由於 Perl 6 已經將一些常見的高階函式寫好,我們不需要每次都從頭撰寫,以下是一些例子:
grep
:傳入串列,回傳符合特定條件的串列map
:將串列經轉換後,回傳新串列reduce
:將串列縮減為純量sort
:依照特定條件排列後回傳新串列zip
:將兩個串列組合,回傳新串列
以下為實例:
高階函數間可進一步組合,如下例:
上例是將處理順序倒著寫,一開始會覺得較不易閱讀。也可以將其改為方法呼叫的方式,如下:
迭代器
Perl 6 提供 gather
... take
的語法,可用來撰寫迭代器。gather
區塊內每次碰到 take
時,都會跳出該區塊,並取出一個值,下一次迭代時,會跳回原先的位置,繼續走迴圈。
我們將先前的 filter
副程式改寫如下:
這樣子看起來和先前的寫法沒啥差異,不過,迭代器有 laziness 的特性,如以下實例:
由於 gather
區塊有 laziness 的特性,即使是無限長的串列,也是可以順利執行。如果用傳統的迴圈則會變成無窮迴圈,無法無法執行。
以下例子用 gather
重寫質數數列,可用更簡潔的語法取質數:
以下例子用 gather
區塊重寫費伯那西數列:
由此可見,take
不一定要在 gather
區塊的最尾端,可使程式寫法更靈活。