python - "उपज" कीवर्ड क्या करता है?
iterator generator (20)
पाइथन में
yield
कीवर्ड क्या करता है?
उत्तर रूपरेखा / सारांश उत्तर दें
-
yield
साथ एक समारोह, जिसे बुलाया जाता है, Generator देता है। - जनरेटर इटरेटर हैं क्योंकि वे इटरेटर प्रोटोकॉल को कार्यान्वित करते हैं , ताकि आप उन्हें फिर से चालू कर सकें।
- जनरेटर को अवधारणात्मक रूप से कोरआउट बनाने के लिए जानकारी भी भेजी जा सकती है।
- पायथन 3 में, आप
yield from
साथ दोनों दिशाओं में एक जनरेटर से दूसरे में प्रतिनिधि हो सकते हैं। - (परिशिष्ट आलोचकों को दो उत्तरों सहित आलोचकों की आलोचना करता है, और जनरेटर में
return
के उपयोग पर चर्चा करता है।)
जेनरेटर:
yield
केवल फ़ंक्शन परिभाषा के अंदर कानूनी है, और फ़ंक्शन परिभाषा में yield
को शामिल करने से यह जनरेटर लौटाता है।
जेनरेटर के लिए विचार अलग-अलग कार्यान्वयन के साथ अन्य भाषाओं (फुटनोट 1 देखें) से आता है। पायथन के जेनरेटर में, उपज के बिंदु पर कोड का निष्पादन frozen है। जब जनरेटर को बुलाया जाता है (नीचे विधियों पर चर्चा की जाती है) निष्पादन फिर से शुरू होता है और फिर अगली उपज पर जमा हो जाता है।
yield
निम्नलिखित दो तरीकों से परिभाषित इटरेटर प्रोटोकॉल को कार्यान्वित करने का एक आसान तरीका प्रदान करता है: __iter__
और next
(पायथन 2) या __next__
(पायथन 3)। उन दोनों विधियों में एक ऑब्जेक्ट एक इटरेटर है जो आप collections
मॉड्यूल से Iterator
सार बेस क्लास के साथ टाइप-चेक कर सकते हैं।
>>> def func():
... yield 'I am'
... yield 'a generator!'
...
>>> type(func) # A function with yield is still a function
<type 'function'>
>>> gen = func()
>>> type(gen) # but it returns a generator
<type 'generator'>
>>> hasattr(gen, '__iter__') # that's an iterable
True
>>> hasattr(gen, 'next') # and with .next (.__next__ in Python 3)
True # implements the iterator protocol.
जनरेटर प्रकार एक उप-प्रकार का इटरेटर है:
>>> import collections, types
>>> issubclass(types.GeneratorType, collections.Iterator)
True
और यदि आवश्यक हो, तो हम इस प्रकार टाइप-चेक कर सकते हैं:
>>> isinstance(gen, types.GeneratorType)
True
>>> isinstance(gen, collections.Iterator)
True
एक Iterator
की एक विशेषता यह है कि एक बार थक गया , आप इसका पुन: उपयोग या रीसेट नहीं कर सकते:
>>> list(gen)
['I am', 'a generator!']
>>> list(gen)
[]
यदि आप फिर से अपनी कार्यक्षमता का उपयोग करना चाहते हैं तो आपको एक और बनाना होगा (फुटनोट 2 देखें):
>>> list(func())
['I am', 'a generator!']
कोई प्रोग्राम प्रोग्रामिक रूप से उत्पन्न कर सकता है, उदाहरण के लिए:
def func(an_iterable):
for item in an_iterable:
yield item
उपरोक्त सरल जनरेटर नीचे के बराबर है - पायथन 3.3 (और पायथन 2 में उपलब्ध नहीं है) के रूप में, आप yield from
उपयोग कर सकते हैं:
def func(an_iterable):
yield from an_iterable
हालांकि, yield from
उपनिवेशकों को प्रतिनिधिमंडल की अनुमति भी मिलती है, जिसे उप-कोरआउट के साथ सहकारी प्रतिनिधिमंडल पर निम्नलिखित खंड में समझाया जाएगा।
Coroutines:
yield
एक अभिव्यक्ति बनाती है जो जनरेटर में डेटा भेजने की अनुमति देती है (फुटनोट 3 देखें)
यहां एक उदाहरण दिया गया है, received
चर का ध्यान रखें, जो जनरेटर को भेजे गए डेटा को इंगित करेगा:
def bank_account(deposited, interest_rate):
while True:
calculated_interest = interest_rate * deposited
received = yield calculated_interest
if received:
deposited += received
>>> my_account = bank_account(1000, .05)
सबसे पहले, हमें जेनरेटर को बिल्टिन फ़ंक्शन के साथ कतारबद्ध करना होगा। यह आपके द्वारा उपयोग किए जा रहे पायथन के संस्करण के आधार पर उचित next
या __next__
विधि को कॉल करेगा:
>>> first_year_interest = next(my_account)
>>> first_year_interest
50.0
और अब हम जनरेटर में डेटा भेज सकते हैं। ( None
भेजना next
कॉलिंग जैसा ही None
है ।):
>>> next_year_interest = my_account.send(first_year_interest + 1000)
>>> next_year_interest
102.5
उप-कोरोटाइन से yield from
साथ सहकारी प्रतिनिधिमंडल
अब, याद रखें कि yield from
पाइथन 3 में उपलब्ध है। यह हमें एक उपकोराउटिन में कोरआउट को प्रतिनिधि करने की अनुमति देता है:
def money_manager(expected_rate):
under_management = yield # must receive deposited value
while True:
try:
additional_investment = yield expected_rate * under_management
if additional_investment:
under_management += additional_investment
except GeneratorExit:
'''TODO: write function to send unclaimed funds to state'''
finally:
'''TODO: write function to mail tax info to client'''
def investment_account(deposited, manager):
'''very simple model of an investment account that delegates to a manager'''
next(manager) # must queue up manager
manager.send(deposited)
while True:
try:
yield from manager
except GeneratorExit:
return manager.close()
और अब हम उप-जनरेटर को कार्यक्षमता का प्रतिनिधित्व कर सकते हैं और इसे उपरोक्त के रूप में जनरेटर द्वारा उपयोग किया जा सकता है:
>>> my_manager = money_manager(.06)
>>> my_account = investment_account(1000, my_manager)
>>> first_year_return = next(my_account)
>>> first_year_return
60.0
>>> next_year_return = my_account.send(first_year_return + 1000)
>>> next_year_return
123.6
आप पीईपी 380 में yield from
के सटीक अर्थशास्त्र के बारे में अधिक पढ़ सकते हैं ।
अन्य तरीके: बंद करें और फेंक दें
फंक्शन निष्पादन जमे हुए बिंदु पर GeneratorExit
को close
करने की विधि close
। इसे __del__
द्वारा भी बुलाया जाएगा ताकि आप __del__
संभालने के लिए कोई क्लीनअप कोड डाल सकें:
>>> my_account.close()
आप एक अपवाद भी फेंक सकते हैं जिसे जनरेटर में संभाला जा सकता है या उपयोगकर्ता को वापस प्रसारित किया जा सकता है:
>>> import sys
>>> try:
... raise ValueError
... except:
... my_manager.throw(*sys.exc_info())
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
File "<stdin>", line 2, in <module>
ValueError
निष्कर्ष
मेरा मानना है कि मैंने निम्नलिखित प्रश्नों के सभी पहलुओं को शामिल किया है:
पाइथन में
yield
कीवर्ड क्या करता है?
यह पता चला है कि yield
बहुत कुछ करता है। मुझे यकीन है कि मैं इसके लिए और भी गहन उदाहरण जोड़ सकता हूं। यदि आप अधिक चाहते हैं या कुछ रचनात्मक आलोचना है, तो मुझे नीचे टिप्पणी करके मुझे बताएं।
अनुबंध:
शीर्ष / स्वीकृत उत्तर की आलोचना **
- यह एक उदाहरण के रूप में एक सूची का उपयोग कर, एक पुनरावर्तनीय बनाता है पर उलझन में है। ऊपर दिए गए मेरे संदर्भ देखें, लेकिन संक्षेप में: एक पुनरावृत्त में एक
__iter__
विधि है जो एक__iter__
है । एक पुनरावर्तक एक.next
(पायथन 2 या.__next__
(पायथन 3) विधि प्रदान करता है, जिसे स्पष्ट रूप से लूप केfor
बुलाया जाता है जब तक कि यहStopIteration
नहीं बढ़ाता है, और एक बार ऐसा करने पर, ऐसा करना जारी रहेगा। - यह जनरेटर क्या है इसका वर्णन करने के लिए जनरेटर अभिव्यक्ति का उपयोग करता है। चूंकि जनरेटर एक इटरेटर बनाने का एक सुविधाजनक तरीका है , यह केवल इस मामले को भ्रमित करता है, और हम अभी भी
yield
भाग तक नहीं पहुंच पाए हैं। - जेनरेटर थकावट को नियंत्रित करने में वह
.next
विधि को कॉल करता है, जब इसके बजाय उसे.next
फ़ंक्शन का उपयोग करना चाहिए, तोnext
। यह संकेत की एक उचित परत होगी, क्योंकि उसका कोड पायथन 3 में काम नहीं करता है। - Itertools? यह
yield
लिए प्रासंगिक नहीं था। - पाइथन 3 में नई कार्यक्षमता
yield from
के साथ प्रदान की जाने वाली विधियों की कोई चर्चा नहीं । शीर्ष / स्वीकृत उत्तर एक बहुत ही अपूर्ण उत्तर है।
उत्तर की आलोचना जनरेटर अभिव्यक्ति या समझ में yield
का सुझाव yield
है।
व्याकरण वर्तमान में सूची समझ में किसी भी अभिव्यक्ति की अनुमति देता है।
expr_stmt: testlist_star_expr (annassign | augassign (yield_expr|testlist) |
('=' (yield_expr|testlist_star_expr))*)
...
yield_expr: 'yield' [yield_arg]
yield_arg: 'from' test | testlist
चूंकि उपज एक अभिव्यक्ति है, इसलिए इसे समझने या जेनरेटर अभिव्यक्ति में इसका उपयोग करने के लिए दिलचस्प कुछ लोगों द्वारा बताया गया है - विशेष रूप से अच्छा उपयोग-मामला उद्धृत करने के बावजूद।
सीपीथन कोर डेवलपर्स इसके भत्ते को खत्म करने पर चर्चा कर रहे हैं। मेलिंग सूची से एक प्रासंगिक पोस्ट यहां दी गई है:
30 जनवरी 2017 को 1 9:05 को ब्रेट कैनन ने लिखा:
सूर्य पर, 2 9 जनवरी 2017 को 16:39 क्रेग रॉड्रिग्स ने लिखा:
मैं या तो दृष्टिकोण के साथ ठीक हूँ। चीजें छोड़कर जिस तरह से वे पाइथन 3 में हैं, कोई अच्छा नहीं है, IMHO।
मेरा वोट यह एक सिंटेक्स त्रुटि है क्योंकि आप सिंटैक्स से जो अपेक्षा करते हैं उसे प्राप्त नहीं कर रहे हैं।
मैं सहमत हूं कि हमारे लिए समाप्त होने के लिए एक समझदार जगह है, क्योंकि वर्तमान व्यवहार पर निर्भर कोई भी कोड वास्तव में बनाए रखने योग्य होने के लिए बहुत चालाक है।
वहां पहुंचने के मामले में, हम शायद चाहेंगे:
- 3.7 में सिंटेक्स चेतावनी या गिरावट चेतावनी
- 2.7.x में Py3k चेतावनी
- 3.8 में सिंटेक्स त्रुटि
चीयर्स, निक।
- निक कोग्लान | gmail.com पर ncoghlan | ब्रिस्बेन, ऑस्ट्रेलिया
इसके अलावा, एक उत्कृष्ट मुद्दा (10544) है जो इस दिशा में इंगित करता है कि यह कभी भी एक अच्छा विचार नहीं है (पीईपीई, पायथन में लिखे गए एक पायथन कार्यान्वयन, पहले ही सिंटैक्स चेतावनियां बढ़ा रहा है।)
नीचे की रेखा, जब तक कि सीपीथन के डेवलपर्स हमें अन्यथा न बताएं: जनरेटर अभिव्यक्ति या समझ में न डालें yield
।
return
एक जनरेटर में बयान
में अजगर 2 :
जनरेटर फ़ंक्शन में,
return
कथन को शामिल करने की अनुमति नहीं हैexpression_list
। उस संदर्भ में, एक नंगेreturn
इंगित करता है कि जेनरेटर किया जाता है और इसेStopIteration
उठाया जाएगा।
ए expression_list
मूल रूप से अल्पविराम से अलग अभिव्यक्तियों की संख्या है - अनिवार्य रूप से, पायथन 2 में, आप जनरेटर को रोक सकते हैं return
, लेकिन आप एक मूल्य वापस नहीं कर सकते हैं।
में अजगर 3 :
जेनरेटर फ़ंक्शन में,
return
कथन इंगित करता है कि जेनरेटर किया जाता है और इसेStopIteration
उठाया जाएगा। लौटाया गया मूल्य (यदि कोई है) का निर्माण करने के लिए तर्क के रूप में प्रयोग कियाStopIteration
जाता है औरStopIteration.value
विशेषता बन जाती है ।
फुटनोट
पाइथन को जनरेटर की अवधारणा को पेश करने के प्रस्ताव में सीएलयू, सादर और आइकन भाषाएं संदर्भित की गई थीं। सामान्य विचार यह है कि एक फ़ंक्शन आंतरिक स्थिति को बनाए रख सकता है और उपयोगकर्ता द्वारा मांग पर मध्यवर्ती डेटा बिंदु उत्पन्न कर सकता है। इसने पाइथन थ्रेडिंग समेत अन्य दृष्टिकोणों के प्रदर्शन में बेहतर होने का वादा किया , जो कि कुछ सिस्टमों पर भी उपलब्ध नहीं है।
इसका मतलब है, उदाहरण के लिए, वह
xrange
वस्तुएं (range
पायथन 3 में) नहीं हैंIterator
, भले ही वे पुन: प्रयोज्य हों, क्योंकि उनका पुन: उपयोग किया जा सकता है। सूचियों की तरह, उनके__iter__
तरीके इटेटरेटर ऑब्जेक्ट्स लौटते हैं।yield
मूल रूप से एक बयान के रूप में पेश किया गया था, जिसका अर्थ है कि यह केवल कोड ब्लॉक में एक रेखा की शुरुआत में दिखाई दे सकता है। अबyield
उपज अभिव्यक्ति बनाता है। https://docs.python.org/2/reference/simple_stmts.html#grammar-token-yield_stmt यह परिवर्तन किसी उपयोगकर्ता को जनरेटर में डेटा भेजने की अनुमति देने के लिए प्रस्तावित किया गया था जैसा कि कोई इसे प्राप्त कर सकता है। डेटा भेजने के लिए, कोई इसे किसी चीज़ को असाइन करने में सक्षम होना चाहिए, और उसके लिए, एक कथन बस काम नहीं करेगा।
पायथन में yield
कीवर्ड का उपयोग क्या है? यह क्या करता है?
उदाहरण के लिए, मैं इस कोड को समझने की कोशिश कर रहा हूं 1 :
def _get_child_candidates(self, distance, min_dist, max_dist):
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
और यह कॉलर है:
result, candidates = [], [self]
while candidates:
node = candidates.pop()
distance = node._get_dist(obj)
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
क्या होता है जब विधि _get_child_candidates
कहा जाता है? क्या एक सूची वापस आ गई है? एक तत्व? क्या इसे फिर से बुलाया जाता है? बाद के कॉल कब रुकेंगे?
1. कोड जोचन शूलज़ (jrschulz) से आता है, जिन्होंने मीट्रिक रिक्त स्थान के लिए एक महान पायथन पुस्तकालय बनाया है। यह संपूर्ण स्रोत का लिंक है: मॉड्यूल mspace ।
Grokking yield
लिए शॉर्टकट
जब आप yield
बयान के साथ एक समारोह देखते हैं, तो यह समझने के लिए यह आसान चाल लागू करें कि क्या होगा:
- फ़ंक्शन की शुरुआत में एक लाइन
result = []
डालें। -
result.append(expr)
साथ प्रत्येकyield expr
को बदलें। - फ़ंक्शन के नीचे एक लाइन
return result
डालें। - हाँ - कोई और
yield
बयान नहीं! कोड पढ़ें और समझें। - कार्य को मूल परिभाषा से तुलना करें।
यह चाल आपको फ़ंक्शन के पीछे तर्क का एक विचार दे सकती है, लेकिन yield
साथ वास्तव में क्या होता yield
यह महत्वपूर्ण है कि सूची आधारित दृष्टिकोण में क्या होता है। कई मामलों में उपज दृष्टिकोण बहुत अधिक स्मृति कुशल और तेज़ भी होगा। अन्य मामलों में यह चाल आपको एक अनंत लूप में फंस जाएगी, भले ही मूल कार्य ठीक काम करता हो। अधिक जानकारी के लिए पढ़ें...
अपने Iterables, Iterators और जनरेटर को भ्रमित मत करो
सबसे पहले, इटरेटर प्रोटोकॉल - जब आप लिखते हैं
for x in mylist:
...loop body...
पाइथन निम्नलिखित दो चरणों का पालन करता है:
मेरी
mylist
लिए एक पुनरावर्तक हो जाता है:कॉलर
iter(mylist)
कॉल करें -> यह एक ऑब्जेक्ट कोnext()
विधि (या__next__()
पायथन 3 में देता है)।[यह वह कदम है जो अधिकांश लोग आपको बताना भूल जाते हैं]
आइटम पर लूप करने के लिए इटरेटर का उपयोग करता है:
चरण 1 से लौटाए गए इटरेटर पर
next()
विधि को कॉल करना जारी रखें।next()
से वापसी मानx
को आवंटित किया जाता है और लूप बॉडी निष्पादित की जाती है। यदि कोई अपवादStopIteration
next()
भीतर से उठाया गया है, तो इसका मतलब है कि इटरेटर में कोई और मूल्य नहीं है और लूप निकल गया है।
सच्चाई यह है कि पाइथन उपरोक्त दो चरणों को निष्पादित करता है जब भी वह किसी ऑब्जेक्ट की सामग्री पर लूप करना चाहता है - तो यह लूप के लिए हो सकता है, लेकिन यह otherlist.extend(mylist)
जैसे otherlist.extend(mylist)
भी हो सकता है otherlist.extend(mylist)
(जहां otherlist
सूची एक पायथन सूची है) ।
यहां मेरी mylist
एक पुनरावर्तनीय है क्योंकि यह इटरेटर प्रोटोकॉल लागू करती है। उपयोगकर्ता परिभाषित वर्ग में, आप अपनी कक्षा के उदाहरणों को __iter__()
बनाने के लिए __iter__()
विधि को कार्यान्वित कर सकते हैं। इस विधि को एक पुनरावर्तक वापस करना चाहिए। एक पुनरावर्तक एक ऑब्जेक्ट है जो next()
विधि के साथ होता है। एक ही कक्षा में __iter__()
और next()
दोनों को कार्यान्वित करना संभव है, और __iter__()
self
वापस कर दें। यह साधारण मामलों के लिए काम करेगा, लेकिन जब आप एक ही ऑब्जेक्ट पर एक ही ऑब्जेक्ट पर लूपिंग करना चाहते हैं तो नहीं।
तो यह इटरेटर प्रोटोकॉल है, कई ऑब्जेक्ट्स इस प्रोटोकॉल को लागू करते हैं:
- अंतर्निहित सूचियां, शब्दकोश, tuples, सेट, फ़ाइलें।
- उपयोगकर्ता परिभाषित कक्षाएं जो
__iter__()
कार्यान्वित__iter__()
। - जेनरेटर।
ध्यान दें कि लूप के लिए यह नहीं पता कि यह किस प्रकार की ऑब्जेक्ट से निपट रहा है - यह केवल इटरेटर प्रोटोकॉल का पालन करता है, और आइटम के बाद आइटम प्राप्त करने में प्रसन्नता है क्योंकि यह next()
कॉल next()
। बिल्ट-इन सूचियां एक-एक करके अपने आइटम लौटती हैं, शब्दकोष एक-एक करके चाबियाँ लौटाते हैं, फाइलें एक-एक करके लाइनें लौटाती हैं, और जेनरेटर वापस आते हैं ... वैसे ही जहां yield
आती है:
def f123():
yield 1
yield 2
yield 3
for item in f123():
print item
yield
बयानों के बजाय, यदि आपके पास f123()
में तीन return
स्टेटमेंट थे तो केवल पहले ही निष्पादित हो जाएगा, और फ़ंक्शन बाहर निकल जाएगा। लेकिन f123()
कोई साधारण कार्य नहीं है। जब f123()
कहा जाता है, तो यह उपज बयान में किसी भी मूल्य को वापस नहीं करता है ! यह जनरेटर ऑब्जेक्ट देता है। साथ ही, फ़ंक्शन वास्तव में बाहर नहीं निकलता है - यह एक निलंबित राज्य में जाता है। जब लूप जनरेटर ऑब्जेक्ट पर लूप करने का प्रयास करता है, तो फ़ंक्शन अपनी निलंबित स्थिति से अगली पंक्ति में फिर से लौटाई जाने वाली yield
बाद फिर से शुरू होता है, कोड की अगली पंक्ति निष्पादित करता है, इस मामले में yield
स्टेटमेंट, और उसे वापस देता है अगली वस्तु यह तब तक होता है जब तक कार्य बाहर निकलता है, जिस बिंदु पर जेनरेटर StopIteration
बढ़ाता है, और लूप निकलता है।
तो जेनरेटर ऑब्जेक्ट एडाप्टर की तरह है - एक तरफ यह लूप को खुश रखने के for
__iter__()
और next()
विधियों को उजागर करके, __iter__()
प्रोटोकॉल प्रदर्शित करता है। दूसरी तरफ, यह उस समारोह को चलाता है जो इसके अगले मूल्य को प्राप्त करने के लिए पर्याप्त है, और इसे वापस निलंबित मोड में रखता है।
जनरेटर का उपयोग क्यों करें?
आम तौर पर आप कोड लिख सकते हैं जो जेनरेटर का उपयोग नहीं करता है लेकिन उसी तर्क को लागू करता है। एक विकल्प है कि मैंने पहले वर्णित अस्थायी सूची 'चाल' का उपयोग करना है। यह सभी मामलों में काम नहीं करेगा, उदाहरण के लिए यदि आपके पास अनंत लूप हैं, या जब आपके पास वास्तव में लंबी सूची है तो यह स्मृति का अक्षम उपयोग कर सकता है। दूसरा दृष्टिकोण एक नई पुनरावर्तनीय कक्षा SomethingIter
को लागू करना है जो राज्य के उदाहरण में सदस्यों को रखता है और next()
__next__()
चरण में next()
(या __next__()
पायथन 3) विधि में करता है। तर्क के आधार पर, next()
विधि के अंदर कोड बहुत जटिल लग रहा है और बग के लिए प्रवण हो सकता है। यहां जेनरेटर एक स्वच्छ और आसान समाधान प्रदान करते हैं।
यह समझने के लिए कि yield
क्या करती है, आपको समझना होगा कि जनरेटर क्या हैं। और जेनरेटर आने से पहले।
Iterables
जब आप एक सूची बनाते हैं, तो आप इसकी वस्तुओं को एक-एक करके पढ़ सकते हैं। अपनी वस्तुओं को एक-एक करके पढ़ना पुनरावृत्ति कहा जाता है:
>>> mylist = [1, 2, 3]
>>> for i in mylist:
... print(i)
1
2
3
mylist
एक पुनरावर्तनीय है । जब आप एक सूची समझ का उपयोग करते हैं, तो आप एक सूची बनाते हैं, और इसलिए एक पुनरावृत्ति:
>>> mylist = [x*x for x in range(3)]
>>> for i in mylist:
... print(i)
0
1
4
सब कुछ जो आप "के for... in...
" का उपयोग कर सकते हैं एक पुनरावृत्ति है; lists
, strings
, फाइलें ...
ये पुनरावृत्तियों आसान हैं क्योंकि आप जितनी चाहें उतनी पढ़ सकते हैं, लेकिन आप सभी मानों को स्मृति में संग्रहीत करते हैं और यह हमेशा तब नहीं होता जब आप बहुत सारे मूल्य प्राप्त करते हैं।
जेनरेटर
जनरेटर इटरेटर्स हैं, एक प्रकार का पुनरावर्तनीय आप केवल एक बार फिर से सक्रिय हो सकते हैं । जनरेटर स्मृति में सभी मानों को संग्रहीत नहीं करते हैं, वे फ्लाई पर मूल्य उत्पन्न करते हैं :
>>> mygenerator = (x*x for x in range(3))
>>> for i in mygenerator:
... print(i)
0
1
4
यह वही है जो आपने ()
[]
बजाए इस्तेमाल किया ()
। लेकिन, आप for i in mygenerator
में दूसरी बार प्रदर्शन नहीं कर सकते हैं क्योंकि जनरेटर केवल एक बार उपयोग किए जा सकते हैं: वे 0 की गणना करते हैं, फिर इसके बारे में भूल जाते हैं और 1 की गणना करते हैं, और अंत में गणना करते हैं 4, एक-एक करके।
प्राप्ति
yield
एक ऐसा कीवर्ड है जिसका उपयोग return
तरह किया जाता है, सिवाय इसके कि फ़ंक्शन एक जनरेटर लौटाएगा।
>>> def createGenerator():
... mylist = range(3)
... for i in mylist:
... yield i*i
...
>>> mygenerator = createGenerator() # create a generator
>>> print(mygenerator) # mygenerator is an object!
<generator object createGenerator at 0xb7555c34>
>>> for i in mygenerator:
... print(i)
0
1
4
यहां यह एक बेकार उदाहरण है, लेकिन यह आसान है जब आप जानते हैं कि आपका फ़ंक्शन मूल्यों का एक बड़ा सेट लौटाएगा जिसे आपको केवल एक बार पढ़ने की आवश्यकता होगी।
yield
मास्टर करने के yield
, आपको समझना होगा कि जब आप फ़ंक्शन को कॉल करते हैं, तो आपके द्वारा फ़ंक्शन बॉडी में लिखा गया कोड नहीं चलता है। फ़ंक्शन केवल जेनरेटर ऑब्जेक्ट देता है, यह थोड़ा मुश्किल है :-)
फिर, जनरेटर का उपयोग करने के for
प्रत्येक बार आपका कोड चलाया जाएगा।
अब कठिन हिस्सा:
पहली बार आपके फ़ंक्शन से जनरेटर ऑब्जेक्ट for
कॉल करने के for
कॉल करने के for
, यह आपके फ़ंक्शन में कोड को शुरुआत से तब तक चलाएगा जब तक यह yield
हिट न करे, फिर यह लूप का पहला मान वापस कर देगा। फिर, एक-दूसरे कॉल आपके द्वारा फ़ंक्शन में लिखे गए लूप को एक और बार चलाएगा, और अगले मान वापस लौटाएगा, जब तक कि वापस करने के लिए कोई मूल्य न हो।
फ़ंक्शन चलाने के बाद जेनरेटर खाली माना जाता है, लेकिन अब yield
नहीं मारा जाता yield
। ऐसा इसलिए हो सकता है क्योंकि लूप समाप्त हो गया था, या क्योंकि आप अब "if/else"
संतुष्ट नहीं करते हैं।
आपका कोड समझाया गया
जनरेटर:
# Here you create the method of the node object that will return the generator
def _get_child_candidates(self, distance, min_dist, max_dist):
# Here is the code that will be called each time you use the generator object:
# If there is still a child of the node object on its left
# AND if distance is ok, return the next child
if self._leftchild and distance - max_dist < self._median:
yield self._leftchild
# If there is still a child of the node object on its right
# AND if distance is ok, return the next child
if self._rightchild and distance + max_dist >= self._median:
yield self._rightchild
# If the function arrives here, the generator will be considered empty
# there is no more than two values: the left and the right children
कॉलर:
# Create an empty list and a list with the current object reference
result, candidates = list(), [self]
# Loop on candidates (they contain only one element at the beginning)
while candidates:
# Get the last candidate and remove it from the list
node = candidates.pop()
# Get the distance between obj and the candidate
distance = node._get_dist(obj)
# If distance is ok, then you can fill the result
if distance <= max_dist and distance >= min_dist:
result.extend(node._values)
# Add the children of the candidate in the candidates list
# so the loop will keep running until it will have looked
# at all the children of the children of the children, etc. of the candidate
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
return result
इस कोड में कई स्मार्ट पार्ट्स हैं:
लूप एक सूची पर पुनरावृत्त होता है, लेकिन लूप को पुनरावृत्त होने पर सूची विस्तारित होती है :-) यह इन घोंसले वाले डेटा से गुजरने का एक संक्षिप्त तरीका है, भले ही यह एक खतरनाक हो, क्योंकि आप अनंत लूप के साथ समाप्त हो सकते हैं। इस मामले में,
candidates.extend(node._get_child_candidates(distance, min_dist, max_dist))
जेनरेटर के सभी मानों को समाप्त करता है, लेकिन नई जेनरेटर ऑब्जेक्ट्स बनातेwhile
, जो पिछले मानों से अलग-अलग मान उत्पन्न करेगा क्योंकि यह उसी पर लागू नहीं होता है नोड।extend()
विधि एक सूची ऑब्जेक्ट विधि है जो एक पुनरावर्तनीय की अपेक्षा करती है और इसकी मान सूची में जोड़ती है।
आम तौर पर हम इसे एक सूची पास करते हैं:
>>> a = [1, 2]
>>> b = [3, 4]
>>> a.extend(b)
>>> print(a)
[1, 2, 3, 4]
लेकिन आपके कोड में यह जनरेटर प्राप्त करता है, जो कि अच्छा है क्योंकि:
- आपको मूल्यों को दो बार पढ़ने की आवश्यकता नहीं है।
- आपके पास बहुत सारे बच्चे हो सकते हैं और आप उन्हें सभी स्मृति में संग्रहीत नहीं करना चाहते हैं।
और यह काम करता है क्योंकि किसी विधि की तर्क एक सूची है या नहीं, तो पाइथन परवाह नहीं है। पायथन पुनरावृत्तियों की अपेक्षा करता है ताकि यह तार, सूचियों, tuples और जेनरेटर के साथ काम करेगा! इसे बतख टाइपिंग कहा जाता है और यही कारण है कि पाइथन इतना अच्छा है। लेकिन यह एक और कहानी है, एक और सवाल के लिए ...
आप जेनरेटर के उन्नत उपयोग को देखने के लिए यहां रुक सकते हैं, या थोड़ा सा पढ़ सकते हैं:
जनरेटर थकावट को नियंत्रित करना
>>> class Bank(): # Let's create a bank, building ATMs
... crisis = False
... def create_atm(self):
... while not self.crisis:
... yield "$100"
>>> hsbc = Bank() # When everything's ok the ATM gives you as much as you want
>>> corner_street_atm = hsbc.create_atm()
>>> print(corner_street_atm.next())
$100
>>> print(corner_street_atm.next())
$100
>>> print([corner_street_atm.next() for cash in range(5)])
['$100', '$100', '$100', '$100', '$100']
>>> hsbc.crisis = True # Crisis is coming, no more money!
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> wall_street_atm = hsbc.create_atm() # It's even true for new ATMs
>>> print(wall_street_atm.next())
<type 'exceptions.StopIteration'>
>>> hsbc.crisis = False # The trouble is, even post-crisis the ATM remains empty
>>> print(corner_street_atm.next())
<type 'exceptions.StopIteration'>
>>> brand_new_atm = hsbc.create_atm() # Build a new one to get back in business
>>> for cash in brand_new_atm:
... print cash
$100
$100
$100
$100
$100
$100
$100
$100
$100
...
नोट: पायथन 3 के लिए, print(corner_street_atm.__next__())
उपयोग करें print(corner_street_atm.__next__())
या print(next(corner_street_atm))
यह संसाधनों तक पहुंच नियंत्रित करने जैसी विभिन्न चीजों के लिए उपयोगी हो सकता है।
Itertools, आपका सबसे अच्छा दोस्त
Itertools मॉड्यूल में iterables में हेरफेर करने के लिए विशेष कार्य होते हैं। कभी जनरेटर को डुप्लिकेट करना चाहते हैं? दो जेनरेटर चेन? एक नेस्टर सूची में समूह मूल्य एक लाइनर के साथ? एक और सूची बनाने के बिना Map / Zip
?
फिर बस import itertools
।
एक उदाहरण? आइए चार घोड़े की दौड़ के आगमन के संभावित आदेश देखें:
>>> horses = [1, 2, 3, 4]
>>> races = itertools.permutations(horses)
>>> print(races)
<itertools.permutations object at 0xb754f1dc>
>>> print(list(itertools.permutations(horses)))
[(1, 2, 3, 4),
(1, 2, 4, 3),
(1, 3, 2, 4),
(1, 3, 4, 2),
(1, 4, 2, 3),
(1, 4, 3, 2),
(2, 1, 3, 4),
(2, 1, 4, 3),
(2, 3, 1, 4),
(2, 3, 4, 1),
(2, 4, 1, 3),
(2, 4, 3, 1),
(3, 1, 2, 4),
(3, 1, 4, 2),
(3, 2, 1, 4),
(3, 2, 4, 1),
(3, 4, 1, 2),
(3, 4, 2, 1),
(4, 1, 2, 3),
(4, 1, 3, 2),
(4, 2, 1, 3),
(4, 2, 3, 1),
(4, 3, 1, 2),
(4, 3, 2, 1)]
पुनरावृत्ति के आंतरिक तंत्र को समझना
Iteration एक प्रक्रिया है जो __iter__()
लागू करती है ( __iter__()
विधि को कार्यान्वित करती है) और iterators ( __next__()
विधि को कार्यान्वित करना)। Iterables कोई भी वस्तुएं हैं जिन्हें आप एक इटरेटर प्राप्त कर सकते हैं। Iterators वे ऑब्जेक्ट्स हैं जो आपको पुनरावृत्तियों पर फिर से चलने देते हैं।
लूप काम करने के तरीके for
बारे में इस लेख में इसके बारे में और कुछ है।
yield
कीवर्ड दो सरल तथ्यों तक कम हो जाता है:
- यदि संकलक किसी फ़ंक्शन के अंदर कहीं भी
yield
कीवर्ड का पता लगाता है, तो वह फ़ंक्शन रिटर्न स्टेटमेंट के माध्यम सेreturn
नहीं आता है। इसके बजाए , यह तुरंत जेनरेटर नामक एक आलसी "लंबित सूची" ऑब्जेक्ट देता है - एक जनरेटर पुनरावर्तनीय है। एक पुनरावृत्ति क्या है? यह एक निश्चित क्रम में प्रत्येक तत्व पर जाने के लिए एक अंतर्निहित प्रोटोकॉल के साथ एक
list
याset
याrange
या dict-view की तरह कुछ भी है।
संक्षेप में: जनरेटर एक आलसी, वृद्धिशील लंबित सूची है , और yield
बयान आपको जनरेटर को बढ़ते हुए सूची मूल्यों को प्रोग्राम मूल्यों के कार्यक्रम के लिए फ़ंक्शन नोटेशन का उपयोग करने की अनुमति देता है।
generator = myYieldingFunction(...)
x = list(generator)
generator
v
[x[0], ..., ???]
generator
v
[x[0], x[1], ..., ???]
generator
v
[x[0], x[1], x[2], ..., ???]
StopIteration exception
[x[0], x[1], x[2]] done
list==[x[0], x[1], x[2]]
उदाहरण
आइए एक फ़ंक्शन makeRange
को परिभाषित करें जो कि पाइथन की range
की range
। कॉलिंग makeRange(n)
एक जनरेटर makeRange(n)
:
def makeRange(n):
# return 0,1,2,...,n-1
i = 0
while i < n:
yield i
i += 1
>>> makeRange(5)
<generator object makeRange at 0x19e4aa0>
जनरेटर को तुरंत अपने लंबित मूल्यों को वापस करने के लिए मजबूर करने के लिए, आप इसे list()
में पास कर सकते हैं (जैसा कि आप किसी भी पुनरावर्तनीय हो सकते हैं):
>>> list(makeRange(5))
[0, 1, 2, 3, 4]
"केवल एक सूची लौटने" के लिए उदाहरण की तुलना
उपरोक्त उदाहरण को केवल उस सूची को बनाने के बारे में सोचा जा सकता है जिसे आप जोड़ते हैं और वापस आते हैं:
# list-version # # generator-version
def makeRange(n): # def makeRange(n):
"""return [0,1,2,...,n-1]""" #~ """return 0,1,2,...,n-1"""
TO_RETURN = [] #>
i = 0 # i = 0
while i < n: # while i < n:
TO_RETURN += [i] #~ yield i
i += 1 # i += 1 ## indented
return TO_RETURN #>
>>> makeRange(5)
[0, 1, 2, 3, 4]
हालांकि, एक बड़ा अंतर है; अंतिम खंड देखें।
आप जेनरेटर का उपयोग कैसे कर सकते हैं
एक पुनरावृत्ति एक सूची समझ का अंतिम हिस्सा है, और सभी जनरेटर पुनरावर्तनीय हैं, इसलिए उन्हें अक्सर इस प्रकार उपयोग किया जाता है:
# _ITERABLE_
>>> [x+10 for x in makeRange(5)]
[10, 11, 12, 13, 14]
जेनरेटर के लिए बेहतर महसूस करने के लिए, आप itertools
मॉड्यूल के साथ खेल सकते हैं ( chain.from_iterable
होने पर chain
बजाय chain.from_iterable
का उपयोग करना सुनिश्चित करें)। उदाहरण के लिए, आप जेनरेटर का उपयोग असीमित-लंबी आलसी सूचियों जैसे itertools.count()
को लागू करने के लिए भी कर सकते हैं। आप अपने स्वयं के def enumerate(iterable): zip(count(), iterable)
को कार्यान्वित कर सकते हैं def enumerate(iterable): zip(count(), iterable)
, या वैकल्पिक रूप से थोड़ी देर में yield
कीवर्ड के साथ ऐसा करते हैं।
कृपया ध्यान दें: जेनरेटर वास्तव में कई अन्य चीजों के लिए उपयोग किया जा सकता है, जैसे कोरआउट या गैर-निर्धारक प्रोग्रामिंग या अन्य सुरुचिपूर्ण चीजों को लागू करना। हालांकि, यहां मौजूद "आलसी सूचियां" दृष्टिकोण मैं सबसे आम उपयोग है जो आपको मिलेगा।
परदे के पीछे
इस तरह "पायथन पुनरावृत्ति प्रोटोकॉल" काम करता है। यही है, जब आप list(makeRange(5))
करते हैं तो क्या हो रहा है list(makeRange(5))
। यही वह है जिसे मैंने पहले "आलसी, वृद्धिशील सूची" के रूप में वर्णित किया था।
>>> x=iter(range(5))
>>> next(x)
0
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
4
>>> next(x)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
अंतर्निहित फ़ंक्शन next()
केवल ऑब्जेक्ट्स .next()
फ़ंक्शन को कॉल करता है, जो "पुनरावृत्ति प्रोटोकॉल" का हिस्सा है और सभी इटरेटर पर पाया जाता है। फैंसी चीजों को लागू करने के लिए आमतौर पर next()
फ़ंक्शन (पुनरावृत्ति प्रोटोकॉल के अन्य हिस्सों next()
उपयोग कर सकते हैं, आमतौर पर पठनीयता की कीमत पर, इसलिए ऐसा करने से बचने का प्रयास करें ...
ज़रा सी बात
आम तौर पर, अधिकांश लोग निम्नलिखित भेदों की परवाह नहीं करेंगे और शायद यहां पढ़ना बंद करना चाहते हैं।
पायथन-बोलने में, एक पुनरावर्तनीय कोई वस्तु है जो एक सूची [1,2,3]
तरह "फॉर-लूप की अवधारणा को समझती है", और एक पुनरावर्तक अनुरोधित फॉर-लूप की एक विशिष्ट उदाहरण है [1,2,3].__iter__()
। एक जेनरेटर बिल्कुल किसी भी इटरेटर जैसा ही होता है, जिस तरह से लिखा गया था (फ़ंक्शन सिंटैक्स के साथ)।
जब आप एक सूची से एक इटरेटर का अनुरोध करते हैं, तो यह एक नया इटरेटर बनाता है। हालांकि, जब आप एक पुनरावर्तक से एक पुनरावर्तक से अनुरोध करते हैं (जो आप शायद ही कभी करेंगे), तो यह आपको स्वयं की एक प्रति देता है।
इस प्रकार, असंभव घटना में कि आप ऐसा कुछ करने में असफल रहे हैं ...
> x = myRange(5)
> list(x)
[0, 1, 2, 3, 4]
> list(x)
[]
... तो याद रखें कि जनरेटर एक पुनरावर्तक है ; यानी, यह एक बार उपयोग है। यदि आप इसका पुन: उपयोग करना चाहते हैं, तो आपको myRange(...)
फिर से कॉल करना चाहिए। यदि आपको परिणाम दो बार उपयोग करने की आवश्यकता है, तो परिणाम को एक सूची में परिवर्तित करें और इसे एक चर x = list(myRange(5))
। जिन लोगों को पूरी तरह से जेनरेटर क्लोन करने की ज़रूरत है (उदाहरण के लिए, जो भयानक रूप से itertools.tee
कर रहे हैं) पूरी तरह जरूरी है, तो itertools.tee
उपयोग कर सकते हैं, क्योंकि itertools.tee
करने itertools.tee
इटरेटर पायथन PEP मानकों का प्रस्ताव स्थगित कर दिया गया है।
yield
बस जैसा है return
- यह जो भी आप इसे बताते हैं (जेनरेटर के रूप में) देता है। अंतर यह है कि अगली बार जब आप जनरेटर को कॉल करते हैं, तो अंतिम कॉल से निष्पादन शुरू होता है yield
। वापसी के विपरीत, जब उपज होती है तो स्टैक फ्रेम साफ़ नहीं होता है, हालांकि नियंत्रण कॉलर को वापस स्थानांतरित कर दिया जाता है, इसलिए इसकी स्थिति अगली बार फ़ंक्शन को फिर से शुरू कर देगी।
आपके कोड के मामले में, फ़ंक्शन get_child_candidates
एक पुनरावर्तक की तरह कार्य कर रहा है ताकि जब आप अपनी सूची बढ़ाते हैं, तो यह एक समय में नई सूची में एक तत्व जोड़ता है।
list.extend
एक थिटरेटर को कॉल होने तक कॉल करता है। आपके द्वारा पोस्ट किए गए कोड नमूने के मामले में, यह केवल एक टुपल लौटने और सूची में संलग्न करने के लिए बहुत स्पष्ट होगा।
उपज एक वस्तु है
एक return
समारोह में एक एकल मूल्य वापस करेगा।
यदि आप एक फ़ंक्शन को मूल्यों का एक विशाल सेट वापस करने के लिए चाहते हैं , तो उपयोग करें yield
।
सबसे महत्वपूर्ण बात yield
यह है कि एक बाधा है ।
सीयूडीए भाषा में बाधा की तरह, यह पूरा होने तक नियंत्रण स्थानांतरित नहीं करेगा।
यही है, यह शुरुआत से ही आपके कार्य में कोड चलाएगा जब तक कि यह हिट न हो जाए yield
। फिर, यह लूप का पहला मान वापस कर देगा।
फिर, प्रत्येक अन्य कॉल आपके द्वारा फ़ंक्शन में लिखे गए लूप को एक और बार चलाएगा, जब तक कि वापस आने के लिए कोई मूल्य न हो, तब तक अगले मान को वापस कर दें।
जेनरेटर को वास्तव में कार्यान्वित करने के तरीके के बारे में कुछ पायथन उदाहरण यहां दिए गए हैं जैसे कि पाइथन ने उनके लिए वाक्य रचनात्मक चीनी प्रदान नहीं की है:
एक पायथन जनरेटर के रूप में:
from itertools import islice
def fib_gen():
a, b = 1, 1
while True:
yield a
a, b = b, a + b
assert [1, 1, 2, 3, 5] == list(islice(fib_gen(), 5))
जेनरेटर के बजाय लेक्सिकल क्लोजर का उपयोग करना
def ftake(fnext, last):
return [fnext() for _ in xrange(last)]
def fib_gen2():
#funky scope due to python2.x workaround
#for python 3.x use nonlocal
def _():
_.a, _.b = _.b, _.a + _.b
return _.a
_.a, _.b = 0, 1
return _
assert [1,1,2,3,5] == ftake(fib_gen2(), 5)
जनरेटर के बजाय ऑब्जेक्ट क्लोजर का उपयोग करना (क्योंकि ClosuresAndObjectsAreEquivalentऑब्जेक्ट्सएरक्वाइवलेंट )
class fib_gen3:
def __init__(self):
self.a, self.b = 1, 1
def __call__(self):
r = self.a
self.a, self.b = self.b, self.a + self.b
return r
assert [1,1,2,3,5] == ftake(fib_gen3(), 5)
फिर भी एक और टीएल; डीआर
सूची में इटरेटर : सूचीnext()
का अगला तत्व देता है
इटरेटर जनरेटर : next()
फ्लाई पर अगले तत्व की गणना करेगा (कोड निष्पादित करें)
आप उपज / जेनरेटर को बाहर से नियंत्रण प्रवाह को मैन्युअल रूप से चलाने के तरीके के रूप में देख सकते हैं (जैसे लूप एक चरण जारी रखें), कॉल करके next
, हालांकि जटिल जटिल।
नोट : जेनरेटर सामान्य कार्य नहीं है। यह पिछले राज्य को स्थानीय चर (ढेर) जैसे याद करता है। विस्तृत स्पष्टीकरण के लिए अन्य उत्तरों या लेख देखें। जनरेटर केवल एक बार फिर से किया जा सकता है । आप बिना कर सकते हैं yield
, लेकिन यह उतना अच्छा नहीं होगा, इसलिए इसे 'बहुत अच्छी' भाषा चीनी माना जा सकता है।
यील्ड आपको जनरेटर देता है।
def get_odd_numbers(i):
return range(1, i, 2)
def yield_odd_numbers(i):
for x in range(1, i, 2):
yield x
foo = get_odd_numbers(10)
bar = yield_odd_numbers(10)
foo
[1, 3, 5, 7, 9]
bar
<generator object yield_odd_numbers at 0x1029c6f50>
bar.next()
1
bar.next()
3
bar.next()
5
जैसा कि आप देख सकते हैं, पहले मामले में foo पूरी सूची को स्मृति में एक साथ रखती है। यह 5 तत्वों वाली सूची के लिए एक बड़ा सौदा नहीं है, लेकिन यदि आप 5 मिलियन की सूची चाहते हैं तो क्या होगा? न केवल यह एक बड़ी स्मृति खाने वाला है, बल्कि उस समारोह को बनाने के लिए बहुत समय लगता है जब समारोह कहा जाता है। दूसरे मामले में, बार आपको सिर्फ जनरेटर देता है। एक जनरेटर एक पुनरावृत्ति है - जिसका अर्थ है कि आप इसे लूप, आदि में उपयोग कर सकते हैं, लेकिन प्रत्येक मान को केवल एक बार एक्सेस किया जा सकता है। सभी मान एक ही समय में स्मृति में संग्रहीत नहीं होते हैं; जेनरेटर ऑब्जेक्ट "याद करता है" जहां यह आखिरी बार आपको लूपिंग में था - इस तरह, यदि आप 50 अरब तक गिनने के लिए एक अचूक उपयोग कर रहे हैं, तो आपको 50 बिलियन तक गिनना नहीं है एक बार में और 50 अरब संख्याओं को गिनने के लिए स्टोर करें। फिर, यह एक सुंदर योगदान उदाहरण है,यदि आप वास्तव में 50 अरब तक गिनना चाहते हैं तो आप शायद itertools का उपयोग करेंगे। :)
जनरेटर का यह सबसे सरल उपयोग मामला है। जैसा कि आपने कहा था, इसका उपयोग कुछ प्रकार के स्टैक वैरिएबल का उपयोग करने के बजाय कॉल स्टैक के माध्यम से चीजों को धक्का देने के लिए उपज का उपयोग करके कुशल क्रमपरिवर्तन लिखने के लिए किया जा सकता है। जेनरेटर का इस्तेमाल विशेष पेड़ ट्रैवर्सल और अन्य सभी चीजों के लिए भी किया जा सकता है।
ये रहा एक सरल उदाहरण:
def isPrimeNumber(n):
print "isPrimeNumber({}) call".format(n)
if n==1:
return False
for x in range(2,n):
if n % x == 0:
return False
return True
def primes (n=1):
while(True):
print "loop step ---------------- {}".format(n)
if isPrimeNumber(n): yield n
n += 1
for n in primes():
if n> 10:break
print "wiriting result {}".format(n)
आउटपुट:
loop step ---------------- 1
isPrimeNumber(1) call
loop step ---------------- 2
isPrimeNumber(2) call
loop step ---------------- 3
isPrimeNumber(3) call
wiriting result 3
loop step ---------------- 4
isPrimeNumber(4) call
loop step ---------------- 5
isPrimeNumber(5) call
wiriting result 5
loop step ---------------- 6
isPrimeNumber(6) call
loop step ---------------- 7
isPrimeNumber(7) call
wiriting result 7
loop step ---------------- 8
isPrimeNumber(8) call
loop step ---------------- 9
isPrimeNumber(9) call
loop step ---------------- 10
isPrimeNumber(10) call
loop step ---------------- 11
isPrimeNumber(11) call
मैं पाइथन डेवलपर नहीं हूं, लेकिन ऐसा लगता है कि मुझे yield
प्रोग्राम प्रवाह की स्थिति है और अगला लूप "उपज" स्थिति से शुरू होता है। ऐसा लगता है कि यह उस स्थिति की प्रतीक्षा कर रहा है, और उसके ठीक पहले, बाहर एक मूल्य लौट रहा है, और अगली बार काम जारी है।
यह एक दिलचस्प और अच्छी क्षमता प्रतीत होता है: डी
(मेरा नीचे उत्तर केवल पाइथन जनरेटर का उपयोग करने के परिप्रेक्ष्य से बोलता है, जनरेटर तंत्र के अंतर्निहित कार्यान्वयन में नहीं , जिसमें ढेर और ढेर हेरफेर की कुछ चाल शामिल हैं।)
जब एक अजगर समारोह में एक के yield
बजाय प्रयोग किया जाता है return
, तो वह फ़ंक्शन किसी विशेष नाम में बदल जाता है generator function
। वह फ़ंक्शन generator
प्रकार की ऑब्जेक्ट वापस कर देगा । कीवर्ड अजगर संकलक सूचित करने के लिए इस तरह के समारोह विशेष रूप से इलाज करने के लिए एक ध्वज है। एक बार कुछ मूल्य लौटाए जाने के बाद सामान्य कार्य समाप्त हो जाएंगे। लेकिन कंपाइलर की मदद से, जनरेटर फ़ंक्शन को पुन: शुरू करने योग्य माना जा सकता है । यही है, निष्पादन संदर्भ बहाल किया जाएगा और निष्पादन अंतिम रन से जारी रहेगा। जब तक आप स्पष्ट रूप से रिटर्न कॉल नहीं करते हैं, जो एक अपवाद उठाएगा (जो इटरेटर प्रोटोकॉल का हिस्सा भी है), या फ़ंक्शन के अंत तक पहुंच जाएगा। मुझे इसके बारे में बहुत सारे संदर्भ मिले लेकिन यह oneyield
StopIteration
generator
oneसे functional programming perspective
सबसे पाचक है।
(अब मैं पीछे तर्क के बारे में बात करना चाहता हूं generator
, और iterator
अपनी समझ के आधार पर बात करना चाहता हूं । मुझे उम्मीद है कि यह आपको इटरेटर और जनरेटर के आवश्यक प्रेरणा को समझने में मदद कर सकता है । ऐसी अवधारणा अन्य भाषाओं में भी दिखाई देती है जैसे सी #।)
जैसा कि मैं समझता हूं, जब हम डेटा के समूह को संसाधित करना चाहते हैं, तो हम आम तौर पर पहले डेटा को स्टोर करते हैं और फिर इसे एक-एक करके संसाधित करते हैं। लेकिन यह अंतर्ज्ञानी दृष्टिकोण समस्याग्रस्त है। यदि डेटा वॉल्यूम बड़ा है, तो उन्हें पहले से ही स्टोर करना महंगा है। तो data
खुद को सीधे संग्रहित करने के बजाय , किसी प्रकार का metadata
अप्रत्यक्ष रूप से स्टोर क्यों न करेंthe logic how the data is computed
।
ऐसे मेटाडेटा को लपेटने के लिए 2 दृष्टिकोण हैं।
- ओओ दृष्टिकोण, हम मेटाडेटा लपेटते हैं
as a class
। यह तथाकथित हैiterator
जो इटरेटर प्रोटोकॉल (यानी__next__()
, और__iter__()
विधियों) को लागू करता है । यह आमतौर पर देखा जाने वाला इटरेटर डिजाइन पैटर्न भी है । - कार्यात्मक दृष्टिकोण, हम मेटाडेटा लपेटते हैं
as a function
। यह तथाकथित हैgenerator function
। लेकिन हुड के तहत,generator object
अभी भीIS-A
पुनरावर्तक लौटाया गया क्योंकि यह इटरेटर प्रोटोकॉल को भी लागू करता है।
किसी भी तरह से, एक इटरेटर बनाया जाता है, यानी कुछ वस्तु जो आपको इच्छित डेटा दे सकती है। ओओ दृष्टिकोण थोड़ा जटिल हो सकता है। वैसे भी, जो उपयोग करने के लिए आप पर निर्भर है।
yield
कीवर्ड बस लौटने परिणाम एकत्र करता है। yield
पसंद के बारे में सोचोreturn +=
उन लोगों के लिए जो कम से कम कामकाजी उदाहरण पसंद करते हैं, इस इंटरैक्टिव Python सत्र पर ध्यान दें :
>>> def f():
... yield 1
... yield 2
... yield 3
...
>>> g = f()
>>> for i in g:
... print i
...
1
2
3
>>> for i in g:
... print i
...
>>> # Note that this time nothing was printed
उल्लेख करने के लिए एक अतिरिक्त बात है: पैदा करने वाला एक कार्य वास्तव में समाप्त नहीं होता है। मैंने इस तरह कोड लिखा है:
def fib():
last, cur = 0, 1
while True:
yield cur
last, cur = cur, last + cur
फिर मैं इसे इस तरह के दूसरे कोड में उपयोग कर सकता हूं:
for f in fib():
if some_condition: break
coolfuncs(f);
यह वास्तव में कुछ समस्याओं को सरल बनाने में मदद करता है, और कुछ चीजों को काम करना आसान बनाता है।
ऐसे कई प्रकार के उत्तर हैं जिन्हें मुझे अभी तक नहीं दिया गया है, कई महान उत्तरों में जो जनरेटर का उपयोग करने का वर्णन करते हैं। प्रोग्रामिंग भाषा सिद्धांत उत्तर यहां दिया गया है:
yield
अजगर में बयान एक जनरेटर देता है। पायथन में एक जनरेटर एक ऐसा कार्य है जो निरंतरता (और विशेष रूप से एक प्रकार का कोरआउटिन देता है, लेकिन निरंतरता यह समझने के लिए अधिक सामान्य तंत्र का प्रतिनिधित्व करती है) क्या हो रहा है)।
प्रोग्रामिंग भाषा सिद्धांत में निरंतरता गणना का एक और अधिक मौलिक प्रकार है, लेकिन इनका उपयोग अक्सर नहीं किया जाता है, क्योंकि उन्हें तर्क करना बहुत मुश्किल होता है और इसे लागू करना बहुत मुश्किल होता है। लेकिन निरंतरता का विचार सरल है: यह एक गणना की स्थिति है जो अभी तक समाप्त नहीं हुई है। इस स्थिति में, चर के वर्तमान मूल्य, संचालन जो अभी तक किए जाने वाले हैं, और इसी तरह, सहेजे गए हैं। फिर कुछ बिंदु बाद कार्यक्रम में निरंतरता का आह्वान किया जा सकता है, जैसे कि प्रोग्राम के चर उस राज्य को रीसेट कर दिए जाते हैं और सहेजे गए ऑपरेशन किए जाते हैं।
निरंतरता, इस सामान्य रूप में, दो तरीकों से लागू किया जा सकता है। में call/cc
जिस तरह से, कार्यक्रम के ढेर सचमुच सहेजा जाता है और फिर जब निरंतरता शुरू हो जाती है, ढेर बहाल है।
निरंतरता गुजरने वाली शैली (सीपीएस) में, निरंतरताएं सामान्य कार्य होती हैं (केवल उन भाषाओं में जहां फ़ंक्शंस प्रथम श्रेणी होती है) जो प्रोग्रामर स्पष्ट रूप से प्रबंधित करता है और सबराउटिन के आसपास गुजरता है। इस शैली में, कार्यक्रम स्थिति को स्टैक पर कहीं भी रहने वाले चर के बजाय बंद होने (और वेरिएबल्स जो उन्हें एन्कोड किए जाते हैं) द्वारा दर्शाया जाता है। नियंत्रण प्रवाह का प्रबंधन करने वाले कार्य तर्कों के रूप में निरंतरता स्वीकार करते हैं (सीपीएस के कुछ बदलावों में, कार्य कई निरंतरताओं को स्वीकार कर सकते हैं) और उन्हें कॉल करके और बाद में लौटकर उन्हें नियंत्रित करके प्रवाह प्रवाह में हेरफेर करते हैं। निरंतरता उत्तीर्ण शैली का एक बहुत ही सरल उदाहरण इस प्रकार है:
def save_file(filename):
def write_file_continuation():
write_stuff_to_file(filename)
check_if_file_exists_and_user_wants_to_overwrite(write_file_continuation)
इस (बहुत सरल) उदाहरण में, प्रोग्रामर वास्तव में फ़ाइल को निरंतरता में लिखने के संचालन को बचाता है (जो संभावित रूप से लिखने के लिए कई विवरणों के साथ एक जटिल परिचालन हो सकता है), और फिर उस निरंतरता को पारित करता है (यानी, पहले- कक्षा बंद) एक और ऑपरेटर के लिए जो कुछ और प्रसंस्करण करता है, और फिर आवश्यक होने पर इसे कॉल करता है। (मैं इस डिजाइन पैटर्न का उपयोग वास्तविक जीयूआई प्रोग्रामिंग में बहुत अधिक करता हूं, या तो क्योंकि यह मुझे कोड की लाइनों को बचाता है या, सबसे महत्वपूर्ण बात यह है कि जीयूआई घटनाओं के ट्रिगर के बाद नियंत्रण प्रवाह का प्रबंधन किया जाता है।)
इस पोस्ट का बाकी, सामान्यता के नुकसान के बिना, सीपीएस के रूप में निरंतरता को अवधारणा बनाएगा, क्योंकि यह समझना और पढ़ना बहुत आसान है।
अब चलिए पाइथन में जेनरेटर के बारे में बात करते हैं। जनरेटर निरंतरता का एक विशिष्ट उप प्रकार हैं। जबकि निरंतरता एक के राज्य को बचाने के लिए सामान्य रूप में सक्षम हैं अभिकलन (यानी, कार्यक्रम की कॉल स्टैक), जनरेटर केवल एक ओवर यात्रा के राज्य को बचाने के लिए सक्षम हैं इटरेटर । हालांकि, जेनरेटर के कुछ उपयोग मामलों के लिए यह परिभाषा थोड़ा भ्रामक है। उदाहरण के लिए:
def f():
while True:
yield 4
यह स्पष्ट रूप से एक उचित पुनरावर्तनीय है जिसका व्यवहार अच्छी तरह से परिभाषित किया जाता है - प्रत्येक बार जेनरेटर इसे फिर से चालू करता है, यह 4 लौटाता है (और ऐसा हमेशा करता है)। लेकिन यह संभवतः प्रोटोटाइपिकल प्रकार का पुनरावृत्ति नहीं है जो इटरेटर (यानी for x in collection: do_something(x)
) के बारे में सोचते समय दिमाग में आता है । यह उदाहरण जेनरेटर की शक्ति को दिखाता है: यदि कुछ भी इटेटरेटर है, तो जनरेटर इसके पुनरावृत्ति की स्थिति को बचा सकता है।
दोहराने के लिए: निरंतरता प्रोग्राम के ढेर की स्थिति को बचा सकती है और जेनरेटर पुनरावृत्ति की स्थिति को बचा सकते हैं। इसका मतलब है कि जेनरेटर की तुलना में निरंतरता बहुत शक्तिशाली है, लेकिन यह भी कि जेनरेटर बहुत आसान हैं। भाषा डिजाइनर को कार्यान्वित करने के लिए वे आसान हैं, और प्रोग्रामर के उपयोग के लिए वे आसान हैं (यदि आपके पास जला देने के लिए कुछ समय है, तो जारी रखने और कॉल / सीसी के बारे में इस पृष्ठ को पढ़ने और समझने का प्रयास करें )।
लेकिन आप जेनरेटर को निरंतर उत्तीर्ण शैली के एक साधारण, विशिष्ट मामले के रूप में आसानी से कार्यान्वित (और अवधारणा) कर सकते हैं:
जब भी yield
कहा जाता है, यह समारोह को निरंतरता वापस करने के लिए कहता है। जब फ़ंक्शन को फिर से कॉल किया जाता है, तो यह जहां से छोड़ा जाता है वहां से शुरू होता है। तो, छद्म-छद्म कोड (यानी, छद्म कोड नहीं, लेकिन कोड नहीं) जनरेटर की next
विधि मूल रूप से निम्नानुसार है:
class Generator():
def __init__(self,iterable,generatorfun):
self.next_continuation = lambda:generatorfun(iterable)
def next(self):
value, next_continuation = self.next_continuation()
self.next_continuation = next_continuation
return value
जहां yield
वास्तविक वास्तविक जनरेटर फ़ंक्शन के लिए कीवर्ड वास्तव में वाक्य रचनात्मक चीनी है, मूल रूप से कुछ ऐसा है:
def generatorfun(iterable):
if len(iterable) == 0:
raise StopIteration
else:
return (iterable[0], lambda:generatorfun(iterable[1:]))
याद रखें कि यह सिर्फ छद्म कोड है और पाइथन में जनरेटर के वास्तविक कार्यान्वयन अधिक जटिल है। लेकिन यह समझने के लिए एक अभ्यास के रूप में क्या हो रहा है, yield
कीवर्ड के उपयोग के बिना जेनरेटर ऑब्जेक्ट्स को लागू करने के लिए निरंतरता उत्तीर्ण शैली का उपयोग करने का प्रयास करें ।
कई लोग return
इसके बजाए उपयोग करते हैं yield
, लेकिन कुछ मामलों में yield
काम करने के लिए और अधिक कुशल और आसान हो सकता है।
यहां एक उदाहरण दिया गया है जो yield
निश्चित रूप से सर्वोत्तम है:
वापसी (समारोह में)
import random
def return_dates():
dates = [] # With 'return' you need to create a list then return it
for i in range(5):
date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
dates.append(date)
return dates
उपज (समारोह में)
def yield_dates():
for i in range(5):
date = random.choice(["1st", "2nd", "3rd", "4th", "5th", "6th", "7th", "8th", "9th", "10th"])
yield date # 'yield' makes a generator automatically which works
# in a similar way. This is much more efficient.
कॉलिंग फ़ंक्शंस
dates_list = return_dates()
print(dates_list)
for i in dates_list:
print(i)
dates_generator = yield_dates()
print(dates_generator)
for i in dates_generator:
print(i)
दोनों कार्य एक ही काम करते हैं, लेकिन yield
पांच की बजाय तीन लाइनों का उपयोग करते हैं और इसके बारे में चिंता करने के लिए एक कम चर है।
यह कोड से परिणाम है:
जैसा कि आप देख सकते हैं कि दोनों कार्य एक ही काम करते हैं। एकमात्र अंतर return_dates()
एक सूची yield_dates()
देता है और जनरेटर देता है।
वास्तविक जीवन उदाहरण लाइन द्वारा फ़ाइल लाइन पढ़ने या यदि आप जनरेटर बनाना चाहते हैं तो कुछ ऐसा होगा।
प्रत्येक उत्तर सुझावों की तरह, yield
अनुक्रम जनरेटर बनाने के लिए प्रयोग किया जाता है। इसका उपयोग गतिशील रूप से कुछ अनुक्रम उत्पन्न करने के लिए किया जाता है। उदाहरण के लिए, नेटवर्क पर लाइन द्वारा फ़ाइल लाइन पढ़ने के दौरान, आप yield
फ़ंक्शन का उपयोग इस प्रकार कर सकते हैं :
def getNextLines():
while con.isOpen():
yield con.read()
आप इसे अपने कोड में निम्नानुसार उपयोग कर सकते हैं:
for line in getNextLines():
doSomeThing(line)
निष्पादन नियंत्रण स्थानांतरण गोचा
for
उपज निष्पादित होने पर निष्पादन नियंत्रण को getNextLines () से लूप में स्थानांतरित कर दिया जाएगा । इस प्रकार, हर बार NextLines () को आवंटित किया जाता है, निष्पादन उस बिंदु से शुरू होता है जहां इसे पिछली बार रोक दिया गया था।
इस प्रकार संक्षेप में, निम्नलिखित कोड के साथ एक समारोह
def simpleYield():
yield "first time"
yield "second time"
yield "third time"
yield "Now some useful value {}".format(12)
for i in simpleYield():
print i
प्रिंट करेंगे
"first time"
"second time"
"third time"
"Now some useful value 12"
प्रोग्रामिंग दृष्टिकोण से, इटरेटर को thunks रूप में कार्यान्वित किया thunks ।
समवर्ती निष्पादन के लिए इटरेटर, जनरेटर और थ्रेड पूल को लागू करने के लिए, थंक्स (जिसे अनाम कार्य भी कहा जाता है) के रूप में, एक बंद करने वाले ऑब्जेक्ट को भेजे गए संदेशों का उपयोग करता है, जिसमें एक प्रेषक होता है, और प्रेषक "संदेश" का उत्तर देता है।
http://en.wikipedia.org/wiki/Message_passing
" अगला " एक संदेश को बंद करने के लिए भेजा गया है, जिसे " इटर " कॉल द्वारा बनाया गया है ।
इस गणना को लागू करने के कई तरीके हैं। मैंने उत्परिवर्तन का उपयोग किया, लेकिन वर्तमान मूल्य और अगले उपजकर्ता को लौटकर, उत्परिवर्तन के बिना इसे करना आसान है।
यहां एक प्रदर्शन है जो आर 6 आरएस की संरचना का उपयोग करता है, लेकिन अर्थशास्त्र पूरी तरह से पाइथन के समान है। यह गणना का एक ही मॉडल है, और केवल पायथन में इसे फिर से लिखने के लिए सिंटैक्स में बदलाव की आवश्यकता है।
Welcome to Racket v6.5.0.3. -> (define gen (lambda (l) (define yield (lambda () (if (null? l) 'END (let ((v (car l))) (set! l (cdr l)) v)))) (lambda(m) (case m ('yield (yield)) ('init (lambda (data) (set! l data) 'OK)))))) -> (define stream (gen '(1 2 3))) -> (stream 'yield) 1 -> (stream 'yield) 2 -> (stream 'yield) 3 -> (stream 'yield) 'END -> ((stream 'init) '(a b)) 'OK -> (stream 'yield) 'a -> (stream 'yield) 'b -> (stream 'yield) 'END -> (stream 'yield) 'END ->
यहां क्या yield
होता है इसकी एक मानसिक छवि है।
मुझे धागे के बारे में सोचना पसंद है (यहां तक कि जब इसे इस तरह लागू नहीं किया जाता है)।
जब एक सामान्य कार्य कहा जाता है, तो यह अपने स्थानीय चर को ढेर पर रखता है, कुछ गणना करता है, फिर ढेर और रिटर्न को साफ़ करता है। इसके स्थानीय चर के मान कभी नहीं देखे जाते हैं।
एक yield
फ़ंक्शन के साथ , जब उसका कोड चलना शुरू होता है (यानी फ़ंक्शन को कॉल करने के बाद, जनरेटर ऑब्जेक्ट को वापस कर दिया जाता है, जिसका next()
विधि तब लागू किया जाता है), यह समान रूप से स्टैक और गणना के लिए अपने स्थानीय चर डालता है। लेकिन फिर, जब यह yield
स्टैक के अपने हिस्से को साफ़ करने और लौटने से पहले बयान को हिट करता है, तो यह अपने स्थानीय चर के स्नैपशॉट लेता है और उन्हें जेनरेटर ऑब्जेक्ट में संग्रहीत करता है। यह उस स्थान को भी लिखता है जहां यह वर्तमान में अपने कोड (यानी विशेष yield
कथन) में है।
तो यह एक तरह का जमे हुए काम है कि जेनरेटर लटक रहा है।
जब next()
बाद में कहा जाता है, यह ढेर पर समारोह का सामान प्राप्त करता है और इसे फिर से एनिमेट। समारोह जहां से छोड़ा गया है, इसकी गणना करने के लिए जारी है, इस तथ्य से अनजान है कि उसने अभी तक ठंडे भंडारण में अनंत काल बिताया है।
निम्नलिखित उदाहरणों की तुलना करें:
def normalFunction():
return
if False:
pass
def yielderFunction():
return
if False:
yield 12
जब हम दूसरे फ़ंक्शन को कॉल करते हैं, तो यह पहले से बहुत अलग व्यवहार करता है। yield
बयान नहीं पहुंचा जा सकता हो सकता है, लेकिन अगर यह कहीं भी मौजूद है, यह हम क्या साथ काम कर रहे की प्रकृति बदल जाता है।
>>> yielderFunction()
<generator object yielderFunction at 0x07742D28>
कॉलिंग yielderFunction()
अपना कोड नहीं चलाती है, लेकिन कोड से जेनरेटर बनाता है। (शायद yielder
पठनीयता के लिए उपसर्ग के साथ ऐसी चीजों का नाम देना एक अच्छा विचार है।)
>>> gen = yielderFunction()
>>> dir(gen)
['__class__',
...
'__iter__', #Returns gen itself, to make it work uniformly with containers
... #when given to a for loop. (Containers return an iterator instead.)
'close',
'gi_code',
'gi_frame',
'gi_running',
'next', #The method that runs the function's body.
'send',
'throw']
gi_code
और gi_frame
क्षेत्रों जहां जमे हुए राज्य संग्रहीत किया जाता है कर रहे हैं। उन्हें खोजकर dir(..)
, हम पुष्टि कर सकते हैं कि ऊपर हमारा मानसिक मॉडल विश्वसनीय है।
संक्षेप में, yield
कथन आपके फ़ंक्शन को ऐसे कारखाने में बदल देता है जो एक विशेष वस्तु उत्पन्न करता है जिसे generator
आपके मूल कार्य के शरीर के चारों ओर लपेटता है। जब generator
पुनरावृत्त किया जाता है, तो यह आपके फ़ंक्शन को तब तक निष्पादित करता है जब तक कि वह अगले तक पहुंच न जाए, yield
फिर निष्पादन को निलंबित कर देता है और पास किए गए मान का मूल्यांकन करता है yield
। यह प्रत्येक पुनरावृत्ति पर इस प्रक्रिया को दोहराता है जब तक कि निष्पादन का मार्ग फ़ंक्शन से बाहर न हो जाए। उदाहरण के लिए,
def simple_generator():
yield 'one'
yield 'two'
yield 'three'
for i in simple_generator():
print i
बस outputs
one
two
three
बिजली जेनरेटर का उपयोग एक लूप के साथ करने से होती है जो एक अनुक्रम की गणना करता है, जनरेटर गणना के अगले परिणाम को 'उपज' करने के लिए प्रत्येक बार लूप को निष्पादित करता है, इस तरह यह फ्लाई पर एक सूची की गणना करता है, लाभ स्मृति है विशेष रूप से बड़ी गणना के लिए बचाया
मान लें कि आप अपना खुद का range
फ़ंक्शन बनाना चाहते हैं जो संख्याओं की एक दुर्लभ श्रृंखला उत्पन्न करता है, आप ऐसा कर सकते हैं,
def myRangeNaive(i):
n = 0
range = []
while n < i:
range.append(n)
n = n + 1
return range
और इस तरह इसका इस्तेमाल करें;
for i in myRangeNaive(10):
print i
लेकिन यह अक्षम है क्योंकि
- आप एक सरणी बनाते हैं जिसे आप केवल एक बार उपयोग करते हैं (यह कचरा स्मृति)
- यह कोड वास्तव में उस सरणी पर दो बार लूप करता है! :(
सौभाग्य से गिडो और उनकी टीम जेनरेटर विकसित करने के लिए उदार थीं ताकि हम ऐसा कर सकें;
def myRangeSmart(i):
n = 0
while n < i:
yield n
n = n + 1
return
for i in myRangeSmart(10):
print i
अब प्रत्येक पुनरावृत्ति पर जेनरेटर पर एक फ़ंक्शन फ़ंक्शन next()
निष्पादित करता है जब तक कि यह या तो 'उपज' कथन तक नहीं पहुंच जाता है जिसमें यह बंद हो जाता है और 'उपज' देता है या फ़ंक्शन के अंत तक पहुंच जाता है। इस मामले में पहली कॉल पर, next()
उपज कथन तक निष्पादित होता है और अगली कॉल पर 'एन' उपज करता है, यह वृद्धि विवरण को निष्पादित करेगा, 'समय' पर वापस कूदें, इसका मूल्यांकन करें, और यदि सही है, तो यह रुक जाएगा और उपज 'एन' फिर से, यह तब तक जारी रहेगा जब तक कि स्थिति शर्त कम न हो और जनरेटर फ़ंक्शन के अंत तक कूद जाए।