রেফারেন্স দ্বারা পিএইচপি ফরচ পাস: শেষ উপাদান নকল? (বাগ?)


159

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

<?php

$arr = array("foo",
             "bar",
             "baz");

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?

?>

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

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

এটি কি কোনও বাগ বা কিছু সত্যিই অদ্ভুত আচরণ হওয়ার কথা বলে মনে হচ্ছে?


এটি আবার মান দিয়ে দেখুন, তৃতীয় বার পরিবর্তন হয় কিনা ... দেখুন?
শ্যাকরক

1
@ শ্যাকরোক, মান দ্বারা পুনরাবৃত্তি লুপের সাথে আর কোনও পরিবর্তন হবে বলে মনে হয় না।
নিয়মিততা

1
মজার বিষয় হল আপনি যদি lo আইটেম ব্যতীত অন্য কিছু ব্যবহার করতে দ্বিতীয় লুপটি পরিবর্তন করেন তবে এটি প্রত্যাশার মতো কাজ করে।
স্টিভ ক্লারিজ

9
লুপের মূল অংশের শেষে আইটেমটি সর্বদা আনসেট করুন: foreach($x AS &$y){ ... unset($y); }- এটি আসলে php.net এ রয়েছে (কোথায় তা জানেন না) কারণ এটি অনেক বেশি ভুল হয়েছে।
রুডি

উত্তর:


170

প্রথম foreach লুপ পরে, $itemএখনও কিছু মান যা একটি দ্বারা ব্যবহৃত হয় রেফারেন্স হয় $arr[2]। সুতরাং দ্বিতীয় লুপে প্রতিটি ফরচ কল, যা রেফারেন্স দ্বারা কল করে না, সেই মানটি প্রতিস্থাপন করে এবং এইভাবে $arr[2]নতুন মান সহ।

সুতরাং লুপ 1, মান এবং $arr[2]হয়ে $arr[0]যা যা 'ফু'।
লুপ 2, মান এবং $arr[2]হয়ে যায় $arr[1]যা 'বার'।
লুপ 3, মান এবং $arr[2]হয়ে যায় $arr[2]যা 'বার' (লুপ 2 এর কারণে)।

'বাজ' মানটি দ্বিতীয় ফোরচ লুপের প্রথম কলটিতে আসলে হারিয়ে যায়।

আউটপুট ডিবাগিং

লুপের প্রতিটি পুনরাবৃত্তির জন্য, আমরা মানটির প্রতিধ্বনি করব $itemএবং পুনরাবৃত্তভাবে অ্যারে প্রিন্ট করব $arr

প্রথম লুপটি যখন সঞ্চালিত হয়, আমরা এই আউটপুটটি দেখতে পাই:

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

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

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

লুপের শেষে, $itemএখনও একই জায়গার দিকে ইশারা করছে $arr[2]

দ্বিতীয় লুপটি যখন সঞ্চালিত হয়, আমরা এই আউটপুটটি দেখতে পাই:

foo
Array ( [0] => foo [1] => bar [2] => foo )

bar
Array ( [0] => foo [1] => bar [2] => bar )

bar
Array ( [0] => foo [1] => bar [2] => bar )

আপনি লক্ষ্য করবেন যে প্রতিটি সময় অ্যারে কীভাবে একটি নতুন মান স্থাপন করে $item, এটি $arr[3]একই মানটির সাথে আপডেট হয়, যেহেতু তারা উভয়ই একই অবস্থানের দিকে নির্দেশ করছে। যখন লুপটি অ্যারের তৃতীয় মানের দিকে যায় তখন এতে মানটি থাকবে barকারণ এটি কেবলমাত্র সেই লুপটির পূর্ববর্তী পুনরাবৃত্তি দ্বারা সেট করা হয়েছিল।

এটা কি বাগ?

না এটি কোনও রেফারেন্সড আইটেমের আচরণ, এবং কোনও বাগ নয়। এটি এমন কিছু চালানোর অনুরূপ হবে:

for ($i = 0; $i < count($arr); $i++) { $item = $arr[$i]; }

একটি অগ্রণী লুপ প্রকৃতিতে বিশেষ নয় যেখানে এটি রেফারেন্সড আইটেমগুলিকে উপেক্ষা করতে পারে। এটি প্রতিটি বারের মতো আপনি যে লুপের বাইরে যাবেন ঠিক তেমন পরিবর্তনশীলটিকে নতুন মানটিতে সেট করে।


4
আমি একটি সামান্য পেডেন্টিক সংশোধন আছে। $itemকোনও রেফারেন্স নয় $arr[2], এর $arr[2]সাথে থাকা মানটি উল্লেখ করা মানের একটি রেফারেন্স $item। পার্থক্যটি চিত্রিত করার জন্য, আপনি আনসেটও করতে পারেন $arr[2], এবং $itemপ্রভাবিত করবেন না, এবং লিখতে $itemএটি প্রভাব ফেলবে না।
পল বিগগার

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

1
$itemফোরচ লুপটি বের হয়ে গেলে কেন সুযোগের বাইরে চলে যায় না? এটি কি ক্লোজার সমস্যা বলে মনে হচ্ছে?
জোকুল

6
@ জোকুল: পিএইচপি-তে, পূর্ববর্তী সময়ে, অন্যদিকে, তাদের নিজস্ব সুযোগ তৈরি করে না।
animuson

