เส้นใยเป็นสิ่งที่คุณอาจไม่เคยใช้โดยตรงในรหัสระดับแอปพลิเคชัน เป็นแบบดั้งเดิมของการควบคุมการไหลซึ่งคุณสามารถใช้เพื่อสร้างนามธรรมอื่น ๆ ซึ่งคุณจะใช้ในโค้ดระดับสูงกว่า
การใช้เส้นใยอันดับ 1 ใน Ruby คือการใช้Enumerators ซึ่งเป็นคลาส 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
คุณยังสามารถสร้างศูนย์โครูทีนเอนกประสงค์โดยใช้เส้นใย ฉันยังไม่เคยใช้โครูทีนในโปรแกรมใด ๆ เลย แต่เป็นแนวคิดที่ดีที่ควรทราบ
ฉันหวังว่านี่จะช่วยให้คุณทราบถึงความเป็นไปได้ ดังที่ฉันได้กล่าวไปแล้วในตอนต้นว่าเส้นใยเป็นแบบดั้งเดิมที่ควบคุมการไหลในระดับต่ำ ทำให้สามารถรักษา "ตำแหน่ง" โฟลว์การควบคุมหลายรายการไว้ในโปรแกรมของคุณได้ (เช่น "บุ๊กมาร์ก" ที่แตกต่างกันในหน้าหนังสือ) และสลับไปมาระหว่างตำแหน่งต่างๆตามต้องการ เนื่องจากรหัสที่กำหนดเองสามารถทำงานในไฟเบอร์ได้คุณจึงสามารถเรียกไปยังรหัสของบุคคลที่สามบนไฟเบอร์จากนั้น "ตรึง" ไว้และดำเนินการอย่างอื่นต่อเมื่อเรียกกลับเข้าสู่รหัสที่คุณควบคุม
ลองนึกภาพแบบนี้คุณกำลังเขียนโปรแกรมเซิร์ฟเวอร์ซึ่งจะให้บริการลูกค้าจำนวนมาก การโต้ตอบที่สมบูรณ์กับลูกค้าเกี่ยวข้องกับการดำเนินการตามขั้นตอนต่างๆ แต่การเชื่อมต่อแต่ละครั้งจะเกิดขึ้นชั่วคราวและคุณต้องจำสถานะสำหรับไคลเอ็นต์แต่ละรายระหว่างการเชื่อมต่อ (ฟังดูเหมือนการเขียนโปรแกรมบนเว็บ?)
แทนที่จะจัดเก็บสถานะนั้นอย่างชัดเจนและตรวจสอบทุกครั้งที่ลูกค้าเชื่อมต่อ (เพื่อดูว่า "ขั้นตอน" ต่อไปที่พวกเขาต้องทำคืออะไร) คุณสามารถรักษาไฟเบอร์สำหรับลูกค้าแต่ละรายได้ หลังจากระบุไคลเอ็นต์แล้วคุณจะดึงข้อมูลไฟเบอร์และเริ่มต้นใหม่ จากนั้นเมื่อสิ้นสุดการเชื่อมต่อแต่ละครั้งคุณจะระงับไฟเบอร์และจัดเก็บอีกครั้ง ด้วยวิธีนี้คุณสามารถเขียนโค้ดเส้นตรงเพื่อใช้ตรรกะทั้งหมดสำหรับการโต้ตอบที่สมบูรณ์รวมถึงขั้นตอนทั้งหมด (เช่นเดียวกับที่คุณทำตามปกติหากโปรแกรมของคุณถูกสร้างให้ทำงานในเครื่อง)
ฉันแน่ใจว่ามีหลายสาเหตุที่สิ่งนั้นอาจไม่สามารถใช้งานได้จริง (อย่างน้อยก็ในตอนนี้) แต่อีกครั้งฉันแค่พยายามแสดงให้คุณเห็นความเป็นไปได้บางอย่าง ใครจะรู้; เมื่อคุณได้แนวคิดแล้วคุณอาจพบกับแอปพลิเคชั่นใหม่ทั้งหมดที่ยังไม่มีใครคิด!