X
تبلیغات
رایتل

آموزش کامپیوتر و فناوری روز دینا ، اخبار علمی

آموزش برنامه نویسی و وب شبکه نرم افزار و اخبار علمی ، تازه های فناوری ، طب سنتی ، کشاورزی

زبان های برنامه نویسی در هوش مصنوعی (فصل 1)

سه‌شنبه 27 تیر‌ماه سال 1391 09:20 ق.ظ نویسنده: مجید اکبری چاپ

واژه نامه
بندهای برنامه Prolog
شامل مجموعه‌ای از جملات بنام بندها هستند که برای نشان دادن داده‌ها و برنامه‌ها بکار می‌روند.
تابع مرتبه بالا تعریف تابعی است که اجازه می‌دهد آرگومانها یا مقدار بازگشتی تابع، مقدار توابع باشد. نماد ساختار لیستها اغلب نشان‌دهنده نحوه استفاده از لیست ساختاری داده هستند، که یک عنصر لیست ممکن است نماد یا لیست دیگر باشد. لیستها ساختاری مرکزی Lisp هستند که برای نشان دادن داده‌ها و برنامه‌ها بکار می‌روند. بازگشت تکنیکی الگوریتمی برای انجام یک کار است که یک تابع با بعضی از قسمتهای کار خودش را فراخوانی می‌کند.
محاسبات نمادین برنامه‌نویسی AI (اساساً) شامل دستکاری نمادها است نه اعداد. این نمادها می‌توانند اشیاء در جهان و ارتباط بین آن اشیاء را نشان دهند- ساختارهای پیچیده نمادها نیاز به دانش ما از جهان دارند. واژه ساختار اساسی داده‌ها در Prolog واژه‌ای است که می‌تواند یک ثابت، یک متغیر یا یک ساختار باشد. ساختارها موضوعات ریز محاسبات گزاره‌ای را نشان می‌دهند و شامل یک عملگر نام و یک پارامتر لیست هستند.

واژه نامه
بندهای برنامه Prolog
شامل مجموعه‌ای از جملات بنام بندها هستند که برای نشان دادن داده‌ها و برنامه‌ها بکار می‌روند.
تابع مرتبه بالا تعریف تابعی است که اجازه می‌دهد آرگومانها یا مقدار بازگشتی تابع، مقدار توابع باشد. نماد ساختار لیستها اغلب نشان‌دهنده نحوه استفاده از لیست ساختاری داده هستند، که یک عنصر لیست ممکن است نماد یا لیست دیگر باشد. لیستها ساختاری مرکزی Lisp هستند که برای نشان دادن داده‌ها و برنامه‌ها بکار می‌روند. بازگشت تکنیکی الگوریتمی برای انجام یک کار است که یک تابع با بعضی از قسمتهای کار خودش را فراخوانی می‌کند.
محاسبات نمادین برنامه‌نویسی AI (اساساً) شامل دستکاری نمادها است نه اعداد. این نمادها می‌توانند اشیاء در جهان و ارتباط بین آن اشیاء را نشان دهند- ساختارهای پیچیده نمادها نیاز به دانش ما از جهان دارند. واژه ساختار اساسی داده‌ها در Prolog واژه‌ای است که می‌تواند یک ثابت، یک متغیر یا یک ساختار باشد. ساختارها موضوعات ریز محاسبات گزاره‌ای را نشان می‌دهند و شامل یک عملگر نام و یک پارامتر لیست هستند.
زبانهای برنامه‌نویسی هوش مصنوعی(AI) ابزار اصلی بررسی و ساخت برنامه‌های کامپیوتری هستند که می‌توانند در شبیه‌سازی فرایندهای هوشمند مانند یادگیری،‌ استدلال و فهم اطلاعات نمادین بکار بروند. هر چند اخیراً زبان کامپیوتر اصولاً برای استفاده از کامپیوترها برای انجام محاسبات با اعداد طراحی شده بود، اما بزودی دریافتند که رشته‌ای از بیتها نه تنها اعداد بلکه می‌توانند اشیای دلخواه را نیز نمایش دهند. عملیات روی ویژه‌گی‌ها یا نمادها می‌تواند با استفاده از قوانین برای ایجاد، انتساب یا دستکاری نشان داده شود. این تصور از محاسبات نمادین بعنوان تعریف الگوریتمهایی که هر نوع اطلاعات را پردازش می‌کنند و بنابراین می‌تواند برای شبیه‌سازی هوش انسان بکار برود مناسب است.
بزودی برنامه نویسی با نمادها که نیاز به سطح بالایی از چکیدگی دارند تولید می‌شوند، غیر از امکاناتی که با زبانهای برنامه نویسی مخصوص پردازش اعداد ممکن بود مانند فرترن

