در برنامهنویسی به فرایند وصل کردن شناسهها به مولفههای نشان داده شده توسط آنها تطبیق نام میگویند. این نامها ممکن است در جدول نمادها یا به عنوان بخشی از معماری فضای نام ذخیره شوند. هدف از نام گذاری فراهم کردن دسترسی آسان و سریع به مولفههای برنامه است. این فرایند میتواند برای فراخوانی تابع، متغیر یا مولفه ای از یک مجموعه موجودیت نام گذاری شده استفاده شود.[۱]
عبارتهای برنامههای کامپیوتری، متغیرها، نوع دادهها، توابع، کلاسها، اشیا، کتابخانهها، بستهها و دیگر موجودیتها را با اسم فراخوانی میکنند. تطبیق نام، فرایند ارتباط دادن این اسامی (که لزوماً یکتا نیستند) با موجودیتهای معادلشان در برنامه است. الگوریتمهایی که مشخص میکنند این شناسهها در یک بخش خاص برنامه به چه جزئی اشاره میکنند، بخشی از تعریف زبان هستند. میزان پیچیدگی این الگوریتمها به پیچیدگی زبان بستگی دارد. برای مثال، فرایند تطبیق نام در زبان اسمبلی معمولاً با استفاده از یک جدول نماد انجام میشود در حالی که در زبان سی پلاسپلاس این فرایند بسیار دشوارتر است و موارد زیر را دربرمیگیرد:
در زبانهای برنامهنویسی مختلف، تطبیق نام در زمان کامپایل یا زمان اجرا انجام میشود. حالت اول تطبیق نام ایستا و حالت دوم تطبیق نام پویا نام دارد. یک تصور غلط رایج این است که پویا بودن نوع متغیرها معادل پویا بودن فرایند تطبیق نام است. برای مثال در زبان برنامهسازی ارلنگ، نوع متغیرها به صورت پویا تعریف میشود اما تطبیق نام به شکل ایستا صورت میگیرد. با این حال ایستا بودن نوع متغیرها به معنای ایستا بودن فرایند تطبیق نام است. تطبیق نام ایستا که در زمان کامپایل انجام میشود، جلوی استفاده از متغیرهایی که در دامنه نیستند را میگیرد و بنابراین از برخی خطاهای برنامهنویسی جلوگیری میکند. اما در زبانهایی که تطبیق نام پویا دارند این ایمنی فدای انعطافپذیری بیشتر میشود. در این زبانها بهطور معمول میتوان متغیرها را در یک دامنه مقداردهی و استفاده کرد.
برای مثال، در زبان پایتون داریم:
>>> number = 99 >>> first_noun = "problems" >>> second_noun = "hound" >>> # Which variables to use are decided at runtime >>> print(f"I got {number} {first_noun} but a {second_noun} ain't one.") I got 99 problems but a hound ain't one.
هر چند امروزه تکیه کردن بر تطبیق نام پویا توسط جامعه برنامهنویسان پایتون توصیه نمیشود.[۲][۳] همچنین ممکن است این ویژگی در نسخههای بعدی پایتون حذف شود.[۴]
نمونههایی از زبانهایی که از تطبیق نام ایستا استفاده میکنند شامل سی، سی پلاسپلاس، E, ارلنگ، هسکل، جاوا، پاسکال، اسکیم و اسمالتاک میشود. همچنین لیسپ، پرل، پیاچپی، ریبل، تیسیال و پایتون مثالهایی از زبانهایی هستند که از تطبیق نام پویا استفاده میکنند.
پوشاندن زمانی رخ میدهد که برای موجودیتهای متفاوت در دامنههای واژگانی دارای همپوشانی از شناسههای یکسان استفاده شود. این عمل در سطح متغیرها با عنوان سایه انداختن بر متغیر شناخته میشود. شناسهٔ 'I (برای متغیر 'X) شناسهٔ I (برای متغیر X) را میپوشاند وقتی که دو شرط زیر برقرار باشند:
در این صورت گفته میشود که متغیر درونی 'X بر متغیر بیرونی X سایه انداختهاست.
برای مثال در الگوی متداول زیر پارامتر foo بر متغیر محلی foo سایه انداختهاست.
foo
private int foo; // Name "foo" is declared in the outer scope public void setFoo(int foo) { // Name "foo" is declared in the inner scope, and is function-local. this.foo = foo; // Since "foo" will be first found (and resolved) in the ''innermost'' scope, // in order to successfully overwrite the stored value of the attribute "foo" // with the new value of the incoming parameter "foo", a distinction is made // between "this.foo" (the object attribute) and "foo" (the function parameter). } public int getFoo() { return foo; }
در واقع نام foo در دامنهٔ بیرونی تعریف شدهاست. همچنین در تابع setFoo، نام foo در دامنهٔ درونی تعریف شدهاست و متغیر محلی آن تابع است. از آنجایی که foo ابتدا در داخلیترین دامنه یافت میشود، برای اینکه بازنویسی مقدار متغیر کلاس foo با پارامتر foo که به تابع پاس داده شدهاست با موفقیت انجام شود، فرایند تطبیق باید تفاوتی بین this.foo (ویژگی شیء) و foo (پارامتر تابع) قائل شود.
setFoo
this.foo
پوشاندن نام میتواند باعث پیچیدگیهایی در سربار کردن تابع بشود، چرا که در بعضی از زبانها از جمله سیپلاسپلاس سربار کردن در میان دامنهها رخ نمیدهد، بنابراین نیاز است که همهٔ توابع سربار شده مجدداً تعریف شوند یا به صورت صریح در یک فضای نام داده شده وارد شوند.
در زبانهای برنامهنویسی با قواعد دامنهای واژگانی که عمل بازتاب روی نام متغیرها را انجام نمیدهند، میتوان برای آسان کردن تطبیق نام از تبدیل ⍺ (یا تغییر نام ⍺) استفاده کرد. به وسیلهٔ این فرایند میتوان جایگزینیهایی را یافت که به ما کمک میکند از این که هیچ نام متغیری در دامنهاش نام دیگری را پنهان نمیکند اطمینان حاصل کنیم. اگر تغییر نام دهندهٔ ⍺ قوانین دامنهای زبان را بفهمد، آنگاه میتوان با تغییر نام ⍺ آنالیز ایستای برنامه را آسان کرد.
برای مثال به کد زیر توجه نمایید:
class Point { private: double x, y; public: Point(double x, double y) { // x and y declared here mask the privates setX(x); setY(y); } void setX(double newx) { x = newx; } void setY(double newy) { y = newy; } }
در کد بالا، در تابع سازندهٔ کلاس Point، متغیرهای کلاسی x و y در سایهٔ متغیرهای محلی با اسم مشابه قرار گرفتهاند. میتوان با تغییر نام ⍺ به کد زیر رسید:
class Point { private: double x, y; public: Point(double a, double b) { setX(a); setY(b); } void setX(double newx) { x = newx; } void setY(double newy) { y = newy; } }
در این کد جدید، هیچ پنهانسازیای صورت نگرفته، بنابراین واضح است که کدام استفادهها با کدام تعریفها مطابقت دارند.