تطبیق نام (زبان‌های برنامه‌نویسی)

از ویکی‌پدیا، دانشنامهٔ آزاد

در برنامه‌نویسی به فرایند وصل کردن شناسه‌ها به مولفه‌های نشان داده شده توسط آن‌ها تطبیق نام می‌گویند. این نام‌ها ممکن است در جدول نمادها یا به عنوان بخشی از معماری فضای نام ذخیره شوند. هدف از نام گذاری فراهم کردن دسترسی آسان و سریع به مولفه‌های برنامه است. این فرایند می‌تواند برای فراخوانی تابع، متغیر یا مولفه ای از یک مجموعه موجودیت نام گذاری شده استفاده شود.[۱]

مفاهیم[ویرایش]

عبارت‌های برنامه‌های کامپیوتری، متغیرها، نوع داده‌ها، توابع، کلاس‌ها، اشیا، کتاب‌خانه‌ها، بسته‌ها و دیگر موجودیت‌ها را با اسم فراخوانی می‌کنند. تطبیق نام، فرایند ارتباط دادن این اسامی (که لزوماً یکتا نیستند) با موجودیت‌های معادلشان در برنامه است. الگوریتم‌هایی که مشخص می‌کنند این شناسه‌ها در یک بخش خاص برنامه به چه جزئی اشاره می‌کنند، بخشی از تعریف زبان هستند. میزان پیچیدگی این الگوریتم‌ها به پیچیدگی زبان بستگی دارد. برای مثال، فرایند تطبیق نام در زبان اسمبلی معمولاً با استفاده از یک جدول نماد انجام می‌شود در حالی که در زبان سی پلاس‌پلاس این فرایند بسیار دشوارتر است و موارد زیر را دربرمی‌گیرد:

  • قوانین فضای نام به یک شناسه اجازه می‌دهد که با توجه به فضای نام مربوطه‌اش معانی مختلفی داشته باشد.
  • قوانین دامنه که باعث می‌شود یک شناسه بتواند در محدوده‌های مختلف معانی گوناگونی داشته باشد که با قواعدی که برای مخفی کردن و نادیده گرفتن شناسه وجود دارد امکان‌پذیر است. در ابتدایی‌ترین سطح، تطبیق نام معمولاً تلاش می‌کند ارتباط را در کوچکترین محدوده محصور پیدا کند، بنابراین برای مثال متغیرهای محلی بر متغیرهای عمومی اولویت دارند. به این عمل سایه انداختن گفته می‌شود.
  • قوانین مشخص کنندهٔ پدیداری در یک بخش که تعیین می‌کنند آیا شناسه‌های موجود در یک فضای نام یا دامنهٔ خاص، در این بخش از برنامه قابل مشاهده است یا نه.
  • قوانین سربارکردن که اجازه می‌دهد یک شناسه بسته به استفاده‌های مختلفی که دارد، معانی گوناگونی داشته باشد حتی وقتی در یک فضای نام یا دامنه استفاده شود.
  • قوانین دسترسی‌پذیری که تعیین می‌کند آیا شناسه‌های یک دامنهٔ قابل مشاهدهٔ دیگر واقعاً دسترس پذیر هستند و در فرایند تطبیق نام شرکت می‌کنند یا خیر.

تطبیق پویا و ایستا[ویرایش]

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

برای مثال، در زبان پایتون داریم:

>>> 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) را می‌پوشاند وقتی که دو شرط زیر برقرار باشند:

  1. نام 'I با نام I یکسان باشد.
  2. دامنه‌ای که 'I در آن تعریف شده‌است زیر مجموعهٔ دامنهٔ I باشد.

در این صورت گفته می‌شود که متغیر درونی 'X بر متغیر بیرونی X سایه انداخته‌است.

برای مثال در الگوی متداول زیر پارامتر 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 (پارامتر تابع) قائل شود.

پوشاندن نام می‌تواند باعث پیچیدگی‌هایی در سربار کردن تابع بشود، چرا که در بعضی از زبان‌ها از جمله سی‌پلاس‌پلاس سربار کردن در میان دامنه‌ها رخ نمی‌دهد، بنابراین نیاز است که همهٔ توابع سربار شده مجدداً تعریف شوند یا به صورت صریح در یک فضای نام داده شده وارد شوند.

تغییر نام آلفا[ویرایش]

در زبان‌های برنامه‌نویسی با قواعد دامنه‌ای واژگانی که عمل بازتاب روی نام متغیرها را انجام نمی‌دهند، می‌توان برای آسان کردن تطبیق نام از تبدیل ⍺ (یا تغییر نام ⍺) استفاده کرد. به وسیلهٔ این فرایند می‌توان جایگزینی‌هایی را یافت که به ما کمک می‌کند از این که هیچ نام متغیری در دامنه‌اش نام دیگری را پنهان نمی‌کند اطمینان حاصل کنیم. اگر تغییر نام دهندهٔ ⍺ قوانین دامنه‌ای زبان را بفهمد، آن‌گاه می‌توان با تغییر نام ⍺ آنالیز ایستای برنامه را آسان کرد.

برای مثال به کد زیر توجه نمایید:

  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; }
  }

در این کد جدید، هیچ پنهان‌سازی‌ای صورت نگرفته، بنابراین واضح است که کدام استفاده‌ها با کدام تعریف‌ها مطابقت دارند.

جستارهای وابسته[ویرایش]

منابع[ویرایش]

  1. ""Do What I Mean": Name Resolution in Programming Languages". Will Crichton. 16 September 2018. Retrieved 2019-12-23.
  2. "[Python-Ideas] str.format utility function". 9 May 2009. Retrieved 2011-01-23.
  3. "8.6. Dictionary-based string formatting". diveintopython.org. Mark Pilgrim. Retrieved 2011-01-23.
  4. "9. Classes - Python documentation". Retrieved 2019-07-24.

پیوند به بیرون[ویرایش]