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)))))
__________________
مرا سر نهان گر شود زير سنگ -- از آن به كه نامم بر آيد به ننگ
به نام نكو گر بميــرم رواست -- مرا نام بايد كه تن مرگ راست
|