जब यह लूप समाप्त हो जाएगा तो पायथन कोई वादा नहीं करता है। पुनरावृत्ति के दौरान एक सेट को संशोधित करने से स्किड तत्व, बार-बार तत्व, और अन्य अजीबता हो सकती है। ऐसे व्यवहार पर कभी भरोसा न करें।
मैं जो कुछ भी कहने जा रहा हूं वह कार्यान्वयन विवरण है, बिना सूचना के परिवर्तन के अधीन। यदि आप ऐसा कोई प्रोग्राम लिखते हैं, जो उसमें से किसी पर निर्भर करता है, तो आपका प्रोग्राम पायथन कार्यान्वयन और सीपीथॉन 3.8.2 के अलावा किसी अन्य संस्करण के संयोजन पर टूट सकता है।
लूप 16 पर समाप्त क्यों होता है, इसके लिए संक्षिप्त विवरण यह है कि 16 पहला तत्व है जो पिछले तत्व की तुलना में कम हैश टेबल इंडेक्स में रखा जाता है। पूरा विवरण नीचे है।
पायथन सेट की आंतरिक हैश तालिका में हमेशा 2 आकार की शक्ति होती है। आकार 2 ^ n की तालिका के लिए, यदि कोई टक्कर नहीं होती है, तो तत्व हैश तालिका में स्थिति में उनके हैश के कम से कम महत्वपूर्ण बिट्स के अनुरूप संग्रहीत होते हैं। आप इसे इसमें लागू देख सकते हैं set_add_entry
:
mask = so->mask;
i = (size_t)hash & mask;
entry = &so->table[i];
if (entry->key == NULL)
goto found_unused;
अधिकांश छोटे अजगर अपने आप को हैश; विशेष रूप से, आपके टेस्ट हैश में सभी स्याही खुद को। आप इसे कार्यान्वित देख सकते हैं long_hash
। चूँकि आपके सेट में कभी भी दो तत्व नहीं होते हैं, जिनके बराबर हैश में कम बिट्स होते हैं, कोई टक्कर नहीं होती है।
एक पायथन सेट इटेटर एक सेट में अपनी स्थिति का ट्रैक सेट के आंतरिक हैश तालिका में एक साधारण पूर्णांक सूचकांक के साथ रखता है। जब अगले तत्व का अनुरोध किया जाता है, तो इट्रेटर उस सूचकांक पर शुरू होने वाली हैश तालिका में एक आबादी वाली प्रविष्टि की खोज करता है, फिर पाया प्रविष्टि के तुरंत बाद अपने संग्रहीत सूचकांक को सेट करता है और प्रविष्टि के तत्व को वापस करता है। आप इसमें देख सकते हैं setiter_iternext
:
while (i <= mask && (entry[i].key == NULL || entry[i].key == dummy))
i++;
si->si_pos = i+1;
if (i > mask)
goto fail;
si->len--;
key = entry[i].key;
Py_INCREF(key);
return key;
आपका सेट शुरू में हैश तालिका 8 के आकार के साथ शुरू होता है, और 0
हैश तालिका में सूचकांक 0 पर एक इंट ऑब्जेक्ट के लिए एक संकेतक । Iterator भी अनुक्रमणिका 0. पर स्थित है। जैसा कि आप पुनरावृति, तत्वों को हैश तालिका में जोड़ा जाता है, प्रत्येक को अगले अनुक्रमणिका में जोड़ा जाता है क्योंकि यहीं पर उनका हैश उन्हें लगाने के लिए कहता है, और यह हमेशा अगला सूचकांक होता है जो पुनरावृत्त देखता है। हटाए गए तत्वों में टकराव संकल्प उद्देश्यों के लिए एक पुरानी मार्कर अपनी पुरानी स्थिति में संग्रहीत होती है। आप देख सकते हैं कि इसमें लागू set_discard_entry
:
entry = set_lookkey(so, key, hash);
if (entry == NULL)
return -1;
if (entry->key == NULL)
return DISCARD_NOTFOUND;
old_key = entry->key;
entry->key = dummy;
entry->hash = -1;
so->used--;
Py_DECREF(old_key);
return DISCARD_FOUND;
जब 4
सेट में जोड़ा जाता है, तो सेट में तत्वों और डमी की संख्या इतनी अधिक हो जाती है कि set_add_entry
एक हैश टेबल पुनर्निर्माण, कॉलिंग को ट्रिगर करता है set_table_resize
:
if ((size_t)so->fill*5 < mask*3)
return 0;
return set_table_resize(so, so->used>50000 ? so->used*2 : so->used*4);
so->used
हैश तालिका में पॉपुलेटेड, गैर-डमी प्रविष्टियों की संख्या 2 है, इसलिए set_table_resize
8 को इसके दूसरे तर्क के रूप में प्राप्त किया जाता है। इस आधार पर, set_table_resize
निर्णय लेता है नए हैश तालिका आकार 16 होना चाहिए:
/* Find the smallest table size > minused. */
/* XXX speed-up with intrinsics */
size_t newsize = PySet_MINSIZE;
while (newsize <= (size_t)minused) {
newsize <<= 1; // The largest possible value is PY_SSIZE_T_MAX + 1.
}
यह हैश तालिका को आकार 16 के साथ फिर से बनाता है। सभी तत्व अभी भी नई हैश तालिका में अपने पुराने अनुक्रमों पर समाप्त होते हैं, क्योंकि उनके पास अपने हैश में सेट किए गए कोई उच्च बिट्स नहीं थे।
जैसे ही लूप जारी रहता है, तत्वों को अगले इंडेक्स पर रखा जाता है जो इट्रेटर दिखेगा। एक और हैश टेबल पुनर्निर्माण शुरू हो गया है, लेकिन नया आकार अभी भी 16 है।
जब एक तत्व के रूप में लूप 16 जोड़ा जाता है तो पैटर्न टूट जाता है। नए तत्व को रखने के लिए कोई सूचकांक 16 नहीं है। 16 के 4 सबसे कम बिट्स 0000 हैं, 16 को इंडेक्स में डालते हैं। 0. इटरेटर का स्टोर किया हुआ इंडेक्स इस बिंदु पर 16 है, और जब लूप पुनरावृत्त से अगले तत्व के लिए पूछता है, तो इटरेटर देखता है कि यह पिछले छोर पर चला गया है हैश टेबल।
16
सेट में केवल छोड़कर, इट्रेटर इस बिंदु पर लूप को समाप्त करता है ।
s.add(i+1)
(और संभवतः कॉल करने के लिएs.remove(i)
) सेट के पुनरावृत्ति क्रम को बदल सकता है, यह प्रभावित करता है कि सेट इटरेटर जो लूप के लिए बनाया गया है, आगे देखेगा। जब आप सक्रिय पुनरावृत्ति करते हैं, तो किसी वस्तु को उत्परिवर्तित न करें।