مقالات فنی

ارزیابی تنبل در زبان C++11

توسط ژوئن 20, 2014 جولای 1st, 2014 نظر ۲
[مطالبی که در ادامه می‌آید در سطح متوسط و نیازمند آشنایی خواننده با زبان «C++11» و مفهوم «ارزیابی تنبل» است.]

فُک شاید در ظاهر حیوان کُند و تنبلی باشد، اما به هنگام شکار و در آب بسیار سریع و فرز است. با این حال، ترجیح می‌دهد که بیشتر زمان خود را در ساحل و در حال استراحت بگذراند و فقط در صورت نیاز، اصطلاحاً، به آب بزند. به این طریق، از هدر رفتن انرژی‌اش که به صورت لایه‌های چربی در بدنش ذخیره شده، جلوگیری می‌کند تا از آن در سرمای زمستان‌های قطب استفاده کند.

استفاده‌ی بهینه از منابع محدود رایانه‌ها (سرعت پردازنده، ظرفیت حافظه و غیره) همواره مورد توجه برنامه‌نویس‌ها بوده است. آنها این کار را غالباً با تلاش برای دستیابی به الگوریتم‌های سریع‌تر که نیاز به نگاه داشتن داده‌های کمتری دارند، انجام می‌دهند. اما در بسیاری از موارد، دستیابی به چنین الگوریتم‌هایی کار چندان ساده‌ای نیست. یکی از راهکارهای مؤثر برای اصلاح الگوریتم‌های موجود، توجه به یک مفهوم ساده است: «زمانت را برای چیزی که به آن نیاز نداری، صرف نکن!» این مفهموم، پایه و اساس یکی از ابزارهای برنامه‌سازی است که آن را با عنوان «ارزیابی تنبل۱» می‌شناسیم.

در طول این مطلب، برای سادگی، از یک مثال ابتدایی استفاده شده است: کلاس vector برای محاسبات برداری؛ البته خواننده باید به فکر مثال‌های پیچیده‌تر باشد. یک پیاده‌سازی ساده برای کلاس vector به صورت زیر است:

بخش‌هایی از این کد، که مربوط به محاسبات برداری است، به‌جهت خلاصه‌نویسی حذف شده اما پیاده‌سازی آنها برای خواننده بسیار ساده است. همان‌طور که می‌بینید در این پیاده‌سازی مشکلی وجود دارد. اگر در طول برنامه get_length و get_unit چند بار فراخوانی شوند، مقادیر آنها نیز هر بار از نو محاسبه خواهد شد. یک نکته‌ی قابل توجه در مورد کلاس vector «ناوردا۲» بودن آن است؛ یعنی مقدار آن پس از ساخته شدن تغییر نمی‌کند. بنابراین می‌توان مقادیر length و unit را می‌توان در لحظه‌ی ساخته شدن آن از پیش محاسبه کرد و پیاده‌سازی آن را به شکل زیر تغییر داد:

این پیاده‌سازی به‌نظر خیلی بهتر است. اما سؤال اینجاست که آیا واقعاً نیاز به محاسبه‌ی همه‌ی آنها هست؟ و اینکه آیا راهی وجود دارد که عمل پیش‌محاسبه را به صورت خودکار انجام داد؟ در این نقطه، راه حل «ارزیابی تنبل»‌ و کلاس Lazy در دات‌نت به ذهن متبادر می‌شود، اما چنین کلاسی در کتابخانه‌ی استاندارد سی وجود ندارد. یک پیاده‌سازی آن را می‌توان در کتابخانه‌ی boost یافت ولی من ترجیح می‌دهم برای یک امکان کوچک از چنین کتابخانه‌ی بزرگی استفاده نکنم.

آنچه ما نیاز داریم یک کلاس شبیه کلاس Lazy در زبان سی‌شارپ است. پیاده‌سازی چنین کلاسی با استفاده از استاندارد C++11 چندان کار سختی نیست و به‌سرعت می‌توان آن را به شکل زیر نوشت:

این کلاس یک «عبارت لامبدا۳» به‌عنوان محاسبه‌گر مقدار دریافت می‌کند. نحوه‌ی استفاده از آن را با یک مثال می‌توان نشان داد:

در این مثال،‌ مقدار pi تا زمان محاسبه‌ی مقادار area محاسبه نمی‌گردد. و هنگام محاسبه‌ی مقدار perimeter از مقدار پیشین pi استفاده می‌شود و دوباره محاسبه نمی‌شود. طبق این مثال می‌توان پیاده‌سازی کلاس vector را به صورت زیر بازنویسی کرد:

خب! پیاده‌سازی کلاس vector به این شکل شاید به آن تمیزی که می‌خواستیم،‌ نشد ;-) اما کاری را که از آن انتظار داشتیم به خوبی انجام می‌دهد. یادآوری می‌کنم که مثال مورد بررسی در اینجا تنها یک مثال ساده برای دیدن چگونگی پیاده‌سازی کلاس lazy به‌کمک استاندارد C++11 است و خواننده با درک ایده‌ی اصلی باید همواره به دنبال پیاده‌سازی نمونه‌های پیچیده‌تر باشد.

  1. Lazy evaluation
  2. Immutable
  3. Lambda expression
علی اصل روستا

نویسنده علی اصل روستا

نوشته های بیشتر از علی اصل روستا

به گفتگو بپیوندید نظر ۲

  • mojtaba گفت:

    خیلی جالب بود!
    من نمی دونستم بچه های تاد هم سی پلاس پلاس کار می کنن!
    ۱۱++c یه مبحث قشنگ دیگه هم به خودش اضافه کرده به نام async تا از زبون های دیگه عقب نمونه!
    نمی دونم مطلب شما رو خوندم چرا یاد اون افتادم!
    بازم ممنون، من فکر نمی کردم توی سایت های فارسی هم از این جور مقاله ها پیدا بشه!

    • علی اصل روستا گفت:

      شاید در وب و بازی‌سازی C و ++C کمی مهجور مونده باشن ولی به این معنی نیست که به هیچ عنوان استفاده نشن 🙂 ممنون از لطفت. اینکه چرا تو سایت‌های فارسی چنین مقالاتی پیدا نمیشه، شاید به این دلیله که تا بحال سایت ما راه‌اندازی نشده بوده! 😉
      از طریق این سایت ما امیدواریم بتونیم دانشمون رو با هم‌وطنامون به اشتراک بذاریم و گامی در جهت تولید محتوای فنی فارسی برداریم.