ارزیابی اتصال کوتاه
ارزیابی اتصال کوتاه، ارزیابی حداقلی، یا ارزیابی مککارتی (نامگذاری به خاطر جان مککارتی بودهاست)، معنای برخی از اپراتورهای بولی در برخی از زبانهای برنامهنویسی میباشد که در آن آرگومان دوم فقط در صورتی اجرا میشود که آرگومان اول برای ارزیابی یا محاسبه عبارت کافی نباشد. به عنوان مثال، وقتی اولین آرگومان AND دارای مقدار false باشد، در آن صورت بدون توجه به مقدار آرگومان دوم، کل عبارت false میشود. به عنوان مثالی دیگر میتوان گفت زمانی که که اولین آرگومان عملگر OR دارای مقدار true باشد، در آن صورت مقدار کل عبارت برابر true میشود. در برخی از زبانهای برنامهنویسی (مانند Lisp , Perl , Haskell به دلیل ارزیابی تنبلی)، اپراتورهای معمول بولی از نوع اتصال کوتاه میباشند. در سایر موارد (آدا، جاوا، دلفی)، هر دو اپراتور بولی از نوع اتصال کوتاه و استاندارد در دسترس هستند. برای برخی از عملیات بولی مانند منحصر به فرد یا (XOR) امکان اتصال کوتاه وجود ندارد زیرا هر دو آرگومان همیشه برای تعیین نتیجه لازم میباشند.
عبارت اتصال کوتاه x and y برابر با بیان شرطی if x then y else false میباشد. همچنین شایان ذکر است که عبارت x or y نیز معادل if x then true else y میباشد.
در واقع، اپراتورهای اتصال کوتاه به جای اینکه عملگرهای ساده حساب باشند، ساختارهای کنترلی هستند، زیرا اکید نیستند. در اصطلاحات ضروری زبان (خصوصاً C و C ++)، که عوارض جانبی مهم هستند، اپراتورهای اتصال کوتاه یک نقطه دنباله را معرفی میکنند، به این صورت که آنها آرگومان اول را با وجود هر گونه عارضه جانبی و قبل از پردازش آرگومان دوم، کاملاً ارزیابی میکنند. الگول ۶۸ از روش رویهبندی برای رسیدن به اپراتورها و رویههای اتصال کوتاه (که توسط کاربر تعریف شدهاند) استفاده میکند.
در زبانهای با وابستگی کم به نوع (loosely typed) که بیش از دو مقدار حقیقی دارند، اپراتورهای اتصال کوتاه آخرین زیرعبارت ارزیابی شده را برمیگردانند. همانطور که در بالا اشاره شد، عبارت x and y برابر است با y if x else x. همچنین عبارت x or y نیز برابر x if x else y (بدون دوبار محاسبه x). به این نحوه محاسبه، «آخرین مقدار» در جدول زیر میگویند.
در زبانهایی که به صورت پیش فرض از ارزیابی تنبل استفاده میکنند (مانند هسکل)، همه عملکردها بهطور مؤثر اتصال کوتاه هستند و به اپراتورهای اتصال کوتاه ویژه نیازی نیست.
استفاده از اپراتورهای اتصال کوتاه دلیل خاطر مشکل ساز بودن آنها مورد انتقاد میباشد:
در نگاه اول، رابطهای شرطی (به صورت کوتاه "cand" و "cor") بیشتر از آنچه به نظر میآیند مشکل ساز هستند. برای نمونه با مقایسه دو عبارت زیر، متوجه میشویم که "cor" بر روی "cand" توزیع نمیشود.
عبارت اول: A cand B) cor C)
عبارت دوم: (A cor C) cand (B cor C)
در نمونه A ^ C¬، عبارت دوم نیاز به تعریف B دارد در حالی که عبارت اول نیازی ندارد. به دلیل اینکه رابطهای شرطی استنتاج در مورد برنامهها را پیچیده میکنند، بهتر است از آنها دوری شود.
پشتیبانی از زبانهای برنامهنویسی رایج
زبان | Eagerارزیابی | اپراتورهای اتصال کوتاه | نوع نتیجه |
---|---|---|---|
Advanced Business Application Programming (ABAP) | none | and , or |
Boolean1 |
Ada | and , or |
and then , or else |
Boolean |
ALGOL 68 | and, &, ∧ ; or, ∨ | andf , orf (both user defined) | Boolean |
awk | none | && , || |
Boolean |
C, Objective-C | && , || , ? [1] |
int (&& ,|| ), opnd-dependent (? ) | |
C++2 | && , || , ? [2] |
Boolean (&& ,|| ), opnd-dependent (? ) | |
C# | && , || , ? , ?? |
Boolean (&& ,|| ), opnd-dependent (? , ?? ) | |
ColdFusion Markup Language (CFML) | none | AND , OR , && , || |
Boolean |
D3 | && , || , ? |
Boolean (&& ,|| ), opnd-dependent (? ) | |
Eiffel | and , or |
and then , or else |
Boolean |
Erlang | and , or |
andalso , orelse |
Boolean |
Fortran4 | .and. , .or. |
.and. , .or. |
Boolean |
Go, Haskell, OCaml | none | && , || |
Boolean |
Java, MATLAB, R, Swift | && , || |
Boolean | |
JavaScript, Julia | && , || |
Last value | |
Lasso | none | and , or , && , || |
Last value |
Kotlin | and , or |
&& , || |
Boolean |
Lisp, Lua, Scheme | none | and , or |
Last value |
MUMPS (M) | & , ! |
none | Numeric |
Modula-2 | none | AND , OR |
Boolean |
Oberon | none | & , OR |
Boolean |
OCaml | none | && , || |
Boolean |
Pascal | and , or 5,9 |
and_then , or_else 6,9 |
Boolean |
Perl, Ruby | && , and , || , or |
Last value | |
PHP | && , and , || , or |
Boolean | |
Python | none[3] | and , or |
Last value |
Rust | && , || [4] |
Boolean | |
Smalltalk | and: , or: 7 |
Boolean | |
Standard ML | ناشناخته | andalso , orelse |
Boolean |
TTCN-3 | none | and , or [5] |
Boolean |
Visual Basic .NET | And , Or |
AndAlso , OrElse |
Boolean |
Visual Basic, Visual Basic for Applications (VBA) | And , Or |
Select Case 8 |
Numeric |
Wolfram Language | And @@ {...} , Or @@ {...} |
And , Or , && , || |
Boolean |
ZTT | none | Boolean |
موارد رایج مورد استفاده
جلوگیری از عوارض جانبی ناخواسته در آرگومان دوم
به عنوان مثال رایج، با استفاده از یک زبان مبتنی بر C:
int denom = 0;
if (denom != 0 && num / denom)
{
… // ensures that calculating num/denom never results in divide-by-zero error
}
مثال زیر را در نظر بگیرید:
int a = 0;
if (a != 0 && myfunc(b))
{
do_something();
}
در این مثال، ارزیابی اتصال کوتاه تضمین میکند که (myfunc(b هرگز فراخوانده نمیشود. دلیل این اتفاق این میباشد که a != ۰ را به false ارزیابی میشود. این ویژگی دو ساختار برنامهنویسی مفید اضافه میکند:
- اگر اولین زیر عبارت نیازمند محاسبات گرانقیمت باشد و این عبارت به غلط ارزیابی شود، میتوان محاسبه گران را در آرگومان دوم از بین برد.
- این ویژگی یک ساختار را مجاز میکند که در آن عبارت اول شرایطی را تضمین کند که بدون آن عبارت دوم ممکن است خطای زمان اجرا ایجاد کند.
حال هر دو کد بالا در قطعه کد C در زیر نشان داده شدهاست که در آن ارزیابی حداقلی مانع از همزدایی اشارهگر پوچ و دستیابیهای اضافی به حافظه میشود:
bool is_first_char_valid_alpha_unsafe(const char *p)
{
return isalpha(p[0]); // SEGFAULT highly possible with p == NULL
}
bool is_first_char_valid_alpha(const char *p)
{
return p != NULL && isalpha(p[0]); // 1) no unneeded isalpha() execution with p == NULL, 2) no SEGFAULT risk
}
شرط دوم به دلیل عدم تست، منجر به عارضه جانبی اجرا نشده نشده میشود
علیرغم مزایای بیان شده، ارزیابی حداقلی ممکن است مشکلاتی را برای برنامه نویسانی که متوجه نمیشوند (یا فراموش میکنند)، ایجاد کند. مثلاً در کد
if (expressionA && myfunc(b)) { do_something(); }
اگر قرار باشد عبارت (myfunc(b برخی از عملیات ضروری از قبیل تخصیص منابع سیستم را بدون در نظر گرفتن اجرای عبارت ()do_something، اجرا کند و عبارت expressionA برابر false ارزیابی شود، آنگاه عبارت (myfunc(b اجرا نخواهد شد، که میتواند منجر به ایجاد مشکلاتی شود. برخی از زبانهای برنامهنویسی، مانند جاوا، دو عملگر دارند که یکی از ارزیابی حداقلی استفاده میکند ولی دیگری نمیکند تا از این مشکل جلوگیری شود.
مشکلاتی مربوط به عوارض جانبی اجرا نشده میتوانند با سبک برنامهنویسی درست و مناسب، به راحتی حل شوند، یعنی عدم استفاده از عوارض جانبی در عبارات بولی، زیرا استفاده از مقادیر دارای عوارض جانبی در ارزیابیها، معمولاً کد را مبهم و مستعد خطا میکند.
از آنجایی که بازبینی کمینه جزوی از قواعد دستوری یک عملگر است و نه جزوی از یک بهینهساز (هرچند هم اختیاری)، بسیاری از الگوهای برنامهنویسی به آن به عنوان یک ساختار جزئی شرطی وابسته شدهاند. مثالهایی از آن را میتوان در ادامه دید:
اصطلاحات پرل:
some_condition or die; # Abort execution if some_condition is false some_condition and die; # Abort execution if some_condition is true }
اصطلاحات BASH (اسکریپت پوسته UNIX):
modprobe -q some_module && echo "some_module installed" || echo "some_module not installed" }
کارایی کد
وقوع اتصال کوتاه ممکن است در پردازندههای مدرن باعث بروز مشکلات در مسئلهٔ پیشبینی شاخهای، و همچنین باعث کاهش شدید بازده شود.
مثال مناسب این واقعه در پرتوهای به شدت دقیق شده در رهگیری پرتو است. برخی از کامپایلرها میتوانند اینگونه موارد را تشخیص بدهند و کدهای سریعتری تولید کنند، ولی ممکن است قواعد نحوی موجود در ساختار زبانهای برنامهنویسی، اینگونه بهینهسازیها را محدود کنند.
منابع
- ISO/IEC 9899 standard, section 6.5.13
- ISO/IEC IS 14882 draft.
- https://wiki.python.org/moin/BitwiseOperators
- "std::ops - Rust". doc.rust-lang.org. Retrieved 2019-02-12.
- ETSI ES 201 873-1 V4.10.1, section 7.1.4