บล็อกทับทิม โปรคส์ และแลมบ์ดา
มันคืออะไร?
ทำงานอย่างไร
ต่างกันอย่างไร
คุณจะได้เรียนรู้สิ่งนั้นและอีกมากมายโดยการอ่านโพสต์นี้!
สารบัญ
- 1 ทำความเข้าใจกับ Ruby Blocks
- คีย์เวิร์ดผลตอบแทน 2 รูบี้
- 3 บล็อกโดยนัยเทียบกับอย่างชัดเจน
- 4 วิธีตรวจสอบว่ามีการบล็อกหรือไม่
- 5 แลมบ์ดาคืออะไร
- 6 แลมบ์ดาเทียบกับ Procs
- ปิด 7 ครั้ง
- 8 คลาสที่ผูกพัน
- 9 วิดีโอสอน
- 10 บทสรุป
- 10.1 ที่เกี่ยวข้อง
ทำความเข้าใจกับบล็อกทับทิม
บล็อก Ruby เป็นฟังก์ชันที่ไม่ระบุตัวตนเพียงเล็กน้อยที่สามารถส่งผ่านไปยังเมธอดได้
บล็อกอยู่ใน do / end คำสั่งหรือระหว่างวงเล็บ {} และสามารถมีอาร์กิวเมนต์ได้หลายอาร์กิวเมนต์
ชื่ออาร์กิวเมนต์ถูกกำหนดระหว่างสองไพพ์ | ตัวอักษร
หากคุณเคยใช้ each ก่อนหน้านี้คุณได้ใช้บล็อค!
นี่คือตัวอย่าง :
# Form 1: recommended for single line blocks
[1, 2, 3].each { |num| puts num }
^^^^^ ^^^^^^^^
block block
arguments body
# Form 2: recommended for multi-line blocks [1, 2, 3].each do |num| puts num end
บล็อก Ruby มีประโยชน์เพราะช่วยให้คุณบันทึกตรรกะ (รหัส) เล็กน้อยและใช้งานในภายหลังได้
นี่อาจเป็นบางอย่างเช่นการเขียนข้อมูลลงในไฟล์ การเปรียบเทียบว่าองค์ประกอบหนึ่งเท่ากับองค์ประกอบอื่น หรือแม้แต่การพิมพ์ข้อความแสดงข้อผิดพลาด
คีย์เวิร์ดผลตอบแทนทับทิม
yield .อะไร หมายถึงในทับทิม?
ผลตอบแทนคือ คำหลักทับทิม ที่เรียกการบล็อกเมื่อคุณใช้งาน
มันเป็นวิธีที่ใช้บล็อก!
เมื่อคุณใช้ yield คำหลัก โค้ดภายในบล็อกจะทำงาน และลงมือทำ
เช่นเดียวกับเมื่อคุณเรียกใช้วิธี Ruby ปกติ
นี่คือตัวอย่าง :
def print_once
yield
end
print_once { puts "Block is being run" }
การดำเนินการนี้จะเรียกใช้บล็อกที่ส่งผ่านไปยัง print_once ส่งผลให้ "Block is being run" จะถูกพิมพ์บนหน้าจอ
รู้ยัง…
นั่น yield ใช้ได้หลายครั้ง?
ทุกครั้งที่เรียก yield บล็อกจะทำงานจึงเหมือนกับเรียกวิธีการเดิมอีกครั้ง
ตัวอย่าง :
def print_twice
yield
yield
end
print_twice { puts "Hello" }
# "Hello"
# "Hello"
และก็เหมือนกับวิธีการ…
คุณสามารถส่งอาร์กิวเมนต์จำนวนเท่าใดก็ได้ไปยัง yield .
ตัวอย่าง :
def one_two_three
yield 1
yield 2
yield 3
end
one_two_three { |number| puts number * 10 }
# 10, 20, 30
อาร์กิวเมนต์เหล่านี้จะกลายเป็นอาร์กิวเมนต์ของบล็อก
ในตัวอย่างนี้ number .
การบล็อกโดยนัยกับที่ชัดเจน
การบล็อกอาจเป็น "โจ่งแจ้ง" หรือ "โดยนัย" ก็ได้
ชัดเจนหมายความว่าคุณตั้งชื่อในรายการพารามิเตอร์ของคุณ
คุณสามารถส่งการบล็อกที่ชัดเจนไปยังวิธีอื่นหรือบันทึกลงในตัวแปรเพื่อใช้ในภายหลังได้
นี่คือตัวอย่าง :
def explicit_block(&block)
block.call # same as yield
end
explicit_block { puts "Explicit block called" }
สังเกต &block พารามิเตอร์…
นั่นคือวิธีที่คุณกำหนดชื่อบล็อก!
วิธีตรวจสอบว่ามีการบล็อกหรือไม่
หากคุณพยายามที่จะ yield โดยไม่มีบล็อก คุณจะได้รับ no block given (yield) ผิดพลาด
คุณสามารถตรวจสอบว่ามีการบล็อกผ่าน block_given? วิธีการ
ตัวอย่าง :
def do_something_with_block return "No block given" unless block_given? yield end
ซึ่งจะช่วยป้องกันข้อผิดพลาดหากมีผู้เรียกใช้วิธีการของคุณโดยไม่มีการบล็อก
แลมบ์ดาคืออะไร
แลมบ์ดาเป็นวิธีกำหนดบล็อก &พารามิเตอร์ด้วยไวยากรณ์พิเศษบางอย่าง
คุณสามารถบันทึกแลมบ์ดานี้เป็นตัวแปรเพื่อใช้ในภายหลังได้
ไวยากรณ์สำหรับกำหนด Ruby lambda มีลักษณะดังนี้:
say_something = -> { puts "This is a lambda" }
คุณยังสามารถใช้รูปแบบอื่น:
lambdaแทน->.
การกำหนดแลมบ์ดาจะไม่เรียกใช้โค้ดภายใน เช่นเดียวกับการกำหนดเมธอดที่จะไม่เรียกใช้เมธอด คุณต้องใช้ call วิธีการนั้น
ตัวอย่าง :
say_something = -> { puts "This is a lambda" }
say_something.call
# "This is a lambda"
มีวิธีอื่นในการcall lambda ดีที่รู้ว่ามีอยู่จริง แต่ฉันแนะนำให้ติดกับ call เพื่อความชัดเจน
นี่คือรายการ :
my_lambda = -> { puts "Lambda called" }
my_lambda.call
my_lambda.()
my_lambda[]
my_lambda.===
แลมบ์ดาสามารถโต้แย้งได้เช่นกัน นี่คือตัวอย่าง:
times_two = ->(x) { x * 2 }
times_two.call(10)
# 20
หากคุณส่งอาร์กิวเมนต์ผิดจำนวนไปยัง lambda มันจะทำให้เกิดข้อยกเว้นเช่นเดียวกับวิธีการทั่วไป
แลมบ์ดาส vs โปรคส์
Procs เป็นแนวคิดที่คล้ายกันมาก…
ความแตกต่างประการหนึ่งคือวิธีสร้างมันขึ้นมา
ตัวอย่าง :
my_proc = Proc.new { |x| puts x }
ไม่มี lambda . เฉพาะ ระดับ. lambda เป็นเพียง Proc . พิเศษ วัตถุ. หากคุณดูที่วิธีการอินสแตนซ์จาก Proc คุณจะสังเกตเห็นว่ามี lambda? วิธีการ
ตอนนี้ :
Proc มีพฤติกรรมแตกต่างจากแลมบ์ดา โดยเฉพาะอย่างยิ่งเมื่อพูดถึงการโต้แย้ง:
t = Proc.new { |x,y| puts "I don't care about arguments!" }
t.call
# "I don't care about arguments!"
ความแตกต่างอีกประการระหว่าง procs &lambdas เป็นวิธีที่พวกเขาตอบสนองต่อ return คำชี้แจง
lambda จะ return ตามปกติเหมือนวิธีการทั่วไป
แต่เป็น proc จะพยายาม return จากบริบทปัจจุบัน
นี่คือสิ่งที่ฉันหมายถึง :
หากคุณเรียกใช้โค้ดต่อไปนี้ คุณจะสังเกตเห็นว่า proc ยก LocalJumpError ข้อยกเว้น
เหตุผลก็คือคุณไม่สามารถ return จากบริบทระดับบนสุด
ลองสิ่งนี้ :
# Should work
my_lambda = -> { return 1 }
puts "Lambda result: #{my_lambda.call}"
# Should raise exception
my_proc = Proc.new { return 1 }
puts "Proc result: #{my_proc.call}"
ถ้า proc อยู่ในเมธอดแล้วเรียก return จะเทียบเท่ากับการกลับมาจากวิธีการนั้น
นี่แสดงให้เห็นในตัวอย่างต่อไปนี้
def call_proc
puts "Before proc"
my_proc = Proc.new { return 2 }
my_proc.call
puts "After proc"
end
p call_proc
# Prints "Before proc" but not "After proc"
นี่คือบทสรุปของวิธีการ procs และ lambdas ต่างกัน:
- แลมบ์ดาถูกกำหนดด้วย
-> {}และ procs ด้วยProc.new {}. - Procs กลับจากเมธอดปัจจุบัน ในขณะที่ lambdas กลับจากแลมบ์ดาเอง
- Procs ไม่สนใจจำนวนอาร์กิวเมนต์ที่ถูกต้อง ในขณะที่ lambdas จะเพิ่มข้อยกเว้น
เมื่อดูรายการนี้ เราจะพบว่า lambdas ใกล้เคียงกับวิธีปกติมากกว่า procs คือ.
ปิดทำการ
Ruby procs &lambdas ยังมีคุณลักษณะพิเศษอื่นอีกด้วย เมื่อคุณสร้าง Ruby proc มันจะจับขอบเขตการดำเนินการปัจจุบันด้วย
แนวคิดนี้ ซึ่งบางครั้งเรียกว่าการปิด หมายความว่า proc จะนำค่าเช่นตัวแปรท้องถิ่นและวิธีการจากบริบทที่กำหนดไว้
ค่าเหล่านี้ไม่มีค่าจริง แต่เป็นการอ้างอิง ดังนั้นหากตัวแปรเปลี่ยนแปลงหลังจากสร้าง proc แล้ว proc จะมีเวอร์ชันล่าสุดเสมอ
มาดูตัวอย่างกัน :
def call_proc(my_proc)
count = 500
my_proc.call
end
count = 1
my_proc = Proc.new { puts count }
p call_proc(my_proc) # What does this print?
ในตัวอย่างนี้ เรามี count ในเครื่อง ตัวแปรซึ่งตั้งค่าเป็น 1 .
เรายังมี proc ชื่อ my_proc และ call_proc วิธีการที่ทำงาน (ผ่าน call วิธี) proc หรือ lambda ใด ๆ ที่ส่งผ่านเป็นอาร์กิวเมนต์
คุณคิดว่าโปรแกรมนี้จะพิมพ์อะไร?
ดูเหมือนว่า 500 เป็นข้อสรุปที่สมเหตุสมผลที่สุด แต่เนื่องจากเอฟเฟกต์ "ปิด" สิ่งนี้จะพิมพ์ 1 .
สิ่งนี้เกิดขึ้นเนื่องจาก proc ใช้ค่าของ count จากตำแหน่งที่กำหนด proc และนั่นอยู่นอกเหนือคำจำกัดความของเมธอด
คลาสที่ผูกพัน
Ruby procs &lambdas เก็บข้อมูลขอบเขตนี้ไว้ที่ใด
ให้ฉันบอกคุณเกี่ยวกับ Binding คลาส…
เมื่อคุณสร้าง Binding วัตถุผ่าน Binding เมธอด คุณกำลังสร้าง 'จุดยึด' ถึงจุดนี้ในโค้ด
ตัวแปร เมธอด และคลาสทั้งหมดที่กำหนดไว้ ณ จุดนี้จะพร้อมใช้งานผ่านอ็อบเจ็กต์นี้ในภายหลัง แม้ว่าคุณจะอยู่ในขอบเขตที่ต่างไปจากเดิมอย่างสิ้นเชิง
ตัวอย่าง :
def return_binding
foo = 100
binding
end
# Foo is available thanks to the binding,
# even though we are outside of the method
# where it was defined.
puts return_binding.class
puts return_binding.eval('foo')
# If you try to print foo directly you will get an error.
# The reason is that foo was never defined outside of the method.
puts foo
กล่าวคือ ดำเนินการบางอย่างภายใต้บริบทของ binding วัตถุนั้นเหมือนกับว่าโค้ดนั้นอยู่ในที่เดียวกับที่ binding ถูกกำหนด (จำอุปมา 'สมอ')
คุณไม่จำเป็นต้องใช้ Binding วัตถุโดยตรงแต่ก็ยังดีที่รู้ว่าสิ่งนี้คือสิ่ง 🙂
วิดีโอสอน
บทสรุป
ในโพสต์นี้ คุณได้เรียนรู้วิธีการทำงานของบล็อก ความแตกต่างระหว่าง Ruby procs &lambdas และคุณยังได้เรียนรู้เกี่ยวกับเอฟเฟกต์ "การปิด" ที่เกิดขึ้นทุกครั้งที่คุณสร้างบล็อก
สิ่งหนึ่งที่ฉันไม่ได้พูดถึงคือวิธีการแกง
วิธีนี้ช่วยให้คุณส่งผ่านอาร์กิวเมนต์ที่จำเป็นบางส่วนหรือทั้งหมดได้
หากคุณส่งผ่านอาร์กิวเมนต์เพียงบางส่วนเท่านั้น คุณจะได้รับ proc ใหม่โดยที่อาร์กิวเมนต์เหล่านี้ 'โหลดไว้ล่วงหน้า' แล้ว เมื่ออาร์กิวเมนต์ทั้งหมดถูกระบุ proc จะถูกดำเนินการ
ฉันหวังว่าคุณจะสนุกกับโพสต์นี้!
อย่าลืมสมัครรับข้อมูลในแบบฟอร์มด้านล่างและ แชร์สิ่งนี้กับเพื่อนของคุณ 🙂