小試 FP in Ruby
def mem_fun(mf) lambda {|obj, *args| obj.method(mf.to_s).call(*args)} end
給予此函式一個函式名稱字串或 Symbol
,就可以把 member function call 的形式轉換為 plain function call(當然,Ruby 所謂的 plain function 其實也是 member function)。不過相較於 C++ 的 member function adaptor,Ruby 是在執行期以 reflection 喚起那個函式,較為動態但間接層數較多,而不像 C++ 經最佳化後效能幾乎無損(也比較僵硬)。舉個使用例子:
plus = mem_fun(:+) plus.call("abc", "def") # --> "abcdef"
lambda
所接收的 block 參數列內的 *args
功能相當於 C++ 或 Java 1.5 的 "...
" 參數,傳入的剩餘引數會被打包成一個 array(即 args
),因為 Ruby array 是異質(heterogenous)容器,這個設計運作良好。而 block 主體內的 *args
則反其道而行,將 args
array 打散為個別引數。lambda
本身的作用在上一篇文章已經提過,是將其後隨附的 block 轉換為可呼叫的 Proc
object。因此整體效果是將 mem_fun(:mf).call(obj, arg1, arg2, arg3, other arguments)
轉換為 obj.mf(arg1, arg2, arg3, other arguments)
。
接著試寫 bind
,將一個 Proc
object 的某些引數綁定為特定變數或固定值。例如:
bind(plus, :_1, "def").call("abc") # --> "abcdef" bind(plus, "def", :_1).call("abc") # --> "defabc" bind(plus, :_2, :_1).call("abc", "def") # --> "defabc"
在那之前,我們先寫 adapt
,把傳遞給 p
的引數全部先經另一個 unary_p
處理後才真正傳入。
def adapt(p, unary_proc) lambda do |*args| p.call(*args.collect {|x| unary_proc.call(x)}) end end
接著 bind
就很好寫了:
def bind(p, *a) lambda do |*args| adapt(p, lambda do |x| x.class == Symbol && x.to_s =~ /_(\d+)/ ? args[$1.to_i - 1] : x end).call(*a) end end
接著我們可以試寫函式合成(function composition)。或許風格不佳,我把語法設計為 f + g
,如此造出的 function object 將先喚起 f
,然後把結果再傳入 g
。+
運算子必須定義為 class Proc
的 member,而 Ruby 正允許我們這麼做:
class Proc # reopen class Proc def + (unary_p) lambda {|*args| unary_p.call(self.call(*args))} end end
用法如:
g = plus + lambda {|x| puts x} g.call("abc", "def") # prints "abcdef"
當然,以上程式碼全部都是「泛型」(generic)的 ─ Ruby 是個動態型別語言,靜態型別語言所講的「泛型」對它沒有意義。
不過,實用上似乎不太需要這些 higher-order functions,因為 Ruby 的 block 機制本身就相當好用了。而且效率也是問題,每次經過一個 higher-order function,就得負擔 *args
的打包、拆散成本,closures 的時間、空間花費,以及 Ruby 本身的函式呼叫也算昂貴。因此寫這些函式純粹只是好玩而已 XD。
--
好想要 C++ lambda expressions XD。
講到泛型, 目前還是覺得 Haskell 那種較純
道地的 generic + constraint
但是很抽象就是了, Monads 遊客到現在還搞
不懂
嗯,的確,目前「感覺上」泛型和 functional programming 血緣關係比較近(所以「同步率」較高 XD)。為了把「感覺上」三字去掉(或換成較具確定性的詞彙) ─ 輔修數學系(17 日放榜)!
雖然僅只接觸很淺顯的範例,Haskell(and pure functional programming)感覺上真的很有趣呢 :)。
不過 Haskell 是 static typing 喔 ^^
只是它有 type inference, 而且在真正
需要 constraint 的場合中,「感覺上」說
來跟跟靜態語言也不是差很多!
不過這是了解不深的說法, 呵呵, 有待繼續
學習~
<< 回到主頁