เหตุใดจึงใช้เครื่องหมายอัศเจรีย์ในเมธอด Ruby


540

ในทับทิมบางวิธีมีเครื่องหมายคำถาม ( ?) ที่ถามคำถามเช่นinclude?นั้นหากถามว่าวัตถุที่เป็นปัญหานั้นรวมอยู่ด้วยสิ่งนี้จะส่งกลับค่าจริง / เท็จ

แต่ทำไมบางวิธีจึงมีเครื่องหมายอัศเจรีย์ ( !) ที่คนอื่นไม่มี

มันหมายความว่าอะไร?


21
คำพ้องความหมาย: ปัง, เครื่องหมายอัศเจรีย์
prusswan

17
คำตอบที่ได้รับการยอมรับควรจะเปลี่ยนเป็นstackoverflow.com/a/612653/109618 ดูwobblini.net/bang.txtและruby-forum.com/topic/176830#773946 - "เครื่องหมายปังหมายถึง" เวอร์ชั่นปังมีอันตรายมากกว่ารุ่นอื่นที่ไม่ใช่ปัง จัดการด้วยความระมัดระวัง "" -Matz
David J.

2
วิธีปังจะเป็นทางเลือกการออกแบบที่ดีถ้าเพียงและทุกวิธีปังตกอยู่ในอันตราย น่าเศร้าที่พวกเขาไม่ได้และมันจึงกลายเป็นแบบฝึกหัดที่น่าผิดหวังในการจำสิ่งที่และไม่สามารถเปลี่ยนแปลงได้
ดาเมียนโรช

คำตอบ:


617

โดยทั่วไปวิธีการที่สิ้นสุดใน!ระบุว่าวิธีการที่จะปรับเปลี่ยนวัตถุที่มันเรียกว่า ทับทิมเรียกสิ่งเหล่านี้ว่า " วิธีอันตราย " เพราะพวกเขาเปลี่ยนสถานะที่คนอื่นอาจมีการอ้างอิงถึง นี่คือตัวอย่างง่ายๆสำหรับสตริง:

foo = "A STRING"  # a string called foo
foo.downcase!     # modifies foo itself
puts foo          # prints modified foo

สิ่งนี้จะออก:

a string

ในห้องสมุดมาตรฐานมีสถานที่มากมายที่คุณจะเห็นวิธีการตั้งชื่อที่คล้ายกันคู่หนึ่งที่มี!และไม่มี สิ่งที่ไม่มีเรียกว่า "วิธีการที่ปลอดภัย" และพวกเขาจะส่งคืนสำเนาต้นฉบับที่มีการเปลี่ยนแปลงที่นำไปใช้กับสำเนาโดยที่ callee ไม่เปลี่ยนแปลง นี่คือตัวอย่างเดียวกันโดยไม่มี!:

foo = "A STRING"    # a string called foo
bar = foo.downcase  # doesn't modify foo; returns a modified string
puts foo            # prints unchanged foo
puts bar            # prints newly created bar

ผลลัพธ์นี้:

A STRING
a string

โปรดทราบว่านี่เป็นเพียงการประชุม แต่มีคลาส Ruby มากมายที่ติดตาม นอกจากนี้ยังช่วยให้คุณสามารถติดตามสิ่งที่ได้รับการแก้ไขในรหัสของคุณ


2
นอกจากนี้ยังมีกรณีเช่นทางออกกับทางออก! และ (ในราง) บันทึกกับบันทึก!
Andrew Grimm

24
ระวังให้ดี - ห้องสมุดเล็ก ๆ หลายแห่งไม่ปฏิบัติตามอนุสัญญานี้ หากมีสิ่งแปลก ๆ เกิดขึ้นบ่อยครั้งแทนที่สิ่งที่เกิดขึ้น ด้วย obj = obj. อะไรก็ตาม! แก้ไขมัน น่าผิดหวังมาก
Sarah Mei

101
ปังยังใช้สำหรับวิธีการที่ทำให้เกิดข้อยกเว้นเมื่อวิธีที่ไม่มีเช่น: saveและsave!ในActiveRecord
ecoologic

3
@AbhilashAK บันทึก! เพิ่มข้อผิดพลาดหากไม่สามารถบันทึกได้ นี่ตรงข้ามกับการบันทึกปกติที่ส่งคืนจริง / เท็จ
BookOfGreg

31
@tgamblin มีวิธีการมากมายใน Ruby ที่ทำให้กลายพันธุ์โดยไม่ต้องเรียบ มีวิธีการที่หายากที่ไม่กลายพันธุ์กับปัง แต่ทำสิ่งที่น่าแปลกใจเช่นเพิ่มข้อผิดพลาดหรือข้ามข้อผิดพลาด เรียบถูกใช้เพื่อบอกว่านี่เป็นรุ่นที่ผิดปกติมากขึ้นของวิธีการและฉันคิดว่านี่ควรจะสะท้อนให้เห็นในคำตอบของคุณเพราะมันถูกทำเครื่องหมายว่าถูกต้อง
BookOfGreg

143

