บล็อกทับทิม โปรคส์ และแลมบ์ดา
มันคืออะไร?
ทำงานอย่างไร
ต่างกันอย่างไร
คุณจะได้เรียนรู้สิ่งนั้นและอีกมากมายโดยการอ่านโพสต์นี้!
สารบัญ
- 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 จะถูกดำเนินการ
ฉันหวังว่าคุณจะสนุกกับโพสต์นี้!
อย่าลืมสมัครรับข้อมูลในแบบฟอร์มด้านล่างและ แชร์สิ่งนี้กับเพื่อนของคุณ 🙂