Mehdi Asgari
یک شنبه 03 آذر 1387, 22:18 عصر
سلام
این اولین پست از سری مقالات "برنامه نویسی تابعی در C#" هست. تعداد مقالات مشخص نیست ، ولی تمامی مفاهیم تابعی پشتیبانی شده در C# مطرح خواهند شد.
نکات ایمنی:
1- لطفا اگر سوالی هست ، پس از حصول اطمینان از درک این مقاله (و مطالعۀ مقالات مرجع و لینک های داده شده) ، در صورت حل نشدن ، اون رو در همین تاپیک مطرح کنید. عمده ی مثال ها به زبان C# هستن و در برخی قسمت ها برای درک بیشتر ، از زبان های F# ، Haskell و Scheme هم استفاده می کنم. لطفا برای تشکر یا سوالات خیلی ساده پست جدید ایجاد نکنید (نه فقط در اینجا ، بلکه در هر تاپیک مقاله ای) (این رو هم در نظر بگیرید که سطح این مطالب "متوسط تا پیشرفته" هست)
2- من ادعایی در زمینۀ Functional Programming ندارم ، فقط چون خودم الان در حال یادگیری این موضوع هستم ترجیح دادم آموخته هام رو در اینجا قرار بدم. به قول بنیامین دیزرائلی "بهترین روش برای آشنایی با یک موضوع ، نوشتن کتابی دربارۀ اون موضوعه!"
3- تمامی این پست ها ترجمه هستن ، منتها ترجمۀ آزاد از کلی منبع و به صورت در هم آمیخته (یعنی هیچ جمله ای رو مستقیما از جایی برنداشتم ، بلکه اون چیزی رو که خودم از این مقالات و کتاب ها فهمیدم ،نوشتم. تمامی منابع و لینک ها رو خودم کاملا مطالعه کرده و تمامی مثال ها رو تست کردم)
4- این اولین سری از مقالاتمه. احتمالا سری بعدی ، Concurrency (http://en.wikipedia.org/wiki/Concurrency_(computer_science))+ TPL (http://en.wikipedia.org/wiki/Parallel_FX_Library)باشه. (یکی از دلایل توجه بیشتر برنامه نویسان و شرکت های بزرگ برنامه نویسی نسبت به برنامه نویسی تابعی ، موازی شدن راحت تر اون ها به دلیل نداشتن side effect (http://en.wikipedia.org/wiki/Side_effect_(computer_science)) هست)
قسمت 0 : مقدمه (تئوری ها ، مفاهیم و تاریخچه):
علم کامپیوتر مدیون ریاضی دانان زمان جنگ جهانی دومه. خیلی از مفاهیم بنیادی این رشته در این سال ها (دهۀ 30 و 40 میلادی) مطرح شدن ؛ زمانی که هنوز کامپیوتر (به شکل امروزی و گسترده) در دسترس کسی نبود (به استثنای مراکز نظامی). یکی از مراکزی که نخبه های اون زمان رو یک جا جمع کرده بود ، دانشگاه پرینستون (www.princeton.edu/)امریکا بود. آلن تورینگ (http://en.wikipedia.org/wiki/Alan_Turing) ، جان فون نیومن (http://en.wikipedia.org/wiki/John_von_Neumann) ، آلونزو چرچ (http://en.wikipedia.org/wiki/Alonzo_Church) و ...
در سال 1936 ، آلن تورینگ و آلونزو چرچ همزمان به حل مسئلۀ لایبنیتز (http://en.wikipedia.org/wiki/Gottfried_Leibniz)پرداختن (پیدا کردن روشی برای حل تمامی مسائل که در زبان جهانی (universal language) بیان شدن) روش تورینگ رو حتما شنیدید (ماشین تورینگ (http://en.wikipedia.org/wiki/Turing_machine))؛ ماشین های فون نیومن (همین کامپیوترهایی که ازشون استفاده می کنیم) نوعی ماشین تورینگ هستن ؛ همینطور زبان های دستوری مثل اسمبلی ، سی ، پاسکال ، .... بر این اساس کار می کنن (یعنی رشته ای از دستورات)
و اما روش آقای چرچ که اساس برنامه نویسی تابعی رو تشکیل میده ، حساب لامبدا (http://en.wikipedia.org/wiki/Lambda_calculus) هست. حساب لامبدا رو توضیح نمی دم ، به منابع مراجعه کنید. این روش مبنی بر توابع هست (بعدا می بینیم که در زبان های تابعی محض ما متغیر نداریم و همه چیز تابعه و immutable) آقای تورینگ ثابت کرد که هر دو روش از نظر قدرت برابرن.
اصول برنامه نویسی تابعی همونطور که از اسمش پیداست ، تابعه. ما تابع تعریف می کنیم ، آرگومان های توابع از نوع تابع هستن ، تابع بر می گردونیم ، از بازگشتی استفاده می کنیم ، در واقع محاسبۀ هر چیزی بر اساس تابع هست.
در سال 1958 آقای مک کارتی (http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)) LISP (http://en.wikipedia.org/wiki/Lisp_(programming_language)) رو به وجود آورد که اولین زبانی بود که بر اساس حساب لامبدا کار می کرد. (البته کاملا تابعی نبود).از پیاده سازی های معروف میشه به GNU CLISP (http://clisp.cons.org/) اشاره کرد.
از اون موقع تا الان زبان های تابعی زیادی به وجود اومدن :
• Scheme (http://en.wikipedia.org/wiki/Scheme_(programming_language)): توسط Guy Steele (http://research.sun.com/minds/2005-0302/) به وجود اومد. از dialect (http://en.wikipedia.org/wiki/Programming_language_dialect)های LISP هست. (اگه کد LISP یا Scheme رو دیده باشید ، متوجه میشید که خیلی از پرانتز استفاده میشه در این زبان ها) عیب بزرگ Scheme نبود استانداردی واحده ، برای همین پیاده سازی های مختلفی از اون وجود داره (معروف ها: PLT Scheme (http://www.plt-scheme.org/) ، Scheme48 (http://s48.org/)، Ikarus (http://www.cs.indiana.edu/~aghuloum/ikarus/)، IronScheme (www.codeplex.com/IronScheme) ....) Scheme برای تدریس "آشنایی با برنامه نویسی" در یک سری دانشگاه ها و مراکز آموزشی استفاده میشه. (http://www.schemers.com/schools.html) (معروف ترینش کلاس 6.001 (http://ocw.mit.edu/OcwWeb/Electrical-Engineering-and-Computer-Science/6-001Spring-2005/CourseHome/) دانشگاه MIT (http://mit.edu/)برای رشتۀ "Computer Science" بود که اخیرا به جاش از پایتون استفاده می کنن.) توجه: سال 2007 بالاخره یه استاندارد R6RS (www.r6rs.org)به وجود اومد ، ولی هنوز همۀ نسخه ها اون رو پیاده سازی نکردن
• Haskell (http://en.wikipedia.org/wiki/Haskell_(programming_language)): گل سر سبد زبان های تابعی ؛ یکی از معدود زبان هایی که "تابعی محض" هستن (pure functional (http://en.wikipedia.org/wiki/Purely_functional))
• ML (http://en.wikipedia.org/wiki/ML_programming_language): از موفق ترین زبان های تابعی هست. در واقع مثل C زبان های تابعی میمونه. دو dialect بزرگ داره: SML (http://en.wikipedia.org/wiki/Standard_ML)و OCAML (http://en.wikipedia.org/wiki/OCaml)
• Erlang (http://en.wikipedia.org/wiki/Erlang_(programming_language)): توسط شرکت اریکسون (http://en.wikipedia.org/wiki/Ericsson)به وجود اومد و شهرتش رو مدیون پشتیبانی خوبش از concurrent programming (http://en.wikipedia.org/wiki/Concurrent_computing) و real-time computing (http://en.wikipedia.org/wiki/Real-time) هست.
• F# (http://en.wikipedia.org/wiki/F_Sharp_(programming_language)): توسط شرکت مایکروسافت در سال 2005 به وجود اومد (بر اساس OCaml ؛ در واقع سازگاری زیادی با OCaml داره) این زبان علاوه بر برنامه نویسی تابعی ، از OOP (http://en.wikipedia.org/wiki/Object-oriented_programming)هم پشتیبانی می کنه. مزیت بزرگ این زبان (به غیر از پشتیبانی مایکروسافت) بهره گیری از .Net Framework هست که بر خلاف بسیاری از زبان های تابعی ، لااقل مشکل library نداره.
توجه: این زبان ها ، بر روی ماشین های فون نیومن اجرا میشن (همون کامپیوتر های خودمون)، ولی در قدیم ماشین های مخصوصی هم برای بعضی از زبان ها وجود داشته (مثل LISP Machines (http://en.wikipedia.org/wiki/Lisp_machine))
شاید قبلا شنیده باشید که این زبان ها به درد برنامه نویسی واقعی نمی خورن و بیشتر توسط دانشگاهیان و جامعۀ آکادمیک استفاده میشه. بخش اول این حرف غلط و بخش دومش درسته.
هیچ محدودیتی در ایجاد برنامه های واقعی (وب ، پایگاه داده ، گرافیک ، .... ) توسط زبان های تابعی وجود نداره ، این هم لیستی از برنامه های نوشته شده در زبان های تابعی و کاربرد اون ها:
http://homepages.inf.ed.ac.uk/wadler/realworld/
http://bc.tech.coop/blog/041027.html
http://caml.inria.fr/about/successes.en.html
http://wiki.cocan.org/ocaml_success_stories
در بخش jobs خیلی از شرکت ها هم برنامه نویس "OCaml" ، LISP و ... خواهید دید.
پس چرا در برنامه نویسی mainstream کمتر از این زبان ها استفاده میشه ؟
بعضی ها میگن یادگیری و کار با این زبان ها سخته. هر چیز ناآشنا و جدیدی برای اولین بار سخت به نظر می رسه. (فکر کنید اگه از اول با زبان های تابعی کار کرده بودیم ، یادگیری زبانی مثل C برامون خیلی سخت می شد)
نبود استاندارد جامع و کتابخانه های توابع هم یه مشکله. به اینجا (http://community.schemewiki.org/?scheme-faq-standards#implementations) یه سری بزنید تا ببینید چقدر پیاده سازی مختلف از Scheme داریم (که به غیر از هستۀ زبان ، هیچ چیزیشون با هم سازگار نیست. گاها بعضی ساختار های زبانی مثل unless هم بین نسخه ها فرق می کنه)
تاکید بیش از حد بر روی نداشتن side effect در بعضی زبان ها هم باعث کاهش محبوبیتشونه: به قول آقای Hejlsberg (http://channel9.msdn.com/posts/Charles/Anders-Hejlsberg-and-Guy-Steele-Concurrency-and-Language-Design/) ، "به هرحال ما باید یک چیزی رو در صفحه نمایش بدیم یا نه ؟" و این همون side-effect هست (یعنی تغییر state) (در اینجا (http://channel9.msdn.com/posts/Charles/Simon-Peyton-Jones-Towards-a-Programming-Language-Nirvana/)آقای Peyton-Jones (http://en.wikipedia.org/wiki/Simon_Peyton_Jones) در مورد راه حل هایی برای افزایش "مفید بودن" و "کارایی" Haskell صحبت می کنه .
بعضی از زبان های تابعی هم سرعت پایینی در اجرا دارن (البته الان زبان هایی مثل OCaml فوق العاده سریع هستن: http://shootout.alioth.debian.org)
این زبان ها پایۀ ریاضی و تئوری قوی ای دارن و برای حل مسائل علمی خیلی مناسبن و همین باعث جلب توجه جامعۀ اکادمیک شده.
خب ، الان همه چیز فرق کرده:
در چند سال اخیر توجه بیشتری به این زبان ها مبذول شده ؛ مثال بارزش ارائۀ زبان F# از تیم تحقیقاتی مایکروسافت هست (در ابتدا آقای Don Syme (http://research.microsoft.com/~dsyme/) به تنهایی روی این زبان کار می کردن) که قراره در نسخۀ بعدی VS جزو زبان های first class مایکروسافت باشه.
یکی از دلایل ، متوقف شدن قانون Moore (http://en.wikipedia.org/wiki/Moore%27s_law) هست (افزایش سرعت پردازنده ها هر 18 ماه یک بار) الان به جایی رسیدیم که نمی تونیم مثل دهۀ قبل ، برنامه هامونو بنویسیم و از آخرین فن آوری ها استفاده کنیم و خیالمون راحت باشه که پردازنده ها روز به روز دارن سریع تر میشن و برنامه های ما بدون مشکل اجرا خواهند شد. الان عصر Many Core (http://en.wikipedia.org/wiki/Many-core) هست (سرعت تقریبا ثابت مونده ، و تعداد هسته ها افزایش پیدا می کنه.) و این یعنی اگه پرفورمنس بالاتری میخوایم باید برنامه هامون رو طوری بنویسیم که بتونن بر روی thread های مختلف (به نوبۀ خود ، روی چند هسته) اجرا بشن.
خب این چه ربطی به زبان های تابعی داره ؟ زبان های تابعی به دلیل نداشتن side effect (اثر جانبی) قابلیت "موازی شدن" بالایی دارن. یعنی کامپایلر می تونه خیلی راحت دو بخش از برنامه رو موازی کنه (و خیالش راحته که دچار مشکلاتی مثل همزمانی (http://en.wikipedia.org/wiki/Synchronization_(computer_science))و race condition (http://en.wikipedia.org/wiki/Race_condition) و ... نخواهد شد ، چون اصولا بخش اول هیچ state(حالتی) رو تغییر نمیده ، تا این تغییر حالت باعث ایجاد مشکل در بخش دوم بشه. حالا قرار نیست حتما برای پردازش موازی از این زبان ها استفاده کنیم ، ولی می تونیم ازشون ایده بگیریم (کاری که آقای Hejlsberg در سی شارپ کرد و موضوع این سری مقالات هست)
از دیگر دلایل افزایش محبوبیت این زبان ها ، تبلیغ بزرگان (Paul Graham (http://en.wikipedia.org/wiki/Paul_Graham) ، ESR (http://www.catb.org/~esr/faqs/hacker-howto.html#skills1)، ... ) و چاپ کتاب های مختلف در زمینۀ برنامه نویسی "واقعی" با این زبان ها در سال های اخیره (مثال مشهور: Practical Common LISP (http://gigamonkeys.com/book/))
برای آشنایی با سینتکس این زبان ها ، یه برنامۀ کوچیک برای محاسبۀ عدد فیبوناچی با سه زبان سی شارپ ، F# و Scheme می نویسیم:
// C# version
using System;
class Program
{
static int Fibo(int n)
{
if (n <= 1)
return n;
else
return Fibo(n - 1) + Fibo(n - 2);
}
static void Main()
{
int i = Convert.ToInt32( Console.ReadLine());
Console.WriteLine("Fibo({0}) = {1}", i, Fibo(i));
}
}
-------------------------------------------------------------------------------
// F#
#light
open System
let rec fibo n =
match n with
0 -> 0
| 1 -> 1
| n -> fibo(n - 1) + fibo(n - 2)
let i = Int32.Parse(Console.ReadLine())
Console.WriteLine(fibo i)
-------------------------------------------------------------------------------
;Scheme
(define fibo
(lambda (x)
(if (< x 2)
x
(+ (fibo (- x 1)) (fibo (- x 2))))))
(define getInp
(lambda()
(let ((a (read)))
(cond ((number? a) (display (fibo a)))))))
(getInp)
مفاهیم و اصطلاحات:
Side effect: هر چیزی که حالت (state) ماشین رو تغییر بده گفته میشه. (چاپ چیزی در خروجی ، تغییر مقدار یک متغیر ، ... )
Pure functional: زبانی که فقط برنامه نویسی تابعی رو پشتیبانی می کنه. (بیشتر زبان های تابعی multi-paradigm (http://en.wikipedia.org/wiki/Multi-paradigm_programming_language) هستن ، یعنی ساختار هایی از دیگر زبان ها رو هم تا حدودی پشتیبانی می کنن)
First class function (http://en.wikipedia.org/wiki/First-class_function): اگه زبانی بتونه در زمان اجرا تابع ایجاد کنه ، توابع رو در ساختار های داده های ذخیره کنه ، توابع رو به عنوان پارامتر به دیگر توابع ارسال کنه و از تابع به عنوان نوع برگشتی توابع پشتیبانی کنه ، می گیم که توابع در اون زبان first class هستن
Higher-order function (http://en.wikipedia.org/wiki/Higher-order_function): تابعی که یا "یک یا چند تابع رو به عنوان آرگومان می گیره" یا "نوع برگشتی اش تابع هست"
Recursion (http://en.wikipedia.org/wiki/Recursion_(computer_science)): از ساختار هایی که استفادۀ خیلی زیادی در زبان های تابعی داره ، "تابع بازگشتی" هست.
Lazy Evaluation (http://en.wikipedia.org/wiki/Lazy_evaluation): ارزیابی تنبل ؛ فکر کنم تو درس "طراحی و پیاده سازی زبان های برنامه سازی" در مقطع کارشناسی مهندسی کامپیوتر (گرایش نرم افزار) این مفهوم رو خوندیم. یه آدم تنبل ، معمولا دقیقه نودی عمل می کنه ؛ تا مجبور نشه کاری رو نمی کنه. ارزیابی تنبل هم یعنی تا وقتی که به یک تابع (مقدار) نیاز نداشته باشیم ، فراخوانی نمیشه (ارزیابی نمیشه) مثال زیر رو ببینید:
static int F(bool cond, int arg)
{
if (cond)
return arg;
return 0;
}
static void Main(string[] args)
{
int x = SomeFunction ();
F(false, x);
}
تابعی تعریف کردیم که یک متغیر شرط (bool) می گیره و یک عدد ؛ اگه شرط درست بود ، اون عدد رو بر می گردونه ، وگرنه مقدار صفر رو برمی گردونه.
فرض کنید SomeFunction یک تابع سنگین باشه که نیاز به زمان طولانی ای (مثلا 10 ثانیه) برای اجرا داره و cond هم false هست. در یک زبان معمولی ، هنگام فراخوانی کد بالا ، ابتدا SomeFunction فراخوانی شده ، مقدار اون در x ریخته میشه و x به عنوان arg به F داده میشه ، ولی ازش استفاده ای نمیشه! چون شرط false هست. در زبان هایی که از lazy evaluation پشتیبانی می کنن ، وقتی SomeFunction فراخوانی خواهد شد که شرط درست باشه و ما نیاز به مقدار x داشته باشیم. (البته این روش مزایا و معایب خودش رو داره؛ بعضی زبان ها اجازه میدن که خودتون یه statement رو lazy کنید در صورت نیاز) یکی از موارد استفادۀ lazy evaluation ایجاد لیست های نامتناهی هست (وقتی عنصر n+1 ایجاد خواهد شد که نیاز باشه. با yield در سی شارپ همچین چیزی رو میشه شبیه سازی کرد)
این برای جلسۀ اول
(به یکی از مدیران گفته بودم اولین مقاله رو دو هفتۀ قبل میدم ، ولی الان تونستم بنویسم. {شکلک خجالت + سرشلوغی!})
اینم بخونید:
http://www.md.chalmers.se/~rjmh/Papers/whyfp.html
این اولین پست از سری مقالات "برنامه نویسی تابعی در C#" هست. تعداد مقالات مشخص نیست ، ولی تمامی مفاهیم تابعی پشتیبانی شده در C# مطرح خواهند شد.
نکات ایمنی:
1- لطفا اگر سوالی هست ، پس از حصول اطمینان از درک این مقاله (و مطالعۀ مقالات مرجع و لینک های داده شده) ، در صورت حل نشدن ، اون رو در همین تاپیک مطرح کنید. عمده ی مثال ها به زبان C# هستن و در برخی قسمت ها برای درک بیشتر ، از زبان های F# ، Haskell و Scheme هم استفاده می کنم. لطفا برای تشکر یا سوالات خیلی ساده پست جدید ایجاد نکنید (نه فقط در اینجا ، بلکه در هر تاپیک مقاله ای) (این رو هم در نظر بگیرید که سطح این مطالب "متوسط تا پیشرفته" هست)
2- من ادعایی در زمینۀ Functional Programming ندارم ، فقط چون خودم الان در حال یادگیری این موضوع هستم ترجیح دادم آموخته هام رو در اینجا قرار بدم. به قول بنیامین دیزرائلی "بهترین روش برای آشنایی با یک موضوع ، نوشتن کتابی دربارۀ اون موضوعه!"
3- تمامی این پست ها ترجمه هستن ، منتها ترجمۀ آزاد از کلی منبع و به صورت در هم آمیخته (یعنی هیچ جمله ای رو مستقیما از جایی برنداشتم ، بلکه اون چیزی رو که خودم از این مقالات و کتاب ها فهمیدم ،نوشتم. تمامی منابع و لینک ها رو خودم کاملا مطالعه کرده و تمامی مثال ها رو تست کردم)
4- این اولین سری از مقالاتمه. احتمالا سری بعدی ، Concurrency (http://en.wikipedia.org/wiki/Concurrency_(computer_science))+ TPL (http://en.wikipedia.org/wiki/Parallel_FX_Library)باشه. (یکی از دلایل توجه بیشتر برنامه نویسان و شرکت های بزرگ برنامه نویسی نسبت به برنامه نویسی تابعی ، موازی شدن راحت تر اون ها به دلیل نداشتن side effect (http://en.wikipedia.org/wiki/Side_effect_(computer_science)) هست)
قسمت 0 : مقدمه (تئوری ها ، مفاهیم و تاریخچه):
علم کامپیوتر مدیون ریاضی دانان زمان جنگ جهانی دومه. خیلی از مفاهیم بنیادی این رشته در این سال ها (دهۀ 30 و 40 میلادی) مطرح شدن ؛ زمانی که هنوز کامپیوتر (به شکل امروزی و گسترده) در دسترس کسی نبود (به استثنای مراکز نظامی). یکی از مراکزی که نخبه های اون زمان رو یک جا جمع کرده بود ، دانشگاه پرینستون (www.princeton.edu/)امریکا بود. آلن تورینگ (http://en.wikipedia.org/wiki/Alan_Turing) ، جان فون نیومن (http://en.wikipedia.org/wiki/John_von_Neumann) ، آلونزو چرچ (http://en.wikipedia.org/wiki/Alonzo_Church) و ...
در سال 1936 ، آلن تورینگ و آلونزو چرچ همزمان به حل مسئلۀ لایبنیتز (http://en.wikipedia.org/wiki/Gottfried_Leibniz)پرداختن (پیدا کردن روشی برای حل تمامی مسائل که در زبان جهانی (universal language) بیان شدن) روش تورینگ رو حتما شنیدید (ماشین تورینگ (http://en.wikipedia.org/wiki/Turing_machine))؛ ماشین های فون نیومن (همین کامپیوترهایی که ازشون استفاده می کنیم) نوعی ماشین تورینگ هستن ؛ همینطور زبان های دستوری مثل اسمبلی ، سی ، پاسکال ، .... بر این اساس کار می کنن (یعنی رشته ای از دستورات)
و اما روش آقای چرچ که اساس برنامه نویسی تابعی رو تشکیل میده ، حساب لامبدا (http://en.wikipedia.org/wiki/Lambda_calculus) هست. حساب لامبدا رو توضیح نمی دم ، به منابع مراجعه کنید. این روش مبنی بر توابع هست (بعدا می بینیم که در زبان های تابعی محض ما متغیر نداریم و همه چیز تابعه و immutable) آقای تورینگ ثابت کرد که هر دو روش از نظر قدرت برابرن.
اصول برنامه نویسی تابعی همونطور که از اسمش پیداست ، تابعه. ما تابع تعریف می کنیم ، آرگومان های توابع از نوع تابع هستن ، تابع بر می گردونیم ، از بازگشتی استفاده می کنیم ، در واقع محاسبۀ هر چیزی بر اساس تابع هست.
در سال 1958 آقای مک کارتی (http://en.wikipedia.org/wiki/John_McCarthy_(computer_scientist)) LISP (http://en.wikipedia.org/wiki/Lisp_(programming_language)) رو به وجود آورد که اولین زبانی بود که بر اساس حساب لامبدا کار می کرد. (البته کاملا تابعی نبود).از پیاده سازی های معروف میشه به GNU CLISP (http://clisp.cons.org/) اشاره کرد.
از اون موقع تا الان زبان های تابعی زیادی به وجود اومدن :
• Scheme (http://en.wikipedia.org/wiki/Scheme_(programming_language)): توسط Guy Steele (http://research.sun.com/minds/2005-0302/) به وجود اومد. از dialect (http://en.wikipedia.org/wiki/Programming_language_dialect)های LISP هست. (اگه کد LISP یا Scheme رو دیده باشید ، متوجه میشید که خیلی از پرانتز استفاده میشه در این زبان ها) عیب بزرگ Scheme نبود استانداردی واحده ، برای همین پیاده سازی های مختلفی از اون وجود داره (معروف ها: PLT Scheme (http://www.plt-scheme.org/) ، Scheme48 (http://s48.org/)، Ikarus (http://www.cs.indiana.edu/~aghuloum/ikarus/)، IronScheme (www.codeplex.com/IronScheme) ....) Scheme برای تدریس "آشنایی با برنامه نویسی" در یک سری دانشگاه ها و مراکز آموزشی استفاده میشه. (http://www.schemers.com/schools.html) (معروف ترینش کلاس 6.001 (http://ocw.mit.edu/OcwWeb/Electrical-Engineering-and-Computer-Science/6-001Spring-2005/CourseHome/) دانشگاه MIT (http://mit.edu/)برای رشتۀ "Computer Science" بود که اخیرا به جاش از پایتون استفاده می کنن.) توجه: سال 2007 بالاخره یه استاندارد R6RS (www.r6rs.org)به وجود اومد ، ولی هنوز همۀ نسخه ها اون رو پیاده سازی نکردن
• Haskell (http://en.wikipedia.org/wiki/Haskell_(programming_language)): گل سر سبد زبان های تابعی ؛ یکی از معدود زبان هایی که "تابعی محض" هستن (pure functional (http://en.wikipedia.org/wiki/Purely_functional))
• ML (http://en.wikipedia.org/wiki/ML_programming_language): از موفق ترین زبان های تابعی هست. در واقع مثل C زبان های تابعی میمونه. دو dialect بزرگ داره: SML (http://en.wikipedia.org/wiki/Standard_ML)و OCAML (http://en.wikipedia.org/wiki/OCaml)
• Erlang (http://en.wikipedia.org/wiki/Erlang_(programming_language)): توسط شرکت اریکسون (http://en.wikipedia.org/wiki/Ericsson)به وجود اومد و شهرتش رو مدیون پشتیبانی خوبش از concurrent programming (http://en.wikipedia.org/wiki/Concurrent_computing) و real-time computing (http://en.wikipedia.org/wiki/Real-time) هست.
• F# (http://en.wikipedia.org/wiki/F_Sharp_(programming_language)): توسط شرکت مایکروسافت در سال 2005 به وجود اومد (بر اساس OCaml ؛ در واقع سازگاری زیادی با OCaml داره) این زبان علاوه بر برنامه نویسی تابعی ، از OOP (http://en.wikipedia.org/wiki/Object-oriented_programming)هم پشتیبانی می کنه. مزیت بزرگ این زبان (به غیر از پشتیبانی مایکروسافت) بهره گیری از .Net Framework هست که بر خلاف بسیاری از زبان های تابعی ، لااقل مشکل library نداره.
توجه: این زبان ها ، بر روی ماشین های فون نیومن اجرا میشن (همون کامپیوتر های خودمون)، ولی در قدیم ماشین های مخصوصی هم برای بعضی از زبان ها وجود داشته (مثل LISP Machines (http://en.wikipedia.org/wiki/Lisp_machine))
شاید قبلا شنیده باشید که این زبان ها به درد برنامه نویسی واقعی نمی خورن و بیشتر توسط دانشگاهیان و جامعۀ آکادمیک استفاده میشه. بخش اول این حرف غلط و بخش دومش درسته.
هیچ محدودیتی در ایجاد برنامه های واقعی (وب ، پایگاه داده ، گرافیک ، .... ) توسط زبان های تابعی وجود نداره ، این هم لیستی از برنامه های نوشته شده در زبان های تابعی و کاربرد اون ها:
http://homepages.inf.ed.ac.uk/wadler/realworld/
http://bc.tech.coop/blog/041027.html
http://caml.inria.fr/about/successes.en.html
http://wiki.cocan.org/ocaml_success_stories
در بخش jobs خیلی از شرکت ها هم برنامه نویس "OCaml" ، LISP و ... خواهید دید.
پس چرا در برنامه نویسی mainstream کمتر از این زبان ها استفاده میشه ؟
بعضی ها میگن یادگیری و کار با این زبان ها سخته. هر چیز ناآشنا و جدیدی برای اولین بار سخت به نظر می رسه. (فکر کنید اگه از اول با زبان های تابعی کار کرده بودیم ، یادگیری زبانی مثل C برامون خیلی سخت می شد)
نبود استاندارد جامع و کتابخانه های توابع هم یه مشکله. به اینجا (http://community.schemewiki.org/?scheme-faq-standards#implementations) یه سری بزنید تا ببینید چقدر پیاده سازی مختلف از Scheme داریم (که به غیر از هستۀ زبان ، هیچ چیزیشون با هم سازگار نیست. گاها بعضی ساختار های زبانی مثل unless هم بین نسخه ها فرق می کنه)
تاکید بیش از حد بر روی نداشتن side effect در بعضی زبان ها هم باعث کاهش محبوبیتشونه: به قول آقای Hejlsberg (http://channel9.msdn.com/posts/Charles/Anders-Hejlsberg-and-Guy-Steele-Concurrency-and-Language-Design/) ، "به هرحال ما باید یک چیزی رو در صفحه نمایش بدیم یا نه ؟" و این همون side-effect هست (یعنی تغییر state) (در اینجا (http://channel9.msdn.com/posts/Charles/Simon-Peyton-Jones-Towards-a-Programming-Language-Nirvana/)آقای Peyton-Jones (http://en.wikipedia.org/wiki/Simon_Peyton_Jones) در مورد راه حل هایی برای افزایش "مفید بودن" و "کارایی" Haskell صحبت می کنه .
بعضی از زبان های تابعی هم سرعت پایینی در اجرا دارن (البته الان زبان هایی مثل OCaml فوق العاده سریع هستن: http://shootout.alioth.debian.org)
این زبان ها پایۀ ریاضی و تئوری قوی ای دارن و برای حل مسائل علمی خیلی مناسبن و همین باعث جلب توجه جامعۀ اکادمیک شده.
خب ، الان همه چیز فرق کرده:
در چند سال اخیر توجه بیشتری به این زبان ها مبذول شده ؛ مثال بارزش ارائۀ زبان F# از تیم تحقیقاتی مایکروسافت هست (در ابتدا آقای Don Syme (http://research.microsoft.com/~dsyme/) به تنهایی روی این زبان کار می کردن) که قراره در نسخۀ بعدی VS جزو زبان های first class مایکروسافت باشه.
یکی از دلایل ، متوقف شدن قانون Moore (http://en.wikipedia.org/wiki/Moore%27s_law) هست (افزایش سرعت پردازنده ها هر 18 ماه یک بار) الان به جایی رسیدیم که نمی تونیم مثل دهۀ قبل ، برنامه هامونو بنویسیم و از آخرین فن آوری ها استفاده کنیم و خیالمون راحت باشه که پردازنده ها روز به روز دارن سریع تر میشن و برنامه های ما بدون مشکل اجرا خواهند شد. الان عصر Many Core (http://en.wikipedia.org/wiki/Many-core) هست (سرعت تقریبا ثابت مونده ، و تعداد هسته ها افزایش پیدا می کنه.) و این یعنی اگه پرفورمنس بالاتری میخوایم باید برنامه هامون رو طوری بنویسیم که بتونن بر روی thread های مختلف (به نوبۀ خود ، روی چند هسته) اجرا بشن.
خب این چه ربطی به زبان های تابعی داره ؟ زبان های تابعی به دلیل نداشتن side effect (اثر جانبی) قابلیت "موازی شدن" بالایی دارن. یعنی کامپایلر می تونه خیلی راحت دو بخش از برنامه رو موازی کنه (و خیالش راحته که دچار مشکلاتی مثل همزمانی (http://en.wikipedia.org/wiki/Synchronization_(computer_science))و race condition (http://en.wikipedia.org/wiki/Race_condition) و ... نخواهد شد ، چون اصولا بخش اول هیچ state(حالتی) رو تغییر نمیده ، تا این تغییر حالت باعث ایجاد مشکل در بخش دوم بشه. حالا قرار نیست حتما برای پردازش موازی از این زبان ها استفاده کنیم ، ولی می تونیم ازشون ایده بگیریم (کاری که آقای Hejlsberg در سی شارپ کرد و موضوع این سری مقالات هست)
از دیگر دلایل افزایش محبوبیت این زبان ها ، تبلیغ بزرگان (Paul Graham (http://en.wikipedia.org/wiki/Paul_Graham) ، ESR (http://www.catb.org/~esr/faqs/hacker-howto.html#skills1)، ... ) و چاپ کتاب های مختلف در زمینۀ برنامه نویسی "واقعی" با این زبان ها در سال های اخیره (مثال مشهور: Practical Common LISP (http://gigamonkeys.com/book/))
برای آشنایی با سینتکس این زبان ها ، یه برنامۀ کوچیک برای محاسبۀ عدد فیبوناچی با سه زبان سی شارپ ، F# و Scheme می نویسیم:
// C# version
using System;
class Program
{
static int Fibo(int n)
{
if (n <= 1)
return n;
else
return Fibo(n - 1) + Fibo(n - 2);
}
static void Main()
{
int i = Convert.ToInt32( Console.ReadLine());
Console.WriteLine("Fibo({0}) = {1}", i, Fibo(i));
}
}
-------------------------------------------------------------------------------
// F#
#light
open System
let rec fibo n =
match n with
0 -> 0
| 1 -> 1
| n -> fibo(n - 1) + fibo(n - 2)
let i = Int32.Parse(Console.ReadLine())
Console.WriteLine(fibo i)
-------------------------------------------------------------------------------
;Scheme
(define fibo
(lambda (x)
(if (< x 2)
x
(+ (fibo (- x 1)) (fibo (- x 2))))))
(define getInp
(lambda()
(let ((a (read)))
(cond ((number? a) (display (fibo a)))))))
(getInp)
مفاهیم و اصطلاحات:
Side effect: هر چیزی که حالت (state) ماشین رو تغییر بده گفته میشه. (چاپ چیزی در خروجی ، تغییر مقدار یک متغیر ، ... )
Pure functional: زبانی که فقط برنامه نویسی تابعی رو پشتیبانی می کنه. (بیشتر زبان های تابعی multi-paradigm (http://en.wikipedia.org/wiki/Multi-paradigm_programming_language) هستن ، یعنی ساختار هایی از دیگر زبان ها رو هم تا حدودی پشتیبانی می کنن)
First class function (http://en.wikipedia.org/wiki/First-class_function): اگه زبانی بتونه در زمان اجرا تابع ایجاد کنه ، توابع رو در ساختار های داده های ذخیره کنه ، توابع رو به عنوان پارامتر به دیگر توابع ارسال کنه و از تابع به عنوان نوع برگشتی توابع پشتیبانی کنه ، می گیم که توابع در اون زبان first class هستن
Higher-order function (http://en.wikipedia.org/wiki/Higher-order_function): تابعی که یا "یک یا چند تابع رو به عنوان آرگومان می گیره" یا "نوع برگشتی اش تابع هست"
Recursion (http://en.wikipedia.org/wiki/Recursion_(computer_science)): از ساختار هایی که استفادۀ خیلی زیادی در زبان های تابعی داره ، "تابع بازگشتی" هست.
Lazy Evaluation (http://en.wikipedia.org/wiki/Lazy_evaluation): ارزیابی تنبل ؛ فکر کنم تو درس "طراحی و پیاده سازی زبان های برنامه سازی" در مقطع کارشناسی مهندسی کامپیوتر (گرایش نرم افزار) این مفهوم رو خوندیم. یه آدم تنبل ، معمولا دقیقه نودی عمل می کنه ؛ تا مجبور نشه کاری رو نمی کنه. ارزیابی تنبل هم یعنی تا وقتی که به یک تابع (مقدار) نیاز نداشته باشیم ، فراخوانی نمیشه (ارزیابی نمیشه) مثال زیر رو ببینید:
static int F(bool cond, int arg)
{
if (cond)
return arg;
return 0;
}
static void Main(string[] args)
{
int x = SomeFunction ();
F(false, x);
}
تابعی تعریف کردیم که یک متغیر شرط (bool) می گیره و یک عدد ؛ اگه شرط درست بود ، اون عدد رو بر می گردونه ، وگرنه مقدار صفر رو برمی گردونه.
فرض کنید SomeFunction یک تابع سنگین باشه که نیاز به زمان طولانی ای (مثلا 10 ثانیه) برای اجرا داره و cond هم false هست. در یک زبان معمولی ، هنگام فراخوانی کد بالا ، ابتدا SomeFunction فراخوانی شده ، مقدار اون در x ریخته میشه و x به عنوان arg به F داده میشه ، ولی ازش استفاده ای نمیشه! چون شرط false هست. در زبان هایی که از lazy evaluation پشتیبانی می کنن ، وقتی SomeFunction فراخوانی خواهد شد که شرط درست باشه و ما نیاز به مقدار x داشته باشیم. (البته این روش مزایا و معایب خودش رو داره؛ بعضی زبان ها اجازه میدن که خودتون یه statement رو lazy کنید در صورت نیاز) یکی از موارد استفادۀ lazy evaluation ایجاد لیست های نامتناهی هست (وقتی عنصر n+1 ایجاد خواهد شد که نیاز باشه. با yield در سی شارپ همچین چیزی رو میشه شبیه سازی کرد)
این برای جلسۀ اول
(به یکی از مدیران گفته بودم اولین مقاله رو دو هفتۀ قبل میدم ، ولی الان تونستم بنویسم. {شکلک خجالت + سرشلوغی!})
اینم بخونید:
http://www.md.chalmers.se/~rjmh/Papers/whyfp.html