1
@ জোকুল, পিএইচপি-র স্থানীয় ভেরিয়েবলগুলি নেই (ব্লক)। এটি আমাকে বিরক্ত করার অন্যতম কারণ।
কিউট্যাক্স

29

$itemএটি একটি রেফারেন্স $arr[2]এবং এনিমাসন নির্দেশিত হিসাবে দ্বিতীয় ফরচ লুপ দ্বারা ওভাররাইট করা হচ্ছে।

foreach ($arr as &$item) { /* do nothing by reference */ }
print_r($arr);

unset($item); // This will fix the issue.

foreach ($arr as $item) { /* do nothing by value */ }
print_r($arr); // $arr has changed....why?

3

যদিও এটি আনুষ্ঠানিকভাবে কোনও বাগ নাও হতে পারে, আমার মতে এটি। আমি মনে করি যে এখানে সমস্যাটি হ'ল আমাদের $itemযখন লুপটি বাইরে বেরোনোর ​​বাইরে অন্যান্য প্রোগ্রামিং ভাষাগুলির মতো হবে তখনই সুযোগের বাইরে চলে যাওয়ার প্রত্যাশা থাকে । তবে এটি মনে হয় না ...

এই কোড ...

$arr = array('one', 'two', 'three');
foreach($arr as $item){
    echo "$item\n";
}    
echo $item;

আউটপুট দেয় ...

one
two
three
three

অন্য লোকেরা ইতিমধ্যে বলেছে, আপনি $arr[2]আপনার দ্বিতীয় লুপের সাহায্যে রেফারেন্সড ভেরিয়েবলটি ওভাররাইট করছেন , তবে এটি কেবল ঘটছে কারণ $itemকখনই সুযোগের বাইরে যায়নি। তোমরা কি ভাবছো ... বাগ?


4
1) একটি বাগ না। এটি ইতিমধ্যে ম্যানুয়ালটিতে ডেকে আনা হয়েছে এবং উদ্দিষ্ট হিসাবে বেশ কয়েকটি বাগ রিপোর্টে বরখাস্ত। 2) সত্যই প্রশ্নের উত্তর দেয় না ...
বোল্টক্লক

স্কোপ ইস্যু না বলে এটি আমাকে ধরে ফেলল, আমি প্রত্যাশা করছিলাম $ আইটেমটি প্রাথমিক ভবিষ্যদ্বাণীটির পরে থাকবে, তবে আমি বুঝতে পারি নি যে পূর্ববর্তী পূর্বে এটি পরিবর্তন করার পরিবর্তে পরিবর্তনশীলটিকে আপডেট করে। যেমন দ্বিতীয় লুপের আগে আনসেট চালানো ($ আইটেম) এর সমান same নোট করুন যে আনসেটটি মানটি সাফ করে না (এবং এইভাবে অ্যারের শেষ উপাদানটি) এটি কেবল পরিবর্তনশীলটিকে সরিয়ে দেয়।
প্রোগ্রামার

দুর্ভাগ্যক্রমে, পিএইচপি {}সাধারণভাবে লুপ বা ব্লকগুলির জন্য কোনও নতুন সুযোগ তৈরি করে না । ভাষাটি
এইভাবে

0

এর আরও সহজ ব্যাখ্যা, পিএইচপি এর মূল নির্মাতা রাসমাস লেয়ার্ডর্ফের থেকে মনে হচ্ছে: https://bugs.php.net/bug.php?id=71454


: অনুরূপ ব্যাখ্যা এই সাইটের আগে দেওয়া হয়েছিল stackoverflow.com/a/19829715
qdinar

অনুরূপ ব্যাখ্যা এর আগে rasmus@php.net লিখে bugs.php.net/bug.php?id=29992
Qdinar

0

পিএইচপি-র সঠিক আচরণটি আমার বিপরীতে একটি বিজ্ঞপ্তি ত্রুটি হতে পারে। যদি ফোরচ লুপে তৈরি একটি রেফারেন্সড ভেরিয়েবল লুপের বাইরে ব্যবহার করা হয় তবে এটি কোনও বিজ্ঞপ্তির কারণ হবে। এই আচরণের জন্য পড়ে যাওয়া খুব সহজ, যখন এটি ঘটে তখন এটি চিহ্নিত করা খুব কঠিন। এবং কোনও বিকাশকারী ফোরচ ডকুমেন্টেশন পৃষ্ঠাটি পড়ছেন না, এটি কোনও সহায়তা নয়।

unset()এই ধরণের সমস্যা এড়াতে আপনার লুপের পরে রেফারেন্স হওয়া উচিত । কোনও রেফারেন্সে আনসেট () কেবলমাত্র মূল ডেটা ক্ষতি না করে রেফারেন্সটি সরিয়ে ফেলবে।


0

কারণ আপনি রেফ নির্দেশিকা (&) দ্বারা ব্যবহার করেন। শেষ মানটি দ্বিতীয় লুপ দ্বারা প্রতিস্থাপন করা হবে এবং এটি আপনার অ্যারেকে দূষিত করে। দ্বিতীয় সমাধানটির জন্য পৃথক নাম ব্যবহার করা সহজ সমাধান:

foreach ($arr as &$item) { ... }

foreach ($arr as $anotherItem) { ... }
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.