รหัสต่อไปนี้หมายความว่าอะไรในทับทิม?
||=
มันมีความหมายหรือเหตุผลสำหรับไวยากรณ์หรือไม่
รหัสต่อไปนี้หมายความว่าอะไรในทับทิม?
||=
มันมีความหมายหรือเหตุผลสำหรับไวยากรณ์หรือไม่
คำตอบ:
คำถามนี้ถูกกล่าวถึงบ่อยครั้งในรายการส่งเมลของ Ruby และบล็อก Ruby ที่ตอนนี้มีแม้แต่เธรดในรายการส่งเมลของ Ruby ซึ่งมีจุดประสงค์เพียงอย่างเดียวคือรวบรวมลิงก์ไปยังกระทู้อื่น ๆ ทั้งหมดในรายการส่งเมลของ Ruby ที่กล่าวถึงปัญหานี้ .
นี่คือหนึ่ง: รายการที่ชัดเจนของเธรดและหน้า | | = (หรือเท่ากัน)
หากคุณจริงๆต้องการที่จะรู้ว่าสิ่งที่เกิดขึ้นจะดูที่มาตรา 11.4.2.3 "ที่ได้รับมอบหมายโดยย่อว่า" ของที่ร่างจำเพาะทับทิมภาษา
เป็นการประมาณครั้งแรก
a ||= b
เทียบเท่ากับ
a || a = b
และไม่เทียบเท่า
a = a || b
อย่างไรก็ตามนั่นเป็นเพียงการประมาณครั้งแรกโดยเฉพาะอย่างยิ่งถ้าa
ไม่ได้กำหนด ความหมายยังแตกต่างกันไปขึ้นอยู่กับว่าเป็นการกำหนดตัวแปรอย่างง่ายการกำหนดวิธีการหรือการกำหนดดัชนี:
a ||= b
a.c ||= b
a[c] ||= b
ทุกคนต่างปฏิบัติกัน
a = false; a ||= true
ไม่ได้ทำในสิ่งที่คำตอบของคุณบอกว่ามันจะ "แตกต่างกันนิดหน่อย"
a ||= b
เป็นผู้ประกอบการที่ได้รับมอบหมายตามเงื่อนไข มันหมายความว่าถ้าa
จะไม่ได้กำหนดหรือfalseyแล้วประเมินb
และการตั้งค่าa
เพื่อผล หากa
มีการกำหนดและประเมินความจริงอย่างเท่าเทียมกันb
จะไม่มีการประเมินและจะไม่มีการมอบหมายใด ๆ เกิดขึ้น ตัวอย่างเช่น:
a ||= nil # => nil
a ||= 0 # => 0
a ||= 2 # => 0
foo = false # => false
foo ||= true # => true
foo ||= false # => true
ดูเหมือนว่าผู้ประกอบการที่ได้รับมอบหมายคนอื่น ๆ (เช่น+=
) จะสับสนแต่ก็มีพฤติกรรมแตกต่างกัน
a += b
แปลเป็น a = a + b
a ||= b
แปลโดยประมาณเป็น a || a = b
a || a = b
มันเป็นความใกล้ชวเลข ความแตกต่างคือว่าเมื่อa
ไม่ได้กำหนด, a || a = b
จะยกNameError
ในขณะที่a ||= b
ชุดที่จะa
b
ความแตกต่างนี้ไม่สำคัญถ้าa
และb
ทั้งสองเป็นตัวแปรท้องถิ่น แต่มีความสำคัญถ้าอย่างใดอย่างหนึ่งเป็นวิธีการ getter / setter ของชั้นเรียน
อ่านเพิ่มเติม:
h = Hash.new(0); h[1] ||= 2
พิจารณา ตอนนี้พิจารณาทั้งสองเป็นไปได้ขยายVSh[1] = h[1] || 2
h[1] || h[1] = 2
ทั้งสองนิพจน์จะประเมิน0
แต่ครั้งแรกจะเพิ่มขนาดของแฮช บางทีนั่นอาจเป็นสาเหตุที่ Matz เลือกที่จะ||=
ทำตัวเหมือนการขยายตัวครั้งที่สอง (ฉันใช้สิ่งนี้จากตัวอย่างจากหนึ่งในกระทู้ที่เชื่อมโยงกับคำตอบอื่น)
a || a = b
ยกNameError
ถ้าa
ยังไม่ได้กำหนด a ||= b
ไม่ แต่แทนที่จะเริ่มต้นและชุดมันa
b
นั่นคือความแตกต่างระหว่างสองเท่าที่ฉันรู้ ในทำนองเดียวกันความแตกต่างเพียงอย่างเดียวระหว่างa = a || b
และa ||= b
ฉันรู้ว่าถ้าa=
เป็นวิธีการก็จะถูกเรียกโดยไม่คำนึงถึงสิ่งที่a
ส่งกลับ นอกจากนี้ยังมีความแตกต่างเพียงอย่างเดียวระหว่างa = b unless a
และa ||= b
ที่ฉันรู้ก็คือว่าคำสั่งประเมินnil
แทนa
ถ้าa
เป็น truthy มีการประมาณจำนวนมาก แต่ไม่มีอะไรที่เทียบเท่า ...
a ||= b
ประเมินวิธีเดียวกันกับแต่ละบรรทัดต่อไปนี้
a || a = b
a ? a : a = b
if a then a else a = b end
-
ในทางกลับกัน,
a = a || b
ประเมินวิธีเดียวกันกับแต่ละบรรทัดต่อไปนี้
a = a ? a : b
if a then a = a else a = b end
-
แก้ไข: ตามที่ AJedi32 ชี้ให้เห็นในความคิดเห็นสิ่งนี้จะเป็นจริงถ้า: 1. a เป็นตัวแปรที่กำหนดไว้ 2. การประเมินหนึ่งครั้งและสองครั้งไม่ส่งผลให้เกิดความแตกต่างในสถานะของโปรแกรมหรือระบบ
a
เป็นเท็จ / ศูนย์ / ไม่ได้กำหนดก็จะถูกประเมินสองครั้ง (แต่ฉันไม่รู้ Ruby ดังนั้นฉันไม่รู้เลยว่า lvalues สามารถ 'ประเมิน' ได้หรือไม่ ... )
a || a = b
, a ? a : a = b
, if a then a else a = b end
และif a then a = a else a = b end
จะโยนข้อผิดพลาดถ้าa
จะไม่ได้กำหนดในขณะที่a ||= b
และa = a || b
จะไม่ นอกจากนี้a || a = b
, a ? a : a = b
, if a then a else a = b end
, a = a ? a : b
และif a then a = a else a = b end
ประเมินa
สองครั้งเมื่อa
เป็น truthy ขณะa ||= b
และa = a || b
ทำไม่ได้
a || a = b
จะไม่ประเมินa
สองครั้งเมื่อa
เป็นจริง
the end state will be equivalent after the whole line has been evaluated
นั้นไม่จำเป็นต้องเป็นความจริง เกิดอะไรขึ้นถ้าa
เป็นวิธีการ? วิธีการสามารถมีผลข้างเคียง เช่นกับpublic; def a=n; @a=n; end; def a; @a+=1; end; self.a = 5
, self.a ||= b
จะกลับ 6 แต่self.a ? self.a : self.a = b
จะกลับมา 7
ในระยะสั้นa||=b
หมายถึง: ถ้าa
เป็นundefined, nil or false
กำหนดที่จะb
a
มิฉะนั้นให้a
เหมือนเดิม
x ||= y
วิธี
หากx
มีค่าใด ๆ ปล่อยไว้ตามลำพังและไม่เปลี่ยนค่ามิฉะนั้นจะตั้งค่าไว้x
ให้เป็นy
มันหมายถึงหรือเท่ากับ มันจะตรวจสอบว่ามีการกำหนดค่าทางด้านซ้ายจากนั้นใช้ค่านั้น หากไม่ใช่ให้ใช้ค่าทางด้านขวา คุณสามารถใช้มันใน Rails เพื่อแคชตัวแปรอินสแตนซ์ในแบบจำลอง
ตัวอย่างการใช้ Rails อย่างรวดเร็วซึ่งเราสร้างฟังก์ชั่นเพื่อดึงข้อมูลผู้ใช้ที่เข้าสู่ระบบในปัจจุบัน:
class User > ActiveRecord::Base
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
end
มันตรวจสอบเพื่อดูว่ามีการตั้งค่าตัวแปรอินสแตนซ์ @current_user ถ้าเป็นมันจะส่งคืนซึ่งจะเป็นการบันทึกการโทรฐานข้อมูล หากยังไม่ได้ตั้งค่าเราจะทำการโทรแล้วตั้งค่าตัวแปร @current_user เป็นค่านั้น มันเป็นเทคนิคการแคชอย่างง่าย ๆ แต่ดีเยี่ยมเมื่อคุณดึงตัวแปรอินสแตนซ์เดียวกันในแอปพลิเคชันหลาย ๆ ครั้ง
undefined
แต่ยังบนfalse
และnil
ซึ่งอาจจะไม่เกี่ยวข้องcurrent_user
แต่โดยเฉพาะอย่างยิ่งfalse
สามารถ unexpectecd ในกรณีอื่น ๆ
x ||= y
คือ
x || x = y
"ถ้า x เป็นเท็จหรือไม่ได้กำหนดดังนั้น x ชี้ไปที่ y"
จะแม่นยำa ||= b
หมายความว่า "ถ้าa
จะไม่ได้กำหนดหรือ falsy ( false
หรือnil
) ชุดa
การb
และประเมินผลการ (เช่นกลับ) b
มิฉะนั้นประเมินa
"
อื่น ๆ มักจะพยายามที่จะแสดงให้เห็นถึงนี้โดยบอกว่าa ||= b
จะเทียบเท่ากับหรือa || a = b
a = a || b
สิ่งที่เทียบเท่าเหล่านี้อาจเป็นประโยชน์สำหรับการทำความเข้าใจแนวคิด แต่โปรดทราบว่าสิ่งเหล่านี้ไม่ถูกต้องภายใต้เงื่อนไขทั้งหมด ให้ฉันอธิบาย:
a ||= b
⇔a || a = b
?
พฤติกรรมของคำสั่งเหล่านี้แตกต่างกันเมื่อa
เป็นตัวแปรท้องถิ่นที่ไม่ได้กำหนด ในกรณีที่a ||= b
จะตั้งa
ไปb
(และประเมินb
) ในขณะที่จะเพิ่มa || a = b
NameError: undefined local variable or method 'a' for main:Object
a ||= b
⇔a = a || b
?
เทียบเท่าของงบเหล่านี้มักจะสันนิษฐานว่าตั้งแต่เท่าเทียมคล้ายเป็นความจริงอื่น ๆย่อมอบหมายผู้ประกอบการ (เช่น+=
, -=
, *=
, /=
, %=
, **=
, &=
, |=
, ^=
, <<=
และ>>=
) อย่างไรก็ตามสำหรับ||=
พฤติกรรมของข้อความเหล่านี้อาจแตกต่างกันเมื่อa=
เป็นวิธีการในวัตถุและa
เป็นความจริง ในกรณีที่a ||= b
จะทำอะไร (นอกเหนือจากการประเมินa
) ในขณะที่a = a || b
จะเรียกa=(a)
ในa
's รับ ตามที่คนอื่น ๆได้ชี้ให้เห็นสิ่งนี้สามารถสร้างความแตกต่างเมื่อการโทรa=a
มีผลข้างเคียงเช่นการเพิ่มปุ่มลงในแฮช
a ||= b
⇔a = b unless a
??
พฤติกรรมของข้อความเหล่านี้แตกต่างกันในสิ่งที่พวกเขาประเมินว่าเมื่อใดa
เป็นความจริง ในกรณีที่a = b unless a
จะมีการประเมินการnil
(แม้ว่าa
จะยังคงไม่ได้รับการตั้งค่าตามที่คาดไว้) ในขณะที่จะมีการประเมินเพื่อa ||= b
a
a ||= b
⇔defined?(a) ? (a || a = b) : (a = b)
????
ยังคงไม่. งบเหล่านี้อาจแตกต่างกันเมื่อมีmethod_missing
วิธีการที่มีอยู่ซึ่งจะส่งกลับค่า truthy a
สำหรับ ในกรณีนี้a ||= b
จะมีการประเมินเพื่อสิ่งที่method_missing
ผลตอบแทนและไม่พยายามที่จะตั้งค่าa
ในขณะที่defined?(a) ? (a || a = b) : (a = b)
จะตั้งค่าa
การและประเมินผลการb
b
เอาล่ะเอาล่ะเพื่อให้สิ่งที่เป็น a ||= b
เทียบเท่ากับ? มีวิธีที่จะแสดงสิ่งนี้ในรูบีหรือไม่?
อืมสมมติว่าฉันไม่ได้มองอะไรเลยฉันเชื่อว่าa ||= b
เป็นหน้าที่เทียบเท่ากับ ... ( drumroll )
begin
a = nil if false
a || a = b
end
ยึดมั่นใน! นั่นเป็นเพียงตัวอย่างแรกที่มี noop มาก่อนหรือไม่ ก็ไม่มาก โปรดจำไว้ว่าฉันพูดก่อนหน้าa ||= b
นี้ว่าไม่เท่ากับa || a = b
เมื่อa
เป็นตัวแปรท้องถิ่นไม่ได้กำหนด? ดีa = nil if false
เพื่อให้แน่ใจว่าa
จะไม่ได้กำหนดแม้ว่าสายที่ไม่เคยดำเนินการ ตัวแปรโลคัลใน Ruby ถูกกำหนดขอบเขตไว้เล็กน้อย
(a=b unless a) or a
a
เป็นวิธีการมันจะถูกเรียกสองครั้งแทนที่จะเป็นครั้งเดียว (ถ้ามันคืนค่าความจริงในครั้งแรก) ซึ่งอาจทำให้พฤติกรรมแตกต่างกันหากยกตัวอย่างเช่นa
ใช้เวลานานในการกลับหรือมีผลข้างเคียง
b
ให้a
หรือไม่ rhs ยังคงกำหนดให้กับ lhs หรือกล่าวอีกนัยหนึ่ง lhs ยังไม่ได้กำหนดค่าของมันเพื่อ rhs?
a ||= b
คำตอบที่ดีที่สุดที่ฉันพบบนอินเทอร์เน็ต ขอบคุณ
unless x
x = y
end
ยกเว้นว่า x มีค่า (ไม่ใช่ศูนย์หรือเท็จ) ให้ตั้งค่าเท่ากับ y
เทียบเท่ากับ
x ||= y
สมมติ a = 2
และb = 3
จากนั้นa ||= b
จะส่งผลให้a
มูลค่าของเช่น2
เช่นค่า
เช่นเมื่อประเมินค่าบางอย่างไม่ได้ผลfalse
หรือnil
.. นั่นเป็นเหตุผลที่มันll
ไม่ได้ประเมินb
ค่าของ
ตอนนี้สมมติว่า a = nil
และb = 3
และ
จากนั้นa ||= b
จะส่งผลให้ 3
เช่นb
ค่าของ
ในขั้นแรกให้ลองประเมินค่าของซึ่งส่งผลให้nil
.. เพื่อประเมินb
ค่าของ
ตัวอย่างที่ดีที่สุดที่ใช้ในแอพ Ror คือ:
#To get currently logged in iser
def current_user
@current_user ||= User.find_by_id(session[:user_id])
end
# Make current_user available in templates as a helper
helper_method :current_user
โดยที่User.find_by_id(session[:user_id])
จะถูกไล่ออกถ้าหาก@current_user
ไม่ได้เริ่มต้นก่อน
a || = b
บ่งบอกว่าค่าใด ๆ ที่มีอยู่ใน 'a' และคุณไม่ต้องการที่จะเปลี่ยนแปลงมันให้ใช้ค่านั้นมิฉะนั้นถ้า 'a' ไม่มีค่าใด ๆ ให้ใช้ค่าของ 'b'
คำศัพท์ง่าย ๆ ถ้าด้านซ้ายถ้าไม่ใช่ null ให้ชี้ไปที่ค่าที่มีอยู่แล้วชี้ไปที่ค่าที่ด้านขวา
a ||= b
เทียบเท่ากับ
a || a = b
และไม่
a = a || b
เนื่องจากสถานการณ์ที่คุณกำหนดแฮชด้วยค่าเริ่มต้น (แฮชจะส่งคืนค่าเริ่มต้นสำหรับคีย์ที่ไม่ได้กำหนด)
a = Hash.new(true) #Which is: {}
ถ้าคุณใช้:
a[10] ||= 10 #same as a[10] || a[10] = 10
ยังคงเป็น:
{}
แต่เมื่อคุณเขียนมันอย่างนั้น:
a[10] = a[10] || 10
กลายเป็น:
{10 => true}
เพราะคุณได้กำหนดค่าของตัวเองที่สำคัญ10
ซึ่งเป็นค่าเริ่มต้นที่เป็นจริงดังนั้นตอนนี้แฮชถูกกำหนดสำหรับคีย์10
แทนที่จะไม่ทำการกำหนดในตอนแรก
มันเหมือนกับการเริ่มต้นที่ขี้เกียจ หากตัวแปรมีการกำหนดไว้แล้วมันจะใช้ค่านั้นแทนการสร้างค่าอีกครั้ง
โปรดจำไว้ด้วยว่านั่น||=
ไม่ใช่การดำเนินการของอะตอมดังนั้นจึงไม่ปลอดภัยสำหรับเธรด ตามกฎของหัวแม่มืออย่าใช้มันสำหรับวิธีการเรียน
นี่คือสัญลักษณ์การกำหนดค่าเริ่มต้น
ตัวอย่างเช่น: x || = 1
สิ่งนี้จะตรวจสอบเพื่อดูว่า x เป็นศูนย์หรือไม่ ถ้า x ไม่มีจริงแล้วมันจะกำหนดค่าใหม่นั้น (1 ในตัวอย่างของเรา)
ชัดเจนมากขึ้น:
ถ้า x == nil
x = 1
จุดสิ้นสุด
nil
หรือfalse
ไม่เท่านั้นnil
|| =เป็นผู้ดำเนินการกำหนดเงื่อนไข
x ||= y
เทียบเท่ากับ
x = x || y
หรืออีกวิธีหนึ่ง
if defined?(x) and x
x = x
else
x = y
end
ถ้าไม่ได้มีค่าก็จะได้รับการกำหนดค่าของX
Y
มิฉะนั้นจะเก็บค่าเดิมไว้ 5 ในตัวอย่างนี้:
irb(main):020:0> x = 5
=> 5
irb(main):021:0> y = 10
=> 10
irb(main):022:0> x ||= y
=> 5
# Now set x to nil.
irb(main):025:0> x = nil
=> nil
irb(main):026:0> x ||= y
=> 10
ในฐานะที่เป็นความเข้าใจผิดกัน, a ||= b
จะไม่เทียบเท่ากับแต่ก็จะทำงานเช่นa = a || b
a || a = b
แต่นี่เป็นกรณีที่ยุ่งยาก ถ้าa
ไม่ได้ถูกกำหนดa || a = 42
ยกNameError
ในขณะที่ผลตอบแทนa ||= 42
42
ดังนั้นพวกเขาดูเหมือนจะไม่แสดงออกอย่างเท่าเทียมกัน
||=
กำหนดค่าให้ถูกต้องเฉพาะเมื่อเหลือ == nil (หรือไม่ได้กำหนดหรือเป็นเท็จ)
ไวยากรณ์ ruby-lang นี้ คำตอบที่ถูกต้องคือตรวจสอบเอกสารทับทิม คำอธิบายอื่น ๆ ทั้งหมดทำให้งงงวย
"ruby-lang docs ย่อการมอบหมาย"
https://docs.ruby-lang.org/en/2.4.0/syntax/assignment_rdoc.html#label-Abbreviated+Assignment
irb(main):001:0> a = 1
=> 1
irb(main):002:0> a ||= 2
=> 1
เพราะa
ได้ตั้งไว้แล้ว1
irb(main):003:0> a = nil
=> nil
irb(main):004:0> a ||= 2
=> 2
เนื่องจากว่าa
เป็นnil
b = 5
a ||= b
สิ่งนี้แปลเป็น:
a = a || b
ซึ่งจะเป็น
a = nil || 5
ดังนั้นในที่สุด
a = 5
ตอนนี้ถ้าคุณเรียกสิ่งนี้อีกครั้ง:
a ||= b
a = a || b
a = 5 || 5
a = 5
b = 6
ตอนนี้ถ้าคุณเรียกสิ่งนี้อีกครั้ง:
a ||= b
a = a || b
a = 5 || 6
a = 5
หากคุณสังเกตเห็นb
ค่าจะไม่ถูกมอบหมายให้a
ค่าจะไม่ได้รับมอบหมายให้จะยังคงมีa
5
เป็นรูปแบบการบันทึกที่ใช้ใน Ruby เพื่อเพิ่มความเร็วของ accessors
def users
@users ||= User.all
end
สิ่งนี้แปลเป็น:
@users = @users || User.all
ดังนั้นคุณจะโทรไปยังฐานข้อมูลเป็นครั้งแรกที่คุณเรียกวิธีนี้
การเรียกใช้เมธอดนี้ในอนาคตจะส่งคืนค่าของ@users
ตัวแปรอินสแตนซ์
||=
เรียกว่าผู้ประกอบการที่ได้รับมอบหมายตามเงื่อนไข
มันใช้งานได้=
แต่ยกเว้นว่าถ้าตัวแปรได้รับการกำหนดแล้วแล้วมันจะไม่ทำอะไรเลย
ตัวอย่างแรก:
x ||= 10
ตัวอย่างที่สอง:
x = 20
x ||= 10
ในตัวอย่างแรกx
ตอนนี้เท่ากับ 10 อย่างไรก็ตามในตัวอย่างที่สองx
ถูกกำหนดเป็น 20 แล้วดังนั้นตัวดำเนินการตามเงื่อนไขจะไม่มีผลกระทบ x
ยังคงเป็น 20 x ||= 10
หลังจากทำงาน
a ||= b
เหมือนกับคำพูดa = b if a.nil?
หรือa = b unless a
แต่ตัวเลือกทั้ง 3 ตัวนี้แสดงประสิทธิภาพเดียวกันหรือไม่ ด้วย Ruby 2.5.1 สิ่งนี้
1000000.times do
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
a ||= 1
end
ใช้เวลา 0.099 วินาทีบนพีซีของฉันในขณะที่
1000000.times do
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
a = 1 unless a
end
ใช้เวลา 0.062 วินาที เร็วกว่าเกือบ 40%
แล้วเราก็มี:
1000000.times do
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
a = 1 if a.nil?
end
ซึ่งใช้เวลา 0.166 วินาที
ไม่ใช่ว่าสิ่งนี้จะส่งผลกระทบต่อประสิทธิภาพการทำงานที่สำคัญโดยทั่วไป แต่ถ้าคุณต้องการการปรับให้เหมาะสมสุดท้ายนั้นให้พิจารณาผลลัพธ์นี้ ยังไงซะ:a = 1 unless a
ง่ายต่อการอ่านสำหรับสามเณรมันเป็นตัวอธิบาย
หมายเหตุ 1: เหตุผลในการทำซ้ำบรรทัดการมอบหมายซ้ำหลาย ๆ ครั้งคือการลดค่าใช้จ่ายของลูปในเวลาที่วัด
หมายเหตุ 2: ผลลัพธ์จะคล้ายกันถ้าฉันa=nil
ไม่มีศูนย์ก่อนการมอบหมายแต่ละครั้ง