ruby 2.6 多了一些有趣的新特性和功能,來了解一下
Endless ranges
利用 ..
代表無窮的範圍
早期的寫法 100..Float::INFINITY
1
2
3
4
5
6
7
8
9
| n = 123
case n
when 1..9 then 'Single digit'
when 10..99 then 'Two digit'
when 100.. then 'Three or more'
end
# => "Three or more"
|
但也要小心,會一直跑下去
1
| (1..).each {|index| puts index }
|
Kernel#then (Kernel#yield_self alias)
新增一個 yield_self
新的 alias then
主要跟 tap
相反,tap
最後是返回原本的 object
,then
則是返回 block
大概是下面這樣的差別
1
2
3
4
5
6
7
8
9
10
| class Object
def tap
yield self
self
end
def then
yield(self)
end
end
|
範例
1
2
3
4
| 3.next.then {|x| x**x }.to_s
#=> "256"
"my string".yield_self {|s| s.upcase }
#=> "MY STRING"
|
1
2
3
4
5
6
7
| cisbn = '978-1-93778-549-9'
cisbn.gsub('-', '')
.then { |isbn| URI("#{API_URL}?q=isbn:#{isbn}") }
.then { |uri| Net:HTTP.get(uri) }
.then { |json_response| JSON.parse(json_response) }
.then { |response| response.dig('items', 'volumeInfo') }
|
Enumerable#chain and Enumerator#+
1
2
3
4
| (1..3).chain((5..7), [9, 10]).to_a
# => [1, 2, 3, 5, 6, 7, 9, 10]
(1..3).each + (5..7) + (9..10).to_a
# => [1, 2, 3, 5, 6, 7, 9, 10]
|
Composition operators << and >> to Proc and Method
1
2
3
4
| f = proc{|x| x + 2}
g = proc{|x| x * 3}
(f << g).call(3) # -> 11; identical to f(g(3))
(f >> g).call(3) # -> 15; identical to g(f(3))
|
Array#union and Array#difference
1
2
3
4
5
6
7
8
9
10
| a = [1, 2, 3]
b = [2, 3, 4]
a.union(b) # => [1, 2, 3, 4]
a.difference(b) # => [1]
a | b # => [1, 2, 3, 4]
a & b # => [2, 3]
a - b # => [1]
a && b # => [2, 3, 4]
a || b # => [1, 2, 3]
|
Array#filter (Array#select alias)
主要是其他語言 Javascript, PHP, Haskell, Java 8, Scala, R
等等都是用 fliter
1
| [:foo, :bar].filter { |x| x == :foo }
|
Enumerable#to_h with block
to_h
支援 block,就可以直接作轉換 key
, value
1
2
3
4
5
6
| hash = { foo: 2, bar: 3 }
hash.to_h { |k, v| [k.upcase, v*v] } #=> { FOO: 4, BAR: 9 }
# ruby 2.5:
# hash.map { |k, v| [k.upcase, v*v] }.to_h
# hash.reduce({}) { |result, (k, v)| result.merge(k.upcase => v*v) }
|
Enumerator::ArithmeticSequence
之前無法用 first
和 last
1
2
3
4
5
6
| (1..10).step(2).last
# 9
(1..10).step(2).last
# ruby 2.5:
# NoMethodError: undefined method `last' for #<Enumerator: 1..10:step(2)>
|
%
is step
alias
1
2
| ((1..10) % 2).to_a
# => [1, 3, 5, 7, 9]
|
另一個改變
1
2
3
4
| (1..10).step(2) == (1..10).step(2)
# false - Ruby 2.5 (and older)
(1..10).step(2) == (1..10).step(2)
# true - Ruby 2.6
|
Merge multiple hashes
1
2
3
4
5
6
7
8
9
10
| a = { a: 1 }
b = { b: 2 }
c = { c: 3 }
a.merge(b, c)
# {:a=>1, :b=>2, :c=>3}
a.merge(b).merge(c)
# ruby 2.5:
# {:a=>1, :b=>2, :c=>3}
|
Random.bytes
1
2
3
4
5
6
| Random.bytes(8)
# => "\xA4\xFB\xC4\x94\xC5U\xA0\x1A"
# ruby 2.5
Random.new.bytes(10)
# => "\xCEn@\xFA\x93\xB3\xB9\x80p\xA9"
|
Range#=== now uses uses #cover? instead of #include?
===
: case equality
,原本是用 include?
的方式,改為用 cover?
- include? 會將所有值一一拿出來做比對,因此效率較差
- cover? 只會取出開頭和結尾,去比對,值 => 開頭 && 值 <= 結尾,效能比較好
cover?
Returns true if obj is between the begin and end of the range.
This tests begin <= obj <= end when exclude_end? is false and begin <= obj < end when exclude_end? is true.
1
2
3
4
5
6
7
8
9
10
| ("a".."z").cover?("c") #=> true
("a".."z").cover?("5") #=> false
("a".."z").cover?("cc") #=> true
(1..5).cover?(2..3) #=> true
(1..5).cover?(0..6) #=> false
(1..5).cover?(1...6) #=> true
("a".."z").include?("g") #=> true
("a".."z").include?("A") #=> false
("a".."z").include?("cc") #=> false
|
1
2
3
4
5
6
7
8
| require 'date'
case DateTime.now
when Date.today..Date.today+1
puts 'matched'
else
puts 'not matched'
end
|
參考文件