เครื่องหมายอัศเจรีย์หมายถึงสิ่งต่าง ๆ มากมายและบางครั้งคุณไม่สามารถบอกอะไรได้มากไปกว่า "สิ่งนี้เป็นอันตรายระวังตัว"

ดังที่คนอื่น ๆ ได้กล่าวไว้ในวิธีการมาตรฐานมักใช้เพื่อระบุวิธีการที่ทำให้วัตถุกลายพันธุ์เอง แต่ไม่เสมอไป โปรดทราบว่าวิธีการมาตรฐานหลายเปลี่ยนรับของพวกเขาและไม่ได้มีเครื่องหมายอัศเจรีย์ ( pop, shift, clear) และวิธีการบางอย่างที่มีเครื่องหมายอัศเจรีย์ไม่เปลี่ยนเครื่องรับของพวกเขา ( exit!) ดูตัวอย่างบทความนี้

ห้องสมุดอื่น ๆ อาจใช้มันแตกต่างกัน ใน Rails มีเครื่องหมายอัศเจรีย์บ่อยครั้งซึ่งหมายความว่าวิธีการนี้จะทำให้เกิดข้อยกเว้นเกี่ยวกับความล้มเหลวแทนที่จะล้มเหลวอย่างเงียบ ๆ

มันเป็นแบบแผนการตั้งชื่อ แต่หลายคนใช้มันในวิธีที่ต่างกันอย่างละเอียด ในรหัสของคุณกฎง่ายๆคือใช้เมื่อใดก็ตามที่วิธีการทำบางอย่าง "อันตราย" โดยเฉพาะอย่างยิ่งเมื่อมีสองวิธีที่มีชื่อเดียวกันอยู่และหนึ่งในนั้นคือ "อันตราย" มากกว่าอีกวิธีหนึ่ง "อันตราย" อาจหมายถึงเกือบทุกอย่าง


75

นี้ตั้งชื่อการประชุมจะถูกยกขึ้นจากโครงการ

1.3.5 อนุสัญญาการตั้งชื่อ

ตามแบบแผนชื่อของโพรซีเดอร์ที่ส่งคืนค่าบูลีนมักจะลงท้ายด้วย ``? '' ขั้นตอนดังกล่าวเรียกว่าภาคแสดง

ตามแบบแผนชื่อของขั้นตอนที่เก็บค่าไว้ในตำแหน่งที่จัดสรรไว้ก่อนหน้านี้ (ดูหัวข้อ 3.4) มักจะลงท้ายด้วย ``! '' ขั้นตอนดังกล่าวเรียกว่าขั้นตอนการกลายพันธุ์ ตามแบบแผนค่าที่ส่งคืนโดยโพรซีเดอร์การกลายพันธุ์จะไม่ได้รับการระบุ


2
+1 กับคำตอบนี้เนื่องจากมีเอกสารที่ให้คำอธิบายที่สมเหตุสมผลสำหรับ! การใช้ คำตอบที่ดีจริงๆ Steven
DavidSilveira

ขอบคุณ @DavidSilveira!
Steven Huwig

24

! โดยทั่วไปหมายถึงว่าวิธีการกระทำกับวัตถุแทนที่จะส่งกลับผลลัพธ์ จากหนังสือRuby Programming :

วิธีการที่เป็น "อันตราย" หรือปรับเปลี่ยนผู้รับอาจถูกตั้งชื่อด้วยการต่อท้าย "!"


18

มันถูกต้องที่สุดที่จะบอกว่าวิธีการที่มีบาง! เป็นเวอร์ชั่นที่อันตรายหรือน่าแปลกใจมากขึ้น มีวิธีการมากมายที่ทำให้กลายพันธุ์โดยไม่ใช้ Bang เช่น.destroyและโดยทั่วไปจะมีวิธีเรียบที่มีทางเลือกที่ปลอดภัยกว่าใน lib หลัก

ตัวอย่างเช่นใน Array เรามี.compactและ.compact!ทั้งสองวิธีกลายพันธุ์อาร์เรย์ แต่.compact!คืนค่าศูนย์แทนที่จะเป็นตัวเองหากไม่มีศูนย์ในอาร์เรย์ซึ่งน่าแปลกใจมากกว่าแค่คืนค่าตัวเอง

วิธีการเพียงคนเดียวไม่ใช่กรรมวิธีที่ฉันได้พบกับปังคือKernel's .exit!ซึ่งเป็นที่น่าแปลกใจมากกว่า.exitเพราะคุณไม่สามารถจับSystemExitในขณะที่กระบวนการคือปิด

Rails และ ActiveRecord ยังคงแนวโน้มเช่นนี้ในการที่มันใช้ผลบางอย่างที่น่าแปลกใจเช่นเอฟเฟกต์.create!ซึ่งทำให้เกิดข้อผิดพลาดในความล้มเหลว


16

จาก themomorohoax.com:

ปังสามารถใช้ในวิธีด้านล่างตามลำดับความชอบส่วนตัวของฉัน

1) วิธีการบันทึกที่ใช้งานทำให้เกิดข้อผิดพลาดหากวิธีการไม่ได้ทำในสิ่งที่มันบอกว่ามันจะ

