পিএইচপি 'ফোরচ' আসলে কীভাবে কাজ করে?


2018

আমি এটি উপস্থাপন করে বলি যে আমি জানি এটি কী foreach, কী করে এবং কীভাবে এটি ব্যবহার করতে হয়। এই প্রশ্নটি বোনেটের নীচে কীভাবে কাজ করে তা নিয়ে উদ্বেগ প্রকাশ করে এবং "এইভাবে আপনি কীভাবে অ্যারে লুপ করবেন" এর লাইন ধরে কোনও উত্তর চাই না foreach


একটি দীর্ঘ সময়ের জন্য আমি ধরে নিয়েছি যে foreachঅ্যারে নিজেই কাজ করে। তারপরে আমি অনেকগুলি উল্লেখ খুঁজে পেয়েছি যে এটি অ্যারের অনুলিপি সহ কাজ করে এবং এরপরেই আমি এটি গল্পের শেষ বলে ধরে নিয়েছি। তবে আমি সম্প্রতি বিষয়টি নিয়ে একটি আলোচনায় এসেছি এবং কিছুটা পরীক্ষার পরে দেখতে পেলাম যে এটি বাস্তবে 100% সত্য নয়।

আমার অর্থ কী তা আমাকে দেখাতে দিন। নিম্নলিখিত পরীক্ষার ক্ষেত্রে, আমরা নিম্নলিখিত অ্যারের সাথে কাজ করব:

$array = array(1, 2, 3, 4, 5);

পরীক্ষার কেস 1 :

foreach ($array as $item) {
  echo "$item\n";
  $array[] = $item;
}
print_r($array);

/* Output in loop:    1 2 3 4 5
   $array after loop: 1 2 3 4 5 1 2 3 4 5 */

এটি স্পষ্টভাবে দেখায় যে আমরা সোর্স অ্যারের সাথে সরাসরি কাজ করছি না - অন্যথায় লুপটি চিরতরে অবিরত থাকবে, যেহেতু আমরা ক্রমাগত লুপের সময় অ্যারেগুলিতে আইটেমগুলি চাপছি। তবে কেবল এটি নিশ্চিত হওয়ার জন্য:

পরীক্ষার কেস 2 :

foreach ($array as $key => $item) {
  $array[$key + 1] = $item + 2;
  echo "$item\n";
}

print_r($array);

/* Output in loop:    1 2 3 4 5
   $array after loop: 1 3 4 5 6 7 */

এটি আমাদের প্রাথমিক উপসংহারের ব্যাক আপ করে, আমরা লুপ চলাকালীন উত্স অ্যারের অনুলিপি নিয়ে কাজ করছি, অন্যথায় আমরা লুপ চলাকালীন পরিবর্তিত মানগুলি দেখতে পাই। কিন্তু ...

আমরা যদি ম্যানুয়ালটি সন্ধান করি তবে আমরা এই বিবৃতিটি পাই:

যখন ফরচ প্রথমত সম্পাদন শুরু করে, অভ্যন্তরীণ অ্যারে পয়েন্টারটি স্বয়ংক্রিয়ভাবে অ্যারের প্রথম উপাদানটিতে পুনরায় সেট হয়ে যায়।

ডান ... foreachএটি উত্স অ্যারের অ্যারে পয়েন্টার উপর নির্ভর করে বলে মনে হচ্ছে । তবে আমরা ঠিক প্রমাণ করেছি যে আমরা উত্স অ্যারের সাথে কাজ করছি না , তাই না? ভাল, পুরোপুরি না।

পরীক্ষার কেস 3 :

// Move the array pointer on one to make sure it doesn't affect the loop
var_dump(each($array));

foreach ($array as $item) {
  echo "$item\n";
}

var_dump(each($array));

/* Output
  array(4) {
    [1]=>
    int(1)
    ["value"]=>
    int(1)
    [0]=>
    int(0)
    ["key"]=>
    int(0)
  }
  1
  2
  3
  4
  5
  bool(false)
*/

সুতরাং, আমরা সোর্স অ্যারের সাথে সরাসরি কাজ করছি না তা সত্ত্বেও, আমরা সোর্স অ্যারে পয়েন্টারটির সাথে সরাসরি কাজ করছি - লুপের শেষে পয়েন্টার অ্যারের শেষে রয়েছে তা এটিকে দেখায়। এটি সত্য হতে পারে না - যদি এটি হয় তবে পরীক্ষার কেস 1 চিরতরে লুপ হয়ে যায়।

পিএইচপি ম্যানুয়ালটিতে আরও বলা হয়েছে:

যেমন পূর্বাভাস অভ্যন্তরীণ অ্যারে পয়েন্টারের উপর নির্ভর করে এটি লুপের মধ্যে পরিবর্তন করা অপ্রত্যাশিত আচরণের দিকে পরিচালিত করতে পারে।

