یکشنبه, ۹ اردیبهشت, ۱۴۰۳ / 28 April, 2024
مجله ویستا

برنامه نویسی تابعی


برنامه نویسی تابعی
برنامه نویسی تابعی یک دسته بندی برنامه نویسی است که با محاسبه به عنوان یک ارزیابی از تابع های ریاضی رفتار می کند.
در مقایسه با برنامه نویسی آمرانه، برنامه نویسی تابعی روی ارزیابی عبارات تابعی نسبت به اجرای فرامین تاکید بیشتری دارد. عبارات در این زبان با استفاده از توابع به منظور ترکیب مقادیر پایه ای شکل می گیرند.
● مقدمه
توابع ریاضی استحکام زیادی از لحاظ تحلیل و تطابق دارند. به عنوان مثال، اگر یک تابع idempotent شناخته شده باشد، یک فراخوانی تابع که خودش را به عنوان آرگومان می گیرد، و هیچ اثرات جانبی ندارد، ممکن است به صورت کارآمدی بدون فراخوانی های چندگانه محاسبه شود.
یک تابع از این جهت، صفر یا تعداد بیشتری پارامتر ورودی، و یک مقدار بازگشتی دارد. پارامترها – یا آرگومان ها، که گاهی به این نام خوانده می شوند- ورودی های تابع هستند، و مقدار بازگشتی، خروجی تابع است. تعریف تابع تشریح می کند که تابع چگونه باید برحسب توابع دیگر ارزیابی شود. به عنوان مثال، تابع"f(x) = x۲ + ۲" برحسب توابع توان و جمع تعریف شده است. از بعضی دیدگاه ها، زبان باید توابع اساسی را داشته باشد تا نیاز به تعریف بیشتر نباشد.
توابع می توانند از راه های گوناگونی در یک زبان برنامه نویسی تابعی دستکاری شوند. با توابع به عنوان مقدارهای کلاس اول رفتار می شود، که باید گفته شود توابع می توانند پارامترها یا ورودی های توابع دیگر بوده ونیز مقادیر بازگشتی یا خروجی های یک تابع باشند. این به توابعی مثل mapcar در LISP و map در Haskell اجازه می دهد که هم یک تابع و هم یک لیست را به عنوان ورودی بردارند و تابع ورودی را برای هر عنصر از لیست بکار برند. توابع می توانند نامگذاری شوند، مانند زبان های دیگر، یا به صورت بی نام ( گاهی در حین اجرای برنامه) با استفاده از انتزاع lambda تعریف شده و به عنوان مقدار در توابع دیگر استفاده شوند. زبان های تابعی همچنین به توابع، امکان "پرداخت" شدن را می دهد. پرداخت یک تکنیک برای دوباره نویسی یک تابع با پارامترهای چندگانه به عنوان ترکیبی از توابع یک پارامتر است. یک تابع پرداخت شده می تواند برای یک زیر مجموعه از پارامترهایش به کار رود. نتیجه تابعی است جایی که پارامترها در این زیر مجموعه حالا به عنوان ثابت ها، ثابت شده اند و مقادیر بقیه ی پارامترها هنوز نامشخص است. این تابع جدید می تواند برای پارامترهای باقی مانده برای بدست آوردن مقدار تابع نهایی بکار رود.
به عنوان مثال، یک تابع add(x,y) = x +y می تواند پرداخت شود بنابراین مقدار بازگشتی (۲)add – توجه داشته باشید که پارامتر y وجود ندارد – یک تابع بی نام خواهد بود که معادل تابع add۲ (y) = ۲ + y است.
این تابع جدید فقط یک پارامتر دارد و معادل جمع ۲ با یک عدد است. دوباره تاکید می کنیم که چنین چیزی تنها به این دلیل ممکن است که با توابع به عنوان مقادیر کلاس اول رفتار میشود.
● تاریخچه
حساب Lambda می تواند اولین زبان برنامه نویسی تابعی در نظر گرفته شود، اگر چه برای اجرا روی یک کامپیوتر طراحی نشده است. حساب Lambda یک مدل محاسبه است که به وسیله ی Alonzo Church در دهه ی ۱۹۳۰ طراحی شده است که یک راه خیلی رسمی برای توصیف ارزیابی تابع تامین می کند. اولین زبان برنامه نویسی تابعی بر اساس کامپیوتر زبان پردازش اطلاعات (IPL) بود، که بوسیله ی Newell، Shaw، و Simon در شرکت RAND برای کامپیوتر JOHNNIAC در اواسط دهه ی ۱۹۵۰ گسترش یافت. زبان برنامه نویسی تابعی پیشرفته تر LISP بود که به وسیله ی John McCarthy در انستیتوی تکنولوژی ماساچوست برای کامپیوترهای علمی IBMسری ۷۰۰/۷۰۰۰ در اواخر دهه ی ۱۹۵۰ گسترش یافت. در حالیکه LISP یک زبان برنامه نویسی تابعی کامل نبود، ولی بسیاری از خصیصه هایی را که الان در زبان های برنامه نویسی تابعی مدرن یافت می شود را معرفی می کرد. زبان Scheme تلاش بعدی برای ساده سازی و گسترش LISP بود. در دهه ی ۱۹۷۰ زبان ML در دانشگاه ادینبرگ ایجاد شد، و دیوید ترنر زبان Muranda را در دانشگاه کنت گسترش داد. زبان Haskell در اواخر دهه ی ۱۹۸۰ در یک تلاش برای جمع آوری نظرات در تحقیق برنامه نویسی تابعی ایجاد شد.
● مقایسه با برنامه نویسی آمرانه
برنامه نویسی تابعی می تواند با برنامه نویسی آمرانه مقایسه شود. برنامه نویسی تابعی به نظر می رسد ساختارهای مختلفی را که برای یک زبان آمرانه ضروری است مثل C یا پاسکال از دست می دهد. به عنوان مثال، در برنامه نویسی تابعی دقیق، هیچ تخصیص حافظه ی صریح و هیچ تخصیص متغیر صریحی وجود ندارد. اگرچه، این عملیات وقتی تابعی احضار می شود به صورت اتوماتیک اتفاق می افتد. تخصیص حافظه برای ایجاد فضا برای پارامترها و مقدار بازگشتی انجام می شود و تخصیص برای کپی پارامترها به این فضای اختصاص یافته ی جدید و کپی مقدار بازگشتی به تابع فراخوانی شده اتفاق می افتد. هر دو عملیات می توانند فقط روی خروج و ورود تابع اجرا شوند، بنابراین اثرات جانبی ارزیابی تابع حذف می شود.
با رد کردن اثرات جانبی در توابع، زبان شفافیت ارجاعی پیدا می کند. این سبب می شود که نتیجه ی یک تابع برای یک مجموعهی مشخص از پارامترها یکسان شود بدون اهمیت به اینکه کی و کجا ارزیابی شده است. شفافیت ارجاعی به میزان زیادی وظیفه ی اثبات صحت برنامه و وظیفه ی شناسایی اتوماتیک محاسبات مستقل برای اجرای موازی را آسان می کند.
حلقه، دیگر ساختار برنامه نویسی آمرانه، در طول ساختار تابعی عمومی تر بازگشت انجام می شود. توابع بازگشتی خودشان را احضار می کنند، و به یک عمل اجازه ی اجرای مکرر را می دهند. در حقیقت، می توان ثابت کرد که حلقه معادل نوع خاصی از بازگشت است که بازگشت دنباله ای نامیده می شود. بازگشت در برنامه نویسی تابعی می تواند شکل های گوناگونی بگیرد و عموماً یک تکنیک قوی تری نسبت به حلقه است. به همین دلیل، تقریباً همه ی زبان های آمرانه نیز آنرا پشتیبانی می کنند (بجز زبان هایی مثل FORTRAN ۷۷ و COBOL ).
● زبان های برنامه نویسی تابعی
برنامه های تابعی "خالص" نیاز به هیچ متغیر و اثرات جانبی ندارد، و بنابراین به صورت اتوماتیک امنیت رشته ای ، قابلیت تغییر اتوماتیک ( تا زمانی که هر سیکل بازگشتی عاقبت بایستد) و بسیاری ماهیت های خوب از این نوع دارند. توابع تودرتو فقط نتایج شان را به تابع اصلی باز می گردانند. تکمیل این زبان ها معمولاً استفاده ی کاملاً ماهرانه ای از دستکاری stack را ممکن می سازد، همان طوری که معمولاً نیز استفاده می شود.
برنامه نویسی تابعی اغلب بستگی زیادی به بازگشت دارد. زبان برنامه نویسی Scheme حتی نیاز به انواع مشخص بازگشت بازگشت دنباله ای دارد که شناخته شود و به صورت اتوماتیک به وسیله کامپایلر بهینه شود.
درضمن، زبان های برنامه نویسی تابعی احتمال دارد شفافیت مرجعی را اجرا کند، تصور آشنایی است که مساویها می توانند با یکدیگر جایگزین شوند: اگر دو عبارت با مقدارهای مساوی تعریف شوند، یکی می تواند درهر عبارت بزرگتری، بدون تاثیر روی نتیجه ی محاسبه جایگزین دیگری شود.
به عنوان مثال، در
z = f ( sqrt (۲) , sqrt (۲) );
ما می توانیم sqrt (۲) را فاکتور بگیریم و بنویسیم
s = sqrt (۲) ;
z = f ( s, s) ;
بنابراین ارزیابی اضافی از تابع ریشه ی دوم حذف می شود.
بدیهی است که چنین موردی همیشه در زبان آمرانه برقرار نیست. یک مورد مناسب، تابع getchar ( ) زبان برنامه‌نویسی C است، که اکیداً تابعی است نه از آرگومان هایش که از محتوای جریان ورودی stdin و مقداری که قبلاً خوانده شده است. به مثال روبرو توجه کنید:
z = f ( getchar ( ) , getchar ( ) ) ;
ما نمی توانیم getchar ( ) را مانند sqrt (۲) حذف کنیم، چون در C، " getchar ( )" باید دو مقدار متفاوت را در دو باری که فراخوانی می شودبرگرداند.
اثرات جانبی مخفی، عموماً یک قانون از زبان های برنامه نویسی قدیمی است، تا استثناء،. هر بار یک رویه یک مقدار را می خواند یا یک مقدار را در یک متغیر global یا اشتراکی می نویسد، پتانسیل اثرات جانبی مخفی وجود دارد. این نشر اطلاعات در طول مرزهای رویه به طریقی که صریحاً با تعاریف و فراخوانی های تابع نشان داده نمی شود شدیداً پیچیدگی مخفی برنامه های نوشته شده در زبان های غیر تابعی قراردادی را افزایش می دهد.
با حذف این شکاف های اطلاعاتی مخفی، زبان های برنامه نویسی تابعی امکان برنامه های بهتر که برای طراحی و عیب یابی، ساده ترند را فراهم می کند. اگر چه، آنها محاسن دیگری نیز دارند.
بیشتر برنامه نویسان به دسته بندی آمرانه عادت کرده اند و یادگیری برنامه نویسی تابعی، که شامل کل راه های مختلف ترکیب برنامه ها می باشد را سخت می دانند. این مشکل همراه با این حقیقت که محیط های برنامه نویسی تابعی، کتابخانه و وسایل جامع در دسترس برای زبان های برنامه نویسی قدیمی را ندارند، از مهمترین دلایلی است که برنامه نویسی تابعی استفاده ی کمی در صنعت نرم افزار پیدا کرده است. زبان های تابعی در حوزه ی دانشگاهی و سرگرمی باقی مانده اند، تاخت و تاز کمی که صورت گرفته در نتیجه ی زبان های تابعی ناخالص مانندErlang و Common Lisp است. می توان چنین بحث کرد که بیشترین تاثیر برنامه نویسی تابعی روی صنعت نرم افزار به وسیله ی برنامه نویسان با تحصیلات دانشگاهی انجام شده که کاربرد مدل برنامه نویسی تابعی ناخالص را در کارهایشان در زبان های آمرانه قدیمی ادامه دادند.
● توابع مرتبه بالاتر
یک مکانیزم قدرتمند که گاهی اوقات در برنامه نویسی تابعی استفاده می شود عقیده ی توابع مرتبه بالاتر است. توابع مرتبه بالاتر، توابعی هستند که بتوانند توابع دیگر را به عنوان آرگومان قبول کنند و یا آنها را به عنوان نتیجه برگردانند، کلمه مشتق در حساب یک نمونه ی معمول از یک تابعی است که تابعی را به تابع دیگر تصویر می کند.)
توابع مرتبه بالاتر درتئوری حساب Lambda قبل از این که تصور برنامه نویسی تابعی به وجود بیاید، مطالعه شده بودند، و روی طراحی بسیاری از زبان های برنامه نویسی تابعی، مانند Haskell تاثیر گذاشته اند، و حتی بذر دسته بندی برنامه نویسی سطح تابع را که شامل زبان هایی مثل Backus&#۰۳۹;FP است را افشاندند.
● ملاحظات فضا و سرعت
زبان های تابعی به عنوان گرسنه ی منبع مورد انتقاد قرار گرفته اند، هم برحسب حافظه و هم منابع CPU. این در اصل نتیجه دو چیز است:
۱) زبان های تابعی اولیه بدون هیچ تلاشی در بازدهی تکمیل شده بودند.
۲) زبان های غیر تابعی با ترک کردن خصیصه هایی مثل چک مرزها یا جمع آوری زباله که به عنوان بخش ضروری چارچوب های کاری محاسبات مدرن، سربار چیزی که به وسیله ی پیش فرض جزئی از ساختمان زبانهای تابعی بود، به نظر می رسد حداقل تا یک اندازه سرعت بدست می آورند.
وقتی زبان های آمرانه ی مدرن و تکمیلات آنها شروع به در برگرفتن تاکید بیشتر روی صحت نسبت به سرعت خام کردند و تکمیلات زبانهای تابعی شروع به تاکید روی سرعت به اندازه ی صحت کرد، اجرای زبانهای تابعی و زبان های آمرانه شروع به همگرایی کرد.
برای زبان هایی که بیشتر وقتشان را روی انجام محاسبات عددی می گذرانند، بعضی از زبانهای تابعی (مثل OCmal و Clean ) می توانند به سرعت C)) نزدیک شوند، در حالی که برای برنامه هایی که ماتریس های بزرگ و بانک های اطلاعاتی چند بعدی را دستکاری می کنند، زبان های تابعی ((آرایه ای (مثل J)) و ((K) معمولاً سریعتر از برنامه های C غیر بهینه هستند.
اگرچه، زبان های تابعی خالص می توانند به صورت قابل ملاحظه ای هنگام دستکاری ساختارهای اطلاعاتی بزرگ در نتیجه ی استفاده ی حافظه ی کمتر کارآمد، کند شود. ارزیابی تنبل نیز یک سربار اضافی در زبان هایی مثل Haskell اضافه می کند.
● زبان های نمونه
قدیمی ترین نمونه ی زبان تابعی LISP است ( با استفاده از اسم اصلی برای تاکید روی Lisp "خالص"). نمونه ی قانونی جدید Haskell است. بقیه شامل Scheme، Erlang، Clean، و Miranda می باشد.
منبع : شبکه رشد


همچنین مشاهده کنید