I-زبانهای برنامه نویسی AIدر AI خودکار کردن یا برنامه‌نویسی همه جنبه‌های شناخت انسانی بوسیله بنیادهای شناخت علمی روشهای نمادین و غیر نمادین AI، پردازش زبان طبیعی، دید کامپیوتری و سیستمهای تکامل یا سازگار مطرح می‌شود. لازم است دامنه مسئله‌های خیلی پیچیده در ابتدای مرحله برنامه‌نویسی یک مسئله AI معین، مشخص شود که کافی نیست. تنها بوسیله تعامل و افزایش اصلاحات خصوصیات بسیار دقیق ممکن است. در حقیقت مسئله‌های معمول AI به بسیاری از زمینه‌های خاص گرایش دارند، بنابراین روشهای ذهنی باید بوسیله تولید و آزمایش روشها بطور تجربی توسعه یابند(مشهور به نمونه سازی سریع). در اینصورت برنامه‌نویسی AI بطور قابل توجهی با روشهای استاندارد مهندسی نرم‌افزار متفاوت بوده زیرا برنامه‌نویسی معمولا از یک مشخصات رسمی با جزئیات شروع می‌شود. در برنامه‌نویسی AI پیاده‌سازی در واقع جزئی از پردازش مشخصات مسئله است. به اقتضای طبیعت مسئله‌های AI برنامه‌نویسی AI مزایای بسیاری دارد اگر زبانهای برنامه نویسی، برنامه‌نویسAI را آزاد بگذارند و در بسیاری از ساختارهای فنی محدود نکنند (مانند ساختار انواع داده‌ای جدید سطح پایین، دستیابی دستی به حافظه). ترجیحاً سبک برنامه‌نویسی اعلانی برای استفاده در ساختارهای پیش‌ساخته داده‌ای سطح بالا(مانند لیستها و درختها) و عملیات(مانند تطبیق الگوها) مناسب است، بنابراین محاسبات نمادین سطح خلاصه‌سازی بیشتری نسبت به آنچه که با زبانهای دستوری استاندارد مانند فرترن، پاسکال یا C امکان‌پذیر خواهد بود را پشتیبانی می‌کند. البته طبقه‌بندی خلاصه سازی آسان نیست،‌ زیرا تدوین برنامه‌های AI روی کامپیوترهای استاندارد وان نیومن نمی‌تواند به کارآمدی زبانهای دستوری باشد. هر چند یک مسئله مسلم AI فهم آن است (حداقل جزئیات) امکان دارد با تنظیم مجدد آن به شکل خصوصیات جزئی شده با بکار بردن یک زبان دستوری پیاده‌ سازی مجدد شود. با توجه به نیازمندیهای محاسبات نمادین و برنامه‌نویسی AI دو الگوی جدید برنامه‌نویسی که به سبک دستوری پیشنهاد می‌شوند بوجود می‌‌آید: سبک برنامه‌نویسی تابعی و منطقی. هر دو بر مبنای ریاضیات طرح‌ریزی شده‌اند، یعنی نظریه توابع بازگشتی و منطق رسمی. اولین زبان برنامه‌نویسی AI کاربردی که هنوز هم بطور گسترده استفاده می‌شود زبان برنامه‌نویسی Lisp است که در اواخر دهه 1950 توسط جان مک کارتی توسعه یافته است. Lisp برمبنای نظریه توابع ریاضی و خلاصه‌سازی Lambda است. تعدادی از کاربردهای مهم و موثرAI در Lisp نوشته شده است. که ما بعضی از جزئیات این زبان برنامه‌نویسی را در این مقاله شرح خواهیم داد. در اوایل دهه 1970 یک الگوی برنامه‌نویسی جدید بنام برنامه‌نویسی منطقی بر اساس محاسبات گزاره‌ای بوجود آمد. اولین و مهمترین زبان برنامه‌نویسی منطقی Prolog است که توسط آلن کالمرار، رابرت کوالسکی و فیلیپ راسل توسعه یافته است. مسئله‌ها در prolog بصورت حقایق، بدیهیات و قوانین منطقی برای استنباط حقایق جدید بیان می‌شوند. Prolog با قانون ریاضی در محاسبات گزاره‌ای و نتایج نظری بدست آمده در زمینه اثبات قضیه خودکار در اواخر دهه 1960 بنا نهاده شده است.