2) วิธีการบันทึกที่ใช้งานบันทึกบันทึกหรือวิธีการบันทึกวัตถุ (เช่นแถบ!)

3) วิธีการทำสิ่งที่ "พิเศษ" เช่นโพสต์ไปยังบางแห่งหรือดำเนินการบางอย่าง

ประเด็นคือใช้ปังเมื่อคุณคิดจริงๆว่าจำเป็นหรือไม่เพื่อช่วยผู้พัฒนารายอื่นให้รำคาญใจที่ต้องตรวจสอบว่าทำไมคุณถึงใช้ปัง

ปังให้สองตัวชี้นำให้กับนักพัฒนาอื่น ๆ

1) ไม่จำเป็นต้องบันทึกวัตถุหลังจากเรียกใช้เมธอด

2) เมื่อคุณเรียกใช้เมธอด db จะถูกเปลี่ยน

http://www.themomorohoax.com/2009/02/11/when-to-use-a-bang-exclamation-point-after-rails-methods


6

คำอธิบายง่ายๆ:

foo = "BEST DAY EVER" #assign a string to variable foo.

=> foo.downcase #call method downcase, this is without any exclamation.

"best day ever"  #returns the result in downcase, but no change in value of foo.

=> foo #call the variable foo now.

"BEST DAY EVER" #variable is unchanged.

=> foo.downcase! #call destructive version.

=> foo #call the variable foo now.

"best day ever" #variable has been mutated in place.

แต่ถ้าคุณเคยเรียกวิธีการdowncase!ในคำอธิบายข้างต้นfooจะเปลี่ยนเป็นตัวพิมพ์เล็กอย่างถาวร downcase!จะไม่ส่งคืนวัตถุสตริงใหม่ แต่แทนที่สตริงในสถานที่โดยสิ้นเชิงเปลี่ยนเป็นfoodowncase ฉันขอแนะนำให้คุณอย่าใช้downcase!จนกว่าจะจำเป็นจริงๆ


1
!

ฉันชอบที่จะคิดว่าสิ่งนี้เป็นการเปลี่ยนแปลงที่รุนแรงที่ทำลายทุกสิ่งที่ผ่านไปก่อนหน้านี้ เครื่องหมายตกใจหรือเครื่องหมายอัศเจรีย์หมายความว่าคุณกำลังทำการเปลี่ยนแปลงที่บันทึกไว้อย่างถาวรในรหัสของคุณ

หากคุณใช้ตัวอย่างเช่นวิธีการของรูบี้สำหรับการทดแทนทั่วโลกgsub!การทดแทนที่คุณทำนั้นเป็นสิ่งถาวร

อีกวิธีหนึ่งที่คุณสามารถจินตนาการได้คือการเปิดไฟล์ข้อความและทำการค้นหาและแทนที่ตามด้วยการบันทึก !ทำเช่นเดียวกันในรหัสของคุณ

สิ่งเตือนความจำที่มีประโยชน์อีกอย่างถ้าคุณมาจากโลกทุบตีก็sed -iมีผลเช่นเดียวกันกับการเปลี่ยนแปลงอย่างถาวรที่บันทึกไว้


1

เรียกว่า "วิธีการทำลายล้าง" พวกเขามักจะเปลี่ยนสำเนาต้นฉบับของวัตถุที่คุณอ้างถึง

numbers=[1,0,10,5,8]
numbers.collect{|n| puts n*2} # would multiply each number by two
numbers #returns the same original copy
numbers.collect!{|n| puts n*2} # would multiply each number by two and destructs the original copy from the array
numbers   # returns [nil,nil,nil,nil,nil]

0

Bottom line: !วิธีการเพียงแค่เปลี่ยนค่าของวัตถุที่พวกเขาจะเรียกว่าในขณะที่วิธีการที่ไม่!ส่งกลับค่าการจัดการโดยไม่ต้องเขียนเหนือวัตถุวิธีการที่ถูกเรียก

ใช้เฉพาะใน!กรณีที่คุณไม่ได้วางแผนที่จะต้องการค่าดั้งเดิมที่เก็บไว้ในตัวแปรที่คุณเรียกว่าเมธอด

ฉันชอบทำสิ่งที่ชอบ:

foo = "word"
bar = foo.capitalize
puts bar

หรือ

foo = "word"
puts foo.capitalize

แทน

foo = "word"
foo.capitalize!
puts foo

ในกรณีที่ฉันต้องการเข้าถึงค่าเดิมอีกครั้ง


1
เพราะคำตอบของคุณไม่เป็นประโยชน์ แต่อย่างใด "Bottom line:! วิธีการเพียงแค่เปลี่ยนค่าของวัตถุที่พวกเขาเรียกว่า" ไม่เป็นความจริง
ดาร์วิน

@Darwin มันไม่เปลี่ยนค่าของวัตถุ !เปลี่ยนวัตถุแทนการส่งคืนสำเนาที่ถูกดัดแปลง
ชาร์ลส์

แล้วคุณคิดว่านี่เป็นอย่างไร User.create!
ดาร์วิน

@ ดาร์วินในบริบทใด ActiveRecord?
ชาร์ลส์

ใช่ ActiveRecord
ดาร์วิน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.