ঠিক আছে, সেই "অপ্রত্যাশিত আচরণ" কী তা খুঁজে বার করুন (প্রযুক্তিগতভাবে, কোনও আচরণ অপ্রত্যাশিত, যেহেতু আমি আর কী প্রত্যাশা করব তা জানি না)।

পরীক্ষার কেস 4 :

foreach ($array as $key => $item) {
  echo "$item\n";
  each($array);
}

/* Output: 1 2 3 4 5 */

পরীক্ষার কেস 5 :

foreach ($array as $key => $item) {
  echo "$item\n";
  reset($array);
}

/* Output: 1 2 3 4 5 */

... সেখানে অপ্রত্যাশিত কিছুই নয়, বাস্তবে এটি "উত্সের অনুলিপি" তত্ত্বকে সমর্থন করে বলে মনে হচ্ছে।


প্রশ্নটি

এখানে কি হচ্ছে? আমার সি-ফু আমার পক্ষে যথেষ্ট নয়, কেবল পিএইচপি উত্স কোডটি দেখে কোনও সঠিক উপসংহার বের করতে সক্ষম হন, যদি কেউ আমার জন্য এটি ইংরেজিতে অনুবাদ করতে পারে তবে আমি এটির প্রশংসা করব।

আমার কাছে এটি অ্যারের অনুলিপি নিয়ে foreachকাজ করে বলে মনে হয় তবে উত্স অ্যারের অ্যারের পয়েন্টারটি লুপের পরে অ্যারের শেষে সেট করে।

  • এটি কি সঠিক এবং পুরো গল্প?
  • তা না হলে আসলে কী করছে?
  • লুপের ফলাফলকে প্রভাবিত করতে পারে এমন কোনও ফাংশন ব্যবহার করে যা কোনও অ্যারে পয়েন্টার ( each(), reset()ইত্যাদি ইত্যাদি) সমন্বয় করে foreach?

5
@ ডেভেন্ড্যান্ডম এখানে একটি পিএইচপি-ইন্টার্নাল ট্যাগ রয়েছে যা সম্ভবত পাওয়া উচিত, তবে অন্য 5 টি ট্যাগের মধ্যে কোনটি প্রতিস্থাপন করতে হবে তা সিদ্ধান্ত নিতে আমি আপনাকে তা ছেড়ে দেব leave
মাইকেল বারকোভস্কি

5
হ্যান্ডেল মোছা ছাড়াই COW এর মতো দেখাচ্ছে
zb '

149
প্রথমে আমি ভেবেছিলাম - গোশ, আরেকটি নবাগত প্রশ্ন। দস্তাবেজগুলি পড়ুন… এইচএম, পরিষ্কারভাবে অপরিবর্তিত আচরণ «« তারপরে আমি সম্পূর্ণ প্রশ্নটি পড়েছি এবং অবশ্যই বলতে হবে: এটি আমার পছন্দ হয়েছে। আপনি এটিতে বেশ কিছু প্রচেষ্টা রেখেছেন এবং সমস্ত টেস্টকেস লিখেছেন। পুনশ্চ. টেস্টকেস 4 এবং 5 একই?
নিটল

21
অ্যারে পয়েন্টারটি স্পর্শ পেয়েছে কেন এটি বোঝার জন্য কেবল একটি চিন্তাভাবনা: পিএইচপি কে অনুলিপি সহ অনুলিপি সহ মূল অ্যারের অভ্যন্তরীণ অ্যারে পয়েন্টারটি সরানো দরকার, কারণ ব্যবহারকারী বর্তমান মান ( foreach ($array as &$value)) - এর জন্য একটি রেফারেন্স চাইতে পারে - পিএইচপি-র মূল অ্যারেতে বর্তমান অবস্থানটি জানা দরকার যদিও এটি আসলে কোনও অনুলিপি দ্বারা পুনরুক্ত হয়।
নিকো

4
@ শিয়ান: আইএমএইচও, পিএইচপি ডকুমেন্টেশন মূল ভাষার বৈশিষ্ট্যগুলির সংক্ষিপ্তসারগুলি বর্ণনা করতে সত্যিই খুব খারাপ। তবে এটি সম্ভবত, কারণ এতগুলি অ্যাড-হক বিশেষ ক্ষেত্রে ভাষায় বেক করা হয়েছে ...
অলিভার চার্লসওয়ার্থ

উত্তর:


1660

foreach মান বিভিন্ন ধরণের উপর পুনরাবৃত্তি সমর্থন করে:

  • অ্যারেগুলির
  • সাধারণ জিনিস
  • Traversable বস্তু