II- برنامه نویسی تابعی یک تابع ریاضی نگاشتی از یک مجموعه (دامنه) به مجموعه دیگر(برد) است. تعریف یک تابع توصیف این نگاشت است که یا بطور صریح بوسیله شمارش و یا بطور ضمنی بوسیله یک عبارت است. تعریف یک تابع بوسیله نام تابع که بدنبال آن لیستی از پارامترها در داخل پرانتز قرار دارند و به دنبال آن نیز عبارت توصیفی نگاشت است مشخص می شود مانند:
X یک عدد حقیقی است cube(X) ≡ X X X , where X is a real number.
آلونسو چارچ توابع بی نام را با استفاده از نمادLambda معرفی می کند. یک عبارت Lambda پارامترها و نگاشت تابع را با استفاده از عملگر X مشخص می کند, مانند λ (X)X X X آن خودش تابع است, بنابراین شرح بکار رفته در مثال تابع بی نام با یک آرگومان مشخص است. برای مثال:(λ (X) X X X)(4).
برنامه نویسی در یک زبان تابعی شامل ساختمان تعریف توابع و بکاربردن کامپیوتر برای ارزیابی عبارات است. یعنی بکاربردن توابع با آرگومانهای واقعی. کار اصلی برنامه نویسی پس از ساخت یک تابع برای یک مساله خاص ترکیب توابع تعریف شده قبلی با توجه به اصول ریاضی است. کار اصلی کامپیوتر ارزیابی توابع فراخوانی شده و چاپ حاصل مقادیر تابع است. در این روش کامپیوتر مشابه یک کامپیوتر جیبی معمولی بکار می رود البته بسیار انعطاف پذیرتر و قدرتمندتر. یک خاصیت برنامه نویسی تابعی این است که اگر عبارت به خوبی مقداردهی شود آنگاه ترتیب انجام ارزیابی کامپیوتر در نتایج ارزیابی تاثیری ندارد. بنابراین نتیجه ارزیابی یک عبارت تنها مقدار آن است. بدین معنی است که در یک زبان تابعی ناب اثرات جانبی وجود ندارد. اثرات جانبی در مدل موقعیت های حافظه به متغیرها متصل شده اند.بنابراین در یک زبان برنامه نویسی ناب در مفهوم زبانهای دستوری متغیر وجود ندارد. روشهای اصلی کنترل جریان، بازگشت (تکرار) و عبارات شرطی هستند. این کاملاً با زبانهای دستوری در مفهوم اساسی کنترل ترتیب و تکرار متفاوت است. برنامه نویسی تابعی نیز خصوصیات توابع مرتبه بالا را پشتیبانی می کند. تابع مرتبه بالا تعریف تابعی است که اجازه می دهد آرگومانها یا مقدار بازگشتی تابع, مقدار توابع باشند. همه این جوانب با هم مخصوصاً آخری از اصلی ترین مزایای سبک برنامه نویسی تابعی در برابر سبک برنامه نویسی دستوری هستند. خلاصه برنامه نویسی تابعی سطح بالایی از درجه پیمانه ای بودن را فراهم می کند. وقتی یک مسئله با تقسیم آن به مجموعه ای از زیر مسئله ها تعریف می شود, موضوع اصلی روشهایی است که می توان زیر مسئله ها را به یکدیگر چسباند. بنابراین برای افزایش قابلیت پیمانه ای بودن یک مسئله مفهومی, ابتدا باید نوع جدیدی از چسب در زبان برنامه نویسی فراهم شود- قدرت اصلی برنامه نویسی تابعی .

