วัตถุที่จะกลายพันธุ์หมายความว่าอย่างไร
อย่าให้คำพูดแฟนซีทำให้คุณสับสน “การเปลี่ยนแปลง ” หมายความว่าสถานะภายในของวัตถุสามารถเปลี่ยนแปลงได้ นี่เป็นค่าเริ่มต้นของออบเจ็กต์ทั้งหมด ยกเว้นออบเจ็กต์ที่หยุดนิ่ง หรือที่เป็นส่วนหนึ่งของรายการวัตถุพิเศษ
พูดอีกอย่างก็คือ ไม่ใช่ทุกอ็อบเจกต์ใน Ruby ที่จะกลายพันธุ์ได้!
ตัวอย่างเช่น :
มันไม่สมเหตุสมผลเลยสำหรับตัวเลขหรือสัญลักษณ์ หรือแม้แต่ true
หรือ false
(ซึ่งเป็นวัตถุด้วย) ให้เปลี่ยนแปลง
หมายเลข 1 จะเป็น 1 เสมอ
แต่ออบเจ็กต์อื่นๆ โดยเฉพาะออบเจ็กต์ที่มีไว้เพื่อจัดเก็บข้อมูล เช่น ออบเจ็กต์ Array หรือ Hash ควรจะสามารถเปลี่ยนแปลงได้ด้วยเหตุผลด้านประสิทธิภาพ
ทางเลือกคืออะไร
คุณสามารถทำสำเนาใหม่ ของวัตถุที่มีการเปลี่ยนแปลง แล้วส่งคืนวัตถุใหม่นี้ โดยปล่อยให้วัตถุเดิมไม่บุบสลาย
หากอาร์เรย์ เปลี่ยนไม่ได้ และคุณต้องการเปลี่ยนเพียงองค์ประกอบเดียวของอาร์เรย์ คุณจะต้องคัดลอกข้อมูลทั้งหมด รวมถึงองค์ประกอบที่ไม่เปลี่ยนแปลง
ลองนึกภาพว่าคุณต้องคัดลอกอาร์เรย์องค์ประกอบหนึ่งล้าน (หรือมากกว่า) ทุกครั้งที่คุณต้องทำการเปลี่ยนแปลง ไม่สำคัญว่าจะเล็กน้อยแค่ไหน! ไม่ค่อยมีประสิทธิภาพ…
ยังไงก็ได้
มาดูวิธีการทำงานของ mutability ใน Ruby กันดีกว่า
การเปลี่ยนแปลงและตัวแปรเป็นตัวชี้
มีประเภทของข้อผิดพลาดในการเขียนโปรแกรมที่เกิดจากการรวมกันของสองสิ่ง:
- วัตถุที่เปลี่ยนแปลงได้
- ข้อเท็จจริงที่ว่าตัวแปรไม่มีข้อมูลโดยตรง แต่เป็นการอ้างอิงถึงตำแหน่งที่จัดเก็บข้อมูลนี้
วิธีหนึ่งที่ข้อผิดพลาดเหล่านี้แสดงให้เห็นคือเมื่อคุณพยายาม 'นามแฝง' ตัวแปร
นี่คือตัวอย่าง :
name = "Peter" other_name = name puts other_name # "Peter"
ในตัวอย่างนี้ ทั้ง name
และ other_name
มีการอ้างอิงไปยังวัตถุสตริงเดียวกัน คุณสามารถใช้เพื่อแสดงหรือแก้ไขเนื้อหาของสตริงนี้ได้
ปัญหาจะปรากฏขึ้นหากเราปฏิบัติต่อ other_name
เหมือนสำเนาของสตริง
other_name[0] = 'T' name # "Teter" other_name # "Teter"
เนื่องจากตัวแปรทั้งสองชี้ไปที่สตริงเดียวกัน เราจึงเปลี่ยน "Peter" เป็น "Teter" นั่นเป็นปัญหาเพราะเราน่าจะอยากให้ “ปีเตอร์” อยู่ใกล้ๆ
การโคลนวัตถุ
วิธีหนึ่งที่จะจัดการกับปัญหานี้คือการใช้ dup
วิธีการ
การดำเนินการนี้จะบอกให้ Ruby มอบสำเนาของวัตถุให้คุณ นอกจากนี้ยังมี clone
เมธอด ซึ่งนอกจากจะให้สำเนาของอ็อบเจ็กต์แก่คุณแล้ว ยังคัดลอกสถานะการตรึง &เมธอดซิงเกิลตันใดๆ ที่กำหนดไว้บนอ็อบเจ็กต์
มาดูตัวอย่างกัน :
numbers = [1, 2, 3] more_numbers = numbers.dup more_numbers << 4 numbers # [1, 2, 3] more_numbers # [1, 2, 3, 4]
ในตัวอย่างนี้ คุณสามารถดูได้ว่า numbers
เดิมเป็นอย่างไร อาร์เรย์ยังคงไม่เปลี่ยนแปลง ลองลบ dup
. นั้นออก โทรไปสายที่สามแล้วมาดูกันว่าจะเป็นอย่างไร 🙂
วิธีการแช่แข็งทับทิม
อีกวิธีหนึ่งในการทำให้วัตถุปลอดภัยจากการเปลี่ยนแปลงที่ไม่ต้องการคือการ "หยุด" วัตถุนั้น วัตถุ Ruby ใดๆ จะถูกตรึงโดยใช้ freeze
วิธีการ
เมื่อวัตถุถูกตรึง ความพยายามใดๆ ในการเปลี่ยนแปลงวัตถุนี้จะส่งผลให้ RuntimeError
ข้อยกเว้น
หมายเหตุ:คุณสามารถใช้
frozen?
วิธีการตรวจสอบว่าวัตถุถูกแช่แข็งหรือไม่
ตัวอย่าง :
animals = %w( cat dog tiger ) animals.freeze animals << 'monkey' # RuntimeError: can't modify frozen Array
สิ่งหนึ่งที่ต้องจำไว้ก็คือ การดำเนินการนี้จะหยุดเพียงหนึ่งอ็อบเจ็กต์ ในตัวอย่างนี้ อาร์เรย์เอง ซึ่งทำให้เราไม่สามารถเพิ่มหรือนำไอเท็มออกจากอ็อบเจ็กต์ได้ แต่ สตริงภายในอาร์เรย์จะไม่ถูกตรึง จึงสามารถเปลี่ยนแปลงได้ !
animals[1][0] = 't' # => ["cat", "tog", "tiger"]
หากคุณต้องการหยุดสตริง คุณต้องเรียก freeze
กับพวกเขา ชอบสิ่งนี้:animals.each(&:freeze)
.
สายแข็ง
ออบเจ็กต์ที่ไม่แน่นอนยังมีผลกระทบต่อประสิทธิภาพการทำงาน โดยเฉพาะสตริง เหตุผลก็คือมีโอกาสดีที่โปรแกรมขนาดใหญ่จะใช้สตริงเดียวกันหลายครั้ง
Ruby จะสร้างออบเจ็กต์ใหม่ให้กับทุกๆ สตริง แม้ว่าสตริงสองสตริงจะมีลักษณะเหมือนกัน หรือกล่าวอีกนัยหนึ่ง พวกมันมี 'เนื้อหา' เหมือนกัน คุณสามารถเห็นสิ่งนี้เกิดขึ้นได้อย่างง่ายดายใน irb
หากคุณใช้ object_id
วิธีการ
นี่คือตัวอย่าง :
a = 'test' b = 'test' a.object_id # 76325640 b.object_id # 76317550
นี่เป็นปัญหาเนื่องจากอ็อบเจ็กต์เหล่านี้กำลังใช้หน่วยความจำพิเศษและรอบ CPU พิเศษ
เริ่มต้นด้วย Ruby 2.1 เมื่อคุณใช้ สตริงที่ค้าง , Ruby จะใช้วัตถุสตริงเดียวกัน เพื่อหลีกเลี่ยงการสร้างสำเนาใหม่ของสตริงเดียวกัน ซึ่งส่งผลให้ประหยัดหน่วยความจำและเพิ่มประสิทธิภาพเพียงเล็กน้อย
Rails ทำให้ กว้างขวาง การใช้สตริงที่ตรึงไว้ด้วยเหตุนี้ ตัวอย่างเช่น ลองดู PR นี้
สิ่งนี้ทำให้ทีมพัฒนา Ruby เริ่มพิจารณาย้ายสตริงไปที่ไม่เปลี่ยนรูป วัตถุโดยค่าเริ่มต้น อันที่จริง Ruby 2.3 ซึ่งเพิ่งเปิดตัวเมื่อไม่กี่วันก่อน มีวิธีเปิดใช้งานสองวิธีสำหรับโปรเจ็กต์ของคุณ
หนึ่งคือการรวม # frozen_string_literal: true
ที่ด้านบนของทุกไฟล์ที่คุณต้องการให้สตริงไม่เปลี่ยนรูป และอีกอย่างคือการใช้อาร์กิวเมนต์บรรทัดคำสั่ง --enable=frozen-string-literal
.
สตริงที่ไม่เปลี่ยนรูปโดยค่าเริ่มต้นอาจจะลงจอดใน Ruby 3.0
อย่าเพิ่งคลั่งไคล้และเริ่มแช่แข็งสตริงทั้งหมดในแอปของคุณ คุณต้องการทำเช่นนี้สำหรับสตริงที่ใช้หลายร้อยครั้งเพื่อดูประโยชน์บางประเภท . ต้องบอกว่านี่คือเครื่องมือที่คุณสามารถใช้ค้นหาสตริงที่อาจจะหยุดได้
รู้วิธีการของคุณ
ไม่ใช่ว่าทุกวิธีในวัตถุที่เปลี่ยนแปลงได้จะเปลี่ยนวัตถุได้จริง ตัวอย่างเช่น วิธี gsub จะคืนค่าสตริงใหม่ โดยปล่อยให้ต้นฉบับไม่ถูกแตะต้อง
วิธีการเหล่านี้บางวิธีมีเวอร์ชันทางเลือก ซึ่งเปลี่ยนวัตถุเดิมในตำแหน่งซึ่งมักจะมีประสิทธิภาพมากกว่า วิธีการเหล่านี้มักลงท้ายด้วยเครื่องหมายอัศเจรีย์ !
เพื่อแสดงผลกระทบ
ตัวอย่างสองวิธีของ 'ปัง' เหล่านี้คือ gsub!
และ map!
.
หมายเหตุ :
เมธอดที่ลงท้ายด้วย !
ไม่ได้หมายความว่าเป็น "วิธีการที่เปลี่ยนแปลงวัตถุ" เสมอไป
โดยทั่วไปแล้ว !
สัญลักษณ์ใช้เพื่อแสดงถึง 'อันตราย' ตัวอย่างหนึ่งตัวอย่าง นี่คือ exit!
เมธอด ซึ่งจะออกจากโปรแกรมทันที โดยไม่สนใจตัวจัดการทางออก
นอกจากนี้ยังมีวิธีการที่เปลี่ยนวัตถุและไม่ได้ลงท้ายด้วย !
เครื่องหมาย. ตัวอย่างเช่น:delete
, clear
, push
, concat
และอีกมากมาย
บทสรุป
ความแปรปรวนอาจเป็นเรื่องยุ่งยาก แต่เนื่องจากคุณอ่านโพสต์นี้ คุณจึงพร้อมรับมือกับมันได้ดีขึ้นมาก ตรวจสอบเอกสารประกอบของ Ruby หากคุณไม่แน่ใจว่าวิธีการนั้นกำลังทำอะไรอยู่ วิธีนี้จะช่วยให้คุณหลีกเลี่ยงปัญหาได้
ฉันหวังว่าคุณจะพบว่าบทความนี้มีข้อมูล โปรดแบ่งปัน กับเพื่อนๆ ของคุณเพื่อที่พวกเขาจะได้เพลิดเพลิน มันเกินไป เข้าร่วมจดหมายข่าวของฉันด้านล่างด้วย คุณจะได้ไม่พลาดเนื้อหาแบบนี้เมื่อมันออกมา!