跳至內容
GitHub 儲存庫 論壇 RSS 新聞饋送

to_proc

Ary Borenzweig

Ruby 的 to_proc

Ruby 的區塊非常強大。您可以輕鬆地將數字陣列轉換為字串

[1, 2, 3].map { |n| n.to_s } #=> ["1", "2", "3"]

當然,您也可以使用這個捷徑來做到這一點

[1, 2, 3].map &:to_s #=> ["1", "2", "3"]

& 運算子會將物件轉換為適合作為區塊傳遞的 Proc。您可以透過實作 to_proc 方法,讓任何類別回應此運算子。Symbol 有一個 to_proc 方法。

這一切都很好,但是如果您想將參數傳遞給方法,該怎麼辦。例如

[10, 20, 30].map { |n| n.modulo(3) } #=> [1, 2, 0]

我們可以寫一些像 &:modulo(3) 這樣的東西來使其工作嗎?結果 不行,至少 沒有那麼容易

不僅如此,由於 Ruby 必須將 Symbol 轉換為 Proc,因此與使用普通區塊相比,效能會略微下降。

最後,Ruby 的 Symbol#to_proc 實作具有 Proc 的快取,因此每次使用相同的符號時都不會建立它們,但仍然比普通的區塊慢一點。

Crystal 的 to_proc?

起初我們考慮讓 Crystal 具有相同的語法,但是有點 hacky:如果您執行 &:to_s,由於 & 的參數是 Symbol,我們可以重寫原始碼以接收一個區塊

# This:
[1, 2, 3].map &:to_s

# is rewritten to this:
[1, 2, 3].map { |x| x.to_s }

對於其他參數,我們會做一些不同的事情(例如將函式類型轉換為區塊)。

幸運的是,waj 提出了更好的建議:如果我們寫成 &.to_s 呢?

[1, 2, 3].map &.to_s

現在,這是一種新的語法,與 Ruby 不同。如果您在 Ruby 中執行此操作...

irb(main):001:0> [1, 2, 3].map &.to_s
SyntaxError: (irb):1: syntax error, unexpected '.'
[1, 2, 3].map &.to_s
               ^

這表示在 & 之後放置一個點在 Ruby 中沒有任何意義,這也表示此語法可用於賦予它新的含義。因此,在 Crystal 中,我們選擇改用此語法。

透過這個小小的更改,我們可以很容易地將參數傳遞給方法

[10, 20, 30].map &.modulo(3) #=> [1, 2, 0] ... but only in Crystal ;-)

不僅如此,您還可以寫這個

[1, 20, 300].map &.to_s.size #=> 1, 2, 3

或這個

[[1, -2], [-3, -4]].map(&.map(&.abs)) #=> [[1, 2], [3, 4]]

當然還有這個

[1, 2, 3, 4].map &.**(2) #=> [1, 4, 9, 16]

最好的是,這只是一個語法重寫,沒有任何效能損失。