foreach
मूल्यों के तीन अलग-अलग प्रकारों पर चलना का समर्थन करता है:
निम्नलिखित में, मैं ठीक से समझाने की कोशिश करूंगा कि विभिन्न मामलों में पुनरावृत्ति कैसे काम करती है। अब तक सबसे सरल मामला Traversable
वस्तुओं का है, क्योंकि foreach
इन पंक्तियों के लिए कोड के लिए अनिवार्य रूप से केवल सिंटेक्स चीनी है:
foreach ($it as $k => $v) { /* ... */ }
/* translates to: */
if ($it instanceof IteratorAggregate) {
$it = $it->getIterator();
}
for ($it->rewind(); $it->valid(); $it->next()) {
$v = $it->current();
$k = $it->key();
/* ... */
}
आंतरिक कक्षाओं के लिए, वास्तविक एपीआई को आंतरिक एपीआई का उपयोग करके टाल दिया जाता है जो अनिवार्य रूप Iterator
से सी स्तर पर इंटरफ़ेस को प्रतिबिंबित करता है।
सरणियों और सादे वस्तुओं का वर्गीकरण काफी अधिक जटिल है। सबसे पहले, यह ध्यान दिया जाना चाहिए कि PHP में "सरणियों" को वास्तव में शब्दकोशों का आदेश दिया गया है और उन्हें इस आदेश के अनुसार ट्रेस किया जाएगा (जो सम्मिलन क्रम से मेल खाती है जब तक कि आप कुछ का उपयोग नहीं करते sort
)। यह कुंजियों के प्राकृतिक क्रम (अन्य भाषाओं में सूचियाँ कैसे काम करती हैं) या इसमें कोई परिभाषित क्रम नहीं होने के कारण इसका विरोध किया जाता है (कैसे अन्य भाषाओं में शब्द अक्सर काम करते हैं)।
यही बात वस्तुओं पर भी लागू होती है, क्योंकि वस्तु के गुणों को उनके मूल्यों के लिए एक और (ऑर्डर किया गया) शब्दकोश मानचित्रण संपत्ति के नाम के रूप में देखा जा सकता है, साथ ही कुछ दृश्यता हैंडलिंग भी। अधिकांश मामलों में, ऑब्जेक्ट गुण वास्तव में इस अक्षम तरीके से संग्रहीत नहीं होते हैं। हालाँकि, यदि आप किसी ऑब्जेक्ट पर चलना शुरू करते हैं, तो सामान्य रूप से उपयोग किए जाने वाले पैक्ड प्रतिनिधित्व को एक वास्तविक शब्दकोश में बदल दिया जाएगा। उस समय, सादे वस्तुओं का पुनरावृत्ति सरणियों के पुनरावृत्ति के समान हो जाता है (यही कारण है कि मैं यहाँ सादा वस्तु पुनरावृत्ति पर चर्चा नहीं कर रहा हूँ)।
अब तक सब ठीक है। एक शब्द से अधिक उच्चारण करना बहुत कठिन नहीं है, है ना? समस्याएं तब शुरू होती हैं जब आपको पता चलता है कि पुनरावृत्ति के दौरान एक सरणी / वस्तु बदल सकती है। इसके कई तरीके हो सकते हैं:
- यदि आप संदर्भ का उपयोग करके पुनरावृति करते हैं
foreach ($arr as &$v)
तो संदर्भ $arr
में बदल जाता है और आप इसे पुनरावृत्ति के दौरान बदल सकते हैं।
- PHP 5 में भी यही बात लागू होती है, भले ही आप मूल्य से पुनरावृति करते हों, लेकिन सरणी पहले से एक संदर्भ थी:
$ref =& $arr; foreach ($ref as $v)
- वस्तुओं के पास पासिंग शब्दार्थ होते हैं, जो अधिकांश व्यावहारिक उद्देश्यों के लिए इसका मतलब है कि वे संदर्भों की तरह व्यवहार करते हैं। इसलिए वस्तुओं को हमेशा पुनरावृत्ति के दौरान बदला जा सकता है।
पुनरावृत्ति के दौरान संशोधनों की अनुमति के साथ समस्या वह मामला है जहां आप जिस तत्व पर वर्तमान में हैं उसे हटा दिया गया है। कहते हैं कि आप वर्तमान में किस सरणी तत्व पर नज़र रखने के लिए एक पॉइंटर का उपयोग करते हैं। यदि यह तत्व अब मुक्त हो गया है, तो आप एक झूलने वाले पॉइंटर के साथ छोड़ दिए जाते हैं (आमतौर पर सीगफॉल्ट के परिणामस्वरूप)।
इस मुद्दे को हल करने के विभिन्न तरीके हैं। PHP 5 और PHP 7 इस संबंध में काफी भिन्न हैं और मैं निम्नलिखित में दोनों व्यवहारों का वर्णन करूँगा। सारांश यह है कि PHP 5 का दृष्टिकोण गूंगा था और सभी प्रकार के अजीब धार वाले मुद्दों के लिए नेतृत्व करता था, जबकि PHP 7 के अधिक शामिल दृष्टिकोण से अधिक पूर्वानुमान और सुसंगत व्यवहार होता है।
अंतिम प्रारंभिक के रूप में, यह ध्यान दिया जाना चाहिए कि PHP संदर्भ गणना और मेमोरी को प्रबंधित करने के लिए कॉपी-ऑन-राइट का उपयोग करता है। इसका मतलब यह है कि यदि आप किसी मूल्य को "कॉपी" करते हैं, तो आप वास्तव में पुराने मूल्य का पुन: उपयोग करते हैं और इसकी संदर्भ संख्या (प्रतिफल) बढ़ाते हैं। केवल एक बार जब आप किसी प्रकार का संशोधन करते हैं तो एक वास्तविक प्रतिलिपि (जिसे "दोहराव" कहा जाता है) हो जाएगी। देखें कि आप इस विषय पर अधिक व्यापक परिचय के लिए झूठ बोले जा रहे हैं।
PHP 5
आंतरिक सरणी सूचक और हैशपॉइंट
PHP 5 में एरर्स में एक समर्पित "आंतरिक सरणी पॉइंटर" (IAP) है, जो संशोधनों का ठीक से समर्थन करता है: जब भी कोई तत्व हटा दिया जाता है, तो एक जाँच होगी कि क्या IAP इस तत्व को इंगित करता है। यदि ऐसा होता है, तो इसके बजाय अगले तत्व के लिए उन्नत है।
foreach
आईएपी का उपयोग करते समय , एक अतिरिक्त जटिलता है: केवल एक आईएपी है, लेकिन एक सरणी कई foreach
लूप का हिस्सा हो सकती है:
// Using by-ref iteration here to make sure that it's really
// the same array in both loops and not a copy
foreach ($arr as &$v1) {
foreach ($arr as &$v) {
// ...
}
}
केवल एक आंतरिक सरणी पॉइंटर के साथ दो एक साथ लूप्स का समर्थन करने के लिए, foreach
निम्नलिखित शनीगैन करता है: लूप बॉडी को निष्पादित करने से पहले, foreach
एक पॉइंटर को वर्तमान तत्व में वापस कर दिया जाएगा और इसके हैश को प्रति-फ़ॉर्च में लाया जाएगा HashPointer
। लूप बॉडी के चलने के बाद, यदि यह अभी भी मौजूद है, तो IAP को इस तत्व पर वापस सेट किया जाएगा। यदि तत्व को हटा दिया गया है, तो हम अभी जहां भी IAP वर्तमान में उपयोग करेंगे। यह योजना ज्यादातर तरह-तरह के काम करती है, लेकिन बहुत सारे अजीब व्यवहार हैं जो आप इससे बाहर निकल सकते हैं, जिनमें से कुछ मैं नीचे प्रदर्शित करूंगा।
एरे डुप्लीकेशन
IAP एक सरणी ( current
फ़ंक्शन के परिवार के माध्यम से उजागर ) की एक दृश्यमान विशेषता है , जैसे कि IAP की गिनती कॉपी-ऑन-राइट सेमेंटिक्स के तहत संशोधनों के रूप में होती है। यह, दुर्भाग्य से, इसका मतलब यह foreach
है कि कई मामलों में यह सरणी को डुप्लिकेट करने के लिए मजबूर किया जाता है। सटीक शर्तें हैं:
- सरणी एक संदर्भ नहीं है (is_ref = 0)। यदि यह एक संदर्भ है, तो बदल जाता है करने के लिए यह कर रहे हैं चाहिए प्रचार करने के लिए, तो यह दोहराया नहीं जाना चाहिए।
- सरणी में refcount> 1 है। यदि
refcount
1 है, तो सरणी साझा नहीं की जाती है और हम इसे सीधे संशोधित करने के लिए स्वतंत्र हैं।
यदि सरणी को डुप्लिकेट नहीं किया गया है (is_ref = 0, refcount = 1), तो केवल इसका refcount
वेतन वृद्धि (*) होगी। इसके अतिरिक्त, यदि foreach
संदर्भ द्वारा उपयोग किया जाता है, तो (संभावित डुप्लिकेट) सरणी को संदर्भ में बदल दिया जाएगा।
इस कोड को एक उदाहरण के रूप में देखें जहां दोहराव होता है:
function iterate($arr) {
foreach ($arr as $v) {}
}
$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
यहां, $arr
IAP परिवर्तनों को $arr
लीक होने से रोकने के लिए डुप्लिकेट किया जाएगा $outerArr
। उपरोक्त शर्तों के संदर्भ में, सरणी एक संदर्भ नहीं है (is_ref = 0) और इसका उपयोग दो स्थानों पर किया जाता है (refcount 2)। यह आवश्यकता दुर्भाग्यपूर्ण है और उप-अपनाने के कार्यान्वयन की एक कलाकृति है (यहाँ पुनरावृत्ति के दौरान संशोधन की कोई चिंता नहीं है, इसलिए हमें वास्तव में पहली जगह में IAP का उपयोग करने की आवश्यकता नहीं है)।
(*) refcount
यहाँ बढ़ाना अहानिकर लगता है, लेकिन कॉपी-ऑन-राइट (COW) शब्दार्थ का उल्लंघन करता है: इसका मतलब है कि हम एक refcount = 2 सरणी के IAP को संशोधित करने जा रहे हैं, जबकि गाय का कहना है कि संशोधन केवल refcount = पर किए जा सकते हैं 1 मान। यह उल्लंघन उपयोगकर्ता-दृश्यमान व्यवहार परिवर्तन (जबकि एक सामान्य रूप से पारदर्शी है) में परिणाम होता है क्योंकि पुनरावृत्त सरणी पर IAP परिवर्तन देखने योग्य होगा - लेकिन केवल सरणी पर पहले गैर-IAP संशोधन तक। इसके बजाय, तीन "वैध" विकल्प हमेशा a (डुप्लिकेट) होते, b) वृद्धि नहीं करते हैं refcount
और इस प्रकार पुनरावृत्त सरणी को मनमाने ढंग से लूप या c में संशोधित करने की अनुमति देते हैं) IAP का उपयोग बिल्कुल भी न करें (PHP) 7 समाधान)।
स्थिति उन्नति क्रम
एक अंतिम कार्यान्वयन विवरण है जिसे आपको नीचे दिए गए कोड नमूनों को ठीक से समझने के लिए जागरूक होना होगा। कुछ डेटा संरचना के माध्यम से लूपिंग का "सामान्य" तरीका छद्मकोड में कुछ इस तरह दिखाई देगा:
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
code();
move_forward(arr);
}
हालांकि foreach
, बल्कि एक विशेष हिमपात का एक खंड होने के नाते, चीजों को थोड़ा अलग तरीके से करने का विकल्प चुनता है:
reset(arr);
while (get_current_data(arr, &data) == SUCCESS) {
move_forward(arr);
code();
}
स्वचालित रूप से, सरणी सूचक लूप बॉडी के चलने से पहले ही आगे बढ़ जाता है । इसका मतलब यह है कि जब लूप बॉडी तत्व पर काम कर रहा है $i
, तो IAP पहले से ही तत्व में है $i+1
। यही वजह है कि कोड नमूने यात्रा के दौरान संशोधन दिखा है हमेशा अगले तत्व के बजाय वर्तमान एक।unset
उदाहरण: आपके परीक्षण के मामले
ऊपर वर्णित तीन पहलुओं को आपको foreach
कार्यान्वयन की अज्ञातताओं के बारे में पूरी तरह से छाप देना चाहिए और हम कुछ उदाहरणों पर चर्चा करने के लिए आगे बढ़ सकते हैं।
इस बिंदु पर समझाने के लिए आपके परीक्षण मामलों का व्यवहार सरल है:
परीक्षण के मामलों में 1 और 2 $array
प्रतिफल = 1 से शुरू होता है, इसलिए इसे इसके द्वारा दोहराया नहीं जाएगा foreach
: केवल refcount
वृद्धि होती है। जब लूप बॉडी बाद में सरणी को संशोधित करता है (जिसमें उस बिंदु पर Refcount = 2 है), तो उस बिंदु पर दोहराव होगा। Foreach की अनमॉडिफाइड कॉपी पर काम करना जारी रखेगा $array
।
टेस्ट केस 3 में, एक बार फिर एरे को डुप्लिकेट नहीं किया जाता है, इस प्रकार foreach
यह $array
वेरिएबल के IAP को संशोधित करेगा । पुनरावृत्ति के अंत में, IAP NULL है (इसका अर्थ है कि पुनरावृत्ति ने किया है), जो each
कि लौटने से इंगित करता है false
।
परीक्षण के मामलों में 4 और 5 दोनों हैं each
और reset
उप-संदर्भ फ़ंक्शन हैं। $array
एक है refcount=2
जब यह, उन से पारित हो जाता है तो यह दोहराया जाना चाहिए। जैसे कि foreach
एक अलग सरणी पर फिर से काम करना होगा।
उदाहरण: current
फोर्क में प्रभाव
विभिन्न दोहराव व्यवहारों को दिखाने का एक अच्छा तरीका current()
एक foreach
लूप के अंदर फ़ंक्शन के व्यवहार का निरीक्षण करना है । इस उदाहरण पर विचार करें:
foreach ($array as $val) {
var_dump(current($array));
}
/* Output: 2 2 2 2 2 */
यहां आपको पता होना चाहिए कि current()
यह एक बाय-रिफ फ़ंक्शन है (वास्तव में: पसंद-रेफ), भले ही यह सरणी को संशोधित नहीं करता है। यह अन्य सभी कार्यों के साथ अच्छा खेलने के लिए होना चाहिए, जैसे next
कि सभी उप-रेफ। बाय-रेफरेंस पासिंग का मतलब है कि एरे को अलग करना होगा और इस तरह $array
और foreach-array
वसीयत अलग होगी। इसके 2
बजाय आपको जो कारण मिलता है 1
वह भी ऊपर उल्लेखित है: उपयोगकर्ता कोड को चलाने से पहलेforeach
सरणी सूचक को आगे बढ़ाता है , बाद में नहीं। तो भले ही कोड पहले तत्व पर है, पहले से ही सूचक को दूसरे में उन्नत किया।foreach
अब एक छोटे से संशोधन की कोशिश करते हैं:
$ref = &$array;
foreach ($array as $val) {
var_dump(current($array));
}
/* Output: 2 3 4 5 false */
यहां हमारे पास is_ref = 1 मामला है, इसलिए सरणी की प्रतिलिपि नहीं बनाई गई है (बस ऊपर की तरह)। लेकिन अब जब यह एक संदर्भ है, तो सरणी को उप-रेफ current()
फ़ंक्शन में पास होने पर दोहराया नहीं जाना है । इस प्रकार current()
और foreach
उसी सरणी पर काम करते हैं। आप अभी भी ऑफ-द-वन व्यवहार देखते हैं, हालांकि, foreach
पॉइंटर को आगे बढ़ाने के तरीके के कारण ।
पुनरावृत्ति करते समय आपको समान व्यवहार मिलता है:
foreach ($array as &$val) {
var_dump(current($array));
}
/* Output: 2 3 4 5 false */
यहां महत्वपूर्ण हिस्सा यह है कि $array
जब यह संदर्भ द्वारा पुनरावृत्त होता है, तो foreach is_ref = 1 बना देगा , इसलिए मूल रूप से आपके पास ऊपर जैसी स्थिति है।
एक और छोटा बदलाव, इस बार हम दूसरे वेरिएबल में ऐरे को असाइन करेंगे:
$foo = $array;
foreach ($array as $val) {
var_dump(current($array));
}
/* Output: 1 1 1 1 1 */
यहां $array
लूप शुरू होने पर 2 का रिफंड होता है, इसलिए एक बार के लिए हमें वास्तव में डुप्लिकेट अपफ्रंट करना होगा। इस प्रकार $array
, फ़ॉर्च द्वारा उपयोग किए जाने वाले सरणी को शुरू से पूरी तरह से अलग किया जाएगा। इसीलिए आपको लूप से पहले जहां भी IAP की स्थिति मिलती है (इस मामले में यह पहले स्थान पर था)।
उदाहरण: पुनरावृत्ति के दौरान संशोधन
पुनरावृत्ति के दौरान संशोधनों के लिए खाते में लाने की कोशिश करना हमारी सभी परेशानियों की उत्पत्ति है, इसलिए यह इस मामले के लिए कुछ उदाहरणों पर विचार करता है।
एक ही सरणी पर इन नीडिंत छोरों पर विचार करें (जहां बाय-रेफ पुनरावृत्ति का उपयोग यह सुनिश्चित करने के लिए किया जाता है कि यह वास्तव में एक ही है):
foreach ($array as &$v1) {
foreach ($array as &$v2) {
if ($v1 == 1 && $v2 == 1) {
unset($array[1]);
}
echo "($v1, $v2)\n";
}
}
// Output: (1, 1) (1, 3) (1, 4) (1, 5)
यहां अपेक्षित भाग (1, 2)
आउटपुट से गायब है क्योंकि तत्व 1
को हटा दिया गया था। संभवतः यह अप्रत्याशित है कि पहले तत्व के बाद बाहरी लूप बंद हो जाता है। ऐसा क्यों है?
इसके पीछे का कारण ऊपर वर्णित नेस्टेड-लूप हैक है: लूप बॉडी के चलने से पहले, वर्तमान IAP स्थिति और हैश का समर्थन किया जाता है HashPointer
। लूप बॉडी के बाद इसे बहाल किया जाएगा, लेकिन केवल अगर तत्व अभी भी मौजूद है, अन्यथा इसके बजाय वर्तमान IAP स्थिति (जो भी हो सकती है) का उपयोग किया जाता है। ऊपर दिए गए उदाहरण में ठीक यही स्थिति है: बाहरी लूप का वर्तमान तत्व हटा दिया गया है, इसलिए यह IAP का उपयोग करेगा, जिसे पहले से ही आंतरिक लूप द्वारा समाप्त के रूप में चिह्नित किया गया है!
HashPointer
बैकअप + पुनर्स्थापना तंत्र का एक और परिणाम यह है कि आईएपी के माध्यम से परिवर्तन reset()
आदि आमतौर पर प्रभावित नहीं करते हैं foreach
। उदाहरण के लिए, निम्न कोड निष्पादित करता है जैसे कि reset()
बिल्कुल मौजूद नहीं थे:
$array = [1, 2, 3, 4, 5];
foreach ($array as &$value) {
var_dump($value);
reset($array);
}
// output: 1, 2, 3, 4, 5
कारण यह है कि, reset()
अस्थायी रूप से IAP को संशोधित करते हुए , यह लूप बॉडी के बाद वर्तमान फ़ॉरच्यू तत्व में पुनर्स्थापित किया जाएगा। reset()
लूप पर एक प्रभाव बनाने के लिए मजबूर करने के लिए, आपको अतिरिक्त रूप से वर्तमान तत्व को निकालना होगा, ताकि बैकअप / पुनर्स्थापना तंत्र विफल हो जाए:
$array = [1, 2, 3, 4, 5];
$ref =& $array;
foreach ($array as $value) {
var_dump($value);
unset($array[1]);
reset($array);
}
// output: 1, 1, 3, 4, 5
लेकिन, वे उदाहरण अभी भी समझदार हैं। असली मज़ा तब शुरू होता है जब आपको याद आता है कि HashPointer
पुनर्स्थापना तत्व और उसके हैश का संकेत देता है कि क्या यह अभी भी मौजूद है। लेकिन: हैश में टकराव होते हैं, और संकेत का पुन: उपयोग किया जा सकता है! इसका मतलब यह है कि, सरणी कुंजियों के सावधानीपूर्वक चयन के साथ, हम यह foreach
विश्वास कर सकते हैं कि एक तत्व जो अभी भी हटा दिया गया है, वह अभी भी मौजूद है, इसलिए यह सीधे उस पर कूद जाएगा। एक उदाहरण:
$array = ['EzEz' => 1, 'EzFY' => 2, 'FYEz' => 3];
$ref =& $array;
foreach ($array as $value) {
unset($array['EzFY']);
$array['FYFY'] = 4;
reset($array);
var_dump($value);
}
// output: 1, 4
यहां हमें सामान्य रूप 1, 1, 3, 4
से पिछले नियमों के अनुसार आउटपुट की उम्मीद करनी चाहिए । कैसे होता है कि 'FYFY'
हटाए गए तत्व के समान हैश होता है 'EzFY'
, और आवंटनकर्ता तत्व को संग्रहीत करने के लिए समान मेमोरी स्थान का पुन: उपयोग करने के लिए होता है। इसलिए फॉर्च्यूस सीधे नए सम्मिलित तत्व पर कूदता है, इस प्रकार लूप को शॉर्ट-कट करता है।
लूप के दौरान पुनरावृत्त इकाई को प्रतिस्थापित करना
एक आखिरी अजीब मामला जिसका मैं उल्लेख करना चाहूंगा, वह यह है कि PHP आपको लूप के दौरान पुनरावृत्त इकाई को प्रतिस्थापित करने की अनुमति देता है। तो आप एक एरे पर चलना शुरू कर सकते हैं और फिर इसे दूसरे ऐरे से आधे रास्ते से बदल सकते हैं। या किसी सरणी पर पुनरावृति शुरू करें और फिर उसे किसी ऑब्जेक्ट से बदलें:
$arr = [1, 2, 3, 4, 5];
$obj = (object) [6, 7, 8, 9, 10];
$ref =& $arr;
foreach ($ref as $val) {
echo "$val\n";
if ($val == 3) {
$ref = $obj;
}
}
/* Output: 1 2 3 6 7 8 9 10 */
जैसा कि आप इस मामले में देख सकते हैं कि एक बार प्रतिस्थापन होने के बाद ही PHP दूसरी इकाई को शुरू करना शुरू कर देगी।
PHP 7
हैशटेबल पुनरावृत्तियों
यदि आपको अभी भी याद है, तो सरणी पुनरावृत्ति के साथ मुख्य समस्या यह थी कि तत्वों को हटाने के लिए मध्य-पुनरावृत्ति को कैसे प्रबंधित किया जाए। PHP 5 ने इस उद्देश्य के लिए एक एकल आंतरिक सरणी पॉइंटर (IAP) का उपयोग किया, जो कुछ हद तक उप-रूपी था, क्योंकि एक सरणी पॉइंटर को एक साथ कई फ़ॉरच्युन लूप और उसके साथ बातचीत reset()
आदि का समर्थन करने के लिए बढ़ाया जाना था ।
PHP 7 एक अलग दृष्टिकोण का उपयोग करता है, अर्थात्, यह बाहरी, सुरक्षित हैशटेबल पुनरावृत्तियों की एक मनमानी राशि बनाने का समर्थन करता है। इन पुनरावृत्तियों को सरणी में पंजीकृत किया जाना है, जिस बिंदु से उनके पास आईएपी के समान शब्दार्थ है: यदि कोई सरणी तत्व हटा दिया जाता है, तो उस तत्व की ओर इशारा करने वाले सभी हैशटेबल पुनरावृत्त अगले तत्व के लिए उन्नत होंगे।
इसका मतलब है कि foreach
अब IAP का उपयोग बिल्कुल नहीं किया जाएगा । foreach
पाश के परिणामों पर बिल्कुल कोई असर होगा current()
आदि और अपने स्वयं के व्यवहार जैसे कार्यों से कभी नहीं प्रभावित होगा reset()
आदि
एरे डुप्लीकेशन
PHP 5 और PHP 7 के बीच एक और महत्वपूर्ण बदलाव सरणी दोहराव से संबंधित है। अब जब IAP का उपयोग नहीं किया जाता है, तो refcount
सभी मामलों में बाय-वैल्यू एरेशन केवल एक वृद्धि (डुप्लिकेट एरे की जगह) ही करेगा । यदि foreach
लूप के दौरान सरणी को संशोधित किया गया है, तो उस बिंदु पर एक दोहराव घटित होगा (कॉपी-ऑन-राइट के अनुसार) और foreach
पुराने सरणी पर काम करना जारी रखेगा।
ज्यादातर मामलों में, यह परिवर्तन पारदर्शी है और बेहतर प्रदर्शन के अलावा इसका कोई अन्य प्रभाव नहीं है। हालाँकि, एक ऐसा अवसर होता है, जहाँ इसका परिणाम भिन्न व्यवहार के रूप में होता है, अर्थात् जहाँ मामला पहले एक संदर्भ था:
$array = [1, 2, 3, 4, 5];
$ref = &$array;
foreach ($array as $val) {
var_dump($val);
$array[2] = 0;
}
/* Old output: 1, 2, 0, 4, 5 */
/* New output: 1, 2, 3, 4, 5 */
पूर्व में संदर्भ-सरणियों का मूल्य-निर्धारण पुनरावृत्ति विशेष मामले थे। इस मामले में, कोई दोहराव नहीं हुआ, इसलिए पुनरावृति के दौरान सरणी के सभी संशोधन लूप द्वारा परिलक्षित होंगे। PHP 7 में यह विशेष मामला चला गया है: किसी सरणी का बाय-वैल्यू चलना हमेशा मूल तत्वों पर काम करता रहेगा , लूप के दौरान किसी भी संशोधन की उपेक्षा करता है।
यह, निश्चित रूप से, उप-संदर्भ पुनरावृत्ति पर लागू नहीं होता है। यदि आप संदर्भ के अनुसार इसे संशोधित करते हैं तो सभी संशोधन लूप द्वारा परिलक्षित होंगे। दिलचस्प बात यह है कि सादे वस्तुओं के मूल्य-निर्धारण पुनरावृत्ति के लिए भी यही सच है:
$obj = new stdClass;
$obj->foo = 1;
$obj->bar = 2;
foreach ($obj as $val) {
var_dump($val);
$obj->bar = 42;
}
/* Old and new output: 1, 42 */
यह वस्तुओं के उप-संभाल शब्दार्थ को दर्शाता है (यानी वे संदर्भ-जैसे व्यवहार को भी-मूल्य संदर्भों में दर्शाते हैं)।
उदाहरण
आइए कुछ उदाहरणों पर विचार करें, जो आपके परीक्षण मामलों से शुरू होते हैं:
टेस्ट केस 1 और 2 एक ही आउटपुट को बनाए रखते हैं: बाय-वैल्यू ऐरे इट्रेशन हमेशा मूल तत्वों पर काम करते रहते हैं। (इस मामले में, यहां तक कि refcounting
और दोहराव का व्यवहार PHP 5 और PHP 7 के बीच बिल्कुल समान है)।
टेस्ट केस 3 में बदलाव: Foreach
आईएपी का उपयोग नहीं करता है, इसलिए each()
लूप से प्रभावित नहीं होता है। यह पहले और बाद में एक ही आउटपुट होगा।
परीक्षण के मामले 4 और 5 समान रहते हैं: each()
और reset()
IAP को बदलने से पहले सरणी को डुप्लिकेट करेगा, जबकि foreach
अभी भी मूल सरणी का उपयोग करता है। (ऐसा नहीं है कि IAP परिवर्तन मायने रखता था, भले ही सरणी साझा की गई थी।)
उदाहरणों का दूसरा सेट current()
विभिन्न reference/refcounting
विन्यासों के तहत व्यवहार से संबंधित था । यह अब समझ में नहीं आता है, जैसा current()
कि लूप द्वारा पूरी तरह से अप्रभावित है, इसलिए इसका वापसी मूल्य हमेशा एक जैसा रहता है।
हालाँकि, पुनरावृत्ति के दौरान संशोधनों पर विचार करने पर हमें कुछ दिलचस्प बदलाव मिलते हैं। मुझे आशा है कि आप नए व्यवहार को पा लेंगे। पहला उदाहरण:
$array = [1, 2, 3, 4, 5];
foreach ($array as &$v1) {
foreach ($array as &$v2) {
if ($v1 == 1 && $v2 == 1) {
unset($array[1]);
}
echo "($v1, $v2)\n";
}
}
// Old output: (1, 1) (1, 3) (1, 4) (1, 5)
// New output: (1, 1) (1, 3) (1, 4) (1, 5)
// (3, 1) (3, 3) (3, 4) (3, 5)
// (4, 1) (4, 3) (4, 4) (4, 5)
// (5, 1) (5, 3) (5, 4) (5, 5)
जैसा कि आप देख सकते हैं, बाहरी लूप अब पहले पुनरावृत्ति के बाद गर्भपात नहीं करता है। कारण यह है कि दोनों छोरों में अब पूरी तरह से अलग-अलग हैशटेबल पुनरावृत्तियां हैं, और साझा आईएपी के माध्यम से दोनों छोरों का कोई क्रॉस-संदूषण नहीं है।
एक और अजीब बढ़त का मामला जो अब तय हो गया है, जब आप हटाते हैं और एक ही हैश के साथ होने वाले तत्वों को जोड़ते हैं तो आपको मिलने वाला अजीब प्रभाव होता है:
$array = ['EzEz' => 1, 'EzFY' => 2, 'FYEz' => 3];
foreach ($array as &$value) {
unset($array['EzFY']);
$array['FYFY'] = 4;
var_dump($value);
}
// Old output: 1, 4
// New output: 1, 3, 4
पहले HashPointer पुनर्स्थापना तंत्र ने नए तत्व के लिए सही छलांग लगाई क्योंकि यह "देखा" था जैसे कि हटाए गए तत्व (टकराने वाले हैश और पॉइंटर के कारण) के समान है। जैसा कि अब हम किसी चीज़ के लिए तत्व हैश पर निर्भर नहीं हैं, यह अब एक मुद्दा नहीं है।