III- برنامه نویسی تابعی در LispLisp اولین زبان برنامه نویسی تابعی است: آن برای پشتیبانی محاسبات نمادین با استفاده از لیستهای پیوندی بعنوان ساختار مرکزی داده ها ابداع شده بود ( Lisp یعنی پردازشگر لیست). جان مک کارتی دریافت که روشهای کنترل جریان توابع ریاضی (بازگشت و تکرار) وسیله نظری مناسبی برای انجام محاسبات نمادین هستند. علاوه براین مفاهیم خلاصه سازی تابعی و کاربرد تابعی تعریف شده در محاسبات Lambda , سطح بالایی از خلاصه سازی موردنیاز برای مسئله های AI مشخص شده را فراهم می کنند.
Lisp در سال 1958 توسط مک کارتی ابداع شد و اولین نگارش محیط برنامه نویسی Lisp در سال 1960 آماده شد که شامل یک مفسر, یک کامپایلر و مکانیسم تخصیص و بازپسگیری حافظه پویا بود (بعنوان مجموعه فضای هرز شناخته شده است). یکسال بعد اولین زبان استاندارد با نام Lisp1.5 معرفی شد. پس از آن تعدادی از نسخه ها و محیط های برنامه نویسی Lisp توسعه یافته اند. مانند MacLisp، FranzLisp، InterLisp، CommonLisp، Scheme هر چند آنها در بعضی جزئیات خاص متفاوتند ولی هسته Syntax (نحو) و Semantic (معنی) آنها اساساً یکسان است. هسته را در جای دیگر معرفی خواهیم کرد. پر استفاده ترین نسخه‌های
Lisp ، Common Lisp و scheme هستند. در این مقاله ما Common Lisp را برای نشان دادن جنبه های مختلف Lisp با مثالهای معمولی انتخاب کرده ایم. هرچند مثالها نیز به راحتی می توانند در نسخه های دیگر Lisp سازگار شوند.

Syntax .A. (نحو) و semantics (معانی) Lisp
1. عبارات نمادین: عناصر نحوی Lisp عبارات نمادین نامیده می شوند (که به صورتS-expressionsشناخته شده‌اند). داده ها و توابع (یعنی برنامه های Lisp ) بصورت عبارات نمادین نشان داده شده اند که می توانند اتم ها یا لیست ها باشند. اتم ها کلمه ای شبیه اشیا‌ هستند. اتم‌ها وابسته به نوع کاراکترهایی که برای شکل دادن یک اتم مجازند می توانند به انواع مختلفی تقسیم شوند. انواع اصلی عبارتنداز:
Numbers:1 234-43.14159265358979 -7.5 6.02E+23

Symbols:SymbolSym23another-one t false NILBLUE

