فرهاد
فرهاد
خواندن ۲ دقیقه·۱ سال پیش

رفرنس معلق در حلقه foreach زبان php

عکس تزئینی
عکس تزئینی

اگر میخواهید روی هر عنصر آرایه‌ای که در حال foreach روی آن هستید عملیاتی انجام دهید، استفاده از مراجع (reference) مفید خواهد بود. به عنوان مثال:

$arr = array(1, 2, 3, 4); foreach ($arr as &$value) { $value = $value * 2; } // $arr is now array(2, 4, 6, 8)

مشکل این است که، اگر مراقب نباشید می تواند عوارض و عواقب نامطلوبی داشته باشد. در مثال بالا، پس از اجرای کد، $value در اسکوپ باقی می‌ماند و به آخرین عنصر آرایه اشاره می‌کند. بنابراین اگر در ادامه از $value استفاده کنیم، ناخواسته آخرین عنصر آرایه را تغییر می یابد.

به یاد داشته باشید: حلقه foreach ، اسکوپ (محدوده) ایجاد نمی کند

مثال:

$array = [1, 2, 3]; echo implode(',', $array), &quot\n&quot foreach ($array as &$value) {} // by reference echo implode(',', $array), &quot\n&quot foreach ($array as $value) {} // by value (i.e., copy) echo implode(',', $array), &quot\n&quot

کد بالا خروجی زیر را خواهد داشت:

1,2,3 1,2,3 1,2,2

آخرین مقدار در خط سوم در واقع 2 است، نه 3.

چرا؟

پس از اجرای اولین حلقه foreach، آرایه بدون تغییر باقی می ماند، اما، همانطور که در بالا توضیح داده شد، $value به عنوان یک dangling reference به آخرین عنصر در آرایه اشاره می کند و باقی می ماند (بعد از اتمام حلقه ، از بین نمی رود).

در نتیجه، هنگامی که حلقه دوم اجرا می شود، به نظر می رسد چیزهای عجیب و غریب اتفاق می افتد. از آنجایی که $value اکنون با مقدار (یعنی با کپی) دسترسی می یابد، foreach هر عنصر آرایه را توی $value در هر مرحله کپی می کند.

مرحله اول: مقدار array[0] یعنی عدد 1 کپی می شود به $value اما توجه داشته باشید که $value از اتمام حلقه قبلی به array[2] رفرنس شده است پس array[2] مقدار 1 را دریافت می کند. اکنون $array بدین صورت می شود [1, 2, 1]

مرحله دوم: مقدار array[1] یعنی عدد 2 کپی می شود به $value (که رفرنس شده به array[2]) پس array[2] الان مقدار 2 را دریافت می کند. اکنون $array بدین صورت می شود [2, 2, 1]

مرحله سوم: مقدار array[2] یعنی عدد 2 کپی می شود به $value (که رفرنس شده به array[2]) پس array[2] الان مقدار 2 را دریافت می کند. اکنون $array بدین صورت می شود [2, 2, 1]

راه حل:

برای اینکه reference در حلقه‌های foreach بدون این نوع مشکلات استفاده کنیم، باید تابع unset را روی متغیر، بلافاصله پس از حلقه فراخوانی کنیم تا reference حذف شود. به عنوان مثال:

$arr = array(1, 2, 3, 4); foreach ($arr as &$value) { $value = $value * 2; } unset($value); // $value no longer references $arr[3]
phpdangling referenceبرنامه نویسیحلقه تکرار
علاقه‌مند به مهندسی نرم افزار، هوش مصنوعی و موسیقی
شاید از این پست‌ها خوشتان بیاید