Ruby 2.7
- ruby
นอกจากวันคริสต์มาสจะเป็นวันประสูติของพระเยชูแล้ว เราเหล่า Rubyist ก็ให้ความสำคัญกับวันคริสต์มาสเช่นเดียวกัน เนื่องจากเป็นวันที่มีการปล่อย Ruby เวอร์ชันใหม่ออกมานั้นเอง สำหรับวันคริสต์มาสที่ผ่านมา (25/12/2562) ก็ได้มีการประกาศปล่อย Ruby เวอร์ชัน 2.7 ออกมาอย่างเป็นทางการ ซึ่งน่าเป็นเวอร์ชัน 2 ตัวสุดท้ายก่อนที่จะกลายเป็นเวอร์ชัน 3 ที่ทางทีมพัฒนาได้วางแผนที่จะปล่อยออกมาในวันคริสต์มาสในปีนี้ (แต่ก็แอบเห็นมีเวอร์ชัน 2.8-DEV ในรายการติดตั้งของ rbenv) เกริ่นกันมาพอสมควรแหละ มาดูกันว่าเวอร์ชัน 2.7 นี้มีฟีเจอร์อะไรใหม่ ปรับปรุงแก้ไขอะไรบ้าง
ฟีเจอร์ใหม่ใน Ruby 2.7
Pattern Matching (Experimental)
Pattern Matching เป็นฟีเจอร์ที่ใช้ตรวจสอบจับคู่เพื่อเปรียบเทียบข้อมูล ในเวอร์ชันนี้เราสามารถนำมาใช้งานในชุดคำสั่งแบบ case
ซึ่งโดยปกติเราจะใช้ชุดคำสั่ง case/when
แต่ในกรณีที่จะใช้งานร่วมกับ Pattern Matching เราจะใช้ชุดคำสั่ง case/in
แทน นอกจากนี้ยังรับการใช้งานร่วมกับ if
หรือ unless
ด้วยดังแสดงในรูปแบบด้านล่าง
case [variable or expression]
in [pattern] [if|unless condition]
...
in [pattern] [if|unless condition]
...
else
...
end
- ตัวอย่างการใช้งานกับชุดข้อมูล Array
# Simple array
data = ['John', 'Doe', 27]
case data
in [name, lastname, age]
puts "#{name} #{lastname} #{age}"
end
# Complex array
language = ['python', [3, 0, 1]]
case language
in ['ruby', [2, 7, *]]
puts "Get ruby 2.7.x"
in ['python', [3, 0, *]]
puts "Get python 3.0.x"
else
puts "Unknown language"
end
- ตัวอย่างการใช้งานกับชุดข้อมูล Hash
# Hash
user = { username: 'bob123', nickname: 'bob', language: 'th' }
case user
in { username: 'bob123', nickname: nickname, language: language }
puts "bob123 (#{nickname}) uses #{language} language"
end
- ตัวอย่างการใช้งานกับชุดข้อมูล JSON
# JSON
person = {
username: 'karn18',
profile: {
firstName: 'Karn',
lastName: 'Tirasoontorn'
},
age: 35
}
require 'json'
case JSON.parse(person.to_json, symbolize_names: true)
in { username: "karn18", profile: { firstName: first_name } }
puts "Username: karn18, First Name: #{first_name}"
end
สำหรับการใช้งาน Pattern Matching นั้นมีความหลากหลาย ดังนั้นสามารถศึกษาเพิ่มเติมได้ที่ https://www.toptal.com/ruby/ruby-pattern-matching-tutorial
อย่างไรก็ตาม Matz ได้บอกว่าการใช้งาน Pattern Matching ใน Ruby นั้นค่อนข้างช้า ดังนั้นควรใช้อย่างระมัดระวังเพื่อประสิทธิภาพความรวดเร็วของโปรแกรม
Numbered Parameters (Experimental)
พารามิเตอร์ตัวเลขถูกนำมาใช้เป็นค่ามาตรฐานสำหรับพารามิเตอร์ในบล็อก ทำให้เราสามารถละการใช้งาน |value| ได้
# Array
[1, 2, 10].map { puts _1 } => 1, 2, 10
# Hash
{ id: 1, name: 'ruby 101', price: 30 }.map { puts "#{_1} - #{_2}" } => id - 1, name - ruby 101, price - 30
Separation of positional and keyword arguments
ปกติการส่งพารามิเตอร์ที่มีการจัดลำดับตำแหน่ง และแบบคีย์เวิร์ดร่วมกันนั้น Ruby จะทำการตรวจสอบและจัดการให้อัตโนมัติ แต่สำหรับในเวอร์ชัน 3 การส่งพารามิเตอร์เข้าไปในฟังก์ชันจะต้องระบุให้ชัดเจนว่าเป็นการส่งพารามิเตอร์แบบใดเข้าไป ซึ่งในเวอร์ชัน 2.7 นี้จะเพียงแค่แสดงคำเตือนขึ้นมาเมื่อเราไม่ได้มีการระบุรูปแบบที่ถูกต้องเท่านั้น ที่นี้เรามาดูมันแตกต่างจากเดิมอย่างไร
- เมื่อฟังก์ชันนิยามให้รับคีย์เวิร์ดเป็นพารามิเตอร์ตัวสุดท้าย แต่ฟังก์ชันถูกเรียกใช้งานโดยการส่ง Hash เข้าไปจะต้องทำการใส่ ** นำหน้า { } ไว้ด้วย ทั้งนี้โดยเวอร์ชันก่อนหน้า Ruby จะทำการแปลงข้อมูลที่อยู่ใน Hash ให้อยู่ในรูปคีย์เวิร์ดให้อัตโนมัติ
def foo(key: 42); end; foo({key: 42}) # warned
def foo(**kw); end; foo({key: 42}) # warned
def foo(key: 42); end; foo(**{key: 42}) # OK
def foo(**kw); end; foo(**{key: 42}) # OK
- เมื่อฟังก์ชันนิยามให้รับพารามิเตอร์แบบลำดับ และคีย์เวิร์ดแบบไม่จำกัดจำนวน เมื่อมีการเรียกใช้ฟังก์ชันโดยไม่ได้ระบุพารามิเตอร์แบบลำดับ แต่ระบุพารามิเตอร์แบบคีย์เวิร์ดเท่านั้น ซึ่งปกติพารามิเตอร์แบบคียเวิร์ดจะถูกว่าเป็นพารามิเตอร์ตัวสุดท้าย เมื่อรันโปรแกรม Ruby จะแสดงคำเตือนขึ้นมา ดังนั้นถ้าจะการเรียกใช้งานทำได้ถูกต้องจะต้องใส่ { } คลอบไว้ด้วย
def foo(h, **kw); end; foo(key: 42) # warned
def foo(h, key: 42); end; foo(key: 42) # warned
def foo(h, **kw); end; foo({key: 42}) # OK
def foo(h, key: 42); end; foo({key: 42}) # OK
- เมื่อฟังก์ชันนิยามให้รับพารามิเตอร์ที่เป็น Hash และคีย์เวิร์ดร่วมกัน และมีการส่งพารามิเตอร์ที่เป็น Hash มีคีย์ที่เป็นทั้ง Symbol และไม่เป็น Symbol ถ้าไม่ได้ระบุประเภทที่ชัดเจน Ruby จะแสดงคำเตือนขึ้นมา ดังนั้นเวลาใช้งานควรระบุประเภทให้ชัดเจนว่าเป็น Hash หรือคีย์เวิร์ด
def foo(h={}, key: 42); end; foo("key" => 43, key: 42) # warned
def foo(h={}, key: 42); end; foo({"key" => 43, key: 42}) # warned
def foo(h={}, key: 42); end; foo({"key" => 43}, key: 42) # OK
- เมื่อฟังก์ชันนิยามให้รับพารามิเตอร์ที่เป็น Hash เมื่อมีการเรียกใช้งานฟังก์ชันโดยส่งพารามิเตอร์แบบคีย์เวิร์ด Ruby ยังคงทำงานได้ถูกต้องไม่แสดงคำเตือนขึ้นมา
def foo(opt={}); end; foo( key: 42 ) # OK
- ไม่อนุญาตให้ส่งพารามิเตอร์แบบคีย์เวิร์ดที่ไม่เป็น Symbol ในกรณีที่ฟังก์ชันนิยามพารามิเตอร์แบบคีย์เวิร์ดไม่จำกัดจำนวน
def foo(**kw); p kw; end; foo("str" => 1) #=> {"str"=>1}
- อนุญาตให้ประกาศ
**nil
ในฟังก์ชัน เพื่อระบุว่าฟังก์ชันไม่รับพารามิเตอร์แบบคีย์เวิร์ด ถ้าหากมีการเรียกฟังก์ชันโดยการส่งพารามิเตอร์เป็นแบบคีย์เวิร์ดเข้าไปจะแแสดง ArgumentError ออกมา
def foo(h, **nil); end; foo(key: 1) # ArgumentError
def foo(h, **nil); end; foo(**{key: 1}) # ArgumentError
def foo(h, **nil); end; foo("str" => 1) # ArgumentError
def foo(h, **nil); end; foo({key: 1}) # OK
def foo(h, **nil); end; foo({"str" => 1}) # OK
Argument Forwarding
เราสามารถส่งต่อพารามิเตอร์จากฟังก์ชันที่ถูกเรียกไปยังฟังก์ชันอื่นได้ด้วย ...
def foo(...)
bar(...)
end
ฟังก์ชันใหม่ใน Array
#intersection
เป็นฟังก์ชันที่หาข้อมูลที่เหมือนกันระหว่าง Array 2 ตัว หรือจะใช้&
แทนได้เหมือนกัน
[1, 2, 3].intersection([2, 3, 4]) => [2, 3]
[1, 2, 3] & [2, 3, 4] => [2, 3]
ฟังก์ชันใหม่ใน Enumerable
#tally
ส่งคืนผลลัพท์เป็น Hash ของข้อมูลพร้อมจำนวน
%w(a a a b b c).tally => { "a"=>3, "b"=>2, "c"=>1 }
#filter_map
จากการที่#select
และ#map
ถูกใช้งานบ่อยในการแปลงข้อมูลเป็น Array ขณะที่มีการกรองข้อมูลบางอย่างด้วย ดังนั้นจึงได้มีการเพิ่มฟังก์ชันfilter_map
ขึ้นมาเพื่อทำให้ใช้งานได้ง่ายขึ้น
[1, 2, 3].filter_map {|x| x.odd? ? x.to_s : nil } #=> ["1", "3"]
ฟังก์ชันใหม่ใน Enumerator
- #produce เป็นฟังก์ชันสำหรับการสร้างลำดับที่ไม่สิ้นสุด โดยการส่งค่าล่าสุดที่ได้ไปในบล็อกถัดไป
Enumerator.produce(1, &:next).take(5)
สำหรับฟีเจอร์ในเวอร์ชัน 2.7 นั้นยังมีอีก แต่ในบทความนี้จะขอสรุปไว้เพียงเท่านี้ หากต้องการศึกษาเพิ่มเติมสามารถค้นหาได้จากลิงค์ด้านล่าง