নীচে, আমি পুনরাবৃত্তি বিভিন্ন ক্ষেত্রে কীভাবে কাজ করে তা বিশদভাবে ব্যাখ্যা করার চেষ্টা করব। এখনও পর্যন্ত সহজতম বিষয় হ'ল 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বহু ক্ষেত্রে অ্যারেটি পুনরাবৃত্তি করার জন্য নকল করতে বাধ্য হয়। সুনির্দিষ্ট শর্তগুলি হ'ল:

  1. অ্যারেটি কোনও রেফারেন্স নয় (is_ref = 0)। যদি এটি একটি রেফারেন্স হয়, তবে এতে পরিবর্তনগুলি প্রচার করার কথা , তাই এটি সদৃশ হওয়া উচিত নয়।
  2. অ্যারেতে পুনরায় গণনা> 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


4
@ বাবা এটি করেন। কোনও ফাংশনে এটি পাস করা $foo = $arrayলুপের আগে করার মতোই ;)
NikiC

32
আপনাদের মধ্যে যারা কি একটি zval জানি না জন্য, এখানে সারা Goleman এর পড়ুন দয়া blog.golemon.com/2007/01/youre-being-lied-to.html
Shu zOMG চেন

1
গৌণ সংশোধন: আপনি বালতি যাকে বলে তাকে সাধারণত হ্যাশটেবলে বালতি বলা হয় না। সাধারণত বালতি একই হ্যাশ% আকারের এন্ট্রিগুলির একটি সেট। আপনি সাধারণত এন্ট্রি বলা হয় এটির জন্য এটি ব্যবহার করছেন বলে মনে হয়। লিঙ্কযুক্ত তালিকা বালতিতে নয়, এন্ট্রিগুলিতে।
আনবেলি

12
@ ইউনেলি আমি পিএইচপি দ্বারা অভ্যন্তরীণভাবে ব্যবহৃত পরিভাষা ব্যবহার করছি। Bucketগুলি হ্যাশ দুর্ঘটনায় জন্য একটি দোকর লিঙ্ক তালিকা এবং অর্ডারের জন্য একটি দোকর লিঙ্ক তালিকা অংশ অংশ;)
NikiC

4
দুর্দান্ত অ্যাঞ্জার ser আমি মনে করি আপনি বোঝানো হয়েছে iterate($outerArr);এবং iterate($arr);কোথাও না ।
নিয়াহু

116

উদাহরণস্বরূপ 3 আপনি অ্যারে সংশোধন করবেন না। অন্যান্য সমস্ত উদাহরণে আপনি বিষয়বস্তু বা অভ্যন্তরীণ অ্যারে পয়েন্টারটি সংশোধন করেন। এটি গুরুত্বপূর্ণ যখন অ্যাসাইনমেন্ট অপারেটরের শব্দার্থকতার কারণে পিএইচপি অ্যারে আসে ।

পিএইচপি-তে অ্যারেগুলির জন্য অ্যাসাইনমেন্ট অপারেটর একটি অলস ক্লোনটির মতো আরও কাজ করে। একটি অ্যারে যুক্ত করে একটি ভেরিয়েবল বরাদ্দ করা বেশিরভাগ ভাষার চেয়ে আলাদা অ্যারেটিকে ক্লোন করবে। তবে এটির প্রয়োজন না হলে আসল ক্লোনিং করা হবে না। এর অর্থ হ'ল ক্লোনটি কেবল তখনই সঞ্চালিত হবে যখন দুটি ভেরিয়েবল সংশোধিত হবে (অনুলিপি-অনুলিপি)।

এখানে একটি উদাহরণ:

$a = array(1,2,3);
$b = $a;  // This is lazy cloning of $a. For the time
          // being $a and $b point to the same internal
          // data structure.

$a[] = 3; // Here $a changes, which triggers the actual
          // cloning. From now on, $a and $b are two
          // different data structures. The same would
          // happen if there were a change in $b.

আপনার পরীক্ষার ক্ষেত্রে ফিরে আসার পরে আপনি সহজেই কল্পনা করতে পারেন foreachযা অ্যারের সাথে একটি রেফারেন্স সহ এক ধরণের পুনরাবৃত্তি তৈরি করে। এই উল্লেখটি $bআমার উদাহরণে ভেরিয়েবলের মতো কাজ করে । যাইহোক, রেফারেন্স সহ পুনরুক্তি কেবল লুপের সময় লাইভ করে এবং তারপরে, সেগুলি উভয়ই বাতিল করা হয়। এখন আপনি দেখতে পাচ্ছেন যে, সমস্ত ক্ষেত্রে 3 ছাড়াও অ্যারেটি লুপের সময় পরিবর্তিত হয়, যখন এই অতিরিক্ত রেফারেন্সটি জীবিত থাকে। এটি একটি ক্লোনকে ট্রিগার করে এবং এখানে কী চলছে তা ব্যাখ্যা করে!

