เส้นใยเป็นสิ่งที่คุณอาจไม่เคยใช้โดยตรงในรหัสระดับแอปพลิเคชัน เป็นแบบดั้งเดิมของการควบคุมการไหลซึ่งคุณสามารถใช้เพื่อสร้างนามธรรมอื่น ๆ ซึ่งคุณจะใช้ในโค้ดระดับสูงกว่า
การใช้เส้นใยอันดับ 1 ใน Ruby คือการใช้Enumerator
s ซึ่งเป็นคลาส Ruby หลักใน Ruby 1.9 สิ่งเหล่านี้มีประโยชน์อย่างเหลือเชื่อ
ใน Ruby 1.9 หากคุณเรียกใช้เมธอด iterator เกือบทุกชนิดในคลาสหลักโดยไม่ผ่านบล็อกมันจะส่งคืนEnumerator
ไฟล์.
irb(main):001:0> [1,2,3].reverse_each
=> #<Enumerator: [1, 2, 3]:reverse_each>
irb(main):002:0> "abc".chars
=> #<Enumerator: "abc":chars>
irb(main):003:0> 1.upto(10)
=> #<Enumerator: 1:upto(10)>
Enumerator
วัตถุเหล่านี้เป็นวัตถุที่นับได้และeach
วิธีการของพวกเขาให้องค์ประกอบที่จะได้รับผลจากวิธีการวนซ้ำดั้งเดิมหากถูกเรียกด้วยบล็อก ในตัวอย่างที่ฉันเพิ่งให้ตัวแจงนับที่ส่งคืนโดยreverse_each
มีeach
วิธีการที่ให้ผล 3,2,1 Enumerator ส่งคืนโดยchars
ให้ "c", "b", "a" (และอื่น ๆ ) แต่ไม่เหมือนกับวิธีการวนซ้ำแบบเดิม Enumerator ยังสามารถส่งคืนองค์ประกอบทีละรายการหากคุณเรียกnext
มันซ้ำ ๆ :
irb(main):001:0> e = "abc".chars
=> #<Enumerator: "abc":chars>
irb(main):002:0> e.next
=> "a"
irb(main):003:0> e.next
=> "b"
irb(main):004:0> e.next
=> "c"
คุณอาจเคยได้ยิน "ตัวทำซ้ำภายใน" และ "ตัวทำซ้ำภายนอก" (คำอธิบายที่ดีของทั้งสองอย่างมีอยู่ในหนังสือรูปแบบการออกแบบ "Gang of Four") ตัวอย่างข้างต้นแสดงให้เห็นว่า Enumerators สามารถใช้เพื่อเปลี่ยน iterator ภายในให้เป็น external
นี่เป็นวิธีหนึ่งในการสร้างตัวนับของคุณเอง:
class SomeClass
def an_iterator
# note the 'return enum_for...' pattern; it's very useful
# enum_for is an Object method
# so even for iterators which don't return an Enumerator when called
# with no block, you can easily get one by calling 'enum_for'
return enum_for(:an_iterator) if not block_given?
yield 1
yield 2
yield 3
end
end
มาลองดูกัน:
e = SomeClass.new.an_iterator
e.next # => 1
e.next # => 2
e.next # => 3
เดี๋ยวก่อน ... มีอะไรแปลก ๆ ไหม? คุณเขียนyield
คำสั่งan_iterator
เป็นรหัสเส้นตรง แต่ Enumerator สามารถเรียกใช้ทีละรายการได้ ระหว่างการโทรไปยังnext
การดำเนินการan_iterator
คือ "หยุด" ทุกครั้งที่คุณโทรnext
มันจะยังคงทำงานต่อไปยังข้อความต่อไปนี้yield
จากนั้น "ค้าง" อีกครั้ง
คุณสามารถคาดเดาวิธีการใช้งานได้หรือไม่? แจงนับ wraps การเรียกร้องให้an_iterator
เส้นใยและผ่านบล็อกที่ระงับเส้นใย ดังนั้นทุกครั้งที่ส่งan_iterator
ผลให้กับบล็อกไฟเบอร์ที่ทำงานอยู่จะถูกระงับและการดำเนินการจะดำเนินต่อไปบนเธรดหลัก ครั้งต่อไปที่คุณโทรระบบnext
จะส่งผ่านการควบคุมไปยังไฟเบอร์บล็อกจะส่งกลับและan_iterator
ดำเนินการต่อจากจุดที่ค้างไว้
มันจะเป็นคำแนะนำที่จะคิดว่าจะต้องทำอย่างไรหากไม่มีเส้นใย ทุกระดับที่ต้องการที่จะให้ทั้งสอง iterators next
ภายในและภายนอกจะต้องมีรหัสอย่างชัดเจนในการติดตามของรัฐระหว่างการโทรไปยัง การเรียกแต่ละครั้งจะต้องตรวจสอบสถานะนั้นและอัปเดตก่อนส่งคืนค่า ด้วยเส้นใยเราสามารถแปลงตัววนซ้ำภายในเป็นเครื่องภายนอกได้โดยอัตโนมัติ
สิ่งนี้ไม่เกี่ยวข้องกับเส้นใย persay แต่ขอพูดถึงอีกสิ่งหนึ่งที่คุณสามารถทำได้กับ Enumerators: ช่วยให้คุณสามารถใช้วิธีการ Enumerable ลำดับที่สูงกว่ากับตัวทำซ้ำอื่น ๆ นอกเหนือeach
จาก คิดเกี่ยวกับมันได้ตามปกติทุกวิธี Enumerable รวมทั้งmap
, select
, include?
, inject
, และอื่น ๆทั้งหมดที่each
ทำงานในองค์ประกอบที่ให้ผลโดย แต่ถ้าออบเจ็กต์มีตัวทำซ้ำอื่นที่ไม่ใช่each
ล่ะ?
irb(main):001:0> "Hello".chars.select { |c| c =~ /[A-Z]/ }
=> ["H"]
irb(main):002:0> "Hello".bytes.sort
=> [72, 101, 108, 108, 111]
การเรียกตัววนซ้ำโดยไม่มีบล็อกจะส่งกลับตัวนับจากนั้นคุณสามารถเรียกใช้เมธอด Enumerable อื่น ๆ ได้
กลับไปที่เส้นใยคุณใช้take
วิธีจาก Enumerable หรือไม่?
class InfiniteSeries
include Enumerable
def each
i = 0
loop { yield(i += 1) }
end
end
ถ้ามีอะไรเรียกeach
วิธีนั้นดูเหมือนว่ามันไม่ควรกลับมาใช่มั้ย? ลองดู:
InfiniteSeries.new.take(10) # => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
ฉันไม่รู้ว่าสิ่งนี้ใช้เส้นใยใต้ฝากระโปรงหรือเปล่า แต่ทำได้ เส้นใยสามารถใช้เพื่อใช้รายการที่ไม่มีที่สิ้นสุดและการประเมินแบบเกียจคร้านของชุดข้อมูล สำหรับตัวอย่างของวิธีขี้เกียจที่กำหนดด้วย Enumerators ฉันได้กำหนดไว้ที่นี่: https://github.com/alexdowad/showcase/blob/master/ruby-core/collections.rb
คุณยังสามารถสร้างศูนย์โครูทีนเอนกประสงค์โดยใช้เส้นใย ฉันยังไม่เคยใช้โครูทีนในโปรแกรมใด ๆ เลย แต่เป็นแนวคิดที่ดีที่ควรทราบ
ฉันหวังว่านี่จะช่วยให้คุณทราบถึงความเป็นไปได้ ดังที่ฉันได้กล่าวไปแล้วในตอนต้นว่าเส้นใยเป็นแบบดั้งเดิมที่ควบคุมการไหลในระดับต่ำ ทำให้สามารถรักษา "ตำแหน่ง" โฟลว์การควบคุมหลายรายการไว้ในโปรแกรมของคุณได้ (เช่น "บุ๊กมาร์ก" ที่แตกต่างกันในหน้าหนังสือ) และสลับไปมาระหว่างตำแหน่งต่างๆตามต้องการ เนื่องจากรหัสที่กำหนดเองสามารถทำงานในไฟเบอร์ได้คุณจึงสามารถเรียกไปยังรหัสของบุคคลที่สามบนไฟเบอร์จากนั้น "ตรึง" ไว้และดำเนินการอย่างอื่นต่อเมื่อเรียกกลับเข้าสู่รหัสที่คุณควบคุม
ลองนึกภาพแบบนี้คุณกำลังเขียนโปรแกรมเซิร์ฟเวอร์ซึ่งจะให้บริการลูกค้าจำนวนมาก การโต้ตอบที่สมบูรณ์กับลูกค้าเกี่ยวข้องกับการดำเนินการตามขั้นตอนต่างๆ แต่การเชื่อมต่อแต่ละครั้งจะเกิดขึ้นชั่วคราวและคุณต้องจำสถานะสำหรับไคลเอ็นต์แต่ละรายระหว่างการเชื่อมต่อ (ฟังดูเหมือนการเขียนโปรแกรมบนเว็บ?)
แทนที่จะจัดเก็บสถานะนั้นอย่างชัดเจนและตรวจสอบทุกครั้งที่ลูกค้าเชื่อมต่อ (เพื่อดูว่า "ขั้นตอน" ต่อไปที่พวกเขาต้องทำคืออะไร) คุณสามารถรักษาไฟเบอร์สำหรับลูกค้าแต่ละรายได้ หลังจากระบุไคลเอ็นต์แล้วคุณจะดึงข้อมูลไฟเบอร์และเริ่มต้นใหม่ จากนั้นเมื่อสิ้นสุดการเชื่อมต่อแต่ละครั้งคุณจะระงับไฟเบอร์และจัดเก็บอีกครั้ง ด้วยวิธีนี้คุณสามารถเขียนโค้ดเส้นตรงเพื่อใช้ตรรกะทั้งหมดสำหรับการโต้ตอบที่สมบูรณ์รวมถึงขั้นตอนทั้งหมด (เช่นเดียวกับที่คุณทำตามปกติหากโปรแกรมของคุณถูกสร้างให้ทำงานในเครื่อง)
ฉันแน่ใจว่ามีหลายสาเหตุที่สิ่งนั้นอาจไม่สามารถใช้งานได้จริง (อย่างน้อยก็ในตอนนี้) แต่อีกครั้งฉันแค่พยายามแสดงให้คุณเห็นความเป็นไปได้บางอย่าง ใครจะรู้; เมื่อคุณได้แนวคิดแล้วคุณอาจพบกับแอปพลิเคชั่นใหม่ทั้งหมดที่ยังไม่มีใครคิด!