Strings: ”This is a string””977?” ”setq””He said: \” I’m here.\” ”
توضیح اینکه هرچند نماد خاصی مثل BLUE استفاده می‌شود چون مفهوم مشخص برای برنامه‌نویسی دارد، اما بزودی Lisp تنها ترتیبی از حروف یا تنها یک نماد است. لیستها بندی شبیه اشیاء هستند. یک لیست شامل یک پرانتز باز( دنباله‌ای از اعداد دلخواه که بوسیله فاصله خالی از هم جدا می‌شوند) و یک پرانتز بسته هستند. هر عنصر لیست می‌تواند یک اتم یا لیست باشد. اینها مثالهایی از لیستها هستند:
(This is a list) ((this) ((too))) () (((((((())))))))

(a b c d) (john mary tom) (loves john ?X)

(* (+ 3 4) 8) (append (a b c) (1 2 3))

(defun member (elem list)

(if (eq elem (first list)) T

(member elem (rest list))))
توضیح اینکه در بسیاری از مثالها عناصر لیست خود لیستها هستند.چنین لیستهایی، لیستهای تو در تو نامیده می‌شوند. در مورد تو در تویی محدودیتی وجود ندارد. برای مثال یکی از قویترین Lisp ها را شرح می‌دهیم: پیچیده‌ترین اشیاء را به راحتی می‌توان نوشت. تنها چیزی که در نظر گرفته می‌شود درستی عدد داخل پرانتزهاست. مهم توضیح این است که معنی وابسته به یک لیست نمایش ویژه یا اتم در لیست نمایش وارد نمی‌شود. به این معنی که همه عبارات نمادین که در بالا توصیف شده است از لحاظ نحو برنامه‌های Lisp را اصلاح می‌کنند ولی الزاماً از لحاظ معنی (semantic) برنامه‌ها رااصلاح نمی‌کنند.
2. Semantics (معانی): هسته هر سیستم برنامه‌نویسی Lisp مفسر است که کارش محاسبه مقدار برای یک عبارات نمادین داده شده است. این فرآیند ارزیابی نام دارد. نتیجه یا مقدار یک عبارت نمادین، یک عبارت نمادین است. که بعد از کامل شدن ارزیابی برگردانده شده است. توضیح اینکه در واقع Lispدارای Semantics (معانی) عملیاتی است که با یک تعریف ریاضی دقیق از نظریه تابع بازگشتی بدست می‌آید.
حلقه خواندن- محاسبه- چاپ چگونه می‌تواند مفسر Lisp را فعال کرده و برای محاسبه عبارات نمادین و بنابراین اجرای واقعی برنامه‌های Lisp بکار برود؟
مسئله‌‌های Prolog بصورت حقایق، بدیهیات و قوانین منطقی برای استنباط حقایق جدید بیان می‌‌ شوند . Prolog با قانون ریاضی در محاسبات گزاره‌ ای و ونتایج نظری بدست آمده در زمینه اثبات قضیه خودکار در اواخر دهه1960 بنا شده است. مفسر Lisp در واقع بعنوان یک تابع معمولاً بنام eval و جزئی از هر محیط برنامه‌‌‌نویسی Lisp است تعریف شده است (مانند تابعی که پیش‌ساخته نام دارد). آن بوسیله فراخوانی حلقه خواندن- محاسبه- چاپ در یک سیستم Lisp جاسازی می‌شود، وقتی یک عبارت نمادین توسط کاربر داده می‌‌ شود ابتدا به داخل سیستم Lisp خوانده می‌شود( خواندن هم یک تابع پیش‌ساخته است). سپس مفسر Lisp که via نام دارد تابع eval را فراخوانی می‌کند تا عبارت نمادین را محاسبه و نتیجه عبارت نمادین را با چاپ در دستگاه کاربر برگرداند ( شگفت‌آورنیست گفتن اینکه چاپ هم یک تابع پیش‌‌ساخته است). وقتی سیستم Lispدر کامپیوتر شروع به اجرا می‌‌شود این حلقه خواندن- محاسبه- چاپ بطور خودکار شروع به اجرا کرده و بوسیله علامت ویژه اعلان Lisp در ابتدای خط جدید به کاربر علامت می‌دهد در این مقاله ما علامت سئوا ل (?) را به عنوان اعلان Lisp بکار خواهیم برد. برای مثال:
( 4 3 +) ?
7
هر وقت سیستم Lisp اجرا شود حلقه خواندن- محاسبه- چاپ فعال خواهد بود.
عبارت نمادین ( 4 3 + ) که بوسیله هکر Lisp وارد شده است بوسیله مفسر Lisp بصورت فراخوانی تابع جمع تفسیر شده و نتیجه عبارت نمادین در ابتدای خط جدید 7 چاپ می‌‌شود ارزیابی مفسر Lisp مطابق سه قانون زیر انجام می‌‌شود:
1- یکسانی: یک عدد،‌ یک رشته یا نمادهای t و nil خودشان را ارزیابی می‌کنند (بر می‌گردانند) به این معنی که ارزش عدد 3،3 و ارزش رشته ”house”، رشته ”house”است. نمادt مقدار t برمی‌گرداند که به معنای true تفسیر می‌شود وnil ، nil به معنی false برمی‌‌گرداند
2- نمادها: ارزیابی یک نماد عبارت نمادین مربوط به آن را برمی‌‌‌گرداند. ( چگونگی‌ اش را در زیر نشان خواهیم داد) بنابراین اگر ما فرض کنیم نماد‌ *names* به لیست
(john mary tom) وابسته است آنگاه ارزیابی *names* آن لیست را نتیجه می‌دهد. اگر نماد color را به نماد green وابسته کنیم آنگاه green بعنوان مقدار color برگردانده می‌‌شود.
به بیان دیگر نمادها بعنوان متغیرهایی که به مقادیری متصل(باند) شده‌اند تفسیر می‌‌شوند.
3- لیستها: هر لیست بعنوان یک فراخوانی تابع تفسیر می‌‌شود. مفسر اول لیست دلالت بر تابعی دارد که باید برای بقیه عناصر( بالقوه خالی)‌ که آرگومانهای آن تابع را نشان می‌دهند بکار رود. در واقع آرگومانهای یک تابع قبلا بصورت نمادهای پیشوندی مشخص می‌‌شوند. این مزیت را دارد که توابع به سادگی می‌توانند با تعداد دلخواهی آرگومان مشخص و استفاده شوند. لیست خالی ( ) دارای عبارت نمادین nil بعنوان مقدارش می‌باشد. توضیح اینکه نماد nil در واقع دارای دو معنی است: یک نمایش مقدار منطقی false و دیگری نمایش لیست خالی. هر چند ممکن است این یک بیت فرد بنظر برسد، ولی در واقع در Lisp مشکلی در شناسایی مفهوم nil بکاررفته وجود ندارد.
‌ ولی بطور کل آرگومانها قبل از اینکه توابع مقادیر آنها را استفاده کنند ارزیابی می‌شوند. اولویت ارزیابی ترتیبی از آرگومانها از چپ به راست است. یک آرگو‌مان ممکن است یک اتم یا یک لیست باشد،‌درهر حالت بعنوان یک فراخوانی تابع تفیسر شده و مفسر Lisp برای ارزیابی آن فراخوانی می‌شود. برای مثال، ارزیابی زیر در سیستم Lisp یک تابع به حساب می‌آید:
?(max 4 (min 9 8) 7 5)

