تبلیغات
control - زبانهای برنامه‌نویسی در هوش مصنوعی
درباره وب سایت

ارتباط با ما:


mehdi400v@yahoo.com
آرشیو وب سایت
نویسندگان
موضوعات
لینک دوستان
آمار وب سایت
  • بازدیدهای امروز :
  • بازدیدهای دیروز :
  • كل بازدیدها :
  • بازدید این ماه :
  • بازدید ماه قبل :
  • تعداد نویسندگان :
  • كل مطالب ارسال شده:
  • آخرین بازدید :
  • آخرین بروز رسانی :
ورود به پروژه


زبانهای برنامه‌نویسی در هوش مصنوعی● عناوین متن
زبانهای برنامه‌نویسیAI، برنامه‌نویسی تابعی ، برنامه‌نویسی تابعی در Lisp ، A- Syntax (نحو) و semantic های (معانی) Lisp ، لیست انواع داده ، تعریف توابع جدید ، تعریف ساختارهای كنترلی ، تعریف توابع بازگشتی ، توابع مرتبه بالا ، سایر زبانهای برنامه‌نویسی تابعی غیر از Lisp ، برنامه‌نویسی منطقی در Prolog ، سایر روشهای برنامه‌نویسی
● واژه نامه
بندهای برنامه Prolog شامل مجموعه‌ای از جملات بنام بندها هستند كه برای نشان دادن داده‌ها و برنامه‌ها بكار می‌روند.
تابع مرتبه بالا تعریف تابعی است كه اجازه می‌دهد آرگومانها یا مقدار بازگشتی تابع، مقدار توابع باشد. نماد ساختار لیستها اغلب نشان‌دهنده نحوه استفاده از لیست ساختاری داده هستند، كه یك عنصر لیست ممكن است نماد یا لیست دیگر باشد. لیستها ساختاری مركزی Lisp هستند كه برای نشان دادن داده‌ها و برنامه‌ها بكار می‌روند. بازگشت تكنیكی الگوریتمی برای انجام یك كار است كه یك تابع با بعضی از قسمتهای كار خودش را فراخوانی می‌كند.
محاسبات نمادین برنامه‌نویسی AI (اساساً) شامل دستكاری نمادها است نه اعداد. این نمادها می‌توانند اشیاء در جهان و ارتباط بین آن اشیاء را نشان دهند- ساختارهای پیچیده نمادها نیاز به دانش ما از جهان دارند. واژه ساختار اساسی داده‌ها در Prolog واژه‌ای است كه می‌تواند یك ثابت، یك متغیر یا یك ساختار باشد. ساختارها موضوعات ریز محاسبات گزاره‌ای را نشان می‌دهند و شامل یك عملگر نام و یك پارامتر لیست هستند.
زبانهای برنامه‌نویسی هوش مصنوعی(AI) ابزار اصلی بررسی و ساخت برنامه‌های كامپیوتری هستند كه می‌توانند در شبیه‌سازی فرایندهای هوشمند مانند یادگیری،‌ استدلال و فهم اطلاعات نمادین بكار بروند. هر چند اخیراً زبان كامپیوتر اصولاً برای استفاده از كامپیوترها برای انجام محاسبات با اعداد طراحی شده بود، اما بزودی دریافتند كه رشته‌ای از بیتها نه تنها اعداد بلكه می‌توانند اشیای دلخواه را نیز نمایش دهند. عملیات روی ویژه‌گی‌ها یا نمادها می‌تواند با استفاده از قوانین برای ایجاد، انتساب یا دستكاری نشان داده شود. این تصور از محاسبات نمادین بعنوان تعریف الگوریتمهایی كه هر نوع اطلاعات را پردازش می‌كنند و بنابراین می‌تواند برای شبیه‌سازی هوش انسان بكار برود مناسب است.
بزودی برنامه نویسی با نمادها كه نیاز به سطح بالایی از چكیدگی دارند تولید می‌شوند، غیر از امكاناتی كه با زبانهای برنامه نویسی مخصوص پردازش اعداد ممكن بود مانند فرترن
I) زبانهای برنامه نویسی AI
در AI خودكار كردن یا برنامه‌نویسی همه جنبه‌های شناخت انسانی بوسیله بنیادهای شناخت علمی روشهای نمادین و غیر نمادین AI، پردازش زبان طبیعی، دید كامپیوتری و سیستمهای تكامل یا سازگار مطرح می‌شود. لازم است دامنه مسئله‌های خیلی پیچیده در ابتدای مرحله برنامه‌نویسی یك مسئله AI معین، مشخص شود كه كافی نیست. تنها بوسیله تعامل و افزایش اصلاحات خصوصیات بسیار دقیق ممكن است. در حقیقت مسئله‌های معمول AI به بسیاری از زمینه‌های خاص گرایش دارند، بنابراین روشهای ذهنی باید بوسیله تولید و آزمایش روشها بطور تجربی توسعه یابند(مشهور به نمونه سازی سریع).
در اینصورت برنامه‌نویسی AI بطور قابل توجهی با روشهای استاندارد مهندسی نرم‌افزار متفاوت بوده زیرا برنامه‌نویسی معمولا از یك مشخصات رسمی با جزئیات شروع می‌شود. در برنامه‌نویسی AI پیاده‌سازی در واقع جزئی از پردازش مشخصات مسئله است. به اقتضای طبیعت مسئله‌های AI برنامه‌نویسی AI مزایای بسیاری دارد اگر زبانهای برنامه نویسی، برنامه‌نویسAI را آزاد بگذارند و در بسیاری از ساختارهای فنی محدود نكنند (مانند ساختار انواع داده‌ای جدید سطح پایین، دستیابی دستی به حافظه). ترجیحاً سبك برنامه‌نویسی اعلانی برای استفاده در ساختارهای پیش‌ساخته داده‌ای سطح بالا(مانند لیستها و درختها) و عملیات(مانند تطبیق الگوها) مناسب است، بنابراین محاسبات نمادین سطح خلاصه‌سازی بیشتری نسبت به آنچه كه با زبانهای دستوری استاندارد مانند فرترن، پاسكال یا C امكان‌پذیر خواهد بود را پشتیبانی می‌كند. البته طبقه‌بندی خلاصه سازی آسان نیست،‌ زیرا تدوین برنامه‌های AI روی كامپیوترهای استاندارد وان نیومن نمی‌تواند به كارآمدی زبانهای دستوری باشد. هر چند یك مسئله مسلم AI فهم آن است (حداقل جزئیات) امكان دارد با تنظیم مجدد آن به شكل خصوصیات جزئی شده با بكار بردن یك زبان دستوری پیاده‌ سازی مجدد شود.
با توجه به نیازمندیهای محاسبات نمادین و برنامه‌نویسی AI دو الگوی جدید برنامه‌نویسی كه به سبك دستوری پیشنهاد می‌شوند بوجود می‌‌آید: سبك برنامه‌نویسی تابعی و منطقی. هر دو بر مبنای ریاضیات طرح‌ریزی شده‌اند، یعنی نظریه توابع بازگشتی و منطق رسمی. اولین زبان برنامه‌نویسی AI كاربردی كه هنوز هم بطور گسترده استفاده می‌شود زبان برنامه‌نویسی Lisp است كه در اواخر دهه ۱۹۵۰ توسط جان مك كارتی توسعه یافته است. Lisp برمبنای نظریه توابع ریاضی و خلاصه‌سازی Lambda است. تعدادی از كاربردهای مهم و موثرAI در Lisp نوشته شده است. كه ما بعضی از جزئیات این زبان برنامه‌نویسی را در این مقاله شرح خواهیم داد. در اوایل دهه ۱۹۷۰ یك الگوی برنامه‌نویسی جدید بنام برنامه‌نویسی منطقی بر اساس محاسبات گزاره‌ای بوجود آمد. اولین و مهمترین زبان برنامه‌نویسی منطقی Prolog است كه توسط آلن كالمرار، رابرت كوالسكی و فیلیپ راسل توسعه یافته است. مسئله‌ها در prolog بصورت حقایق، بدیهیات و قوانین منطقی برای استنباط حقایق جدید بیان می‌شوند. Prolog با قانون ریاضی در محاسبات گزاره‌ای و نتایج نظری بدست آمده در زمینه اثبات قضیه خودكار در اواخر دهه ۱۹۶۰ بنا نهاده شده است.
II) برنامه نویسی تابعی
یك تابع ریاضی نگاشتی از یك مجموعه (دامنه) به مجموعه دیگر(برد) است. تعریف یك تابع توصیف این نگاشت است كه یا بطور صریح بوسیله شمارش و یا بطور ضمنی بوسیله یك عبارت است. تعریف یك تابع بوسیله نام تابع كه بدنبال آن لیستی از پارامترها در داخل پرانتز قرار دارند و به دنبال آن نیز عبارت توصیفی نگاشت است مشخص می شود مانند:
X یك عدد حقیقی است cube(X) ≡ X X X , where X is a real number.
آلونسو چارچ توابع بی نام را با استفاده از نمادLambda معرفی می كند. یك عبارت Lambda پارامترها و نگاشت تابع را با استفاده از عملگر X مشخص می كند, مانند &#۹۵۵; (X)X X X آن خودش تابع است, بنابراین شرح بكار رفته در مثال تابع بی نام با یك آرگومان مشخص است. برای مثال:(&#۹۵۵; (X) X X X)(۴).
برنامه نویسی در یك زبان تابعی شامل ساختمان تعریف توابع و بكاربردن كامپیوتر برای ارزیابی عبارات است. یعنی بكاربردن توابع با آرگومانهای واقعی. كار اصلی برنامه نویسی پس از ساخت یك تابع برای یك مساله خاص تركیب توابع تعریف شده قبلی با توجه به اصول ریاضی است. كار اصلی كامپیوتر ارزیابی توابع فراخوانی شده و چاپ حاصل مقادیر تابع است. در این روش كامپیوتر مشابه یك كامپیوتر جیبی معمولی بكار می رود البته بسیار انعطاف پذیرتر و قدرتمندتر. یك خاصیت برنامه نویسی تابعی این است كه اگر عبارت به خوبی مقداردهی شود آنگاه ترتیب انجام ارزیابی كامپیوتر در نتایج ارزیابی تاثیری ندارد. بنابراین نتیجه ارزیابی یك عبارت تنها مقدار آن است. بدین معنی است كه در یك زبان تابعی ناب اثرات جانبی وجود ندارد. اثرات جانبی در مدل موقعیت های حافظه به متغیرها متصل شده اند.بنابراین در یك زبان برنامه نویسی ناب در مفهوم زبانهای دستوری متغیر وجود ندارد. روشهای اصلی كنترل جریان، بازگشت (تكرار) و عبارات شرطی هستند. این كاملاً با زبانهای دستوری در مفهوم اساسی كنترل ترتیب و تكرار متفاوت است. برنامه نویسی تابعی نیز خصوصیات توابع مرتبه بالا را پشتیبانی می كند. تابع مرتبه بالا تعریف تابعی است كه اجازه می دهد آرگومانها یا مقدار بازگشتی تابع, مقدار توابع باشند. همه این جوانب با هم مخصوصاً آخری از اصلی ترین مزایای سبك برنامه نویسی تابعی در برابر سبك برنامه نویسی دستوری هستند. خلاصه برنامه نویسی تابعی سطح بالایی از درجه پیمانه ای بودن را فراهم می كند. وقتی یك مسئله با تقسیم آن به مجموعه ای از زیر مسئله ها تعریف می شود, موضوع اصلی روشهایی است كه می توان زیر مسئله ها را به یكدیگر چسباند. بنابراین برای افزایش قابلیت پیمانه ای بودن یك مسئله مفهومی, ابتدا باید نوع جدیدی از چسب در زبان برنامه نویسی فراهم شود- قدرت اصلی برنامه نویسی تابعی .
III) برنامه نویسی تابعی در Lisp
Lisp اولین زبان برنامه نویسی تابعی است: آن برای پشتیبانی محاسبات نمادین با استفاده از لیستهای پیوندی بعنوان ساختار مركزی داده ها ابداع شده بود ( Lisp یعنی پردازشگر لیست). جان مك كارتی دریافت كه روشهای كنترل جریان توابع ریاضی (بازگشت و تكرار) وسیله نظری مناسبی برای انجام محاسبات نمادین هستند. علاوه براین مفاهیم خلاصه سازی تابعی و كاربرد تابعی تعریف شده در محاسبات Lambda , سطح بالایی از خلاصه سازی موردنیاز برای مسئله های AI مشخص شده را فراهم می كنند.
Lisp در سال ۱۹۵۸ توسط مك كارتی ابداع شد و اولین نگارش محیط برنامه نویسی Lisp در سال ۱۹۶۰ آماده شد كه شامل یك مفسر, یك كامپایلر و مكانیسم تخصیص و بازپسگیری حافظه پویا بود (بعنوان مجموعه فضای هرز شناخته شده است). یكسال بعد اولین زبان استاندارد با نام Lisp۱.۵ معرفی شد. پس از آن تعدادی از نسخه ها و محیط های برنامه نویسی Lisp توسعه یافته اند. مانند MacLisp، FranzLisp، InterLisp، CommonLisp، Scheme هر چند آنها در بعضی جزئیات خاص متفاوتند ولی هسته Syntax (نحو) و Semantic (معنی) آنها اساساً یكسان است. هسته را در جای دیگر معرفی خواهیم كرد. پر استفاده ترین نسخه‌های
Lisp ، Common Lisp و scheme هستند. در این مقاله ما Common Lisp را برای نشان دادن جنبه های مختلف Lisp با مثالهای معمولی انتخاب كرده ایم. هرچند مثالها نیز به راحتی می توانند در نسخه های دیگر Lisp سازگار شوند.
Syntax .A. (نحو) و semantics (معانی) Lisp
۱) عبارات نمادین:
عناصر نحوی Lisp عبارات نمادین نامیده می شوند (كه به صورتS-expressionsشناخته شده‌اند). داده ها و توابع (یعنی برنامه های Lisp ) بصورت عبارات نمادین نشان داده شده اند كه می توانند اتم ها یا لیست ها باشند. اتم ها كلمه ای شبیه اشیا‌ هستند. اتم‌ها وابسته به نوع كاراكترهایی كه برای شكل دادن یك اتم مجازند می توانند به انواع مختلفی تقسیم شوند. انواع اصلی عبارتنداز:
Numbers:۱ ۲۳۴-۴۳.۱۴۱۵۹۲۶۵۳۵۸۹۷۹ -۷.۵ ۶.۰۲E+۲۳
Symbols:SymbolSym۲۳another-one t false NILBLUE
Strings: ”This is a string””۹۷۷?” ”setq””He said: ” I’m here.” ”
توضیح اینكه هرچند نماد خاصی مثل BLUE استفاده می‌شود چون مفهوم مشخص برای برنامه‌نویسی دارد، اما بزودی Lisp تنها ترتیبی از حروف یا تنها یك نماد است. لیستها بندی شبیه اشیاء هستند. یك لیست شامل یك پرانتز باز( دنباله‌ای از اعداد دلخواه كه بوسیله فاصله خالی از هم جدا می‌شوند) و یك پرانتز بسته هستند. هر عنصر لیست می‌تواند یك اتم یا لیست باشد. اینها مثالهایی از لیستها هستند:
(This is a list) ((this) ((too))) () (((((((())))))))
(a b c d) (john mary tom) (loves john ?X)
(* (+ ۳ ۴) ۸) (append (a b c) (۱ ۲ ۳))
(defun member (elem list)
(if (eq elem (first list)) T
(member elem (rest list))))
توضیح اینكه در بسیاری از مثالها عناصر لیست خود لیستها هستند.چنین لیستهایی، لیستهای تو در تو نامیده می‌شوند. در مورد تو در تویی محدودیتی وجود ندارد. برای مثال یكی از قویترین Lisp ها را شرح می‌دهیم: پیچیده‌ترین اشیاء را به راحتی می‌توان نوشت. تنها چیزی كه در نظر گرفته می‌شود درستی عدد داخل پرانتزهاست. مهم توضیح این است كه معنی وابسته به یك لیست نمایش ویژه یا اتم در لیست نمایش وارد نمی‌شود. به این معنی كه همه عبارات نمادین كه در بالا توصیف شده است از لحاظ نحو برنامه‌های Lisp را اصلاح می‌كنند ولی الزاماً از لحاظ معنی (semantic) برنامه‌ها رااصلاح نمی‌كنند.
۲) Semantics (معانی):
هسته هر سیستم برنامه‌نویسی Lisp مفسر است كه كارش محاسبه مقدار برای یك عبارات نمادین داده شده است. این فرآیند ارزیابی نام دارد. نتیجه یا مقدار یك عبارت نمادین، یك عبارت نمادین است. كه بعد از كامل شدن ارزیابی برگردانده شده است. توضیح اینكه در واقع Lispدارای Semantics (معانی) عملیاتی است كه با یك تعریف ریاضی دقیق از نظریه تابع بازگشتی بدست می‌آید.حلقه خواندن- محاسبه- چاپ چگونه می‌تواند مفسر Lisp را فعال كرده و برای محاسبه عبارات نمادین و بنابراین اجرای واقعی برنامه‌های Lisp بكار برود؟
مسئله‌‌های Prolog بصورت حقایق، بدیهیات و قوانین منطقی برای استنباط حقایق جدید بیان می‌‌ شوند . Prolog با قانون ریاضی در محاسبات گزاره‌ ای و ونتایج نظری بدست آمده در زمینه اثبات قضیه خودكار در اواخر دهه۱۹۶۰ بنا شده است. مفسر Lisp در واقع بعنوان یك تابع معمولاً بنام eval و جزئی از هر محیط برنامه‌‌‌نویسی Lisp است تعریف شده است (مانند تابعی كه پیش‌ساخته نام دارد). آن بوسیله فراخوانی حلقه خواندن- محاسبه- چاپ در یك سیستم Lisp جاسازی می‌شود، وقتی یك عبارت نمادین توسط كاربر داده می‌‌ شود ابتدا به داخل سیستم Lisp خوانده می‌شود( خواندن هم یك تابع پیش‌ساخته است). سپس مفسر Lisp كه via نام دارد تابع eval را فراخوانی می‌كند تا عبارت نمادین را محاسبه و نتیجه عبارت نمادین را با چاپ در دستگاه كاربر برگرداند ( شگفت‌آورنیست گفتن اینكه چاپ هم یك تابع پیش‌‌ساخته است). وقتی سیستم Lispدر كامپیوتر شروع به اجرا می‌‌شود این حلقه خواندن- محاسبه- چاپ بطور خودكار شروع به اجرا كرده و بوسیله علامت ویژه اعلان Lisp در ابتدای خط جدید به كاربر علامت می‌دهد در این مقاله ما علامت سئوا ل (?) را به عنوان اعلان Lisp بكار خواهیم برد. برای مثال:
( ۴ ۳ +) ? ۷
هر وقت سیستم Lisp اجرا شود حلقه خواندن- محاسبه- چاپ فعال خواهد بود.
عبارت نمادین ( ۴ ۳ + ) كه بوسیله هكر Lisp وارد شده است بوسیله مفسر Lisp بصورت فراخوانی تابع جمع تفسیر شده و نتیجه عبارت نمادین در ابتدای خط جدید ۷ چاپ می‌‌شود .
▪ ارزیابی مفسر Lisp مطابق سه قانون زیر انجام می‌‌شود:
۱) یكسانی: یك عدد،‌ یك رشته یا نمادهای t و nil خودشان را ارزیابی می‌كنند (بر می‌گردانند) به این معنی كه ارزش عدد ۳،۳ و ارزش رشته ”house”، رشته ”house”است. نمادt مقدار t برمی‌گرداند كه به معنای true تفسیر می‌شود وnil ، nil به معنی false برمی‌‌گرداند
۲) نمادها: ارزیابی یك نماد عبارت نمادین مربوط به آن را برمی‌‌‌گرداند. ( چگونگی‌ اش را در زیر نشان خواهیم داد) بنابراین اگر ما فرض كنیم نماد‌ *names* به لیست
(john mary tom) وابسته است آنگاه ارزیابی *names* آن لیست را نتیجه می‌دهد. اگر نماد color را به نماد green وابسته كنیم آنگاه green بعنوان مقدار color برگردانده می‌‌شود.
به بیان دیگر نمادها بعنوان متغیرهایی كه به مقادیری متصل(باند) شده‌اند تفسیر می‌‌شوند.
۳) لیستها: هر لیست بعنوان یك فراخوانی تابع تفسیر می‌‌شود. مفسر اول لیست دلالت بر تابعی دارد كه باید برای بقیه عناصر( بالقوه خالی)‌ كه آرگومانهای آن تابع را نشان می‌دهند بكار رود. در واقع آرگومانهای یك تابع قبلا بصورت نمادهای پیشوندی مشخص می‌‌شوند. این مزیت را دارد كه توابع به سادگی می‌توانند با تعداد دلخواهی آرگومان مشخص و استفاده شوند. لیست خالی ( ) دارای عبارت نمادین nil بعنوان مقدارش می‌باشد. توضیح اینكه نماد nil در واقع دارای دو معنی است: یك نمایش مقدار منطقی false و دیگری نمایش لیست خالی. هر چند ممكن است این یك بیت فرد بنظر برسد، ولی در واقع در Lisp مشكلی در شناسایی مفهوم nil بكاررفته وجود ندارد.
ولی بطور كل آرگومانها قبل از اینكه توابع مقادیر آنها را استفاده كنند ارزیابی می‌شوند. اولویت ارزیابی ترتیبی از آرگومانها از چپ به راست است. یك آرگو‌مان ممكن است یك اتم یا یك لیست باشد،‌درهر حالت بعنوان یك فراخوانی تابع تفیسر شده و مفسر Lisp برای ارزیابی آن فراخوانی می‌شود. برای مثال، ارزیابی زیر در سیستم Lisp یك تابع به حساب می‌آید:
?(max ۴ (min ۹ ۸) ۷ ۵)
۸
در اینجا آرگومانها ۵, ۷, (min ۹ ۸), ۴ هستند كه در اولویتی قبل از تابعی به نام max كه نتیجه مقادیر آرگومانها را به كار می‌برد ارزیابی می‌شوند. آرگومان اول ۴ ،‌ یك عدد است پس مقدار آن ۴ است.
آرگومان دوم (min ۹ ۸) است كه خودش یك فراخوانی تابع است. بنابراین باید قبل از آرگومان سوم فراخوانی شود، (min ۹ ۸) باید توسط مفسر Lisp ارزیابی شود. چون ما باید مفسر Lispرا برای بعضی آرگومانها در طول ارزیابی همه فراخوانی‌های توابع استفاده كنیم می‌‌توان گفت مفسر Lisp بصورت بازگشتی فراخوانی شده است. مفسر Lisp همان مراحل را به كار می‌برد، پس آرگومان اول ۹ قبل از آرگومان دوم ۸، ارزیابی می‌شود. با بكار برروی تابع min حاصل ۸ می‌شود یعنی تابع كوچكترین عدد یك مجموعه از اعداد صحیح را محاسبه می‌‌كند. برای تابع بیرونی max هم به این معنی است كه آرگومان دوم آن ۸ ارزیابی می‌شود.
آرگومانهای بعدی ۷و۵هستند كه نتیجه ارزیابی آنها مقادیر ۷و۵ می‌شود. حال تابع بزرگترین عدد كه max نام دارد می‌تواند ارزیابی شود كه ۸ برمی‌گرداند. این مقدار نهایی،‌ مقدار فراخوانی همه توابع می‌‌باشد. از آنجایی كه گفته می‌‌‌شود مفسر Lisp همیشه سعی می‌كند مقدار یك نماد یا تفسیر یك لیست بعنوان یك فراخوانی تابع را تشخیص دهد ما چگونه می‌توانیم با نمادها و لیستها بعنوان داده رفتار كنیم؟ برای مثال، اگر ما لیست (peter walks home) را وارد كنیم، آنگاه مفسر Lisp فوراً یك خطا می‌دهد كه چیزی شبیه این خطا می‌گوید: تابع peter ناشناخته است (مفسرLisp باید بقدری باهوش باشد كه بتواند ابتدا كنترل كند كه آیا تعریف تابعی برای نام تابع تعیین شده وجود دارد یا نه، قبل از اینكه هر آرماگونی را ارزیابی كند). یا اگر ما فقط house را وارد كنیم، آنگاه مفسر Lisp با خطایی شبیه این خطا خاتمه می‌یابد: مقداری به house متصل نیست (تخصیص نیافته است). حل این مسئله كاملاً آسان است. زیرا عنصر اصلی هر لیست بعنوان نام تابع تفسیر می‌شود،‌هر سیستم Lisp با یك تابع پیش‌ساخته quote می‌‌آید كه یك عبارت نمادین را بعنوان آرگومان پذیرفته و این عبارت نمادین را بدون ارزیابی آن برمی‌گرداند. برای مثال: لیست(quote(peter walks home)) ، به سادگی مقدار
(peter walks home) را برمی‌گرداند، و برای (quote house)، آن house را بر می‌‌گرداند. از آنجایی كه تابع quote زیاد استفاده می‌‌‌شود، می‌توان آن را با كاراكتر ویژه &#۰۳۹; بیان كرد. بنابراین برای مثال بالا می‌توانیم معادل’(Peter walks home) و’house را مشخص كنیم. برنامه‌ها بعنوان داده، یعنی تابع quote به ما امكان می‌‌‌دهد تا با فراخوانی تابع بعنوان داده رفتار كنیم. برای مثال: (quote (max ۴ (min ۹ ۸) ۷ ۵)) یا ’(max ۴ (min ۹ ۸) ۷ ۵)
قبلاً گفتیم كه مفسر Lisp یك تابع یكتایی پیش‌ساخته است كه eval نام دارد. آن صریحاً آرگومانهایش را وادار می‌كند تا مطابق قوانین مذكور در بالا ارزیابی شوند. در بعضی حالات، آن می‌تواند مقابل تابع quote قرار بگیرد بنابراین به وضوح لازم است كه یك لیست بعنوان داده مشخص شود تا سیستم Lisp بتواند یك فراخوانی تابع تفسیر شود، ما می‌توانیم(eval ’(max ۴ (min ۹ ۸) ۷ ۵)) را مشخص كنیم كه مقدار ۸ را بطوری كه در بالا توصیف شد بر می‌گرداند. به همان صورت مشخص كردن (eval ’(peter walks home)) سبب یك خطای Lisp می‌شود زیرا Lisp سعی می‌كند یك تابع peter فراخوانی كند. مزیت اصلی رفتار برنامه‌ها بعنوان داده این است كه ما می‌توانیم برنامه‌های Lisp (توابع) را طوری تعریف كنیم كه قادر به ساخت یا تولید برنامه‌ها باشند بطوریكه ابتدا لیست نمایش متناظر را ساخته و سپس با استفاده از تابع eval ، مفسر Lisp را به منظور ارزیابی لیست ایجاد شده بعنوان یك تابع فراخوانی می‌كند. شگفت‌آور نیست كه به اقتضای این خصوصیات، Lisp هنوز زبان برنامه‌نویسی برتر در زمینه برنامه‌نویسی ژنتیك AI است.
وقتی مقادیر را به نمادها تخصیص می‌دهیم كه برنامه‌نویسی برنامه‌های كاربردی
real-life به ذخیره مقادیری محاسبه شده در یك متغیر نیاز داشته باشد تا اگر در آینده در برنامه‌ دیگری نیاز باشند از هزینه محاسبه مجدد آن جلوگیری شود. در یك نگارش كاملاً تابعی Lisp ‌مقدار یك تابع تنها به تعریف تابع و مقدار آرگومانهایش در فراخوانی بستگی دارد. برای اینكه Lisp را یك زبان كاربردی بكنیم (كاربردی حداقل در این مفهوم كه بتواند بر روی كامپیوترهای وان نیومن به خوبی اجرا شود)، ما نیاز به روشی داریم تا مقادیر را به نمادها تخصیص دهیم.common Lisp با یك تابع پیش‌ساخته بنام Setq می‌آید. Setq دو آرگومان می‌خواهد: نماد (بنام متغیر) كه یك مقدار به آن متصل شده است و یك عبارت نمادین كه باید مقداری را فراهم كند. مفسر Lisp ارزیابی Setq را در روش خاصی انجام می‌دهد بطوریكه آرگومان اول Setq را ارزیابی می‌كند(متغیر)،‌ اما مقدار آرگومان دوم Setq را به متغیر متصل می‌كند(برای فهم چگونگی اتصال یك مقدار به یك نماد نیاز به جزئیات فنی زیادی خواهیم داشت كه در این معرفی كوتاه نمی‌توان به آن پرداخت). مقدار آرگومان دوم Setq مقدار Setq را بر می‌گرداند. اینها مثالهایی هستند:
?color
error: unbound symbol color
?(setq color ’green)
green
?(setq max (max ۳ ۲ ۵ ۱))
۳
توضیح اینكه در واقع Setq حالت مفسر Lisp را تغییر می‌دهد تا دفعه بعدی كه همان متغیر استفاده می‌شود، دارای مقدار بوده و بنابراین مفسرLisp قادر به بازگرداندن آن خواهد بود. اگر این اتفاق نیفتد آنگاه مفسر Lisp یك اخطار خواهد داد زیرا نماد متصل نشده است.
(گام ۲ مفسر Lisp پیدا نشد). بنابراین آن می‌گویدكه Setq یك اثر جانبی تولید می‌كند زیرا حالت مفسر Lisp بطور پویا تغییر می‌دهد. وقتی استفاده از Setq اجباری شد، به هرحال متوجه شد كه در واقع از مسیر semantics (معانی) Lisp ناب دور می‌شود. پس Setq باید با دقت بسیار استفاده شود.
B۰ نوع داده لیست
برنامه‌نویسی در Lisp در واقع به معنی تعریف توابعی است كه روی لیست عمل می‌كنند. مانند ایجاد، پیمایش،‌كپی، تغییر و حذف لیستها. از آنجایی كه این در Lisp مركزی است، هر سیستم Lisp بر مبنای مجموعه‌ای از توابع پیش‌ساخته ابتدایی كه بطور موثری عملیات اصلی لیست را پشتیبانی می‌كند می‌آید. ما بطور خلاصه یكی از مهمترین آنها معرفی می‌كنیم. ابتدا نوع گزاره‌ای،‌ ما می‌دانیم كه یك عبارت نمادین جاری یا یك لیست است یا نیست (یعنی یك اتم). این كار بوسیله تابع Listp انجام می‌شود كه هر عبارت نمادین expr را بعنوان آرگومان پذیرفته و اگر expr لیست باشد نماد t و در غیر این صورت nil برمی‌گرداند. مثالها هستند (ما از فلش راست => برای نشان دادن نتیجه فراخوانی تابع استفاده خواهیم كرد):
(listp ’(۱ ۲ ۳))==>t
(listp ’( ))==>t
(listp ’۳)==>nil
در انتخاب عناصر لیست دو تابع اساسی برای دست‌یابی به عناصر یك لیست وجود دارد: car وcdr هر دو تابع یك لیست را بعنوان آرگومان می‌پذیرند. تابع car اولین عنصر لیست یا اگر لیست خالی از آرگومان باشد nil بر می‌گرداند،‌و cdr همان لیست را بطوری كه عنصر اول آن حذف شده است یا اگر لیست خالی از آرگومان بود nil برمی‌گرداند. مثالها:
(car ’(a b c)) ==>a (cdr ’(a b c)) ==>(b c)
(car ’( )) ==>nil(cdr ’(a)) ==>nil
(car ’((a b) c))==>(a b)
با استفاده از ترتیبی از فراخوانی‌های توابع car و cdr می‌توان یك لیست را از چپ به راست و از عناصر بیرونی به سمت عناصر داخلی لیست پیمایش كرد.
برای مثال، در طول ارزیابی (car (cdr ’(see the quote))) مفسر Lisp ابتدا عبارت
(cdr ’(see the quote))را ارزیابی خواهد كرد كه لیست (the quote) را برمی‌گرداند، سپس به تابع car پاس می‌شود كه نماد the را بر می‌گرداند. اینها مثالهایی دیگر هستند:
(car (cdr (cdr ’(see the quote)))) ==>quote
(car (cdr (cdr (cdr ’(see the quote))))) ==>nil
(car (car ’(see the quote))) ==>?در طول ارزیابی مثال اخیر چه اتفاقی خواهد افتاد؟ ارزیابی (car ’(see the quote)) نماد see را بر‌می‌گرداند.سپس این به عنوان آرگومان به فراخوانی بیرونی car پاس می‌شود. چون تابع car یك لیست را به عنوان آرگومان می پذیرد پس مفسر Lisp بلافاصله ارزیابی دیگر را با خطایی مانند این خطا متوقف خواهد كرد: سعی می‌شود Car SEE بدست آید ولی Listp نیست. یك توضیح كوتاه تاریخی: نامهای Car,cdr از روشهای قدیمی هستند زیرا آنها در اولین نگارش Lisp كه بر مبنای مجموعه عملیات كد ماشین كامپیوتر انتخاب و پیاده سازی شده بودند (car از محتوای ثبات آدرس استفاده می‌كند و cdr از محتوای ثبات كاهش استفاده می‌كند). به منظور نوشتن كد Lisp خواناتر، common Lisp یا در تابع first و rest بوجود آمد. ما نامهای قدیمی را استفاده می‌كنیم تا برای خواندن و فهم كد AI Lisp قدیمی قادر باشیم. برای ساخت لیستها، یك تابع ابتدایی Cons مانند Car و cdr وجود دارد كه برای ساخت یك لیست بكار می‌رود. Cons دو عبارت نمادین را می‌پذیرد كه اولی بعنوان یك عنصر جدید در جلوی دومی وارد می‌شود. در مثالهای زیر ملاحظه می‌كنید:
(cons ’a ’(b c)) ==>(a b c)
(cons ’(a d) ’(b c))==>((a d) b c)
(cons (first ’(۱ ۲ ۳)) (rest ’(۱ ۲ ۳))) ==>(۱ ۲ ۳)
در اصل، Cons و لیست خالی با هم برای ساخت لیستهای خیلی پیچیده كافی هستند، برای مثال:
(cons ’a (cons ’b (cons ’c ’( )))) ==>(a b c)
(cons ’a (cons (cons ’b (cons ’c ’( ))) (cons ’d ’( )))) ==>(a (b c) d)
چون این كار كاملاً طاقت‌فرساست،‌ سیستمهای Lispبسیاری با توابع لیست پیش‌ساخته بسیار پیشرفته بوجود می‌آیند. برای مثال، تابع List با تعداد دلخواهی عبارت نمادین یك لیست می‌سازد، و تابع append با الحاق آرگومانهایش كه باید لیست باشند یك لیست جدید می‌سازد. equal تابعی است كه اگر عناصر و ترتیب آنها در دو لیست یكسان باشد t ، در غیر این صورت nil بر میگرداند. مثال:
(list ’a ’b ’c) ==>(a b c)
(list (list ۱) ۲ (list ۱ ۲ ۳)) ==>((۱) ۲ (۱ ۲ ۳))
(append ’(۱) (list ۲)) ==>(۱ ۲)
(append ’(۱ ۲) nil ’(۳ ۴))==>(۱ ۲ ۳ ۴)
(equal ’(a b c) ’(a b c)) ==>t
(equal ’(a b c) ’(a c b)) ==>nil
C) تعریف توابع جدید
برنامه‌نوسی در Lisp با تعریف توابع جدید انجام می‌شود. در اصل این به این معنی است كه: مشخص كردن لیستها در یك روش نحوی معین. مشابه تابع setq كه بوسیله مفسر Lisp در یك روش خاص رفتار می‌كرد. تابع خاص defun است كه برای ایجاد اشیای تابع جدید توسط مفسر Lisp بكار می‌رود. defunیك نماد دال برنام تابع، یك لیست از پارامترها(ممكن است خالی باشد) برای تابع جدید و تعداد دلخواهی از عبارات نمادینی كه بدنه تابع جدیدرا تعریف می‌كند را به عنوان آرگومانهایش می‌پذیرد. این تعویض از یك تابع ساده به نام my-sum است كه دو آرگومان می‌پذیرد و با استفاده از تابع پیش‌ساخته آنها را جمع می‌كند.
(defun my-sum (x y)
(+ x y))
این عبارت به همان روشی كه بعنوان یك تابع فراخوانی می‌شود در سیستم Lisp وارد می‌شود. ارزیابی یك تعریف تابع نام تابع را بعنوان مقدار برمی‌گرداند، اما یك شئ تابع را بعنوان اثر جانبی ایجاد خواهد كرد و وقتی Lisp شروع به اجرا می‌‌كند آن را به مجموعه تعاریف توابع شناخته شده توسط سیستم Lisp اضافه می‌كند (حداقل مجموعه توابع پیش‌ساخته)
توضیح اینكه در این مثال بدنه شامل تنها یك عبارت نمادین است. هر چند بدنه می‌تواند شامل ترتیب دلخواهی از عبارات نمادین باشد مقدار آخرین عبارت نمادین از بدنه مقدار تابع را تعیین می‌كند. به این معنی است كه در واقع همه عناصر بدنه بی تاثیر هستند مگر اینكه اثرات جانبی تصمیم‌گیری تولید كنند.
لسیت پارامتر تابع جدیدmy-sum به ما می‌گوید وقتی فراخوانی می‌شود درست دو عبارت نمادین را بعنوان آرگومان می‌پذیرد. بنابراین اگر شما(my-sum ۳ ۵) را در سیستمLisp وارد كنید مفسرLisp قادر خواهد بود كه تعریف برای نام تابع مشخص شده بیابد و سپس آرگومانهای داده شده را از چپ به راست پردازش كند وقتی این كار انجام شد آن مقدار هر آرگومان را مطابق پارامتر مشخص شده در لیست پارامتر تعریف تابع وصل خواهد كرد(تخصیص خواهد داد) در مثال ما بدین معنی است كه مقدار آرگومان اول كه۳ است(۳ همان عدد۳ است كه خودش را ارزیابی كرده است) به پارامترx متصل می‌كند. سپس مقدار آرگومان دوم كه ۵ است به پارامترy متصل می‌شود. چون مقدار یك آرگومان به یك پارامتر متصل می‌شود، این روش فراخوانی با مقدار نامیده شده است. بعد از مقدار‌یابی برای همه پارامترها مفسرLisp قادر به ارزیابی بدنه تابع خواهد بود. مثال بدین معنی است كه ( ۳ ۵ +) فراخوانی خواهد شد. نتیجه فراخوانی۸ است كه بعنوان نتیجه فراخوانی(my-sum ۳ ۵) برگردانده می‌شود. بعد از تكمیل فرا‌خوانی تابع اتصالات موقت پارامترهایx وy حذف می‌شوند. هنگامی كه یك تعریف تابع جدید در سیستمLisp وارد می‌شودمی‌تواند به عنوان جزئی از تعریف تابع جدید به همان روش كه بعنوان تابع پیش ساخته استفاده شده است بكار برده شود بطوریكه در مثال زیر نشان داده شده است.
(defun double-sum (x y)
(+ (my-sum x y) (my-sum x y)))
كه با دوبار فراخوانیmy-sum جمع آرگومانهایش را دو برابر خواهد كرد این مثال دیگری از یك تعریف تابع است نشان دادن استفاده از عبارات نمادین چند‌گانه در بدنه تابع است.
(defun hello-world () (print ”Hello World!”) ’done)
این تعریف تابع پارامتری ندارد زیرا لیست پارامتر آن خالی است بنابراین وقتی(hello-world) فراخوانی می‌شود مفسرLisp بلافاصله (print ”Hello World!”) را ارزیابی و رشته
”Hello World!”را روی نمایشگر شما بعنوان یك اثر جانبی چاپ می‌كند سپس نماد’done را ارزیابی خواهد كرد وdone را به عنوان نتیجه فراخوانی تابع برمی‌گرداند.
D) تعریف ساختارهای كنترلی
هر چنداكنون تعریف توابع جدید با تعریف توابع پیش ساخته و توابعی كه كاربر تعریف می‌كند ممكن است برنامه‌نویسی درLisp بسیار خسته كننده خواهد شداگر كنترل جریان اطلاعات بوسیله شاخه‌های شرطی ممكن نبود شاید بارها تكرار می‌شد تا اینكه یك روند توقف اجرا شود گزینشLisp بر مبنای ارزیابی توابع است توابع كنترل تستهایی روی عبارات نمادین واقعی انجام می‌دهد و ارزیابی عبارات نمادین متناوب را بسته به نتایج انتخاب می‌كنند تابع اساسی برای تعیین اثباتهای شرطی درcond،Lisp است.cond تعداد دلخواهی آرگومان رامی‌پذیرد هر آرگومان یك بخش ممكن را بیان می‌كنند و بعنوان یك لیست نمایش داده شده كه عنصر اول یك تست و بقیه عناصر اعمال (عبارات نمادین) هستند كه اگر تست انجام شود ارزیابی می‌شوند مقدار آخرین عمل به عنوان مقدار پیشنهادی برگردانده می‌شود همه آرگومانهای ممكنcond (یعنی بخشها) تا زمانی كه بخش اول بطور مثبت تست شوداز چپ به راست ارزیابی می‌شوند درآن حالت مقدار آن بخش مقدار كل تابعcond است. در واقع این مفهوم بسیار پیچیده تر از آن است اجازه دهید تابعverbalize-prop زیركه یك مقدار احتمال را بیان می‌كند. به عنوان یك عدد حقیقی فرض می‌كنیم.
(defun verbalize–prop (prob-value)
(cond ((> prob–value ۰.۷۵) ’very-probable)
((> prob–value ۰.۵) ’probable)
((> prob–value ۰.۲۵) ’improbable)
(T ’very-improbable)))
وقتی(verbalize-prop ۰.۳۳) فراخوانی می‌شود مقدار واقعی آرگومانها به پارامترprop-value متصل می‌شود.سپسcond با آن اتصالات ارزیابی می‌شود very-probable)’((>prop-value)است.> یك گزاره پیش ساخته است كه تست می‌كند كه آیا آرگومان اول از دومی بزرگتر است،چونpropvalue،۰.۳۳ است. بهnil ارزیابی می‌شود كه به معنی انجام نشدن تست است. بنابراین ارزیابی این بخش پیشنهادی بلافاصله پایان می‌یابد. و سپس پیشنهاد ((> prob–value ۰.۵) ’probable)ارزیابی می‌شود كه تابع تست باز هم nilبرمی‌گرداندبنابراین ارزیابی هم پایان می‌یابد. سپس ((prop-value ۰.۲۵) ’improbable) ارزیابی می‌شود حال با بكار بردن تابع تستT برگردانده می‌شود كه به معنی انجام تست است.آنگاه همه اعمال این بخش كه بطور مثبت تست شده است.
ارزیابی ومقدار آخرین عمل به عنوان مقدارcond برگردانده می‌شود در مثال ما تنها عملimprobable’ تعیین می‌شود كه مقدارimprobable (غیرمحتمل) را برمی‌گرداند از آنجایی كه این مقدارcond را تعیین می‌كند و عبارت cond تنها عبارت بدنه تابعverbalize-prop است. نتیجه فراخوانی improbable ,((verbalize-prop ۰.۳۳) است. توضیح اینكهاگرما (verbalize- prop ۰.۱)را وارد كنیم مقدارvery- improbable را بر‌می‌گرداند زیرا تست هر سه با شكست مواجه شده و باید بخش (T ’very-improbable)ارزیابی شوددر این حالت نمادT به عنوان تستی كه همیشهT بر‌می‌گرداند استفاده شده است بنابراین مقدار این پیشنهاد very- improbable است.
E
ارسال توسط مهدی میرحسینی | تاریخ : شنبه 8 آذر 1393 | نظرات ()
عناوین آخرین مطالب ارسالی
صفحات دیگر