Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

สุดยอดคู่มือสำหรับ Blocks, Procs &Lambdas

บล็อกทับทิม โปรคส์ และแลมบ์ดา

มันคืออะไร?

ทำงานอย่างไร

ต่างกันอย่างไร

คุณจะได้เรียนรู้สิ่งนั้นและอีกมากมายโดยการอ่านโพสต์นี้!

สารบัญ

  • 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 จะถูกดำเนินการ

ฉันหวังว่าคุณจะสนุกกับโพสต์นี้!

อย่าลืมสมัครรับข้อมูลในแบบฟอร์มด้านล่างและ แชร์สิ่งนี้กับเพื่อนของคุณ 🙂