دستورالعملهای پردازش لیست
دوشنبه 29 آذر 1389 7:34 AM
لیسپ دستورالعملهای زیادی را برای دستیابی و کنترل لیستها فراهم میکند . لیستها میتوانند مستقیما با پردازهٔ لیست ایجاد شوند .لیست هر تعدادی از آرگومانها را میپذیرد و تعدادی از آرگومانها را بر میگرداند.
(list 1 2 ‘a 3 ); Output : (1 2 a 3 ) (list 1 ‘(2 3) 4 ); Output : (1 (2 3) 4)
به این دلیل راهی که لیستها ایجادمی شوند از جفتهای Cons (Car,Cdr) پردازهٔ Cons میتواند برای اضافه کردن یک عنصر به جلوی یک لیست استفاده شود. توجه کنید که پردازهٔ Cons در هدایت و به کار بردن آرگومانهای لیست نامتقارن است ، بدین دلیل روشهای لیستها ایجاد میشوند.
(Cons 1 ‘(2 3)); Output: (1 2 3 ) (Cons ’(1 2) ‘(3 4)) Output : ((1 2) 3 4)
پردازهٔ Oppend دو یا چند لیست را با هم ادغام میکند و یک لیست واحد ایجاد میکند زیرا لیست لیسپ یک لینک لیست است و پیچیدگی زمانی الحاق کردن لیستها از مرتبهٔ پیچیدگی زمانی O(n) میباشد. ساختار اشتراکی: لیستهای لیسپ لینک لیستهای ساده میتوانند با یکی دیگر از لیستها در ساختمان مشترک باشند به عبارت دیگر دو لیست میتوانند دم یکسانی داشته باشندیا رشتهٔ پایانی از Consهای یکسانی داشته باشند مثلا:
(setf foo (list 'a 'b 'c)) (setf bar (cons 'x (cdr foo)))
لیست foo و bar به ترتیب به صورت (a b c) و (X b c ) هستند هرچند دم (b c ) در هر دو لیست ساختار یکسانی دارند ولی مانند هم نیستند، خانههای Cons اشاره گر به b و c در محل حافظهٔ یکسانی برای هردو لیست قرار دارد. ساختار اشتراکی سریع تر از کپی کردن میتواند به صورت چشمگیری کارایی را بهبود بخشند. هرچند ، این مهارت میتواند متقابلا در راههای نامطلوب با عملکردهایی که تغییرات لیستهای گذشته روی آرگومانهای آن تاثیر بگذارد ، اثر کند. تغییرات یک لیست از قبیل تغییر دادن C با یک goose روی دیگری نیز تاثیر میگذارد
setf (third foo) 'goose)
که این تغییر نتیجه را به صورت (a b goose) تغییر میدهد اما bar هم تغییر میکند (X b goose) که ممکن است یک نتیجهٔ غیر منتظره باشد. زبانهای برنامه نویسی Lisp معمولاً از یک خط دستور محاورهای استفاده میکنند،که میتواند با یک محیط پیچیدهٔ گسترش یافته ترکیب شود.کاربر اصطلاحات و دستورات را در خط دستور وارد کرده یا با رهبری IDE آنها را به سیستم Lisp میفرستد. Lisp دستورات را میخواند ، آنها را ارزیابی میکند و نتایج را چاپ میکند. به این دلیل است که خط دستور زبان Lisp به حلقهٔ Read-Eval-Print یا REPL معروف است. نمونهٔ سادهای از عملیات REPL در زیر آمدهاست. این یک شرح سادهاست که بسیاری از المانهای Lispواقعی در آن نمیآید مانند ماکروها و کوئتها. تابع read جملات متنی را به عنوان ورودی میپذیرد و آنها را به ساختار لیست تجزیه میکند. به عنوان مثال ، وقتی شما رشتهٔ (+ 1 2) را در اعلان تایپ میکنید، تابع read آن را به یک لیست پیوندی حاصل از 3 المان ترجمه میکند: علامت + ، عدد 1 و عدد 2 . خیلی اتفاق میافتد که لیست قسمت موثری از یک کد Lisp باشد که قابل ارزیابی است.به همین دلیل است که یک قطار از لیست به یک تابع نام عملگر مع میدهد. تابع eval ساختار لیست را ارزیابی میکند و نوعی دیگر از ساختار را به عنوان نتیجه باز میگرداند.ارزیابی کردن لزوما تفسیر کردن معنی نمیدهد؛ بعضی سیستمهای Lisp هر عبارتی را به زبان ماشین تبدیل میکنند. خیلی ساده است؛ به هر حال؛ برای تعریف ارزیابی به عنوان تفسیر : برای ارزیابی یک لیست که نام تابع دارد ، eval ابتدا تمام آرگومانهای داده شده به cdr اش را ارزیابی میکند و سپس تابع را روی آن آرگومانها اعمال میکند.در این مثال ، تابع عمل جمع است و به آرگومانهای (1 2) اعمال میشود که نتیجه 3 است.این نتیجهٔ ارزیابی است. این وظیفهٔ تابع print است که نتیجه را به کاربر نمایش دهد. برای نتیجهٔ سادهٔ 3 این کار ناقابل است. یک عبارت که با قسمتی از ساختار لیست ارزیابی میشود میاز دارد که print لیست را به حرکت در آورد و در خروجی به شکل یک عبارت S نمایش دهد. برای اجرا کردن یک REPL در Lisp ، تنها لازم است که این سه تابع را اجرا کنید و یک تابع حلقه بی نهایت را.(به طور طبیعی اجرای eval پس از اجرای عملگرهای ویژهای مانند if پیچیده خواهد شد.)یک REPL ساده به خودی خود با یک خط کد انجام شد: (loop(print(eval(red))))
لیست در اصل تعداد کمی ساختار کنترلی دارد. منتها در تکامل و گسترش زبان تعداد زیادی به آن اضافه شدند.(عملگر اصلی شرایط در زبان Lisp که cond بود بعداً متشکل شد از ساختار if-then-else)
برنامه نویسان در نسخهٔ Scheme حلقهها را به صورت بازگشت دم( tail recursion ) بیان میکنند. موسسات متعارف علوم کامپیوتر Scheme بعضی دانشجویان را متعاقد میکند که تنها راه تکرار در زبان Lisp استفاده از بازگشت دم است؛ این اشتباه است. تمامی نسخههای متداول دیده شده از Lisp دارای ساختارهای الزامی برای تکرار هستند.درScheme دستور do به عنوان دستور حلقه پیچیدهٔ Lisp است. علاوه بر این مسالهٔ اصلی که شی گرایی را مهمتر از مسالهٔ فاعلی کرده این است که Scheme نیازهای ویژهای برای کارکردن با فراخوانی دم(tail calls )دارد، در نتیجه دلیل ترغیب Scheme به استفاده از بازگشت دم این است که روش صراحتا با تعریف خود زبان پشتیبانی میشود . در مقابل ، ANSI Common Lisp نیازی به بهینه سازی که معمولاً به حذف فراخوانی دم گفته میشود ندارد. در نتیجه این حقیقت که بازگشت دم به عنوان یک جایگزین تصادفی برای استفاده از ساختارهای مبتنی بر تکرار ( مانند do dolist loop ) توصیه نمیشود تنها یک مسالهٔ برتری ادبی نیست ، ولی بالقوه یکی از کارآمدهاست ( بعد از این که این روش فقط به عنوان یک پرش ساده به کار نرفت) و به عنوان یک تصحیح برنامه است. بعضی از ساختارهای کنترلی Lisp عملگرهای ویژهای هستند ، هم ارز کلیدواژههای ترکیبی باقی زبانها. عباراتی که این عملگرها استفاده میکنند ظاهری شبیه فراخوانی تابع دارد، تفاوت اینجاست که آرگومانها ضرورتا نباید ارزیابی شوند یا در مورد تکرار شاید بارها ارزیابی شوند. در مقابل اکثر زبانهای برنامه نویسی ، Lisp به برنامه نویسان اجازه میدهد با خود زبان ساختاهای کنترلی را پیاده سازی کنند.ساختارهای کنترلی زیادی در ماکروهای Lisp پیاده سازی میشوند و برنامه نویسان میتوانند هر ماکرو را گسترش دهند ،برای آنانی که میخواهند بدانند چطور کار میکند. هر دوی Lisp Common و Scheme دارای عملگرهای کنترلی غیر محلی هستند.تفاوت این عملگرها یکی از عمیقترین تفاوتها مابین این دو نسخهٔ زبان است. Scheme از ورودی مستمر با استفاده از روش call/cc پشتیبانی میکند ، که به برنامه اجازهٔ ذخیره ( و بعداً بازیابی کردن) یک عملیات ویژه را میدهد . Common Lisp از ورودی مستمر پشتیبانی نمیکند ولی از راههای زیادی برای انجام رهایی از تکرار پشتیبانی میکند.