এই অনুলিপি-অনুলিপি আচরণের আরও একটি পার্শ্ব প্রতিক্রিয়াটির জন্য এখানে একটি চমৎকার নিবন্ধ: পিএইচপি টের্নারি অপারেটর: দ্রুত বা না?


আপনার অধিকার হিসাবে মনে হচ্ছে, আমি কিছু উদাহরণ তৈরি করেছি যা প্রমাণ করে যে: codepad.org/OCjtvu8r আপনার উদাহরণ থেকে একটি পার্থক্য - আপনি মান পরিবর্তন করলে এটি অনুলিপি করে না, কেবল কীগুলি পরিবর্তন করলে।
zb '

এটি প্রকৃতপক্ষে উপরের সমস্ত আচরণের শো ব্যাখ্যা করে each()এবং প্রথম পরীক্ষার কেস এর শেষে কল করে এটি সুন্দরভাবে চিত্রিত করা যেতে পারে , যেখানে আমরা দেখি যে মূল অ্যারের অ্যারে পয়েন্টারটি দ্বিতীয় উপাদানটির দিকে নির্দেশ করে, যেহেতু অ্যারের সময় সংশোধন করা হয়েছিল প্রথম পুনরাবৃত্তি এটি এটি প্রদর্শন করে বলে মনে হয় foreachযা লুপের কোড ব্লকটি কার্যকর করার আগে অ্যারে পয়েন্টারটি সরিয়ে নিয়ে যায়, যা আমি প্রত্যাশা করি না - আমি ভাবতাম এটি এটি শেষ পর্যন্ত এটি করবে। অনেক ধন্যবাদ, এটি আমার জন্য এটি সুন্দরভাবে পরিষ্কার করে দেয়।
ডেভর্যান্ডম

49

সাথে কাজ করার সময় কয়েকটি বিষয় লক্ষণীয় foreach():

ক) মূল অ্যারের সম্ভাব্য অনুলিপিটিতেforeach কাজ করে । এর অর্থ এটি পূর্ববর্তী নোটস / ব্যবহারকারীর মন্তব্য তৈরি না করা অবধি বা না হওয়া অবধি ভাগ করা ডেটা স্টোরেজ থাকবে ।foreach()prospected copy

খ) কোন প্রত্যাশিত অনুলিপি ট্রিগার করে ? এর প্রত্যাশিত অনুলিপি তৈরি করা হয় এর নীতির উপর ভিত্তি করে, অর্থাত্ copy-on-writeযখনই একটি অ্যারে পাস করা foreach()হয় তখনই মূল অ্যারের একটি ক্লোন তৈরি হয়।

গ) মূল অ্যারে এবং foreach()পুনরুক্তিকারীর থাকবে DISTINCT SENTINEL VARIABLES, এটি একটি মূল অ্যারের জন্য এবং অন্যটির জন্য foreach; নীচে পরীক্ষা কোড দেখুন। এসপিএল , আইট্রেটারস এবং অ্যারে আইটারেটর

ওভারফ্লো প্রশ্ন স্ট্যাক কীভাবে নিশ্চিত করতে হবে যে পিএইচপি-তে একটি 'ফোরচ' লুপে মানটি পুনরায় সেট করা আছে? আপনার প্রশ্নের কেসগুলিকে সম্বোধন করে (3,4,5)।

নিম্নলিখিত উদাহরণে শো প্রতিটি () এবং রিসেট () প্রভাবিত করে না SENTINELভেরিয়েবল (for example, the current index variable)এর foreach()পুনরুক্তিকারীর।

$array = array(1, 2, 3, 4, 5);

list($key2, $val2) = each($array);
echo "each() Original (outside): $key2 => $val2<br/>";

foreach($array as $key => $val){
    echo "foreach: $key => $val<br/>";

    list($key2,$val2) = each($array);
    echo "each() Original(inside): $key2 => $val2<br/>";

    echo "--------Iteration--------<br/>";
    if ($key == 3){
        echo "Resetting original array pointer<br/>";
        reset($array);
    }
}

list($key2, $val2) = each($array);
echo "each() Original (outside): $key2 => $val2<br/>";

আউটপুট:

each() Original (outside): 0 => 1
foreach: 0 => 1
each() Original(inside): 1 => 2
--------Iteration--------
foreach: 1 => 2
each() Original(inside): 2 => 3
--------Iteration--------
foreach: 2 => 3
each() Original(inside): 3 => 4
--------Iteration--------
foreach: 3 => 4
each() Original(inside): 4 => 5
--------Iteration--------
Resetting original array pointer
foreach: 4 => 5
each() Original(inside): 0=>1
--------Iteration--------
each() Original (outside): 1 => 2