8
در اینجا آرگومانها 5, 7, (min 9 8), 4 هستند که در اولویتی قبل از تابعی به نام max که نتیجه مقادیر آرگومانها را به کار می‌برد ارزیابی می‌شوند. آرگومان اول 4 ،‌ یک عدد است پس مقدار آن 4 است. آرگومان دوم (min 9 8) است که خودش یک فراخوانی تابع است. بنابراین باید قبل از آرگومان سوم فراخوانی شود، (min 9 8) باید توسط مفسر Lisp ارزیابی شود. چون ما باید مفسر Lispرا برای بعضی آرگومانها در طول ارزیابی همه فراخوانی‌های توابع استفاده کنیم می‌‌توان گفت مفسر Lisp بصورت بازگشتی فراخوانی شده است. مفسر Lisp همان مراحل را به کار می‌برد، پس آرگومان اول 9 قبل از آرگومان دوم 8، ارزیابی می‌شود. با بکار برروی تابع min حاصل 8 می‌شود یعنی تابع کوچکترین عدد یک مجموعه از اعداد صحیح را محاسبه می‌‌کند. برای تابع بیرونی max هم به این معنی است که آرگومان دوم آن 8 ارزیابی می‌شود.
آرگومانهای بعدی 7و5هستند که نتیجه ارزیابی آنها مقادیر 7و5 می‌شود. حال تابع بزرگترین عدد که max نام دارد می‌تواند ارزیابی شود که 8 برمی‌گرداند. این مقدار نهایی،‌ مقدار فراخوانی همه توابع می‌‌باشد. از آنجایی که گفته می‌‌‌شود مفسر 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 4 (min 9 8) 7 5)) یا ’(max 4 (min 9 8) 7 5)
قبلاً گفتیم که مفسر Lisp یک تابع یکتایی پیش‌ساخته است که eval نام دارد. آن صریحاً آرگومانهایش را وادار می‌کند تا مطابق قوانین مذکور در بالا ارزیابی شوند. در بعضی حالات، آن می‌تواند مقابل تابع quote قرار بگیرد بنابراین به وضوح لازم است که یک لیست بعنوان داده مشخص شود تا سیستم Lisp بتواند یک فراخوانی تابع تفسیر شود، ما می‌توانیم(eval ’(max 4 (min 9 8) 7 5)) را مشخص کنیم که مقدار 8 را بطوری که در بالا توصیف شد بر می‌گرداند. به همان صورت مشخص کردن (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 3 2 5 1))
3
توضیح اینکه در واقع Setq حالت مفسر Lisp را تغییر می‌دهد تا دفعه بعدی که همان متغیر استفاده می‌شود، دارای مقدار بوده و بنابراین مفسرLisp قادر به بازگرداندن آن خواهد بود. اگر این اتفاق نیفتد آنگاه مفسر Lisp یک اخطار خواهد داد زیرا نماد متصل نشده است.
(گام 2 مفسر Lisp پیدا نشد). بنابراین آن می‌گویدکه Setq یک اثر جانبی تولید می‌کند زیرا حالت مفسر Lisp بطور پویا تغییر می‌دهد. وقتی استفاده از Setq اجباری شد، به هرحال متوجه شد که در واقع از مسیر semantics (معانی) Lisp ناب دور می‌شود. پس Setq باید با دقت بسیار استفاده شود.

