Skip to main content

Command Palette

Search for a command to run...

Ruby 2.6 的新功能

Published
2 min read

Ruby 2.6 除了主打的 JIT 之外,還引進了一些有趣的新功能。其中有一些可以讓 Ruby 在函數式風格的寫法上更加自然流暢。想來稍微展示一下這些功能的用法。

1. Compose operator:
Proc.>>Proc.<<

雖然 Ruby 天生在某些用法上就採用了函數式風格(例如沒有 for 迴圈,而是用 each + block),但由於高階函式接受的是 block 而非 lambda,lambda 本身也無法簡單組合起來。所以一直以來匿名函式在 Ruby 中除了拿來當參數傳遞之外,用法相當受限。

在正統的函數式編程中,用 function composition 的方式將多個小函式組合在一起是很基本的操作,舉個之前的例子,假設你想要這樣的連續調用:

# 注意,因為假設這些函數都是 lambda,所以要用 `.()` 調用
request = generate_request.()
response = get_response.(request)
body = parse_body.(response)
html = render.(body)

其實除了 html 這個最終結果外,我們不需要中間的臨時變數,而可以改寫成這樣(然後被同事記恨):

html = render.(parse_body.(get_response.(generate_request.())))

Ruby 2.6 開始你可以用 << (compose) 或是 >> (pipe) 來組合 Proc (或 lambda)。假設我們有兩個 lambda fg,這兩個運算子的作用如下:

(f >> g).(x)
# 等同於
g(f(x))


(f << g).(x)
# 等同於
f(g(x))

我自己的記憶方式是把這兩個 operator 看成函式呼叫流程的箭頭。f >> g 就是先調用 f ,再將結果傳進 g。反之 f << g 則是先調用 g 再傳給 f

那麼之前的例子就可以改成下列兩者之一:

get_html = generate_request >> get_response >> parse_body >> render
html = get_html.()

# 或是

get_html = render << parse_body << get_response << generate_request
html = get_html.()

Ruby 2.6 的文件範例如下:

f = proc {|x| x * x}
g = proc {|x| x + x}

(f << g).call(2) # => 16
(f >> g).call(2) # => 8

再進階一點配合 Ruby 原本就有的 Object#methodMethod#curry,想來可以組合出非常有意思的寫法。

2. Enumerable#filter

函數式編程最著名的三個高階函式就屬 mapreducefilter 了。但在 Ruby 中,filter 這個高階函式被改名為 select,這一版加上了 filter 的別名,跟其它語言的慣用法一致,再也不會等到測試爆掉才想起函式名稱不一樣了。

[1, 2, 3, 4].filter {|i| i % 2 == 0} # => [2, 4]

3. Endless range

現在範圍運算子 .. 可以不加結束的參數:

[:a, :b, :c, :d][2..] # => [:b, :c, :d]


(:b..).lazy.zip(10..).first(3) # => [[:b, 10], [:c, 11], [:d, 12]]

4. Array#union and Array#difference

Array#union 是聯集,Array#difference 是差集。直接上範例:

[1].union([2, 3], [1, 2, 4, 5]) # => [1, 2, 3, 4, 5]


[1, 1, 2, 2, 3, 3, 4, 5].difference([1, 2, 4]) # => [3, 3, 5]

結語

除了這些之外,Proc.call 調用快了大約 1.4 倍。另外還有 Object#thenHash#mergeEnumerable#to_h 等等,就找機會試玩看看吧。Happy hacking!

More from this blog

聊聊 Elixir 中的 type

最近有幾位朋友分別來問 Elixir 的 type 的問題,想說中文世界好像沒有比較完整的東西,就把知道的東西整理出來。 (目前) Elixir 的 type 能做什麼? tl;dr: 最主要是文件,然後在某種程度下防止錯誤。 我覺得這應該是在研究 Elixir 的 type 時最需要知道的事情了。不像 Haskell 及 F# 這種以型別著稱的 ML 系語言,Elixir / Erlang 本質上是個動態語言,所有與型別有關的標註都會被編譯器忽略。而 Erlang 內建的型別檢查工具 dia...

Oct 18, 20222 min read

Steam 上的程式教學類遊戲

農曆年期間比較有空,玩了一些之前買的遊戲。這次特別試了幾個標榜讓不會寫程式的人學寫程式的遊戲。分享一下試玩的心得。 1. 7 Billion Humans 考慮到劇情的話我最喜歡的是 7 Billion Humans。它用拖拉語法的方式下指令,一開始還蠻好上手的,但是因為只有 goto 那樣的結構,而操作的時候又是一次對所有的 worker 下指令,所以常常要想一下執行後每個人運作的順序。但是介面有正體中文,以「想要體驗一下寫程式大概是怎麼一回事」來說還蠻適合的。 2. while Tru...

Feb 24, 20201 min read

Let's (re)start from here.

最近的時間大半都花在這上面了。 算算應該是第五次弄部落格系統。算一下扣除上古時期用現成的之外,每個系統平均各寫六篇文章,也都撐不過兩年。前幾個分別用了 Refinery CMS -> jekyll -> middleman -> jekyll。想來架系統的總時數應該超過寫文章的時間 XD 而這次用上了 Gatsby + tailwindcss,除了恢復一下 GraphQL 的手感之外,這次還挑戰了不套別人做的版型,自己把類似上一個部落格的 style 刻出來。想說來分享一下這些技術的感想: G...

Jan 11, 20201 min read

Mostly Functional

31 posts

/.(ex|jsx?|rb|hs|rs|py)/, A father, a bookworm, a pluviophile. Co-organizer of http://Elixir.tw. Learning Satir, coaching & mediation.