Functional Programming Ruby

陳漢庭
Oct 1, 2020

--

自從被調去寫前端專案後,前端專案的functional programming的風格深深的吸引到我,於是花了時間研究一下Ruby 的Functional風格,發現早已經有很多篇幅討論Functional Ruby。

在之前的 篇章已經有討論過functional programming,那這一篇主要會介紹一些functional programming的概念,以及Javascript跟Ruby之間的比較。

Imperative and Declarative Paradigm

首先先講Imperative Paradigm和 Declarative Paradigm 的差別,在這篇StackOverflow講得很清楚,但還是覺得兩者的差別很難形容。Functional Programming比較偏好使用 Declarative Paradigm

With imperative programming, you tell the compiler what you want to happen, step by step. With declarative programming, on the other hand, you write code that describes what you want, but not necessarily how to get it (declare your desired results, but not the step-by-step)

Blocks as higher order functions

這是讓我很訝異的地方,但在寫Javascript的時候就覺得Ruby的block跟Javascript的callback function有一些相似的地方。首先先上一段Javascript的callback function

另外一種寫法也很常見:

好那直覺上翻成Ruby會長得像以下這種情勢:

由於Javascript可以將function assign給一個變數(First-class citizens),但Ruby不行。上面這種從Javascript直翻到Ruby的寫法會發生錯誤。

實際上可以這樣寫,其中ask為Higher-Order Function(參考這篇寫法)

或者用block的寫法可能會更直覺一點 :(事後發現showEven, showOdd不符合Ruby的coding style😅)

Ruby 用不同的詮釋方式,我們也可以把code這樣寫。這裡的ask還是Higher-Order Function:(參考這裏

ruby常見的map, each也都是Higher-Order Function

result = ["hi", "hello"].map { |name| name.upcase }
result = ["hi", "hello"].select { |name| name.size == 5}

順道一提Functional Programming的寫法都會避免這樣寫(咦?這種寫法好像很熟悉),下面的方式就是上述討論的Imperative Paradigm

# 對應上面第一個
result = []
["hi", "hello"].each { |name| result << name.upcase }
result
# 對應上面第二個
result = []
["hi", "hello"].each { |name| result << name if name.size == 4 }
result

Ruby也可以Curry

第一次認識curry是我們天才前端寫了2個箭頭,讓我完全暈掉的神操作。那就拿Javascript某個範例拿來用Ruby改寫

或者換個比較好看的寫法:(參考這邊

這是我看過比較實用的寫法:(參考這邊

之後會接著介紹 Proc, lamda, call()等與Javascript相似的用法做比較。

例子

小貼心:下列最外層的大括號皆為block,如果沒有使用|i|(or |n|)可以省略。

10.times.map{rand(10)}.join  #=> 產生10碼數字,型別為字串10.times.collect { |n| {key: "val_#{n}"} }
#=> [{ key: "val_0"}, { key: "val_1"}, ..., { key: "val_9"}]
10.times.each_with_object([]) do |i, result|
result << {:key=>"val_#{i}"}
end
#=> [{ key: "val_0"}, { key: "val_1"}, ..., { key: "val_9"}]
Array.new(10) { |i| {:key=>"val_#{i}"} }
#=> [{ key: "val_0"}, { key: "val_1"}, ..., { key: "val_9"}]

參考連結
1. yield in Ruby: Link
2. Functional Objects in Ruby: Link
3. Functional Programming In Ruby: Link
4. Proc Composition Operator in Ruby: Link
5. Functional Programming with Ruby: Link
6. Functional Programming Paradigm in Ruby: Link中文版
7. Functional Programming Techniques With Ruby: Link

--

--

陳漢庭
陳漢庭

Written by 陳漢庭

由於零散的紀錄過了一段時間之後就看不懂了,於是為了強迫自己用故事的方式記錄學到的東西,就開啟我的發文之路。

No responses yet