اثر جانبی (علوم رایانه)
در علوم رایانه، اگر یک عملیات، تابع یا عبارت بتواند مقدار متغیرهای حالت را خارج از دامنهٔ خود تغییر دهد؛ دارای اثر جانبی (به انگلیسی: Side effect) است. بهعبارت دیگر عملیات شامل یک اثر قابل مشاهده، علاوهبر بازگرداندن مقدار (اثر اصلی) به تابع فراخوانیکننده است. دادههای حالت که «بیرون» از عملیات بهروز میشوند، ممکن است «داخل» یک شیء حالتدار یا یک سیستم گستردهتر مقدار بگیرند. مثالهایی از اثر جانبی شامل مقداردهی یک متغیر غیر محلی، مقداردهی یک متغیر محلی استاتیک، تغییر مقدار یک آرگومان ورودی قابل تغییر است که بهوسیلهٔ ارجاع به تابع پاس داده شده باشد. همچنین انجام عملیات ورودی/خروجی یا فراخوانی سایر توابع دارای اثر جانبی، از مثالهای این مفهوم بهشمار میروند. در صورت وجود اثر جانبی، ممکن است رفتار یک برنامه به تاریخچه بستگی داشته باشد؛ یعنی ترتیب ارزیابی اهمیت دارد. درک و اشکال زدایی یک تابع با اثر جانبی، مستلزم آگاهی از زمینه و تاریخچههای احتمالی آن است.
میزان استفاده از اثر جانبی به زبان برنامهنویسی بستگی دارد. در برنامهنویسی دستوری استفاده از اثر جانبی جهت بهروزرسانی حالت سیستم متداول است. در مقابل، در برنامهنویسی اعلانی اغلب وضعیت سیستم، بدون بهوجود آمدن اثرات جانبی، گزارش میشود.
در برنامهنویسی تابعی اثر جانبی بهندرت استفاده میشود. عدم وجود اثر جانبی، انجام درستییابی صوری یک برنامه را آسانتر میکند. زبانهای کاربردی مانند Standard ML، Scheme و اسکالا عوارض جانبی را ممنوع نمیکنند، اما برای برنامهنویسان این زبانها، خودداری از ایجاد اثر جانبی مرسوم است. ربان تابعی هسکل اثرات جانبی مانند عملیات ورودی/خروجی و دیگر محاسبات حالتدار را با استفاده از مونادها مشخص میکند.
برنامه نویسان زبان اسمبلی باید از اثرات جانبی پنهان آگاه باشند - دستورالعملهایی که بخشهایی از حالت پردازنده را تغییر میدهد و در دستورالعمل ذکر نشدهاند. یک مثال کلاسیک از یک اثر جانبی پنهان یک دستورالعمل حسابی است که بهطور ضمنی کدهای وضعیت را تغییر میدهد (یک اثر جانبی پنهان) در حالی که صریحاً یک ثبات را تغییر میدهد (اثر آشکار). یکی از اشکالات بالقوه یک مجموعه دستورالعمل با عوارض جانبی پنهان این است که اگر تعداد زیادی از دستورالعملها اثر جانبی روی یک حالت مشترک داشته باشند، ممکن است منطق موردنیاز برای بهروزرسانی آن حالت (مانند کد وضعیت) به یک تنگنا در کارایی تبدیل شود. این مشکل بهخصوص در برخی از پردازندههای طراحی شده با بهینهسازی خط لوله (از سال ۱۹۹۰) یا با اجرای خارج ترتیب رایج است. چنین پردازندهای ممکن است برای تشخیص اثرات جانبی پنهان نیاز به مدارات بیشتری داشته باشد؛ یا در صورتی که نتیجهٔ عملیات به اثرات جانبی متکی باشد، حتی ممکن است خط لوله را متوقف کند.
شفافیت مرجع
عدم وجود اثر جانبی شرط لازم برای شفافیت مرجع است، اما کافی نیست. شفافیت مرجع به این معنی است که میتوان یک عبارت (مانند فراخوانی تابع) را با مقدار آن جایگزین کرد. این امر مستلزم این است که این عبارت سره باشد، به این معنی که این عبارت باید قطعی بوده (همیشه برای همان ورودی مقدار یکسانی داشته باشد) و اثر جانبی نداشته باشد.
اثر جانبی موقتی
معمولاً هنگام بحث دربارهٔ اثر جانبی و شفافیت مرجع، اثرات جانبی ناشی از مدت زمان لازم برای اجرای عملیات فعلی نادیده گرفته میشود. همچنین در مواردی مانند «زمانبندی سختافزار» یا «تست»، عملیات دارای اثر جانبی، بهطور ویژه به دلیل اثر جانبی موقتی آنها به برنامه اضافه میشوند. مانند sleep(5000)
یا
for (int i = 0; i <10000; ++i) {}
این دستورها وضعیت را تغییر نمیدهند با این حال صرفاً برای اتلاف زمانی که جهت اجرای آنها نیاز است، استفاده میشود.
تکرارشوندگی
تابع f
با اثر جانبی، تحت ترکیب متوالی f; f
«تکرار شونده» است اگر بعد از دو بار فراخوانی با مقادیر یکسان، اثر جانبی نداشته، و فراخوانی دوم مقداری برابر با فراخوانی اول داشته باشد. (با فرض این که میان دو فراخوانی، هیچ تابع دیگری صدا زده نشدهاست)
به عنوان نمونه کد پایتون زیر را در نظر بگیرید:
x = 0
def setx(n):
global x
x = n
setx(5)
setx(5)
در اینجا، تابع setx
تکرارشونده است زیرا فراخوانی دوم به setx
(با همان مقادیر) حالت مشهود برنامه را تغییر نمیدهد: x
در اولین فراخوانی مقدار ۵ داشت، و در فراخوانی دوم باز هم مقدار ۵ میگیرد. توجه داشته باشید که این مفهوم، با تکرارشوندگی تحت ترکیب ترکیب توابعf ∘ f
متفاوت است. به عنوان تابع، قدر مطلق تحت ترکیب تکرار شونده است:
def abs(n):
if n <0:
return -n
else:
return n
abs(-5) == abs(abs(-5)) == abs(5) == 5
مثال
یکی از مثالهای متداول اثر جانبی، عملکرد اپراتور واگذاری در C++ است. به عنوان مثال، تابع انتساب، عملوند راست را برمیگرداند در حالی که تأثیر جانبی روی مقداردهی به یک متغیر دارد. این ویژگی به مقداردهیهای چندگانه کمک میکند:
int i, j;
i = j = 3;
به دلیل این که وابستگی عملگر انتساب، راست به چپ است، این عبارت همارز عبارت زیر است:
int i, j;
i = (j = 3); // عبارت j = 3 مقدار 3 را برمیگرداند که در ادامه به i انتساب میشود
در حالی که نتیجهٔ انتساب عدد ۳ به متغیر j
، بعداً به متغیر i
اختصاص مییابد. این برای برنامهنویسان مبتدی سردرگمکننده است.
while (b == 10) {} // بررسی میکند که آیا b به مقدار 10 ارزیابی میشود یا خیر
با
while (b = 10) {} // عملگر = مقدار 10 را برمیگرداند. که بهصورت خودکار به true ارزیابی میشود، بنابراین شرط همیشه صادق است
جستارهای وابسته
منابع
- ↑ Spuler, David A.; Sajeev, A. S. M. (January 1994). "Compiler Detection of Function Call Side Effects". James Cook University. CiteSeerX 10.1.1.70.2096.
The term Side effect refers to the modification of the nonlocal environment. Generally this happens when a function (or a procedure) modifies a global variable or arguments passed by reference parameters. But here are other ways in which the nonlocal environment can be modified. We consider the following causes of side effects through a function call: 1. Performing I/O. 2. Modifying global variables. 3. Modifying local permanent variables (like static variables in C). 4. Modifying an argument passed by reference. 5. Modifying a local variable, either automatic or static, of a function higher up in the function call sequence (usually via a pointer).
- ↑ “Research Topics in Functional Programming” ed. D. Turner, Addison-Wesley, 1990, pp 17–42. Retrieved from: Hughes, John, Why Functional Programming Matters (PDF)
- ↑ Collberg, CSc 520 Principles of Programming Languages, Department of Computer Science, University of Arizona
- ↑ Matthias Felleisen et al., How To Design Programs, MIT Press
- ↑ Haskell 98 report, http://www.haskell.org.
- ↑ Imperative Functional Programming, Simon Peyton Jones and Phil Wadler, Conference Record of the 20th Annual ACM Symposium on Principles of Programming Languages, pages 71–84, 1993