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সি স্তরের ইন্টারফেসটি মিরর করে ।
অ্যারে এবং প্লেইন অবজেক্টগুলির আইট্রেটেশন উল্লেখযোগ্যভাবে আরও জটিল। প্রথমত, এটি লক্ষ করা উচিত যে পিএইচপি "অ্যারে "গুলিতে সত্যই অর্ডার করা অভিধানগুলি থাকে এবং সেগুলি এই আদেশ অনুসারে ট্র্যাভার্স করা হবে (যা সন্নিবেশকরণ ক্রমের সাথে মেলে যতক্ষণ না আপনি কিছু ব্যবহার করেন নি sort)। এটি কীগুলির প্রাকৃতিক ক্রম দ্বারা পুনরাবৃত্তি (অন্য ভাষাগুলির তালিকাগুলি প্রায়শই কীভাবে কাজ করে) বা কোনও সংজ্ঞায়িত আদেশ না পেয়ে (অন্য ভাষাগুলির অভিধানগুলি প্রায়শই কীভাবে কাজ করে) এর বিরোধিতা করে।
এটি একই সাথে বস্তুর ক্ষেত্রেও প্রযোজ্য, কারণ বস্তুর বৈশিষ্ট্যগুলিকে অন্য মানের (অর্ডারযুক্ত) অভিধান হিসাবে তাদের মানগুলিতে সম্পত্তির নাম ম্যাপিং হিসাবে দেখা যায়, পাশাপাশি কিছু দৃশ্যমানতা হ্যান্ডলিংও দেখা যায়। বেশিরভাগ ক্ষেত্রে, বস্তুর বৈশিষ্ট্যগুলি আসলে এটি বরং অকার্যকর উপায়ে সংরক্ষণ করা হয়। যাইহোক, আপনি যদি কোনও জিনিসের মাধ্যমে পুনরাবৃত্তি শুরু করেন, সাধারণত ব্যবহৃত হয় এমন প্যাক উপস্থাপনা একটি বাস্তব অভিধানে রূপান্তরিত হবে। এই মুহুর্তে, সরল বস্তুর পুনরাবৃত্তি অ্যারেগুলির পুনরাবৃত্তির সাথে খুব মিল হয়ে যায় (এজন্য আমি এখানে প্লেইন-অবজেক্টের পুনরাবৃত্তি নিয়ে বেশি আলোচনা করছি না)।
এ পর্যন্ত সব ঠিকই. ডিকশনারি নিয়ে আইট্রেট করা খুব বেশি শক্ত হতে পারে না, তাই না? সমস্যাগুলি তখন শুরু হয় যখন আপনি বুঝতে পারবেন যে পুনরাবৃত্তির সময় কোনও অ্যারে / অবজেক্ট পরিবর্তন হতে পারে। এটি ঘটতে পারে এমন একাধিক উপায় রয়েছে:
- আপনি যদি রেফারেন্স ব্যবহার করে পুনরাবৃত্তি করেন
foreach ($arr as &$v)তবে $arrতা একটি রেফারেন্সে পরিণত হয় এবং আপনি পুনরাবৃত্তির সময় এটি পরিবর্তন করতে পারেন।
- পিএইচপি 5 এ একই প্রয়োগ হয় এমনকি যদি আপনি মান দ্বারা পুনরাবৃত্তি করেন তবে অ্যারেটি আগেই একটি রেফারেন্স ছিল:
$ref =& $arr; foreach ($ref as $v)
- অবজেক্টগুলির বাই-হ্যান্ডেল পাসিং শব্দার্থক শব্দ রয়েছে, যার বেশিরভাগ ব্যবহারিক উদ্দেশ্যটির অর্থ তারা রেফারেন্সের মতো আচরণ করে। সুতরাং পুনরাবৃত্তির সময় অবজেক্টগুলি সর্বদা পরিবর্তন করা যায়।
পুনরাবৃত্তি চলাকালীন পরিবর্তনের অনুমতি দেওয়ার ক্ষেত্রে সমস্যাটি হ'ল আপনি বর্তমানে যে উপাদানটি চালু করছেন তা সরানো হয়েছে। বলুন আপনি বর্তমানে কোন অ্যারে উপাদানটিতে রয়েছেন তার খোঁজ রাখতে আপনি একটি পয়েন্টার ব্যবহার করেন। যদি এই উপাদানটি এখন মুক্ত হয় তবে আপনার ঝুঁকির পয়েন্টার (সাধারণত সেগফল্টের ফলে) থাকে।
এই সমস্যাটি সমাধানের বিভিন্ন উপায় রয়েছে। পিএইচপি 5 এবং পিএইচপি 7 এই ক্ষেত্রে উল্লেখযোগ্যভাবে পৃথক এবং আমি নিম্নলিখিত দুটি পদ্ধতি ব্যবহার করব। সংক্ষিপ্তসারটি হ'ল পিএইচপি 5 এর পদ্ধতির বদলে বোবা ছিল এবং সব ধরণের অদ্ভুত প্রান্তের সমস্যাগুলির দিকে নিয়ে যায়, যখন পিএইচপি 7 এর আরও জড়িত পদ্ধতির ফলে আরও অনুমানযোগ্য এবং ধারাবাহিক আচরণের ফলাফল হয়।
শেষ প্রাথমিক হিসাবে, এটি লক্ষ করা উচিত যে পিএইচপি মেমরি পরিচালনা করতে রেফারেন্স গণনা এবং অনুলিপি-অনুলিপি ব্যবহার করে। এর অর্থ হ'ল আপনি যদি কোনও মান "অনুলিপি" করেন তবে আপনি কেবলমাত্র পুরানো মানটি পুনরায় ব্যবহার করুন এবং এর রেফারেন্স গণনাটি (পুনঃনিরোধক) বৃদ্ধি করুন। আপনি একবার কোনও ধরণের পরিবর্তন সম্পাদন করলেই একটি আসল অনুলিপি ("ডুপ্লিকেশন" নামে পরিচিত) সম্পন্ন হবে। দেখুন এই বিষয়ে আরও বিস্তৃত পরিচিতির জন্য আপনাকে মিথ্যা বলা হচ্ছে ।
পিএইচপি 5
অভ্যন্তরীণ অ্যারে পয়েন্টার এবং হ্যাশপয়েন্টার
পিএইচপি 5-এ অ্যারেগুলির একটি উত্সর্গীকৃত "অভ্যন্তরীণ অ্যারে পয়েন্টার" (আইএপি) রয়েছে, যা সঠিকভাবে পরিবর্তনগুলিকে সমর্থন করে: যখনই কোনও উপাদান সরানো হয়, আইএপি এই উপাদানটির দিকে নির্দেশ করে কিনা তা পরীক্ষা করা হবে। যদি এটি হয় তবে এর পরিবর্তে এটি পরবর্তী উপাদানটিতে উন্নত।
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। লুপ বডিটি চলার পরে, আইএপি এখনও বিদ্যমান থাকলে এই উপাদানটিতে ফিরে আসবে। তবে যদি উপাদানটি অপসারণ করা হয়, তবে আইএপি বর্তমানে যেখানে রয়েছে সেখানে আমরা কেবল ব্যবহার করব। এই স্কিমটি বেশিরভাগ ধরণের কাজের ধরণের, তবে আপনি এটি থেকে বেরিয়ে আসতে পারেন এমন প্রচুর অদ্ভুত আচরণ রয়েছে যার মধ্যে কয়েকটি আমি নীচে প্রদর্শন করব।
অ্যারের সদৃশ
আইএপি হ'ল একটি অ্যারের দৃশ্যমান বৈশিষ্ট্য ( currentফাংশনগুলির পরিবারের মাধ্যমে প্রকাশিত ), আইএপি গণনাতে যেমন পরিবর্তন অনুলিপি হিসাবে অনুলিপি হিসাবে লেখা হয়। এটি, দুর্ভাগ্যবশত, এর অর্থ এটি foreachবহু ক্ষেত্রে অ্যারেটি পুনরাবৃত্তি করার জন্য নকল করতে বাধ্য হয়। সুনির্দিষ্ট শর্তগুলি হ'ল:
- অ্যারেটি কোনও রেফারেন্স নয় (is_ref = 0)। যদি এটি একটি রেফারেন্স হয়, তবে এতে পরিবর্তনগুলি প্রচার করার কথা , তাই এটি সদৃশ হওয়া উচিত নয়।
- অ্যারেতে পুনরায় গণনা> 1 রয়েছে। যদি
refcount1 হয়, তবে অ্যারেটি ভাগ করা হয়নি এবং আমরা এটি সরাসরি সংশোধন করতে মুক্ত।
যদি অ্যারেটি সদৃশ না হয় (is_ref = 0, refcount = 1), তবে কেবলমাত্র এটি বাড়ানো refcountহবে (*)। অতিরিক্তভাবে, যদি foreachরেফারেন্স দ্বারা ব্যবহৃত হয়, তবে (সম্ভাব্য নকল) অ্যারেটি একটি রেফারেন্সে রূপান্তরিত হবে।
এই কোডটিকে উদাহরণ হিসাবে বিবেচনা করুন যেখানে সদৃশ ঘটে:
function iterate($arr) {
foreach ($arr as $v) {}
}
$outerArr = [0, 1, 2, 3, 4];
iterate($outerArr);
এখানে, $arrআইএপি পরিবর্তন রোধ করার জন্য সদৃশ হবে $arrথেকে লিক থেকে $outerArr। উপরের শর্তগুলির ক্ষেত্রে, অ্যারেটি কোনও রেফারেন্স নয় (is_ref = 0) এবং দুটি স্থানে ব্যবহৃত হয় (পুনরায় গণনা = 2)। এই প্রয়োজনীয়তাটি দুর্ভাগ্যজনক এবং suboptimal বাস্তবায়নের একটি নিদর্শন (এখানে পুনরাবৃত্তি চলাকালীন পরিবর্তনের কোনও উদ্বেগ নেই, সুতরাং আমাদের প্রথম স্থানে IAP ব্যবহার করার দরকার নেই)।
(*) refcountএখানে বর্ধন করা নিস্পৃহ মনে হয় তবে কপিরাইট-অন-রাইটিং (সিওডাব্লু) শব্দার্থবিধি লঙ্ঘন করে: এর অর্থ হ'ল আমরা একটি রেফকাউন্ট = 2 অ্যারের আইএপি সংশোধন করতে যাচ্ছি, যখন সিডাব্লু নির্দেশ দেয় যে সংশোধনগুলি কেবল রিফকাউন্ট = এ সম্পাদন করা যায় = 1 মান। এই লঙ্ঘনের ফলে ব্যবহারকারী-দৃশ্যমান আচরণ পরিবর্তনের ফলাফল ঘটে (যখন একটি COW সাধারণত স্বচ্ছ হয়) কারণ পুনরাবৃত্ত অ্যারেতে আইএপি পরিবর্তনটি পর্যবেক্ষণযোগ্য হবে - তবে কেবল অ্যারেতে প্রথম অ-আইএপি পরিবর্তন হওয়া পর্যন্ত। পরিবর্তে, তিনটি "বৈধ" বিকল্পগুলি refcountহ'ল ক) সর্বদা নকল করা, খ) এর বৃদ্ধি না করা এবং এভাবে পুনরাবৃত্ত অ্যারেটি নির্বিচারে লুপে পরিবর্তিত হতে দেওয়া বা গ) আইএপি মোটেও ব্যবহার করবেন না (পিএইচপি 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, আইএপি ইতিমধ্যে উপাদানটিতে রয়েছে $i+1। এই কারণে কোড নমুনার পুনরাবৃত্তির সময় পরিবর্তন দেখানো হয় হবে সবসময় পরবর্তী উপাদান বদলে বর্তমান এক।unset
উদাহরণ: আপনার পরীক্ষার কেসগুলি
উপরোক্ত বর্ণিত তিনটি দিক আপনাকে foreachবাস্তবায়নের আইডিসিএনসিজেসির বেশিরভাগ সম্পূর্ণ ছাপ সরবরাহ করবে এবং আমরা কয়েকটি উদাহরণ নিয়ে আলোচনা করতে যেতে পারি।
আপনার পরীক্ষার মামলার আচরণটি এই মুহুর্তে ব্যাখ্যা করা সহজ:
পরীক্ষার ক্ষেত্রে 1 এবং 2 $arrayপুনরায় গণনা = 1 দিয়ে শুরু হয়, সুতরাং এটি দ্বারা সদৃশ হবে না foreach: কেবলমাত্র refcountবৃদ্ধি করা হয়। যখন লুপের বডি পরবর্তীতে অ্যারে সংশোধন করে (যার পুনরায় গণনা = 2 রয়েছে) তখন নকলটি সেই সময়ে ঘটবে। Foreach এর একটি অপরিবর্তিত অনুলিপি নিয়ে কাজ চালিয়ে যাবে $array।
পরীক্ষার ক্ষেত্রে 3, আবার অ্যারে সদৃশ হয় না, এইভাবে ভেরিয়েবলের foreachআইএপি সংশোধন করা হবে $array। পুনরাবৃত্তির শেষে, আইএপি হ'ল নুল (যার অর্থ পুনরাবৃত্তি সম্পন্ন হয়েছে), যা 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 অ্যারে পয়েন্টারটি অগ্রসর করে , পরে নয় after কোডটি প্রথম উপাদানটিতে থাকলেও পয়েন্টারটি ইতিমধ্যে দ্বিতীয়টিতে উন্নত করে।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ফোরচ একটি রেফারেন্স দ্বারা পুনরুক্তি করা হয় যখন একটি_আরএফ = 1 করবে, সুতরাং মূলত আপনার উপরের মতো একই অবস্থা রয়েছে।
আরেকটি ছোট ভিন্নতা, এবার আমরা অ্যারেটিকে অন্য ভেরিয়েবলের জন্য নির্ধারণ করব:
$foo = $array;
foreach ($array as $val) {
var_dump(current($array));
}
/* Output: 1 1 1 1 1 */
$arrayলুপটি শুরু হয়ে গেলে এখানে রিফকাউন্টটি 2 হয়, সুতরাং একবারের জন্য সত্যিকার অর্থে ডুপ্লিকেশনকে সামনে রেখে আমাদের করতে হবে। সুতরাং $arrayএবং foreach দ্বারা ব্যবহৃত অ্যারে শুরু থেকে সম্পূর্ণ পৃথক হবে। এজন্য আপনি লুপের আগে যেখানেই ছিলেন আইএপি-র অবস্থানটি পাবেন (এই ক্ষেত্রে এটি প্রথম অবস্থানে ছিল)।
উদাহরণ: পুনরাবৃত্তির সময় পরিবর্তন during
পুনরাবৃত্তির সময় সংশোধনগুলির জন্য অ্যাকাউন্ট নেওয়ার চেষ্টা করা হ'ল যেখানে আমাদের সমস্ত পূর্বের সমস্যা দেখা দিয়েছে, সুতরাং এটি এই ক্ষেত্রে কয়েকটি উদাহরণ বিবেচনা করে।
একই অ্যারে জুড়ে এই নেস্টেড লুপগুলি বিবেচনা করুন (যেখানে বাই-রেফ পুনরাবৃত্তিটি সত্যই এটি একইরূপে তা নিশ্চিত করতে ব্যবহৃত হয়):
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সরানো হয়েছিল। সম্ভবত যা অপ্রত্যাশিত তা হ'ল প্রথম উপাদানটির পরে বাইরের লুপটি বন্ধ হয়ে যায়। কেন এমন?
এর পেছনের কারণটি নীচে বর্ণিত নেস্টড-লুপ হ্যাক is লুপ বডিটি চালানোর আগে বর্তমান আইএপি অবস্থান এবং হ্যাশটিকে একটি হিসাবে ব্যাক আপ করা হয় HashPointer। লুপের বডি পরে এটি পুনরুদ্ধার করা হবে, তবে কেবলমাত্র যদি উপাদানটি এখনও বিদ্যমান থাকে, অন্যথায় বর্তমান আইএপি অবস্থান (এটি যাই হোক না কেন) পরিবর্তে ব্যবহৃত হয়। উপরের উদাহরণে হুবুহু হ'ল বাইরের লুপের বর্তমান উপাদানটি সরানো হয়েছে, সুতরাং এটি আইএপি ব্যবহার করবে, যা ইতিমধ্যে অভ্যন্তরীণ লুপ দ্বারা সমাপ্ত হিসাবে চিহ্নিত হয়েছে!
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()অস্থায়ীভাবে আইএপি সংশোধন করার সময় , লুপের বডি পরে এটি বর্তমান ফোরচ উপাদানটিতে পুনরুদ্ধার করা হবে। লুপটিতে 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'এবং বরাদ্দকারী উপাদানটিকে সংরক্ষণ করার জন্য একই মেমরির অবস্থানটি পুনরায় ব্যবহার করতে ঘটে। সুতরাং foreach সরাসরি সন্নিবেশ করা উপাদান সরাসরি লাফিয়ে শেষ হয়, এইভাবে লুপটি সংক্ষিপ্ত করে cutting
লুপ চলাকালীন পুনরাবৃত্তি সত্তা প্রতিস্থাপন
একটি সর্বশেষ বিজোড় কেস যা আমি উল্লেখ করতে চাই, এটি হ'ল পিএইচপি আপনাকে লুপের সময় পুনরাবৃত্ত সত্তাটি প্রতিস্থাপন করতে দেয়। সুতরাং আপনি একটি অ্যারেতে পুনরাবৃত্তি শুরু করতে পারেন এবং তারপরে এটি অর্ধেক করে অন্য অ্যারে দিয়ে প্রতিস্থাপন করতে পারেন। অথবা একটি অ্যারেতে পুনরাবৃত্তি শুরু করুন এবং তারপরে এটি কোনও বস্তুর সাথে প্রতিস্থাপন করুন:
$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 */
যেমন আপনি এই ক্ষেত্রে দেখতে পাচ্ছেন পিএইচপি কেবলমাত্র প্রতিস্থাপনটি ঘটে যাওয়ার পরে শুরু থেকেই অন্য সত্তাকে পুনরাবৃত্তি করতে শুরু করবে।
পিএইচপি 7
হ্যাশটেবল পুনরাবৃত্তি
আপনি যদি এখনও মনে রাখেন, অ্যারে পুনরাবৃত্তির মূল সমস্যাটি ছিল মধ্য-পুনরাবৃত্তির উপাদানগুলি অপসারণকে কীভাবে পরিচালনা করতে হয়। পিএইচপি 5 এই উদ্দেশ্যে একটি একক অভ্যন্তরীণ অ্যারে পয়েন্টার (আইএপি) ব্যবহার করেছে, যা কিছুটা সাব-থিমাল ছিল, কারণ উপরে একাধিক একযোগে ফোর্যাচ লুপগুলি এবং এর সাথে মিথস্ক্রিয়া সমর্থন করার জন্য একটি অ্যারে পয়েন্টারটি প্রসারিত করতে হয়েছিল reset()।
পিএইচপি 7 একটি পৃথক পদ্ধতির ব্যবহার করে, যথা, এটি বাহ্যিক, নিরাপদ হ্যাশটেবল পুনরাবৃত্তির একটি স্বেচ্ছাসেবী তৈরির পক্ষে সহায়তা করে। এই পুনরাবৃত্তকারীগুলিকে অ্যারেতে নিবন্ধিত করতে হবে, যার বিন্দু থেকে তাদের আইএপি সমান শব্দার্থক শব্দ রয়েছে: যদি একটি অ্যারের উপাদানটি সরানো হয় তবে সেই উপাদানটির দিকে নির্দেশিত সমস্ত হ্যাশটেবল পুনরাবৃত্তিকে পরবর্তী উপাদানটিতে উন্নত করা হবে।
এর অর্থ এই যে foreachএখন আর আইএপি ব্যবহার করবে এ সব । foreachলুপ ফলের একেবারে কোনো প্রভাব থাকবে current()ইত্যাদি এবং তার নিজস্ব আচরণ মতো কাজগুলির দ্বারা কখনো প্রভাবিত করা হবে reset()ইত্যাদি
অ্যারের সদৃশ
পিএইচপি 5 এবং পিএইচপি 7 এর মধ্যে আরেকটি গুরুত্বপূর্ণ পরিবর্তন অ্যারের সদৃশতার সাথে সম্পর্কিত। এখন যেহেতু আইএপি আর ব্যবহার করা হয় না, তাই বাই-ভ্যালু অ্যারের পুনরাবৃত্তি 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 */
পূর্বে রেফারেন্স-অ্যারে বাই-মান পুনরাবৃত্তি বিশেষ ক্ষেত্রে ছিল। এই ক্ষেত্রে, কোনও সদৃশ ঘটেনি, সুতরাং পুনরাবৃত্তির সময় অ্যারের সমস্ত পরিবর্তন লুপ দ্বারা প্রতিফলিত হবে। পিএইচপি 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পিএইচপি 5 এবং পিএইচপি 7 এর মধ্যে এমনকি এবং সদৃশ আচরণ হুবহু এক)।
পরীক্ষার ক্ষেত্রে 3 পরিবর্তন: Foreachআর আইএপি ব্যবহার করে each()না , সুতরাং লুপ দ্বারা প্রভাবিত হয় না। এর আগে এবং পরে একই আউটপুট থাকবে।
পরীক্ষার কেসগুলি 4 এবং 5 একই থাকে: each()এবং reset()আইএপি পরিবর্তন করার আগে অ্যারেরটিকে নকল করে দেবে, যখন foreachএখনও মূল অ্যারে ব্যবহার করে। (অ্যারে ভাগ করে নিলেও আইএপি পরিবর্তনটি গুরুত্বপূর্ণ হত have
উদাহরণগুলির দ্বিতীয় সেটটি 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
পূর্বে হ্যাশপয়েন্টার পুনরুদ্ধার প্রক্রিয়াটি নতুন উপাদানটির ডানদিকে ঝাঁপিয়ে পড়ে কারণ এটি "দেখায়" এর মতো এটি সরানো উপাদানটির মতো (হ্যাশ এবং পয়েন্টার সংঘর্ষের কারণে)। যেহেতু আমরা আর কোনও কিছুর জন্য এলিমেন্ট হ্যাশের উপর নির্ভর করি না, এটি আর সমস্যা নয় is