2
আপনার উত্তরটি বেশ সঠিক নয়। foreachঅ্যারের সম্ভাব্য অনুলিপিটিতে কাজ করে তবে এটি প্রয়োজন না হলে আসল অনুলিপি তৈরি করে না।
লাইনপোগল

কোডটি কীভাবে এবং কখন সেই সম্ভাব্য অনুলিপি তৈরি হয় তা আপনি প্রদর্শন করতে চান? আমার কোডটি দেখায় যে foreachঅ্যারেটি 100% সময়ের অনুলিপি করছে। আমি জানতে আগ্রহী। আপনার মন্তব্যের জন্য ধন্যবাদ
সখুনজাই

একটি অ্যারে অনুলিপি করতে অনেক খরচ হয়। হয় forবা ব্যবহার করে 100000 উপাদানগুলির সাথে অ্যারের পুনরাবৃত্তি করতে যে সময় লাগে তা গণনার চেষ্টা করুন foreach। আপনি তাদের দুজনের মধ্যে কোনও উল্লেখযোগ্য পার্থক্য দেখতে পাবেন না, কারণ আসল অনুলিপিটি ঘটে না।
লাইনপোগল

তারপরে আমি ধরে নেব যে SHARED data storageঅবধি সেখানে অবধি সংরক্ষিত আছে বা না থাকলেও copy-on-write(আমার কোড স্নিপেট থেকে) এটি স্পষ্টভাবে প্রমাণ করে যে সেখানে সর্বদা দুটি এবং এর SENTINEL variablesজন্য একটির দুটি সেট থাকবে । ধন্যবাদ যে উপলব্ধি করেoriginal arrayforeach
sakhunzai

1
হ্যাঁ এটি "প্রত্যাশিত" অনুলিপি অর্থাৎ "সম্ভাব্য" অনুলিপি you আপনার প্রস্তাব অনুসারে এটি সুরক্ষিত নয়
সখুনজাই

33

পিএইচপি 7 জন্য নোট

কিছুটা জনপ্রিয়তা পেয়েছে বলে এই উত্তরটি আপডেট করার জন্য: এই উত্তরটি আর পিএইচপি app হিসাবে প্রযোজ্য নয় As যেমন " পশ্চাদপদ অসম্পূর্ণ পরিবর্তনগুলি " হিসাবে ব্যাখ্যা করা হয়েছে , পিএইচপি in-এর পূর্বাচন অ্যারের অনুলিপিটিতে কাজ করে, সুতরাং অ্যারে নিজেই যে কোনও পরিবর্তন ঘটে so ফোরচ লুপ প্রতিফলিত হয় না। লিঙ্কে আরও বিশদ।

ব্যাখ্যা ( পিএইচপিএন থেকে উদ্ধৃতি ):

অ্যারে_ এক্সপ্রেশন দ্বারা প্রদত্ত অ্যারের উপরে প্রথম ফর্মটি লুপ করে। প্রতিটি পুনরাবৃত্তির উপরে, বর্তমান উপাদানটির মান $ মান হিসাবে নির্ধারিত হয় এবং অভ্যন্তরীণ অ্যারে পয়েন্টারটি এক দ্বারা উন্নত হয় (সুতরাং পরবর্তী পুনরাবৃত্তির উপর, আপনি পরবর্তী উপাদানটির দিকে তাকাবেন)।

সুতরাং, আপনার প্রথম উদাহরণে আপনার কাছে অ্যারেতে কেবল একটি উপাদান রয়েছে, এবং যখন পয়েন্টারটি সরানো হয় তখন পরবর্তী উপাদানটির উপস্থিতি থাকে না, সুতরাং আপনি নতুন উপাদান যোগ করার পরে শেষ হয় কারণ এটি ইতিমধ্যে "সিদ্ধান্ত নিয়েছে" যে এটি শেষ উপাদান হিসাবে রয়েছে।

আপনার দ্বিতীয় উদাহরণে, আপনি দুটি উপাদান দিয়ে শুরু করুন, এবং ফোরচ লুপ শেষ উপাদানটি নয় তাই এটি পরবর্তী পুনরাবৃত্তিতে অ্যারের মূল্যায়ন করে এবং এইভাবে বুঝতে পারে যে অ্যারেতে নতুন উপাদান রয়েছে।

আমি বিশ্বাস করি যে ডকুমেন্টেশনের ব্যাখ্যার প্রতিটি পুনরাবৃত্তির অংশের এটিই সমস্ত পরিণতি , যার অর্থ সম্ভবত foreachকোডটি কল করার আগে এটি সমস্ত যুক্তিযুক্ত {}

পরীক্ষা ক্ষেত্রে

আপনি যদি এটি চালান:

