[Ruby] Safe Navigation Operator
เรื่องนี้สารภาพตามตรงครับว่า พึ่งรู้ ด้วยเหตุที่ว่า จำเป็นต้อง Maintain Ruby ใน Version ต่ำกว่า 2 มานานเลยไม่ได้ตามข่าวสาร พึ่งมารับข่าวสารกันอีกทีเกี่ยวกับ Ruby เมื่อปีที่ผ่านมา เพราะถึงเวลาที่ต้อง Upgrade มาเป็น Ruby & Rails ใน version ล่าสุดแล้ว
การรับ Input ถือเป็นกระบวนการที่สำคัญที่สุดกระบวนการหนึ่งในการสื่อสารระหว่างคนกับคอมพิวเตอร์ โดยระบบจะนำเอาข้อมูลที่ได้รับมานั้น ไปเข้าสู่ขั้นตอนการประมวลผล เพื่อให้ได้มาซึ่งผลลัพธ์ที่ต้องการ ตัวอย่างเช่น ในช่วงต้นปี ก็เป็นที่รู้กันว่าคนทำงานก็จะต้องเข้าไปที่เว็บของกรมสรรพากร เพื่อที่จะกรอกข้อมูลเพื่อยื่นแบบเสียภาษี เราก็จะนำข้อมูลรายได้ รายจ่ายของเรา เป็น Input เพื่อที่จะให้ระบบของกรมฯ ทำการประมวลผลส่งใหักับกรมสรรพากร และเราก็อาจจะได้รับเงินคืนด้วย
ปัญหา
อย่างไรก็ตาม คนมีร้อยพ่อพันแม่ การกรอกข้อมูลแต่ละคนก็ไม่ได้เหมือนกันซะทีเดียว บางครั้งก็กรอกข้อมูลครบ บางครั้งก็ลืมกรอก ทำให้เราต้องมาตรวจสอบว่าข้อมูลที่ใส่เข้ามามันถูกต้องไหมเช่น ถ้าเราต้องการที่จะดูว่าคนที่กรอกข้อมูล มีอายุไม่เกิน 30 ปีหรือไม่ เราก็จะเขียนโค้ดประมาณนี้มาทดสอบ
# กำหนดข้อมูลวันเกิด แยกวันเดือนปี เป็น property ของ object
BirthDate = struct.new(:day, :month, :year)birth = BirthDate.new(2, 4, 1990)if (Time.now.year — birth.year <= 30)
# อยู่ในเกณฑ์ ทำงานต่อ
...
end...
อย่างไรก็ตาม คนก็อาจจะลืมกรอกได้ ถ้าเราไม่ได้ป้องกันไว้ จะเกิดปัญหาข้อมูลเป็น nil แล้วจะเกิด Error ภายในได้
birth = BirthDate.new(2, 4, nil)if (Time.now.year — birth.year <= 30)
# อยู่ในเกณฑ์ ทำงานต่อ
...
end
NoMethodError (undefined method `year' for nil:NilClass)
ในการที่จะเช็คข้อมูลตรงนี้เราก็ต้องใส่ตรรกกะในการตรวจสอบ ว่า birth เป็น nil หรือไม่อีก
birth = BirthDate.new(2, 4, nil)if birth.year && Time.now.year — birth.year <= 30
# อยู่ในเกณฑ์ ทำงานต่อ
...
end
อย่างไรก็ตาม แม้กระทั่งตัวแปล birth เอง ก็อาจจะไม่ได้มีทั้งหมดก็ได้ ก็ยิ่งจะต้องตรวจสอบให้ครบถ้วน
birth = nilif birth && birth.year && Time.now.year — birth.year <= 30
# อยู่ในเกณฑ์ ทำงานต่อ
...
end
ถึงจุดนี้ เราจะรู้สึกถึงความยากลำบาก ถ้าเราต้องเรียกใช้ตัวแปลหรือฟังก์ชั่นที่อยู่ในระดับที่ลึกมากๆ เพราะอาจจะต้องมีการตรวจสอบไล่เรียงกันไปเรื่อยๆ เช่น
if a && a.b && a.c && a.b.c.d && a.b.c.d.e && a.b.c.d.e.f
...
end
ซึ่งมันน่าจะเป็นการดี ถ้าเราสามารถจัดการการเขียนโปรแกรมในลักษณะนี้ได้ง่ายๆ
Safe Navigation Operator (&.)
Safe Navigation Operator จึงถูกสร้างขึ้นมาเพื่อลดความวุ่นวายกับการต้องมาไล่เช็คเป็นลำดับแบบนั้น โดยในภาษา Ruby จะถูกเพิ่มมาตั้งแต่เวอร์ชั่น 2.3
โดยจะมีหน้าตามภาษาเป็นแบบนี้ครับ
a&.b
โดยหลักการทำงานก็ง่ายมากครับ โดย ที่ a&.b
จะได้ผลลัพธ์อยู่ 2 แนวทาง
เมื่อ a ไม่ใช่
nil
จะได้ผลลัพธ์เท่ากับa.b
เมื่อ a เป็น nil จะได้ผลลัพธ์เป็น nil
ดังนั้นจากตัวอย่างด้านบน เราจะสามารถแก้ไข โค้ดได้เป็น
if birth&.year && Time.now.year — birth.year <= 30
# อยู่ในเกณฑ์ ทำงานต่อ
...
end
ก็จะลดปริมาณโค้ดที่เขียนลงได้ครับ นอกจากนี้เราก็จะไม่เห็น Condition ที่ต้องตรวจเช็คไล่ไปเรื่อยๆ ทำให้มีเวลาทำความเข้าใจกับ Logic ที่เราต้องการใช้งานจริงๆ
แล้วใช้กับเครื่องหมายอื่นๆ ได้ไหม
น่าจะมีคำถามว่าทำไมเราไม่เขียนมาเป็นแบบนี้แทน
if Time.now.year — birth&.year <= 30
# อยู่ในเกณฑ์ ทำงานต่อ
...
end
ซึ่งถ้าเราลองรันโปรแกรมดู เราจะพอทราบว่า มันจะ Error ครับ เพราะส่วนของ birth&.year ในกรณีที่ birth ไม่มีค่ามาให้ มันก็จะคืนค่าเป็น nil นั่นเอง
และเราก็พบว่า ในทางการใช้ operator เราก็ไม่สามารถที่จะบวกลบคูณหารกับ nil ได้ อยู่แล้ว
ดังนั้นเราจึงจำเป็นต้องปรับโค้ดให้ตรวจสอบ อย่างไรก็ตามแม้ว่า จะยังพอมีท่าให้ใช้บ้าง แต่ก็เป็นท่าที่อ่านแล้วเข้าใจยาก จึงแนะนำว่าให้ทำการตรวจสอบผ่าน Safe Navigation Operator จะเข้าใจง่ายกว่าครับ
- ในกรณีนี้ก็ cast ให้ถูก type เช่น เราสามารถใส่ .to_i เพื่อให้ผลลัพธ์ที่ได้จะถูก convert เป็นตัวเลข ถ้า input ไม่เป็นตัวเลขจะคืนค่า 0 มา อย่างไรก็ตามท่านี้จำเป็นต้องพิจารณาผลและเงื่อนไขประกอบดีๆ เพราะอาจจะหลุดเคสได้
if Time.now.year - birth&.year.to_i
...
end
- เรียกผ่าน function เนื่องจาก operator ต่างๆ ใน ruby มันเป็น method ใน class นั่นเอง จึงสามารถทำอะไรประมาณนี้ได้ครับ แต่อย่างไรก็ตามมันกลับด้านแนวคิดตามปกติ มันจะงงเอาง่ายๆ ก็อย่าไปทำท่านี้ดีกว่าครับ
if -birth&.year&.-(Time.now.year)
...
end
สุดท้ายทำท่าทั่วไปจะเข้าใจง่ายสุดครับ
if birth&.year && Time.now.year — birth.year <= 30
# อยู่ในเกณฑ์ ทำงานต่อ
...
end
ส่งท้าย
ก็ถือว่าเป็นท่าที่ช่วงลดการที่จะต้องมาเขียนโค้ดเพื่อตรวจเช็คข้อมูลได้ดีท่านึงครับ ก็หวังว่าจะเป็นประโยชน์ครับ