برنامه نویسی تابعی در Lisp
Lisp اولين زبان برنامه نويسي تابعي است: آن براي پشتيباني محاسبات نمادين با استفاده از ليستهاي پيوندي بعنوان ساختار مركزي داده ها ابداع شده بود ( Lisp يعني پردازشگر ليست). جان مك كارتي دريافت كه روشهاي كنترل جريان توابع رياضي (بازگشت و تكرار) وسيله نظري مناسبي براي انجام محاسبات نمادين هستند. علاوه براين مفاهيم خلاصه سازي تابعي و كاربرد تابعي تعريف شده در محاسبات Lambda , سطح بالايي از خلاصه سازي موردنياز براي مسئله هاي هوش مصنوعی مشخص شده را فراهم مي كنند.
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 sهوش مصنوعیd: \” 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 هنوز زبان برنامهنويسي برتر در زمينه برنامهنويسي ژنتيك هوش مصنوعی است.
وقتي مقادير را به نمادها تخصيص ميدهيم كه برنامهنويسي برنامههاي كاربردي
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 بوجود آمد. ما نامهاي قديمي را استفاده ميكنيم تا براي خواندن و فهم كد هوش مصنوعی 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 را به عنوان نتيجه فراخواني تابع برميگرداند.
|