<?
    $array = Array(
        'foo' => 1,
        'bar' => 2
    );
    foreach($array as $k=>&$v) {
        $array['baz']=3;
        echo $v." ";
    }
    print_r($array);
?>

আপনি এই আউটপুট পাবেন:

1 2 3 Array
(
    [foo] => 1
    [bar] => 2
    [baz] => 3
)

যার অর্থ এটি পরিবর্তনটি গ্রহণ করেছে এবং এটি "সময়মতো" পরিবর্তিত হওয়ার কারণে এটি পেরিয়ে গেছে। তবে আপনি যদি এটি করেন:

<?
    $array = Array(
        'foo' => 1,
        'bar' => 2
    );
    foreach($array as $k=>&$v) {
        if ($k=='bar') {
            $array['baz']=3;
        }
        echo $v." ";
    }
    print_r($array);
?>

তুমি পাবে:

1 2 Array
(
    [foo] => 1
    [bar] => 2
    [baz] => 3
)

যার অর্থ হ'ল অ্যারেটি সংশোধন করা হয়েছিল, তবে যেহেতু foreachইতিমধ্যে অ্যারের শেষ উপাদানটি ছিল তখনই আমরা এটি সংশোধন করেছি , তাই এটি আর লুপ না করার "সিদ্ধান্ত নিয়েছে" এবং যদিও আমরা নতুন উপাদান যুক্ত করেছি, আমরা এটি "খুব দেরী" যুক্ত করেছি এবং এটি লুপ করা হয়নি।

পিএইচপি 'ফোরচ' প্রকৃতপক্ষে কীভাবে কাজ করে তা বিশদ বিবরণে পড়া যায় ? যা এই আচরণের পেছনের অভ্যন্তরীণ দিকটি ব্যাখ্যা করে।


7
আচ্ছা আপনি বাকী উত্তরটি পড়েছেন? এটি নিখুঁতভাবে উপলব্ধি করে যে ভবিষ্যদ্বাণীটি যদি কোডটি চালানোর আগে অন্য সময় লুপ করবে কিনা তা স্থির করে।
dkasipovic

2
না, অ্যারেটি সংশোধিত হয়েছে, তবে "অনেক দেরি" হওয়ায় যেহেতু ফোরচ ইতিমধ্যে "মনে করে" যে এটি শেষ উপাদানটিতে রয়েছে (এটি পুনরাবৃত্তির শুরুতে রয়েছে) এবং এটি আর লুপ করবে না। যেখানে দ্বিতীয় উদাহরণে এটি পুনরাবৃত্তির শুরুতে শেষ উপাদানটিতে নয় এবং পরবর্তী পুনরাবৃত্তির শুরুতে আবার মূল্যায়ন করে। আমি একটি পরীক্ষা কেস প্রস্তুত করার চেষ্টা করছি।
dkasipovic

1
@ আলমাডো lxr.php.net/xref/PHP_TRUNK/Zend/zend_vm_def.h#4509 এ দেখুন যখন এটি পুনরুক্ত হয় তখন এটি সর্বদা পরবর্তী পয়েন্টারে সেট থাকে। সুতরাং, এটি শেষ পুনরাবৃত্তিতে পৌঁছে গেলে এটি সমাপ্ত হিসাবে চিহ্নিত হবে (NULL পয়েন্টারের মাধ্যমে)। এরপরে আপনি যখন সর্বশেষ পুনরাবৃত্তিতে একটি কী যুক্ত করবেন, পূর্বাঞ্চ এটি লক্ষ্য করবে না।
bwebi

1
@DKasipovic নং সেখানে কোনও সম্পূর্ণ ও স্পষ্ট ব্যাখ্যা নেই (কমপক্ষে আপাতত - আমি ভুল হতে পারি)
Alma Do

4
আসলে মনে হচ্ছে @ আলমাডোর নিজস্ব যুক্তি বোঝার ত্রুটি আছে ... আপনার উত্তরটি ঠিক আছে।
bwebi

15

পিএইচপি ম্যানুয়াল দ্বারা সরবরাহকৃত ডকুমেন্টেশন অনুসারে।

প্রতিটি পুনরাবৃত্তিতে, বর্তমান উপাদানটির মান $ v নির্ধারিত হয় এবং অভ্যন্তরীণ
অ্যারে পয়েন্টারটি এক দ্বারা উন্নত হয় (সুতরাং পরবর্তী পুনরাবৃত্তির উপর, আপনি পরবর্তী উপাদানটির দিকে তাকাবেন)।

সুতরাং আপনার প্রথম উদাহরণ হিসাবে:

$array = ['foo'=>1];
foreach($array as $k=>&$v)
{
   $array['bar']=2;
   echo($v);
}

$arrayকেবলমাত্র একটি একক উপাদান রয়েছে, সুতরাং ভবিষ্যদ্বাণী কার্যকর হিসাবে, 1 নির্ধারিত হয় $vএবং এতে পয়েন্টার সরানোর জন্য অন্য কোনও উপাদান নেই

