امکانات محیط برنامه نویسی و نحوه اجرای برنامه ها
Syntax(نحو) و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))
(if (eq elem (first list)) T
(membr elem (rest list))))
توضیح اینکه در بسیاری از مثالها عناصر لیستها هستند. چنین لیستهایی، لیستهای تو در تو نامیده می شوند. در مورد تو در تویی محدودیتی وجود ندارد. برای مثال یکی از قویترین lisp ها را شرح می دهیم: پیچیده ترین اشیاء را به راحتی می توان نوشت. تنها چیزی که در نظر گرفته می شود درستی عدد داخل پرانتزهاست. مهم توشضیح این است که معنی وابسته به یک لیست نمایش ویژه یا اتم در لیست نمایش وارد نمی شود. به این معنی که همه عبارات نمادین که در بالا توصیف شده است از لحاظ نحو برنامه های lisp را اصلاح می کنند ولی الزاما" از لحاظ معنی برنامه ها را اصلاح نمی کنند.
2.semantics(معانی):
هسته هر سیستم برنامه نویسی lispمفسر است که کارش محاسبه مقداربرای یک عبارت نمادین شده است. که بعد از کامل شدن ارزیابی برگردانده شده است. توضیح اینکه در واقعlisp دارایsemantics عملیاتی است که با یک تعریف ریاضی دقیق از نظریه تابع بازگشتی بدست می آید.
حلقه خواندن- محاسبه – چاپ چگونه می تواند مفسر lisp را فعال کرده و برای محاسبه عبارات نمادین و بنابراین اجرای واقعی برنامه های lisp بکار برود؟
مسئله های prologبصورت حقایق،بدیهیات و قوانین منطقی برای استنباط حقایق جدید بیان می شوند.prolog با قانون ریاضی در محاسبات گزاره ای و نتایج نظری بدست آمده در زمینه اثبات قضیه خودکار در اواخر دهه 1960 بنا شده است. مفسر lisp در واقع بعنوان یک تابع معمولا" بنام eval و جزئی از هر محیط برنامه نویسی lisp است تعریف شده است(مانند تابعی که پیش ساخته نام دارد).
آن بوسیله فراخوانی حلقه خواندنی – محاسبه – چاپ در یک سیستم lispجاسازی می شود، وقتی یک عبارت نمادین توسط کاربر داده می شود ابتدا به داخل سیستم lisp خوانده می شود(خواندن هم یک تابع پیش ساخته است). سپس مفسر lisp که via نام دارد تابع eval را فراخوانی می کند تا عبارت نمادین را محاسبه و نتیجه عبارت نمادین را با چاپ در دستگاه کاربر برگرداند. وقتی سیستم lisp در کامپیوتر شروع به اجرا کرده و بوسیله علامت ویژه اعلانlispدر ابتدای خط جدید به کاربر علامت می دهد در اینجا ما علامت (؟) را به عنوان اعلان lisp بکار خواهیم برد. برای مثال:
هروقت سیستم lisp اجرا شود حلقه خواندن – محاسبه – چاپ فعال خواهدبود.
عبارت نمادین(+3 4) که بوسیله هکرlisp وارد شده است بوسیله مفسر lisp بصورت فراخوانی تابع جمع تفسیر شده و نتیجه عبارت نمادین در ابتدای خط جدید7 چاپ میشود ارزیابی مفسرlisp مطابق 3 قانون زیر انجام می شود:
1:یکسانی
یک عدد ، یک رشته یا نمادهای t,nil خودشان را ارزیابی میکنند به این معنی که ارزش عدد3،3 و ارزش رشته "house"، رشته" house" است . نمادt مقدار t بر می گرداند که به معنای فقعث تفسیر می شود و nil،به معنی false بر می گرداند.
2: نمادها
ارزیابی یک نماد عبارت نمادین مربوط به آن را برمی گرداند. بنابراین اگر ما فرض کنیم نماد*names* به لیست(john mary tom) وابسته است آنگاه ارزیابی*names* آن لیست را نتیجه می دهد. اگر نمادcolor را به نمادgreen وابسته کنیم آنگاهgreen بعنوان مقدارcolor برگردانده می شود.
به بیان دیگر نمادها بعنوان متغییرهایی که به مقادیری متصل شده اند تفسیر می شوند.
3:لیستها
هر لیست بعنوان یک فراخوانی تابع تفسیر می شود.مفسر اول لیست دلالت بر تابعی دارد که باید برای بقیه عناصر(بالقوه خالی) که آرگومانهای آن تابع را نشان می دهند بکار رود. در واقع آرگومانهای یک تابع قبلا بصورت نمادهای پیشوندی مشخص می شوند. این مزیت را دارد که توابع بسادگی می توانند با تعداد دلخواهی آرگومان مشخص و استفاده شوند.ایست خالی() دارای عبارت نمادینnil بعنوان مقدارش می باشد. توضیح اینکه نمادnilدر واقع دارای دو معنی است:یک نمایش مقدار منطقی false و دیگری نمایش لیست خالی. هر چند ممکن است این یک بیت فرد بنظر برسد،ولی در واقع lisp مشکلی در شناسایی مفهومnilبکار رفته وجود ندارد.
ولی به طور کل آرگومانها قبل از اینکه توابع مقادیر آنها را استفاده کنند ارزیابی می شوند. اولویت ارزیابی ترتیبی از آرگومانها از چپ به راست است. یک آرگومان ممکن است یک اتم یا یک لیست باشد،در هر حالت بعنوان یک فراخوانی تابع تفسیر شده و مفسرlisp برای ارزیابی آن فراخوانی می شود. برای مثال، ارزیابی زیر در سیستم lisp یک تابع به حساب می آید:
در اینجا آرگومانها(min 9 8)7 5) هستند که دراولویتی قبل از تابعی به نام max که نتیجه مقادیر آرگومانها را بکار می برد ارزیابی می شوند.آرگومان اول 4، یک عدد است پس مقدار آن 4 است. آرگومان دوم(min 9 8) است که خودش یک فراخوانی تابع است. بنابراین باید قبل از آرگومان سوم فراخوانی شود،(min 9 8) باید توسط مفسر lisp ارزیابی شود چون ما باید مفسرlisp را برای بعضی آرگومانها در طول ارزیابی همه فراخوانی های توابع استفاده کنیم می توان گفت مفسرlisp بصورت بازگشتی فراخوانی شده است . مفسرlisp همان مراحل را به کار می برد، پس آرگومان اول 9 قبل از آرگومان دوم8 ارزیابی می شود. با کار برروی تابعmin حاصل8 می شود یعنی تابع کوچکترین عدد یک مجموعه از اعداد صحیح را محاسبه می کند. برای تابع بیرونی max هم به این معنی است که آرگومان دوم آن 8 ارزیابی می شود.
آرگومانهای بعدی 5و7هستند که نتیجه ارزیابی آنها مقادیر 5و7 می شود.حال تابع بزرگترین عدد که max نام دارد می تواند ارزیابی شود که مقدار8 برمی گرداند. این مقدار نهایی، مقدار فراخوانی همه توابع می باشد. از آنجایی که گفته می شود مفسر lisp همیشه سعی می کند مقدار یک نماد یا تفسیر یک لیست بعنوان یک فراخوانی تابع را تشخیص دهد ما چگونه می توانیم با نمادها و لیستها بعنوان داده رفتار کنیم؟ برای مثال،اگر ما لیست (peter home walks) را وارد کنیم ، آنگاه مفسر lisp فورا" یک خطا می دهد که چیزی شبیه این خطا می گوید : تابع peter ناشناخته است یا اگر ما فقط houseرا وارد کنیم،آنگاه مفسرlisp با خطایی شبیه این خطا خاتمه می یابد مقداری به house متصل نیست.حل این مسئله کاملا" آسان است زیرا عنصر اصلی هر لیست بعنوان نام تابع تفسیر می شود، هر سیستم lisp با یک تابع پیش ساخته quote می آید که یک عبارت نمادین را بعنوان آرگومان پذیرفته و این عبارت نمادین را بدون ارزیابی آن بر می گرداند.
برای مثال: لیست((quote(peter walks home)، به سادگی مقدار (peter walks home) را بر می گرداند، و برای (quote house) ،house را بر می گرداند. از آنجایی که تابع qute زیاد استفاده می شود، می توان آن را با کاراکتر ویژه ' بیان کرد. بنابراین برای مثال بالا می توانیم معادل(home peter walks)' وhouse' را مشخص کنیم. برنامه ها بعنوان داده، یعنی تابع quote به ما امکان می دهد تا با فراخوانی تابع بعنوان داده رفتار کنیم.
برای مثال:
(max 4(min 9 8) 7 5)'یا((min 9 8 )7 5) ute(max 4)
قبلا گفتیم که مفسر lispیک تابع یکتایی پیش ساخته است که eval نام دارد.آن صریحا" آرگومانهایش را وادار می کند تا مطابق قوانین مذکور در بالا ارزیابی شوند. در بعضی حالات، آن می تواند مقابل تابعquote قرار بگیرد بنابراین به وضوح لازم است که یک لیست بعنوان داده مشخص شود تا سیستم lisp بتواند یک فراخوانی تابع تفسیر شود،ما می توانیم (9max 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 را بر می گردانداینها مثالهایی هستند:
Error: unbound symbol color
توضیح اینکه در واقع setq حالت مفسرlisp را تغییر می دهد تا دفعه بعدی که همان متغییر استفاده می شود، دارای مقدار بوده و بنابراین مفسر lisp قادر به بازگرداندن آن خواهد بود. اگر این اتفاق نیفتد آنگاه مفسر lisp یک اخطار خواهد داد زیرا نماد متصل نشده است.(گام 2 مفسر lisp پیدا نشد). بنابراین آن می گوید که setq یک اثر جانبی تولید می کند زیرا حالت مفسرlisp به طور پویا تغییر می دهد. وقتی استفاده از setq اجباری شد، به هر حال متوجه شد که در واقع از مسیرsemantics(معانی)lisp ناب دور می شود. پس setq باید با دقت بسیار استفاده شود.
علاقه مندی ها (Bookmarks)