B. نوع داده لیستبرنامه‌نویسی در Lisp در واقع به معنی تعریف توابعی است که روی لیست عمل می‌کنند. مانند ایجاد، پیمایش،‌کپی، تغییر و حذف لیستها. از آنجایی که این در Lisp مرکزی است، هر سیستم Lisp بر مبنای مجموعه‌ای از توابع پیش‌ساخته ابتدایی که بطور موثری عملیات اصلی لیست را پشتیبانی می‌کند می‌آید. ما بطور خلاصه یکی از مهمترین آنها معرفی می‌کنیم. ابتدا نوع گزاره‌ای،‌ ما می‌دانیم که یک عبارت نمادین جاری یا یک لیست است یا نیست (یعنی یک اتم). این کار بوسیله تابع Listp انجام می‌شود که هر عبارت نمادین expr را بعنوان آرگومان پذیرفته و اگر expr لیست باشد نماد t و در غیر این صورت nil برمی‌گرداند. مثالها هستند (ما از فلش راست => برای نشان دادن نتیجه فراخوانی تابع استفاده خواهیم کرد):
(listp ’(1 2 3))==>t

(listp ’( ))==>t

(listp ’3)==>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 ’(1 2 3)) (rest ’(1 2 3))) ==>(1 2 3)

در اصل، 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 1) 2 (list 1 2 3)) ==>((1) 2 (1 2 3))

