Ruby2.3新特色

Posted by JSON on February 3, 2016

Safe navigation operator

在其它語言常見的Safe navigation operator這次在Ruby2.3引入了, &. 運算子可以幫助我們簡化程式:

# 2.3之前,經常要先檢查物件是否為nil,才進一步使用它的方法
if user && user.admin?
...
end

# ruby2.3可簡寫如下
if user&.admin?
...
end

Frozen string literals

Ruby中預設的字串是可以修改的(mutable),若想要凍結(freeze)字串使字串不可被修改 使用#freeze方法,例如:

# Mutable string
str="hello"
str[1]='a'
str # => "hallo"

# Frozen string is immutable
str="hello".freeze
str[1]='a' # => RuntimeError: can't modify frozen String

凍結字串可減少物件的產生以提升效能:

a="hello"
b="hello"
c="hello".freeze
d="hello".freeze
a.object_id # => 70286565492560
b.object_id # => 70286565913160
c.object_id # => 70286565492700
d.object_id # => 70286565492700

Matz said “All String literals are immutable (frozen) on Ruby 3”.

Ruby3.0預計將字面字串預設為frozen,而為了銜接容易,Ruby2.3允許選擇性地啟動 這個功能,只要將註解# frozen_string_literal: true放在檔案開頭處,即可啟動 凍結字面字串,這是為了Ruby3.0鋪路所提供的一個功能:

# frozen_string_literal: true
str="hello"
str[1]='a' # => RuntimeError: can't modify frozen String

Array#dig and Hash#dig

標準函式庫提供這個新方法,用更簡單的API讀取nested element,在沒有這個方法前 可能會遇到下面這種問題:

user = { uid: 123, name: 'hello' }
user[:extra_info][:website] # NameError: undefined local variable or method `person' for main:Object

假設user是某API所回傳的資料,不是每個user都有extra_info,這時候又讀取到第二層就會引發錯誤, 有了#dig就可以減少判斷,回傳nil而不引發錯誤:

user = { uid: 123, name: 'hello' }
user.dig(:extra_info, :website) => nil

“Did you mean?”

NoMethodError錯誤發生時(經常因為typo),錯誤訊息會印出其它與你輸入相似的正確方法

" hello world ".strap
# NoMethodError: undefined method `strap' for " hello world ":String
# Did you mean?  strip
#                strip!

Hash “comparison”

hash物件可以進行比較運算,a>=b會檢查所有出現在b的key-value pairs是否也都出現在a。

{ x: 1, y: 2 } >= { x: 1 } #=> true
{ x: 1, y: 2 } >= { x: 2 } #=> false
{ x: 1 } >= { x: 1, y: 2 } #=> false

Hash#to_proc

Hash#to_proc將hash轉換成一個key->value的lambda:

fruit = { a: 'apple', b: 'banana' }
p = fruit.to_proc

p.call(:a) # => 'apple'
p.call(:b) # => 'banana'

比起原本透過[]方法來讀取hash,這樣的功能還提供一個好處 偷過&將fruit轉為proc傳送給Enumerable區塊:

fruit = { a: 'apple', b: 'banana' }

# 使用[]
[:a, :b].map { |key| fruit[key] } #=> ['apple', 'banana']

# 透過&轉為proc送進Enumerable區塊
[:a, :b].map(&fruit) #=> ['apple', 'banana']

關於&可參考本站 “Ruby語法糖衣: Symbol#to_proc 如何運作的?

Hash#fetch_values

Hash#values_at有相同的功能,差別在於找不到指定key的處理方式, #fetch_values會引發KeyError,而#values_at就是回傳nil:

fruit = { a: 'apple', b: 'banana' }
fruit.fetch_values(:c) # KeyError: key not found: :c
fruit.values_at(:c) # => [nil]

Enumerable#grep_v

#grep回傳相符的物件,grep_v即回傳不相符的物件:

fruits = ['apple', 'banana', 'cherry', 'grape']
fruits.grep(/ap/) # => ["apple", "grape"]
fruits.grep_v(/ap/) # => ["banana", "cherry"]

Numeric#positive? and #negative?

判斷數值為正數或負數,能想到的好處大概是易於閱讀:

1.positive? # => true
1.negative? # => false

參考連結