তবে আপনার দ্বিতীয় উদাহরণে:

$array = ['foo'=>1, 'bar'=>2];
foreach($array as $k=>&$v)
{
   $array['baz']=3;
   echo($v);
}

$arrayদুটি উপাদান রয়েছে, সুতরাং এখন অ্যারে শূন্য সূচকগুলি মূল্যায়ন করুন এবং পয়েন্টারটিকে এক দ্বারা সরান। লুপের প্রথম পুনরাবৃত্তির জন্য, $array['baz']=3;রেফারেন্স দ্বারা পাস হিসাবে যুক্ত করা হয়েছে।


13

দুর্দান্ত প্রশ্ন, কারণ অনেকগুলি বিকাশকারী এমনকি অভিজ্ঞ ব্যক্তিরাও পিএইচপি যেভাবে ফরচ লুপগুলিতে অ্যারে পরিচালনা করে তা দ্বারা বিভ্রান্ত হয়। স্ট্যান্ডার্ড ফোরচ লুপে, পিএইচপি লুপটিতে ব্যবহৃত অ্যারের একটি অনুলিপি তৈরি করে। লুপ শেষ হওয়ার সাথে সাথে অনুলিপিটি বাতিল করা হয়। এটি সাধারণ ফোরচ লুপের ক্রিয়াকলাপে স্বচ্ছ। উদাহরণ স্বরূপ:

$set = array("apple", "banana", "coconut");
foreach ( $set AS $item ) {
    echo "{$item}\n";
}

এই ফলাফলগুলি:

apple
banana
coconut

সুতরাং অনুলিপিটি তৈরি করা হয়েছে তবে বিকাশকারী লক্ষ্য করবেন না, কারণ মূল অ্যারেটি লুপের মধ্যে বা লুপ শেষ হওয়ার পরে উল্লেখ করা যায় না। যাইহোক, আপনি যখন লুপের মধ্যে আইটেমগুলি সংশোধন করার চেষ্টা করেন, আপনি দেখতে পাবেন যে আপনি শেষ করার পরে সেগুলি অশোধিত হয়েছে:

$set = array("apple", "banana", "coconut");
foreach ( $set AS $item ) {
    $item = strrev ($item);
}

print_r($set);

এই ফলাফলগুলি:

Array
(
    [0] => apple
    [1] => banana
    [2] => coconut
)

আসলটি থেকে যে কোনও পরিবর্তন হ'ল তা লক্ষ্য করা যাবে না, প্রকৃতপক্ষে আসল থেকে কোনও পরিবর্তন নেই, যদিও আপনি clearly আইটেমকে স্পষ্টভাবে একটি মূল্য নির্ধারণ করেছেন। এটি কারণ আপনি $ আইটেমে এটি পরিচালনা করছেন যেমন $ সেটটির অনুলিপিটিতে কাজ করা হচ্ছে। আপনি রেফারেন্স দ্বারা reference আইটেমটি ধরে এটিকে ওভাররাইড করতে পারেন:

$set = array("apple", "banana", "coconut");
foreach ( $set AS &$item ) {
    $item = strrev($item);
}
print_r($set);

এই ফলাফলগুলি:

Array
(
    [0] => elppa
    [1] => ananab
    [2] => tunococ
)

সুতরাং এটি স্পষ্ট এবং পর্যবেক্ষণযোগ্য, যখন $ আইটেমটি বাই-রেফারেন্সে পরিচালিত হয়, তখন $ আইটেমে পরিবর্তনগুলি মূল $ সেটের সদস্যদের হয়ে থাকে। রেফারেন্স অনুসারে $ আইটেমটি ব্যবহার করে পিএইচপিকে অ্যারে অনুলিপি তৈরি করতে বাধা দেয়। এটি পরীক্ষা করতে, প্রথমে আমরা অনুলিপিটি প্রদর্শিত একটি দ্রুত স্ক্রিপ্ট দেখাব:

$set = array("apple", "banana", "coconut");
foreach ( $set AS $item ) {
    $set[] = ucfirst($item);
}
print_r($set);

এই ফলাফলগুলি:

Array
(
    [0] => apple
    [1] => banana
    [2] => coconut
    [3] => Apple
    [4] => Banana
    [5] => Coconut
)