(append ’(1) (list 2)) ==>(1 2)

(append ’(1 2) nil ’(3 4))==>(1 2 3 4)

(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 3 5) را در سیستمLisp وارد کنید مفسرLisp قادر خواهد بود که تعریف برای نام تابع مشخص شده بیابد و سپس آرگومانهای داده شده را از چپ به راست پردازش کند وقتی این کار انجام شد آن مقدار هر آرگومان را مطابق پارامتر مشخص شده در لیست پارامتر تعریف تابع وصل خواهد کرد(تخصیص خواهد داد) در مثال ما بدین معنی است که مقدار آرگومان اول که3 است(3 همان عدد3 است که خودش را ارزیابی کرده است) به پارامترx متصل می‌کند. سپس مقدار آرگومان دوم که 5 است به پارامترy متصل می‌شود. چون مقدار یک آرگومان به یک پارامتر متصل می‌شود، این روش فراخوانی با مقدار نامیده شده است. بعد از مقدار‌یابی برای همه پارامترها مفسرLisp قادر به ارزیابی بدنه تابع خواهد بود. مثال بدین معنی است که ( 3 5 +) فراخوانی خواهد شد. نتیجه فراخوانی8 است که بعنوان نتیجه فراخوانی(my-sum 3 5) برگردانده می‌شود. بعد از تکمیل فرا‌خوانی تابع اتصالات موقت پارامترهای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 0.75) ’very-probable)

((> prob–value 0.5) ’probable)

((> prob–value 0.25) ’improbable)

(T ’very-improbable)))
وقتی(verbalize-prop 0.33) فراخوانی می‌شود مقدار واقعی آرگومانها به پارامترprop-value متصل می‌شود.سپسcond با آن اتصالات ارزیابی می‌شود very-probable)’((>prop-value)است.> یک گزاره پیش ساخته است که تست می‌کند که آیا آرگومان اول از دومی بزرگتر است،چونpropvalue،0.33 است. بهnil ارزیابی می‌شود که به معنی انجام نشدن تست است. بنابراین ارزیابی این بخش پیشنهادی بلافاصله پایان می‌یابد. و سپس پیشنهاد
((> prob–value 0.5) ’probable)ارزیابی می‌شود که تابع تست باز هم nilبرمی‌گرداندبنابراین ارزیابی هم پایان می‌یابد. سپس ((prop-value 0.25) ’improbable) ارزیابی می‌شود حال با بکار بردن تابع تستT برگردانده می‌شود که به معنی انجام تست است.آنگاه همه اعمال این بخش که بطور مثبت تست شده است. ارزیابی ومقدار آخرین عمل به عنوان مقدارcond برگردانده می‌شود در مثال ما تنها عملimprobable’ تعیین می‌شود که مقدارimprobable (غیرمحتمل) را برمی‌گرداند از آنجایی که این مقدارcond را تعیین می‌کند و عبارت cond تنها عبارت بدنه تابعverbalize-prop است. نتیجه فراخوانی improbable ,((verbalize-prop 0.33) است. توضیح اینکهاگرما (verbalize- prop 0.1)را وارد کنیم مقدارvery- improbable را بر‌می‌گرداند زیرا تست هر سه با شکست مواجه شده و باید بخش (T ’very-improbable)ارزیابی شوددر این حالت نمادT به عنوان تستی که همیشهT بر‌می‌گرداند استفاده شده است بنابراین مقدار این پیشنهاد
very- improbable است.

نظرات (0)
برای نمایش آواتار خود در این وبلاگ در سایت Gravatar.com ثبت نام کنید. (راهنما)
نام :
ایمیل (پنهان میماند) :
وب/وبلاگ :
متن نظر :