زبانهای برنامه‌نویسی در هوش مصنوعی

نویسنده: Gunter Neumann
German Research Center for Artificial Intelligence (LT–Lab, DFKI)

ترجمه: احد محمّدی خواجه
E-mail: ae1359m@gmail.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 است که در اواخر دهه 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- برنامه نویسی تابعی در Lisp
Lisp اولین زبان برنامه نویسی تابعی است: آن برای پشتیبانی محاسبات نمادین با استفاده از لیستهای پیوندی بعنوان ساختار مرکزی داده ها ابداع شده بود ( 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 است.

E. تعریف توابع بازگشتی
دومین روش اصلی برای تعریف کنترل جریان درLisp تعاریف توابع بازگشتی هستند. تابعی که از تعریفش بعنوان جزئی از تعریفش استفاده می‌کند باز‌گشتی نام دارد. بنابراین، یک تعریف بازگشتی، تا جایی که امکان دارد مسئله‌ای را به قسمتهای کوچکتر تقسیم می‌کند سپس این قسمتهای کوچکتر را با استفاده از توابع مشهور و جمع پاسخهای یکسان حل کرده و حل برنامه را کامل می‌کند. بازگشت یک روش طبیعی برای کنترل ساختارهای داده‌ای است که اندازه معینی ندارد. مانند لیستها، درختها و گرافها. بنابراین برای مسئله‌هایی که در یک فاصله از حالات دنبال حل کاندید می‌گردند مناسب است.
Lisp اولین زبان برنامه‌نویسی کاربردی بود که با روش معین تعریف تعاریف بازگشتی را پشتیبانی کرده است. ما از دو مثال کوچک برای نشان دادن بازگشت درLisp استفاده خواهیم کرد. اولین مثال برای تعیین طول یک لیست طویل دلخواه استفاده می‌شود. طول یک لیست برابر تعداد عناصر آن است. تابع بازگشتی آن به صورت زیر است.
(defun length (list)

(cond ((null list) 0)

(T (+ 1 (length (cdr list))))))
وقتی یک تعریف بازگشتی تعریف می‌شود. ما باید حالتهای اساسی راشناسایی کنیم یعنی آن قسمتهایی که نمی‌توانند بیشتر تجزیه شوند. مسئله اندازه وابسته به لیست است. کوچکترین مسئله اندازه در لیست، لیست خالی است. بنابراین اولین چیزی که ما باید مشخص کنیم تستی برای شناسایی لیست خالی است و تعیین اینکه طول لیست خالی باید چقدر باشد تابع پیش‌ساخته null تست می‌کند که آیا این لیست خالی است در این صورت t برمی‌گرداند. از آنجایی که لیست خالی بدون عنصر است تعریف می‌کنیم که طول لیست خالی صفر باشد کار دیگری که باید انجام شود تجزیه مسئله اندازه به قسمتهای کوچکتر است که همان مسئله می‌تواند برای فسمتهای کوچکتر استفاده شود. تجزیه لیست می‌تواند با استفاده از توابع cdr,car انجام شود. به این معنی که ما باید تعیین کنیم تا وقتی که لیست خالی پیدا شود عنصر اول و بقیه عناصر لیست چه کار بکنند. از آنجایی که ما ازقبل لیست خالی را بعنوان حالت اساسی شناسایی کردیم، می‌توانیم فرض کنیم تجزیه برروی لیستی شامل حداقل یک عنصر انجام خواهد شد. بنابراین هر بار که قادر خواهیم بود تا با بکار بردن cdr بقیه عناصر لیست را بدست آوریم، ما یک عنصر اضافی پیدا کردیم که باید برای افزایش تعداد عناصر لیست قبلا شناسایی شده بوسیله یک استفاده می‌شود. استفاده از این تعریف تابع(length ’( )) بلافاصله صفر بر‌خواهد گرداند و اگر
(length ’(a b c)) را فراخوانی کنیم، نتیجه 3 خواهد بود زیرا برای اینکه لیست خالی شود باید سه فراخوانی بازگشتی member انجام دهیم بعنوان مثال دوم، تعریف بازگشتی را در نظر می‌گیریم که تست می‌کند که آیا عنصر داده شده در لیست داده شده قرار دارد اگر عنصر براستی در لیست پیدا شود زیر لیستی که با عنصر پیدا شده شروع می‌شود را برمی‌گرداند اگر عنصر پیدا نشوددnil برگردانده می‌شود مثال فراخوانی‌ها هستند.
(member ’b ’(a f b d e b c)) ==> (b d e b c)

(member ’k ’(a f b d e b c)) ==> nil
مشابه تعریف بازگشتی ما لیست خالی را به عنوان حالت اساسی استفاده می‌کنیم برایmember لیست خالی به این معنی است که عنصر مورد سوال در لیست پیدا نشود. بنابراین ما باید یک لیست را تا زمانی که عنصر مورد سوال پیدا می‌شود یا لیست خالی است تجزیه می‌کنیم تجزیه با استفاده ازcar وcdr انجام می‌شود.car برای استخراج عنصر اول لیست به کار می‌رود که می‌تواند برای کنترل اینکه با عنصر مورد سوال برابر است استفاده شود در این حالت می‌توانیم پردازشهای اضافی را مستقیماً متوقف کنیم اگر برابر نبود آنگاه باید تابعmember را برای بقیه عناصر تا خالی شدن لیست بکار ببریم بنابراین می‌تواند به صورت زیر تعریف شود.
(defun member (elem list)

(cond ((null list) nil)

((equal elem (car list)) list)

(T (member elem (cdr list)))))
F. توابع مرتبه بالا
درLisp توابع می‌توانند بعنوان آرگومان استفاده شود تابعی که بتواند توابع را بعنوان آرگومانهایش بپذیرد تابع مرتبه بالا نامیده می‌شود. مشکلات فراوانی وجود دارند که یکی پیمایش یک لیست(یا یک درخت یا یک گراف) است که باید برای هر لیست عنصر تابع معینی استفاده شود. برای مثال****** تابعی است که تستی برای عناصر لیست به‌کار می‌برد و آنهایی که شکست می‌خورند را حذف می‌کند. نگاشتها توابعی هستند که همان تابع را روی هر عنصر لیست به کار می‌برند تا لیستی از نتایج را برگردانند. تعاربف توابع مرتبه بالا می‌تواند برای تعریف توابع عمومی پیمایش لیست استفاده شود که آنها از توابع خاصی که برای پردازش عناصر لیست بکار می‌روند خلاصه می‌شوند (چکیده می‌شوند). به منظور پشتیبانی تعاریف مرتبه بالا یک تابع خاص است که یک تابع و دنباله‌ای از آرگومانها را به عنوان آرگومان می‌پذیرد و آن تابع را در آرگومانهای آنها به کار می‌برد. بعنوان مثال با استفاده ازfuncall، تابع عمومی****** را تعریف خواهیم کرد که می‌تواند به این صورت فراخوانی شود:
(****** ’(1 3 -9 -5 6 -3) #’plusp) ==>(1 3 6)
plusp یک تابع پیش ساخته است که کنترل می‌کند آیا یک عدد داده شده مثبت است یا نه؟ اگر باشد آن عدد را بر‌می‌گرداند در غیر این صورتnil بر‌می‌گرداند نماد خاص# بکار می‌رود تا به مفسرLisp بگوید که مقدار آرگومان یک شی تابعی است . تعریف به صورت زیر است:
(defun ****** (list test)

(cond ((null list) list)

((funcall test (car list))

(cons (car list) (****** (cdr list) test)))

(T (****** (cdr list) test))))
اگر لیست خالی باشد آنگاه بسادگی برمی‌گردد در غیر این صورت تابع تست روی عنصر اول لیست بکار می‌رود. اگر تابع تست موفق شودcons بکار می‌رود تا لیست حاصل را با استفاده از این عنصر و همه عناصری که در طول فراخوانی بازگشتی****** ازcdr و تابع تست استفاده می‌کنند بسازد. اگر تابع تست برای عنصر اول با شکست مواجه شود این عنصر بسادگی با بکاربردن****** بصورت بازگشتی روی عناصر باقیمانده پرش می‌کند. یعنی این عنصر نمی‌تواند جزئی از لیست حاصل باشد تابع می‌تواند برای بسیاری از توابع مختلف تست استفاده شود مانند:

(****** ’(1 3 A B 6 C 4) #’numberp) ==> (1 3 6 4)

(****** ’(1 2 3 4 5 6) #’even) ==> (2 4 6)

به عنوان مثال دیگری از تعریف****** تابع مرتبه بالا، مامی‌خواهیم یک تابع نگاشت ساده تعریف کنیم که یک تابع روی همه عناصر یک لیست بکاررفته، لیستی از همه مقادیر بر‌می‌گرداند. اگر تابع my-map را فراخوانی کنیم آنگاه تعریفی شبیه این داریم:
(defun my-map (fn list)

(cond ((null list) list)

(T (cons (funcall fn (car list)) (my-map fn (cdr list))))))
اگر یک تابع Double وجود داشته یاشد که تنها عدد را دو برابر کند آنگاه یک فراخوانی ممکن my-map به این صورت می‌تواند باشد:
(my-map #’double ’(1 2 3 4))==> (2 4 6 8)
بارها شده که یک تابع باید یکبار استفاده می‌شد. بنابراین اگر ما بتوانیم مستقیما تعریفی از یک تابع بعنوان آرگومان از تابع نگاشت فراهم کنیم کاملا مناسب خواهد بود برای اینکار تعریف عبارت lambda را پشتیبانی می‌کند. ما قبلا به طور غیر رسمی نماد‌سازی عبارات را در بخش II بعنوان تعریف توابع بی نام یا مستعار معرفی کردیم. در Lisp عبارات lambda با استفاده از نوع خاصی از lambda تعریف می‌شوند نوع عمومی عبارت lambda به این صورت است:

(lambda ( parameter . . . ) body . . . )

یک عبارت lambda امکان می‌دهد تا ما تعریف تابع را از نام تابع تشخیص دهیم عبارات lambda می‌توانند به جای نام تابع در تابع funcall استفاده شوند مانند عبارت که تابع double ما می‌تواند باشد:
(lambda (x) (+ x x))
برای مثال: فراخوانی تابع my-map بالا می‌تواند با استفاده از عبارت lambda مجدداً به صورت زیر بیان شود:
(my-map #’(lambda (x) (+ x x)) ’(1 2 3 4) ==> (2 4 6 8)
یک عبارت lambda یک شئ تابعی بر می‌گرداند که به نام تابع متصل نیست در تعریف
my-map ، پارامتر fn را بعنوان متغیر نام تابع استفاده می‌کنیم. وقتی شکل lambda محاسبه شد مفسر Lisp شئ تابعی را به متغیر نام تابع متصل خواهد کرد. به این طریق یک پارامتر تابع بصورت یک نام تابع پویا استفاده می‌شود. نماد # صروری است تا به Lisp بگوید که نه تنها یک شئ تابعی را وصل کند بلکه باید اتصالات محلی و سراسری مقادیر وابسته به شئ تابعی را نیز نگه دارد. این تنها با استفاده از عملگر quote امکان‌پذیر نخواهد بود (متأسفانه به دلیل محدودیت جا جزئیات بیشتری داده نمی‌شود).

G. سایر زبانهای برنامه‌نویسی تابعی غیر از Lisp
ما Lisp را به عنوان نماینده اصلی زبان برنامه‌نویسی تابعی معرفی کردیم (مخصوصاً نسخه پر استفاده Common Lisp )، زیرا هنوز هم زبان برنامه‌نویسی پر استفاده‌ای برای تعدادی از مسئله‌های هوش مصنوعی مانند فهم زبان طبیعی، استخراج اطلاعات، یادگیری ماشین،‌ برنامه‌ریزی AI یا برنامه‌نویسی ژنتیک است. درکنار Lispتعدادی از زبانهای برنامه‌نویسی تابعی دیگر توسعه یافتند. ما بطور خلاصه دو عضو مشهور را ذکر می‌کنیم، ML و Haskell.
ML برگرفته از Meta-Language است یک زبان برنامه‌نویسی تابعی با دامنه ایستاست. تفاوت اصلی‌اش با Lisp درsyntax (نحو) است (که بیشتر شبیه پاسکال است)، و یک نوع سیستم چند ریختی محض است (یعنی بکاربردن انواع قوی و نوع استنتاجی بوسیله متغیرهایی که نیاز به اعلان ندارند). نوع هر متغیر اعلان شده و عبارت می‌تواند در زمان کامپایل تعیین شود. MLتعریف انواع داده خلاصه را پشتیبانی می‌کند، به صورتی که در مثال زیر شرح داده شده است:

datatype tree = L of int
| int * tree * tree;

خوانده می‌شود’’ هر درخت دو دویی دارای یک برگ شامل یک عدد صحیح و یا یک گره
شامل یک عدد صحیح و دو درخت است( زیر درختها)‘‘ در مثال بعدی، مثالی از تعریف یک تابع بازگشتی که روی یک ساختار درخت بکار می‌رود نشان داده شده است:

fun depth(L ) = 1

| depth(N(i,l,r)) =

1 + max(depth l, depth r);

تابع depth نگاشتی از درختها به اعداد است. عمق هر برگ 1 است و عمق هر درخت دیگر 1 بعلاوه بیشترین عمق زیر درختهای چپ و راست آن است.
Haskell شبیه ML است: Syntax مشابهی بکار می‌برد، دامنه‌اش هم ایستاست و از همان روش استنتاج استفاده می‌کند. با ML در این تفاوت دارد که یک زبان کاملاً تابعی است. به این معنی است که به اثرات جانبی اجازه نداده و شامل هیچ نوع ویژگی دستوری نیست، در اصل متغیر و جملات انتسابی ندارد. بعلاوه از یک تکنیک ارزیابی کند استفاده می‌‌کند، که زیر عبارت را ارزیابی نمی‌کند تا موقع نیاز مقدارش معلوم باشد. لیستها رایجترین ساختار داده در Haskell هستند. برای مثال [1,2,3] لیستی از سه عدد صحیح 3,2,1 است لیست [1,2,3] در Haskell در واقع خلاصه‌نویسی شده لیست 1:(2:(3:[ ] )) است، که[ ] لیست خالی است و: عملگری میانوندی است که آرگومان اولش را جلوی آرگومان دومش اضافه می‌کند( یک لیست). بعنوان مثالی از یک تابع کاربر تعریفی که روی لیستها عمل می‌کند، مسئله شمارش تعداد عناصر در یک لیست با تعریف تابع length ملاحظه می‌شود.

length :: [a] -> Integer

length [ ] = 0

length (x:xs) = 1 + length xs

خوانده می‌شود’’طول لیست خالی 0 است، و طول لیستی که عنصر اولش x است و بقیه xs است،1 بعلاوه طول xs است‘‘. در Haskell تابع invocation احضار با تطبیق الگو راهنمایی می‌کند، برای مثال طرف چپ معادله داری الگوهایی مانند[ ] و x:xs است. در یک کاربرد تابع این الگوها با پارامترهای واقعی تطبیق داده می‌شوند [ ] ) تنها با لیست خالی مطابقت می‌کند، و x :xs با هر لیست با حداقل یک عنصر با موفقیت تطبیق می‌کند، x به عنصر اول و xs به بقیه لیست متصل می‌شوند). اگر تطبیق موفقیت‌آمیز باشد طرف راست معادله ارزیابی و بعنوان نتیجه کاربرد برگردانده می‌شود. اگر با شکست مواجه شود معادله بعدی سعی می‌شود، و اگر همه معادلات با شکست مواجه شوند،‌ حاصل یک خطا می‌شود.
این پایان کوتاه ما از’’سفر در Lisp ‘‘ است. ما تنهای توانستیم جنبه بسیار مهم Lisp را مطرح کنیم. خوانندگان علاقمند به جزئیات خاص بیشتر باید حداقل یکی از کتابهای مذکور در آخر مقاله را کنکاش کنند. بقیه این مقاله معرفی الگوی برنامه‌نویسی دیگری بنام ‌Prolog است که در برنامه‌نویسی AI بطور گسترده مورد استفاده قرار می‌‌گیرد.

IV. برنامه‌‌نویسی منطقی در Prolog
در دهه 1970 یک الگوی دیگر برای محاسبات نمادین در برنامه‌نویسی AI از موفقیت در زمینه اثبات قضیه خودکار ارئه شد. حل رویه اثبات بطور قابل توجهی توسط رابینسون
(1965) توسعه یافته که که با منطق رسمی نشان داده شده است، در محاسبات گزاره‌ای خاص می‌‌توان بعنوان نمادی برای تعیین الگوریتم‌ها و بنابراین برای انجام محاسبات نمادین استفاده شود. در اوایل (دهه 1970) Prolog ، مخفف(برنامه‌نویسی در منطق) اولین زبان‌‌ برنامه‌‌نویسی بر مبنای منطق پدیدار شد. آن توسط آلن کالمرار، رابرت کووا لسکی و فیلیپ راسل توسعه یافته است. اساس Prolog شامل یک روش برای مشخص کردن گزاره‌های محاسبات گزاره‌ای و تصمیات محدود است. برنامه‌‌نوسی در Prolog شامل مشخصات حقیقی در مورد اشیاء و ارتباط آنها و قوانینی که ارتباطات را مشخص می‌کند، است. برنامه‌های Prolog مجموعه‌ای از جملات اعلانی در مورد یک مسئله هستند زیرا آنها نحوه محاسبه نتیجه را مشخص نمی‌‌‌کند.بلکه ساختار منطقی نتیجه را مشخص می‌‌کنند Prolog با برنامه‌نویسی دستوری و حتی برنامه‌‌نویسی تابعی در تعریف نحوه محاسبه نتیجه کاملاً متفاوت است. با استفاده از Prolog برنامه‌نویسی می‌تواند در یک سطح خیلی خلاصه و کاملاً نزدیک به مشخصات رسمی یک مسئله انجام می‌‌گیرد. Prolog هنوز هم مهمترین زبان برنامه‌نوسی منطقی است. تعدادی از سیستمهای برنامه‌نوسی تجاری در بازار موجود است که شامل ماجولهای مدرن برنامه‌‌‌نویسی هستند، یعنی کامپایلر، Debugger و ابزارهای تجسم. Prolog در تعدادی از زمینه‌های AI مانند سیستم‌های خبره و پردازش زبان طبیعی بطور موفقیت‌آمیزی استفاده شده است. اما در زمینه‌های دیگری مانند سیستم‌ های مدیریت پایگاه داده رابطه‌ای یا در آموزش نیز استفاده می‌شود. یک برنامه Prolog بسیار ساده برنامه‌ای است که شامل دو حقیقت و یک قاعده است.

scientist(godel).

scientist(einstein).

logician(X) :- scientist(X).

دو جمله اول می‌تواند بصورت ’’Godel is a scientist ‘‘ و ’’Einstein is a scientist ‘‘ تفسیر شود.جمله قانون می‌‌‌گوید: ’’X is a logician if x is a scientist ‘‘. برای تست این برنامه باید عبارات پرس و جو( یا قضایا) را مشخص کنیم که Prolog سعی می‌کند با استفاده از برنامه مشخص شده به آنها جواب دهد(یا اثبات کند). یک پرس و جوی ممکن این است: ?- scientist(godel).
که می‌تواند به صورت ’’Is Godel a scientist?‘‘ بیان شود. Prolog با بکار بردن رویه اثبات پیش‌ساخته خودش ’’yes‘‘ جواب خواهد داد، زیرا ممکن است یک حقیقت پیدا شود که کاملاً مطابق با پرس و جو باشد. دیگر پرس و جوی ممکن بصورت سئوال:
’’who is a scientist?‘‘و در Prolog بصورت زیر بیان می‌شود:
?- scientist(X).
Prolog نتیجه خواهد داد’’X = godel , X= Einstein ‘‘. در این حالت Prolog نه‌تنها جواب می‌دهد’’yes ‘‘ بلکه همه متغیرهای متصل به x را که در طول اثبات موفق پرس و جو پیدا می‌کند را بر می‌گرداند. مثال دیگر، ممکن است ما با پرس و جوی Prolog زیر سئوال کنیم ’’who is a logician ‘‘:
?- logician(X).

اثبات این پرس و جو همان مجموعه‌ای از حقایق را که قانون مشخص کرده است را نتیجه می‌دهد. سرانجام ممکن است ما پرس و جوی زیر را مشخص کنیم:
?- logician(mickey-mouse).
در این حالت Prolog جواب خواهد داد با ’’No ‘‘. هر چند قانون می‌گوید کسی منطق‌دان است که دانشمند هم باشد، ‌ولی Prolog حقیقتی نمی‌یابد که بگویدMickey Mouse دانشمند است. توضیح اینکه Prolog تنها نسبت به برنامه داده شده می‌تواند پاسخ بدهد. در واقع به این معنی است که ‘‘ No, I couldn’t deduce the fact‘‘. این ویژگی بعنوان فرض جهان بسته یا رد آن بصورت شکست،‌ مشهور است. به این معنی که Prolog همه اطلاعات لازم برای حل مسئله موجود در پایگاه داده را فرض می‌‌کند.
جملات برنامه‌های Prolog شامل مجموعه‌ای از جملات بنام بند هستند که برای نمایش داده‌ها و برنامه‌ها استفاده می‌شوند. نماد نقطه‌ برای پایان دادن بند بکار می‌رود. یک واژه می‌تواند یک ثابت(نامهای نمادین که با یک حرف کوچک شروع می‌شوند مانند godel یا eInstein )، یک متغیر(نمادهایی که با یک حرف بزرگ شروع می‌شوند مانند x یا ‌ Scientist)، یا یک ساختار باشد. ساختارهای گزاره‌های اتمی محاسبات گزار‌ه‌ای را نمایش می‌دهند و شامل عملگر نام و یک لیست پارامتر هستند. هر پارامتر می‌تواند یک واژه باشد به این معنی که واژه‌ها،‌ اشیاء‌ بازگشتی هستند. Prolog سه نوع بند را تشخیص می‌دهد: حقایق،‌قوانین و پرس و جوها. یک حقیقت با یک ساختار واحد نمایش داده می‌شود که بعنوان یک گزاره درست ساده تفسیر می‌شود. قبلاً در مثال ساده برنامه بالا دو حقیقت ساده را معرفی کردیم.
اینها چند مثال دیگر هستند:

male(john).

male(bill).

female(mary).

female(sue).

father(john, mary).

father(bill,john).

mother(sue,mary).

توضیح اینکه این حقایق دارای معانی ذاتی نیستند یعنی معنی عملگر نام father تعریف نشده است. برای مثال با بکار بردن حواس معمول ممکن است آن را بصورت
’’John is the father of mary‘‘ تفسیر کنیم. هر چند برای Prolog این معنی وجود ندارد و تنها یک نماد است.
قوانین متعلق به نوع دیگری از بندها هستند. یک بند قانون شامل دو قسمت است،‌ سر که تنها یک واژه است و بدنه که تنها یک واژه یا یک اتحاد است. یک اتحاد یک مجموعه از واژه‌هاست که با نماد کاما از هم جدا می‌شوند.
منطقاً یک بند قانون بعنوان یک استدلال تفسیر می‌شود، اگر همه عناصر بدنه درست باشند، آنگاه عنصر سر نیز درست است. بنابراین بدنه بند به صورت قسمت if (اگر) و سر بند بصورت قسمت then (آنگاه) قانون مشخص می‌شوند.
این مثال برای مجموعه‌ای از بندهای قانون است:
parent(X,Y) :- mother(X, Y).

parent(X,Y) :- father(X, Y).

grandparent(X,Z) :- parent(X,Y), parent(Y,Z).

قانون اخیر خوانده می‌شود:
’’X is a grand parent of z if X is a parent of y and y is a parent of z ‘‘

دو قانون اولی می‌گویند:
’’some one is parent if it is the father or mother of some one else‘‘

دلیل رفتار دو قانون اول را هنگام معرفی رویه اثبات Prolog بعنوان فصلی بطور آشکار خواهد آمد. قبل از انجام این کار باید آخرین نوع بند را معرفی کنیم،‌ بند پرس و جو (که بند هدف نامیده می‌شود). یک پرس و جو برای فعال کردن رویه اثبات Prolog بکار می‌رود.
منطقاً یک پرس و جو مشابه یک قضیه مجهول است. آن شکلی مشابه حقیقت دارد تا به Prolog بگوید که یک پرس و جو باید اثبات شود، عملگر مخصوص پرس و جو –?است معمولاً در جلوی پرس و جو نوشته می‌شود. در مثالهای ساده برنامه Prolog معرفی شده در بالا، قبلاً توصیفی غیر رسمی از چگونگی استفاده پرس و جو در Prologرا دیدیم.
فرایند استنتاج Prolog شامل دو مؤلفه اساسی است: روش جستجو و یکی کننده. روش جستجو برای جستجو میان حقیقت و قانون پایگاه داده بکار می‌رود در حالی که یکی‌سازی برای تطبیق الگو و بازگرداندن اتصالاتی که یک عبارت صحیح می‌سازد بکار می‌رود.
یکی‌ساز روی دو واژه بکار می‌رود و سعی می‌کند با ترکیب آن دو یک واژه جدید شکل بدهد. اگر یکی سازی ممکن نباشد آنگاه گفته می‌شود یکی‌سازی شکست خورده است. اگر دو واژه مادی هیچ متغیری نباشند آنگاه یکی‌سازی در واقع از بررسی اینکه آیا واژه‌ها برابرند، خواهد کاست. برای مثال، یکی‌سازی دو واژه father (john,mary) و father(john,mary) موفق می‌شود در حالیکه یکی‌سازی جفت واژه‌های زیر با شکست مواجه خواهند شد.
father(X,mary) و father(john,sue)

sequence(a,b,c) و sequence(a,b)
اگر یک واژه حاوی یک متغیر (یا بیشتر) باشد آنگاه یکی کننده بررسی می‌کند که آیا متغیر می‌تواند با بعضی از اطلاعات واژه دوم متصل شود، هر چند تنها اگر قسمتهای باقیمانده واژه‌ها یکی شوند. برای مثال، برای دو واژه زیر:
father(X,mary) and father(john,mary)
یکی کننده X را به john متصل خواهد کرد زیرا واژه‌های باقیمانده برابرند. هرچند برای
زوج زیر:
father(X,mary) and father(john,sue)
مفهوم اتصال ساخته نمی‌شود چون mary و sue مطابق نیستند. روش جستجویی که برای پیمایش فضای جستجو بکار می‌رود بوسیله حقایق و قوانین برنامه Prolog محدود شده است. Prolog یک روش بالا به پائین، روش جستجوی عمقی (dfs) استفاده می‌کند. این به چه معنا است؟ همه مراحل کاملاً شبیه به روش تابع ارزیابی استفاده شده در Lisp است اگر یک پرس و جوی Q مشخص شده باشد آنگاه ممکن است آن مطابق یک حقیقت یا یک قاعده باشد. در حالتی از قاعده Prolog ,R ابتدا سعی می‌کند سر R را تطبیق دهد و اگر موفق شود آنگاه سعی می‌کندهمه عناصر بدنه R که زیر پرس و جو نامیده می‌شوند را تطبیق دهد اگر سر R حاوی متغیرها باشد آنگاه اتصالات در طول اثبات از زیر پرس و جوها استفاده خواهند کرد. از آنجایی که اتصالات تنها برای زیر پرس و جوها معتبر هستند، گفته می‌شود که برای یک قاعده محلی هستند. یک زیر پرس و جو هم می‌تواند یک قاعده باشد و هم یک حقیقت. اگر یک قاعده باشد آنگاه فرایند استنتاج Prolog بطور بازگشتی برای بدنه این پرس و جو بکار می‌رود. این، قسمت بالا به پائین روش جستجو را می‌سازد. عناصر بدنه یک قاعده از چپ به راست بکار می‌روند و تنها اگر عنصر جاری بتواند با موفقیت اثبات شود عنصر بعدی سعی می‌شود. این روش جستجوی عمقی را می‌سازد. ممکن است برای اثبات یک زیر پرس و جو دو یا چند حقیقت یا قاعده دیگر تعریف شوند. در آن صورت A, Prolog را انتخاب می‌کند و سعی می‌کند آن را اثبات کند، اگر لازم باشد زیر پرس و جوهای A را نیز پردازش می‌کند. اگر A با شکست مواجه شود Prolog به نقطه‌ای که اثبات A شروع شده بر می‌گردد(با حذف همه اتصالهایی که در طول اثبات A انتساب داده شده است) و سعی می‌کند دیگری را اثبات کند. این فرایند عقب‌گرد نام دارد . به منظور شرح همه روشها پرس و جوهای نمونه زیر را می‌توانیم ملاحظه کنید (مثال معرفی شده در بندهای پاراگراف قبلی را بعنوان پایگاه داده Prolog استفاده می‌کنیم):
?- grandparent(bill,mary).
تنها بندی که با این پرس و جو تطبیق می‌کند قاعده زیر است.
grandparent(X,Z) :- parent(X,Y), parent(Y,Z).
و یکی‌سازی پرس و جو با سر قاعده اتصالهای زیر را بر می‌گرداند: Z=mary,X=bill برای اثبات قاعده، باید دو عنصر بدنه قاعده از چپ به راست اثبات شوند. توضیح اینکه متغیرهای مشترک قواعد با سر قاعده و بنابراین اتصالهای محاسبه شده در طول تطبیق سر با پرس و جو برای پاسخ به زیر پرس و جوها موجودند. بنابراین زیر پرس و جوی اول در واقع بصورت parent(bill,y) و زیر پرس و جوی دوم بصورت parent (y,mary) معرفی شود. حال برای اثبات بند اول prolog دو قاعده parent دیگر می‌یابد. اجازه دهید فرض کنیم prolog اولی را انتخاب می‌کند.( برای یاد‌آوری بیش از یک انتخاب، prolog یک نقطه انتخاب مشخص می‌کند)
parent(X,Y) :- mother(X, Y).
یکی‌سازی زیر پرس و جوها با سه قاعده به راحتی ممکن است و متغیرx به واژه bill متصل خواهد شد . این عنصر تک بدنه‌ای بصورت (bill,y) mother معرفی می‌شود. متاسفانه هیچ حقیقتی که این زیر پرس و جو را معتبر کند در پایگاه داده وجود ندارد. چون یکی‌سازی (bill,y) mother با شکست مواجه می‌شود. پس همه قاعده انجام می‌شود. سپس prolog به نقطه انتخابی که اولین قاعده parent ممکن را انتخاب کرده بود، برگشته و دومی را انتخاب می‌کند.
parent(X,Y) :- father(X, Y)
یکی‌سازی زیر پرس و جوی (هنوز فعال) parent(bill,y) ، father(bill,y) معرفی خواهد شد. اینبار یکی‌سازی ممکن است،‌اتصال y=john برگردانده می‌شود. حال اولین زیر پرس و جوی parent از قاعده grand parent اثبات شده متغیرهای واقعی X=bill Z=mary,Y=john, هستند. عنصر دوم از بدنه قاعده grandparent،
parent (john, mary) معرفی می‌شود (توضیح اینکه مقدار z بعد از انتخاب قاعده grand parent فوراً متصل شده است).
همان روش برای این زیر پرس و جو بکار رفته و prolog حقایق کافی برای اثبات موفقیت‌آمیز آن خواهد یافت. وقتی که دو عنصر بدنه قاعده grand parent به طور معتبر اثبات شد، prolog به پایان می‌رسد که اولین پرس و جو true می‌شود. توسعه prolog ، به منظور استفاده از prolog برای برنامه‌نویسی کاربردی است. که با توسعه‌هایی مانند لیست ساختارهای داده، عملکردهایی برای کنترل واضح پیمایش از فاصله جستجو با یک برنامه prolog(بنام عملگر برش) و روالهایی برای رابطهای ورودی /خروجی، تست درستی (ردیابی) و اشکالزدایی می‌آید. ما نمی‌توانیم همه این توسعه‌ها را در متن این مرور کوتاه شرح دهیم. ما تنها بطور خلاصه نشان می‌دهیم که لیستها در prolog چگونه می‌توانند استفاده شوند. Prolog لیستها را بعنوان یک ساختار داده‌ای پایه‌ای با استفاده از syntax متداول پشتیبانی می‌کند. عناصر لیست با کاما جدا می‌شوند. کل لیست با براکت تعیین می‌شود. یک عنصر لیست می‌تواند یک واژه دلخواه یا یک لیست باشد، بنابراین کاملاً شبیه ساختارهای لیست در Lisp است. این مثالی از یک لیست prolog است:
[john, mary, bill]
لیست خالی بصورت [ ] نمایش داده می‌شود. برای ایجاد و پیمایش لیستها، prolog یک ترکیب خاص مبنی بر سر و دنبال یک لیست فراهم می‌کند. [X | Y]یک لیست است شامل یک سرلیست x و یک دنباله y است. برای مثال لیست بالا می‌تواند بصورت زیر مشخص شود.

[john | mary, bill]
ما گزارهmember را بصورت مثالی برای نحوه رفتار لیستها در prolog استفاده خواهیم کرد. این گزاره تعیین خواهد کرد که آیا یک عنصر داده شده در یک لیست داده شده واقع می‌شود؟ با توجه به توضیحات بالا یک عنصر در یک لیست است اگر سر لیست آن لیست باشد یا اگر در جایی از دنباله لیست واقع شود، با استفاده از تعریف غیررسمی گزاره member ما می‌توانیم برنامه prolog زیر را طرح کنیم. (نمادی که یک متغیر بی‌نام را مشخص می‌کند،‌استفاده می‌شود تا به prolog بگوید مهم نیست مقدار یکی کننده به آن متصل شود)
member(Element,[Element | ]).
member(Element,[ | List]) :- member(Element,List).
با فرض پر س و جوی زیر
?- member(a, [b,c,a,d]).
Prolog ابتدا کنترل می‌کند که آیا سر لیست [b | c,a,d]برابر a است.
به این علت بند اول با شکست مواجه می‌شود، پس دومی سعی می‌شود. این زیر پرس و جوی member (a,) معرفی خواهد شد که معنی‌اش این است که از روی عنصر اول بسادگی می‌پرد با بکار بردن بازگشی member،prolog سعی می‌کند تا اثبات کند که آیا سر لیست با a برابر است، که با شکست مواجه می‌شود.، زیر پر س و جوی جدید member (a,[a,d]) را با معرفی بند دوم بدست می‌آوریم. گام بازگشتی بعدی لیست [a | d]را کنترل خواهد کرد. اینبار a براستی با عنصر سر لیست این لیست برابر می‌شود، بنابراین prolog با "yes" پایان خواهد یافت.
برنامه‌نویسی منطقی محدودیت (clp)تصمیمی از سبک برنامه‌نویسی (ساده)‌prologاست. در clp واژه یکی‌سازی به حل محدودیت تعمیم یافته است. در برنامه‌نویسی منطقی محدودیت مولفه‌های اصلی یک مسئله بصورت محدودیت‌ها حالت یافته‌اند (یعنی ساختار اشیاء در سؤال) و مسئله بصورت یک کل که با گذاشتن محدودیتهای مختلف بوسیله قواعد ارائه شده است. (اساساً بوسیله تعریف بندها) برای مثال بند معین زیر نمونه یک تجزیه ریز از گرامر یک زبان طبیعی مانند انگلیسی است.
sign(X0) ←
sign(X1),

sign(X2),

X0 syn cat = s,

X1 syn cat = np,

X2 syn cat = vp,

X1 syn agr = X2 syn ag

بیان می‌شود یک شی زبانی بصورت یک عبارت S طبقه‌بندی می‌شود که باید مرکب از یک شیء طبقه‌بندی شد که بصورت یک NP (عبارت اسمی) و یک شئ طبقه‌بندی شده بصورت یک VP(عبارت لفظی) باشد و قرارداد اطلاعات (مانند شخص، حالت) باید بین NP و VP یکسان باشد. همه اشیایی که حداقل این محدودیتها را انجام می‌دهند جزء‌اشیای S هستند. توضیح اینکه هیچ ترتیب پیش فرضی برای VP,NPبعنوان حالتی برای گرامر زبان طبیعی مبنی بر ظواهر وجود ندارد که متن بدون استحکام به آن تکیه کند. اگر یک محدودیت نیاز به محدودیتهای اضافی داشته باشد. باید به قاعده اضافه شود، برای نمونه زیر ریشه‌ها باید با الحاق ترکیب شوند از نجاطیآنآن
آنجایی که محدودیتهای مثال بالا تنها شرایط لازم برای شئ از کلاس S را مشخص می‌کند آنها اطلاعات مختصری بیان می‌کنند. این برای دانش مبنی بر استدلال خیلی مهم است زیرا در کل ما تنها اطلاعات مختصری درباره جهان (محیط)‌داریم، ‌ما برای پردازش چنین خصوصیاتی دلیل مبنی بر حل محدودیت و الگوی برنامه‌نویسی منطقی می‌خواهیم. چون یکی‌سازی، فقط حالت خاصی از حل محدودیت است، برنامه‌های منطقی محدودیت توان بیان بالایی دارند.
تعدادی از زبانهای برنامه‌نویسی منطقی محدودیت (همراه با رابط کاربر سطح بالا و ابزارهای توسعه) تحقق یافته‌اند. مانند CHIP یا زبان OZ که برنامه‌نویسی اعلانی، برنامه‌نویسی شئ گرا، برنامه‌نویسی محدودیت و همزمانی را بعنوان جزئی از کل منسجم پشتیبانی می‌کند. OZ زبانی محدودیت قدرتمندی با متغیرهای منطقی،‌دامنه‌متناهی، مجموعه‌های متناهی، درختهای عقلانی و رکورد محدودیت‌هاست. آن در صدد است تا یک روش یکتا و انعطاف‌پذیر بدون شاخ و بندها برای برنامه‌نویسی منطقی فراهم کند. OZ بین روشهای مستقیم و غیر مستقیم برنامه‌نویسی منطقی اعلانی تفاوت قایل می‌شود.

V. سایر روشهای برنامه‌نویسی
‌در این مقاله قبلاً زبانهای AI را با روشهای برنامه‌نویسی دستوری مقایسه کردیم. زبانهای شیء گرا به الگوی برنامه‌نویسی مشهور دیگری تعلق دارند. در این جور زبانها اولین وسیله برای تعیین مسئله‌ها، تعیین خلاصه ساختارهای داده است که کلاس‌ها، اشیاء‌نام دارند. یک کلاس شامل یک ساختار داده همراه با عملیات اصلی‌اش که اغلب اسلوبها (روشها) نام دارند است. یک ویژگی مهم این است که ممکن است کلاسها در سلسله مراتبی از کلاسها و زیر کلاسها مرتب شوند. یک کلاس می‌تواند صفات سوپر کلاسهایش که پیمانه‌ای بودن را پشتیبانی می‌کنند را به ارث ببرد.
مشهورترین زبانهای شیءگرا C++,Eiffel و Java (جاوا) هستند. سیستم Common Lisp شیءگرا یک توسعه از common Lisp است. آن یکپارچه‌سازی کامل برنامه‌نویسی تابعی و شیءگرا را پشتیبانی می‌کند. اخیراً جاوا در بعضی از زمینه‌ها AI، خصوصاً در فن‌آوری عامل هوشمند، موتورهای جستجوی اینترنت یا استخراج داده‌ها کاملاً مشهور شده است. جاوا بر مبنای C++ است و زبان اصلی برای برنامه‌نویسی کاربردهای اینترنتی است. مهمترین ویژگیهای زبان که جاوا را از چشم‌آنداز AI جذاب می‌سازد فضای هرز خودکار پیش‌ساخته آن و مکانیزم چند نخی (چند وظیفه‌ای) آن است.
با افزایش تحقیقات در زمینه وب هوشمند یک الگوی برنامه‌نویسی جدید- برنامه‌نویسی عامل‌‌گرا – پدیدار شد. برنامه‌نویسی عامل‌گرا یک الگوی جدید برنامه‌نویسی است که یک نمای اجتماعی از محاسبه را به خوبی پشتیبانی می‌کند. در AOP اشیاء بعنوان عاملهایی شناخته می‌شوند که برای دستیابی به اهداف شخصی عمل می‌کنند. عامل در یک ساختار می‌تواند به پیچیدگی شبکه سراسری اینترنت یا به سادگی یک پیمانه (ماجول) از یک برنامه معمولی باشد. عاملها می‌توانند موجودیتهای مستقل باشند یعنی بدون دخالت کاربر برای گام بعدی‌شان تصمیم بگیرند، یا می‌توانند قابل کنترل باشند، یعنی بعنوان وسیله‌ای بین کاربر و عاملهای دیگر بکار بردند. از آنجایی که عاملها زنده در نظر گرفته می‌شوند، با رشد موجودیتهای نرم‌افزار، به نظر می‌رسد انتقالی از نقطه‌نظر زبانهای برنامه‌نویسی به طرف نقطه‌نظر سکوی پیشرفت نرم‌افزار پدیدار می‌شود. اینجا تأکید روی طراحی سیستم، سکوی پیشرفت و اتصال است. سئوالات حساس عبارتنداز: چگونه تعدادی از منابع پیشرفته AI که در زبانها و سکوهای مختلف موجودند می‌توانند با سایر منابع استفاده‌کننده از ابزارهای پیشرفت سیستم جدید مانند CORBA (معماری عادی رابط درخواست شئ) ترکیب شوند (یکپارچه شوند)، خلاصه‌سازی عمومی انواع داده و زبانهای تفسیری(یادداشت حاشیه‌ای) مانند XML و زبان استاندارد ارتباطات عامل‌گرا مانند KQML (زبان شناخت پرس و جو و دستکاری).
بنابراین آینده برنامه‌نویسی AI کمتر نگران سئوالاتی مثل: ” مناسب‌ترین الگوی برنامه‌نویسی چیست؟ “ است ولی باید به سئوالاتی مثل: ” چگونه می‌توانم الگوهای مختلف برنامه‌نویسی را زیر یک سایبان یکپارچه کنم؟ “ و ” بهترین زبان ارتباطی برای نرم‌افزارهای مستقل پیمانه‌ای هوشمند چیست؟ “ پاسخ دهیم.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

اجرا شده توسط: همیار وردپرس