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 توابع ميتوانند بعنوان آرگومان استفاده شود تابعي كه بتواند توابع را بعنوان آرگومانهايش بپذيرد تابع مرتبه بالا ناميده ميشود. مشكلات فراواني وجود دارند كه يكي پيمايش يك ليست(يا يك درخت يا يك گراف) است كه بايد براي هر ليست عنصر تابع معيني استفاده شود. براي مثالfilter تابعي است كه تستي براي عناصر ليست بهكار ميبرد و آنهايي كه شكست ميخورند را حذف ميكند. نگاشتها توابعي هستند كه همان تابع را روي هر عنصر ليست به كار ميبرند تا ليستي از نتايج را برگردانند. تعاربف توابع مرتبه بالا ميتواند براي تعريف توابع عمومي پيمايش ليست استفاده شود كه آنها از توابع خاصي كه براي پردازش عناصر ليست بكار ميروند خلاصه ميشوند (چكيده ميشوند). به منظور پشتيباني تعاريف مرتبه بالا يك تابع خاص است كه يك تابع و دنبالهاي از آرگومانها را به عنوان آرگومان ميپذيرد و آن تابع را در آرگومانهاي آنها به كار ميبرد. بعنوان مثال با استفاده ازfuncall، تابع عموميfilter را تعريف خواهيم كرد كه ميتواند به اين صورت فراخواني شود:
(filter ’(1 3 -9 -5 6 -3) #’plusp) ==>(1 3 6)
plusp يك تابع پيش ساخته است كه كنترل ميكند آيا يك عدد داده شده مثبت است يا نه؟ اگر باشد آن عدد را برميگرداند در غير اين صورتnil برميگرداند نماد خاص# بكار ميرود تا به مفسرLisp بگويد كه مقدار آرگومان يك شي تابعي است . تعريف به صورت زير است:
(defun filter (list test)
(cond ((null list) list)
((funcall test (car list))
(cons (car list) (filter (cdr list) test)))
(T (filter (cdr list) test))))
اگر ليست خالي باشد آنگاه بسادگي برميگردد در غير اين صورت تابع تست روي عنصر اول ليست بكار ميرود. اگر تابع تست موفق شودcons بكار ميرود تا ليست حاصل را با استفاده از اين عنصر و همه عناصري كه در طول فراخواني بازگشتيfilter ازcdr و تابع تست استفاده ميكنند بسازد. اگر تابع تست براي عنصر اول با شكست مواجه شود اين عنصر بسادگي با بكاربردنfilter بصورت بازگشتي روي عناصر باقيمانده پرش ميكند. يعني اين عنصر نميتواند جزئي از ليست حاصل باشد تابع ميتواند براي بسياري از توابع مختلف تست استفاده شود مانند:
(filter ’(1 3 A B 6 C 4) #’numberp) ==> (1 3 6 4)
(filter ’(1 2 3 4 5 6) #’even) ==> (2 4 6)
به عنوان مثال ديگري از تعريفfilter تابع مرتبه بالا، ماميخواهيم يك تابع نگاشت ساده تعريف كنيم كه يك تابع روي همه عناصر يك ليست بكاررفته، ليستي از همه مقادير برميگرداند. اگر تابع 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,[c,a,d]) معرفي خواهد شد كه معنياش اين است كه از روي عنصر اول بسادگي ميپرد با بكار بردن بازگشي member،prolog سعي ميكند تا اثبات كند كه آيا سر ليست [c | a,d]با 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 كمتر نگران سئوالاتي مثل: ” مناسبترين الگوي برنامهنويسي چيست؟ “ است ولي بايد به سئوالاتي مثل: ” چگونه ميتوانم الگوهاي مختلف برنامهنويسي را زير يك سايبان يكپارچه كنم؟ “ و ” بهترين زبان ارتباطي براي نرمافزارهاي مستقل پيمانهاي هوشمند چيست؟ “ پاسخ دهيم.
- | سعید |
- | پنجشنبه سی ام آبان 1387
-
پیوند ها