যেমন এটি উদাহরণে দেখানো হয়েছে, পিএইচপি $ সেটটি অনুলিপি করেছে এবং এটি লুপের জন্য ব্যবহার করেছিল, কিন্তু যখন $ সেটটি লুপের অভ্যন্তরে ব্যবহৃত হয়েছিল, পিএইচপি অনুলিপিটি মূল অ্যারেতে যুক্ত হয়েছিল, অনুলিপিযুক্ত অ্যারে নয়। মূলত, পিএইচপি কেবল লুপের সম্পাদন এবং $ আইটেমের বরাদ্দকরণের জন্য অনুলিপি করা অ্যারে ব্যবহার করে। এর কারণে, উপরের লুপটি কেবল 3 বার কার্যকর করে এবং প্রতিবার এটি 6 টি উপাদান সহ মূল original সেটটি রেখে মূল $ সেটটির শেষে অন্য মান যুক্ত করে, তবে কখনও অসীম লুপে প্রবেশ করে না।

যাইহোক, যদি আমরা উল্লেখ করেছি reference আইটেমটি রেফারেন্স অনুসারে, যেমনটি আমি আগে উল্লেখ করেছি? উপরোক্ত পরীক্ষায় একটি অক্ষর যুক্ত হয়েছে:

$set = array("apple", "banana", "coconut");
foreach ( $set AS &$item ) {
    $set[] = ucfirst($item);
}
print_r($set);

অসীম লুপের ফলাফল। নোট করুন এটি আসলে একটি অসীম লুপ, আপনাকে নিজেই স্ক্রিপ্টটি মেরে ফেলতে হবে বা আপনার ওএসের স্মৃতি শেষ না হওয়ার অপেক্ষা করতে হবে। আমি আমার স্ক্রিপ্টে নিম্নলিখিত লাইনটি যুক্ত করেছি যাতে পিএইচপি খুব দ্রুত স্মৃতি থেকে সরে যায়, আমি আপনাকে পরামর্শ দিচ্ছি যদি আপনি এই অসীম লুপ পরীক্ষা চালাচ্ছেন:

ini_set("memory_limit","1M");

সুতরাং অসীম লুপের সাথে পূর্ববর্তী উদাহরণে, আমরা পিএইচপি লুপ ওভারে অ্যারের একটি অনুলিপি তৈরি করার জন্য কেন লেখা হয়েছিল তা দেখি। যখন একটি অনুলিপি নিজে তৈরি করে কেবল লুপের কাঠামোর দ্বারা ব্যবহার করা হয়, অ্যারে লুপটির পুরো প্রয়োগের সময় স্থির থাকে, তাই আপনি কখনও সমস্যার সমাধান করবেন না।


7

পিএইচপি ফোরচ লুপটি ব্যবহার করা যেতে পারে Indexed arrays, Associative arraysএবং Object public variables

ফোরচ লুপে, পিএইচপি প্রথম কাজটি হ'ল এটি অ্যারের একটি অনুলিপি তৈরি করে যা পুনরাবৃত্ত হতে হবে। পিএইচপি তারপরে copyমূলটির পরিবর্তে অ্যারের এই নতুনটির উপরে পুনরাবৃত্তি করে । এটি নীচের উদাহরণে প্রদর্শিত হয়:

<?php
$numbers = [1,2,3,4,5,6,7,8,9]; # initial values for our array
echo '<pre>', print_r($numbers, true), '</pre>', '<hr />';
foreach($numbers as $index => $number){
    $numbers[$index] = $number + 1; # this is making changes to the origial array
    echo 'Inside of the array = ', $index, ': ', $number, '<br />'; # showing data from the copied array
}
echo '<hr />', '<pre>', print_r($numbers, true), '</pre>'; # shows the original values (also includes the newly added values).

এটি ছাড়াও পিএইচপি পাশাপাশি ব্যবহার করতে দেয় না iterated values as a reference to the original array value। এটি নীচে প্রদর্শিত হয়:

<?php
$numbers = [1,2,3,4,5,6,7,8,9];
echo '<pre>', print_r($numbers, true), '</pre>';
foreach($numbers as $index => &$number){
    ++$number; # we are incrementing the original value
    echo 'Inside of the array = ', $index, ': ', $number, '<br />'; # this is showing the original value
}
echo '<hr />';
echo '<pre>', print_r($numbers, true), '</pre>'; # we are again showing the original value

দ্রষ্টব্য: এটি original array indexesহিসাবে ব্যবহারের অনুমতি দেয় না references

সূত্র: http://dwellupper.io/post/47/ বোঝাবুঝি- php-foreach-loop-with- উদাহরণ


1
Object public variablesভুল বা সেরা বিভ্রান্তিকর। আপনি সঠিক ইন্টারফেস (উদাহরণস্বরূপ, ট্র্যাভারসিবল) ব্যতীত অ্যারেতে কোনও অবজেক্ট ব্যবহার করতে পারবেন না এবং আপনি যখন foreach((array)$obj ...বাস্তবে কোনও সরল অ্যারে নিয়ে কাজ করছেন তখন কোনও বস্তু নয়।
খ্রিস্টান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.