Payamvdn/NorthPersian_fa_wiki huggingface dataset:
در نظریه کامپایلر، شاخه ای از علوم کامپیوتر نظری، حذف کد مرده (به انگلیسی: ) نوعی از بهینه سازی کامپایلر (به انگلیسی: ) است، که برای حذف بخشی از کد، که در نتیجهٔ برنامه تأثیری ندارد، استفاده می شود. کد مرده شامل بخشی از کد است که هیچ گاه اجرا نمی شود و یا در صورت اجرا، خروجی آن مورد استفاده قرار نگرفته و در واقع فقط بر روی متغیرهای مرده (متغیرهایی که در بقیه بخش های برنامه خوانده نمی شوند و از آن ها استفاده ای نمی شود) تأثیر می گذارد. به طور معمول، کامپایلرها در زمان کامپایل برنامه، به صورت بازگشتی تعیین می کنند که کدام متغیرها و بخش های برنامه استفاده شده اند. متغیرها و بخش های استفاده شده علامت گذاری می شوند و در نهایت بخش های علامت نخورده، از برنامه حذف می شوند. اکثر کامپایلرهای پیشرفته، گزینه های متعددی را در سطوح مختلف برای حذف کد مرده در اختیار قرار می دهند. در سطوح پایین، کامپایلر فقط بخش هایی از کد که اجرای آن های غیرممکن است را حذف می کند. در سطوح بالاتر کامپایلر فضایی را برای ذخیره سازی متغیرهای بی استفاده در نظر نمی گیرد. در سطوح بالاتر از آن نیز، کامپایلر دستورها و توابع بی استفاده را شناسایی و حذ
ف می کند. مزایا. حذف کد مرده مزیت های فراوانی دارد که از جملهٔ آن ها می توان به کم شدن اندازهٔ برنامه اشاره کرد، که بدین ترتیب از اجرا شدن کدی که در نتیجهٔ برنامه تأثیری ندارد و عمل هایی که می توان بدون انجام آن ها به نتیجه رسید، جلوگیری می شود. در نتیجه، مدت زمان اجرای برنامه کاهش می یابد و این موضوع در بسیاری از اوقات، می تواند تأثیر زیادی در بهینه تر شدن برنامه داشته باشد. علاوه بر این، حذف کد مرده باعث ساده تر شدن ساختار برنامه می شود و به این صورت، راه برای انجام دیگر بهینه سازی ها باز می شود. خطرات. از طرفی باید در عملیات حذف کد مرده بسیار دقت نمود، چرا که ممکن است نتیجهٔ محاسبات بخشی از کد، در جای دیگری استفاده نشود، ولی آن محاسبات باعث ایجاد تغییر در یک متغیر سراسری و یا تولید استثنا (به انگلیسی: ) شود، که در این صورت حذف آن بخش از کد، باعث ایجاد خطا در عملکرد برنامه می شود. به طور معمول، کامپایلرها زمانی که در مورد مرده بودن بخشی از کد ابهام هست، به صورت محافظ کارانه عمل می کنند. مثال ها. این برنامه که با زبان سی () نوشته شده است را در نظر می گیریم. int foo(void) int a = 24; int b = 25; /* Assignment
to dead variable */ int c; c = a * 4; return c; b = 24; /* Unreachable code */ return 0; تحلیلی ساده از کاربرد مقادیر، مشخص می کند که متغیر codice_1 بعد از این که برای اولین بار در خط چهارم مقداردهی می شود، در تابع codice_2 استفاده نمی شود. از طرفی، متغیر codice_1 یک متغیر محلی در تابع codice_2 است و به این دلیل نمی تواند خارج از codice_2 استفاده شود. بنابراین متغیر codice_1 مرده است و کامپایلر در زمان بهینه سازی می تواند آن را حذف نماید. همچنین از آن جا که تابع همواره در خط هفتم، با دستور بازگشت به کار خود پایان می دهد، هیچ مسیر اجرایی به خط هشتم که متغیر codice_1 برای دومین بار در آن مقداردهی می شود نمی رسد. بنابراین، دسترسی به این مقداردهی امکان پذیر نمی باشد و این بخش از کد، قابل حذف شدن است. باید دقت شود که اگر برنامه ساختار پیچیده تری داشت، به عنوان مثال بعد از دستور بازگشت از تابع در خط هفتم، برچسبی وجود داشت و از جای دیگری از برنامه دستور codice_8 به آن برچسب بود، آن موقع ممکن بود مسیر اجرایی به دومین مقداردهی متغیر codice_1 وجود داشته باشد و دیگر codice_1 غیرقابل دسترس نبود. علاوه بر این، با این که
محاسباتی در این تابع انجام می شود، مقدار آن محاسبات در جایی که خارج از حوزهٔ این تابع قابل دسترسی باشد ذخیره نمی شود. بنابراین با توجه به اینکه تابع همواره مقدار ۹۶ را بازمی گرداند، ممکن است در زمان بهینه سازی به مقداری که بازمی گرداند ساده شود. حال برنامهٔ دیگری که با زبان سی نوشته شده است را در نظر می گیریم. int main(void) { int a = 5; int b = 6; int c; c = a * (b / 2); if (0) { /* DEBUG */ printf("%d ", c); return c; در این مثال نیز از آنجا که مقدار صفر، همواره برابر با false در نظر گرفته می شود، دستور if هیچ گاه اجرا نخواهد شد و بنابراین کل بخش if در هنگام بهینه سازی از برنامه حذف می شود. حذف پویای کد مرده. گاهی اوقات غیرقابل دسترس و مرده بودن بخشی از کد، در شرایط خاصی اتفاق می افتد و در هنگام کامپایل مشخص نمی شود. به عنوان مثال این شرایط را ورودی برنامه ها می توانند ایجاد کنند و باعث شوند بخشی از کد هیچ گاه در زمان اجرای برنامه، اجرا نشوند. بخشی از کد به صورت پویا مرده (به انگلیسی: Dynamically dead) است اگر بسته به شرایط برنامه در زمان اجرا، مقادیری که آن بخش از کد تولید می کند، در جای دیگری از برنامه اس
تفاده نشود. به بیان دیگر، یک دستور به صورت پویا مرده، نمونه ای از یک دستور ایستا (به انگلیسی: Static instruction) است، که یک مقدار ثبات (به انگلیسی: Register) مرده را ایجاد می کند. محققان دریافته اند که کسر عظیمی از دستورات اجرا شدهٔ برنامه ها به صورت پویا مرده اند.
قضیهٔ پاریک یکی از قضایای علوم کامپیوتر نظری است که در سال ۱۹۶۱ توسط روهیت پاریک(Rohit Parikh) اثبات شده است و در سال ۱۹۶۶ اثبات ساده تری برای آن ارائه شده است.[3] اثبات می کند که یک زبان مستقل از متن روی یک الفبای تک حرفی باید منظم باشد. از قضیهٔ پاریک می توان در اثبات مستقل از متن بودن یا نبودن یک زبان استفاده کرد. علاوه بر این از این قضیه برای اثبات این که یک زبان حاوی رشته ای با تعداد برابری از دو پایانه داده شده هست یا خیر.[1] این قضیه بیان می کند که اگر توالی نشانه ها در نظر گرفته نشود آنگاه زبان های مستقل از متن از نظر مجموعه های منظم و نشان دادن وجود یک زبان مستقل از متن ذاتاً مبهم غیر متمایزاند. تعاریف مهم. اگر formula_1 مجموعهٔ یک الفبا باشد، بردار پاریک یک کلمه به صورت یک تابع در قالب formula_2 خواهد بود به طوری که formula_3 و formula_4 نشان دهندهٔ تعداد حرف formula_5 در کلمهٔ formula_6 است. می گوییم زیرمجموعه ای از formula_7 خطی است اگر به ازای بردارهای formula_8 این زیر مجموعه به صورت formula_9 باشد. می گوییم یک زیرمجموعه از formula_7 شبه خطی است اگر اجتماعی از چند زیرمجموعهٔ خطی باشد. فرض کنید f
ormula_11 یک زبان مستقل از متن و formula_12مجموعهٔ بردارهای پاریک کلمات در formula_11باشد که formula_14. آنگاه formula_12 یک مجموعهٔ شبه خطی است. می گوییم دو زبان جابه جایی پذیرند اگر دارای مجموعهٔ بردارهای پاریک برابر باشند. اگر formula_16 یک مجموعه ی شبه خطی باشد، زبان شامل کلماتی که بردارهای پاریک آن ها در formula_16 است با بعضی از زبان های منطم جابه جایی پذیرند. در نتیجه هر زبان مستقل از متنی با بعضی از زبان های منظم جابه جایی پذیرند. اهمیت آن. در قضیهٔ پاریک اثبات می شود که بعضی از زبان های مستقل از متن فقط میتوانند گرامرهای مبهم داشته باشند که به این زبان ها، زبان های ذاتاً مبهم گفته می شود. به عبارتی دیگر بعضی از گرامرهای مستقل از متن را نمی توان به گرامر مستقل از متن غیر مبهمی تبدیل کرد. نگاشت پاریک. فرض کنید formula_1 الفبای یک زبان باشد که formula_19 و ترتیب formula_20 دلخواه ولی ثابت است. برای formula_21 تصویر پاریک معادل است با: که هر formula_23 تعداد formula_5 ها در formula_6 hsj. نگاشت پاریک روی یک زبان به صورت مقابل تعریف می شود: برای یک زبان دلخواه مانند formula_11 که formula_27 قرار دهید form
ula_28 مثال: (نگاشت پاریک کلمات یک گرامر مستقل از متن) گرامر مستقل از متن formula_29 را با قواعد زیر در نظر بگیرید: این بدین معناست که زبان تولید شده توسط formula_33 دارای رشته هایی است که a و b ها میتوانند در آن به صورت درهم آمیخته ظاهر شوند. اما از آنجایی که از تعریف زبان پیداست تعداد آن ها در رشته برابر خواهد بود. علاوه بر این همیشه از هر حرف حداقل یکی وجود دارد. حال اگر formula_34 و formula_35 را با توجه به تعریف بالا به ترتیب formula_36 و formula_37 بگیریم و رشته های formula_38 و formula_39 فرض کنیم. هر دو این رشته ها دارای نگاشت پاریک یکسان هستند؛ formula_40 که داریم formula_41 اثبات. این قضیه با استقرا روی یک مجموعه از درخت های اشتقاق و با استفاده از نگاشت های پاریک به دست آمده از مجموعه ی درخت های اشتقاق پایانه ها و امکان بزرگ کردن آن ها در طول اشتقاق اثبات می شود.
محمد خان ایروانی (درگذشت ۱۲۵۵ قمری) همچنین معروف به محمدخان سردار ماکویی از فرماندهان نظامی ایران در دوره محمدشاه قاجار بود که درجه امیرتومانی داشت و بخشی از سپاه ایران را در جنگ هرات فرماندهی می کرد. او در سال ۱۲۵۲ قمری به دستور محمدشاه قاجار به حکومت عراق عجم منصوب شد. پسر او از همسر اولش محمدحسن خان سردار ایروانی بود. محمدخان در سال ۱۲۵۴ قمری باردیگر ازدواج کرد و زینت الدوله دختر فتحعلی شاه قاجار را به همسری گرفت و یک سال بعد در زمان محاصره شهر هرات درگذشت.
اردوگاه پناهندگان خان یونس (به عربی: مخیم خان یونس)؛ (انگلیسی: Khan Yunis Camp) یک منطقهٔ مسکونی در فلسطین است، که در خان یونس واقع در غرب شهر خان یونس و ۲ کیلومتری شرق ساحل مدیترانه که در نوار غزه جنوبی قرار دارد. اردوگاه پناهندگان خان یونس ۳۷٬۷۰۵ نفر جمعیت دارد. آمار جمعیت. در حالی که آژانس امداد و اقدام سازمان ملل متحد برای آوارگان فلسطینی در خاور نزدیک UNRWA اعلام کرده است در سال ۲۰۱۰ که حدود ۷۲۰۰۰ پناهنده فلسطینی در اردوگاه زندگی می کنند، اداره مرکزی آمار فلسطین در سرشماری سال ۲۰۰۷ آمار جمعیت خان یونس را ۳۷۷۰۵ نفر ثبت کرده است. تاریخچه. اردوگاه خان یونس پس از جنگ شش روزه ۱۹۴۸-اعراب- اسرائیل احداث شد و حدود ۳۵٫۰۰۰ پناهنده فلسطینی در آن اسکان داده شدند. در ۳ نوامبر ۱۹۵۶، اردوگاه و شهر خان یونس توسط نیروهای ارتش اسرائیل اشغال شد و در عملیات تهاجمی که صورت گرفت حدود ۲۷۵ نفر کشته شدند که ۱۴۰ پناهنده از اردوگاه را در بر می گرفت. ساکنان اردوگاه تصریح کردند که بیشتر تلفات پس از پایان جنگ ها رخ داد و آن هنگامی بود که ارتش به خانه هایی که مظنون به حضور شورشیان مسلح می دید تهاجم کرد. با این حال، مقامات اسرائیلی اعل
ام کردند که تلفات ناشی از مقاومت ساکنان اردوگاه بوده است. وضعیت کنونی. طبق گزارش آژانس امداد و اقدام سازمان ملل متحد برای آوارگان فلسطینی در خاور نزدیک UNRWA، بسیاری از ساکنان این اردوگاه ها به دلیل عملیات توسط ارتش اسرائیل، خانه های خود را از دست دادند. UNRWA در اوائل دهه ۲۰۰۰ تلاش های بازسازی را آغاز کرد اما به دلیل تصرف این منطقه توسط حماس و محاصره اسرائیل در نوار غزه عمدتاً این پروژه متوقف شده است. UNRWA می گوید که حداقل ۱۰٬۰۰۰ خانه باید ساخته شوند.
در میان استراتژی های ارزیابی در نظریهٔ زبان های برنامه نویسی، ارزیابی کندرو یا فراخوانی به هنگام نیاز استراتژی ای برای ارزیابی است که دارای دو ویژگی ارزیابی غیرموکد و اشتراک گذاری می باشد. ارزیابی غیرموکد موجب می شود که محاسبه و ارزیابی یک عبارت تا جای ممکن به تأخیر افتاده و در اولین زمانی که دسترسی به آن نیاز بود، محاسبه شود. ارزیابی کندرو به وسیلهٔ اشتراک گذاری از ارزیابی های تکراری جلوگیری می کند. به کمک اشتراک گذاری، زمان اجرای یک تابع به صورت توانی نسبت به دیگر استراتژی هایی که ارزیابی غیرموکد دارند کاهش می یابد و به این صورت ترکیب این دو ویژگی، ارزیابی کندرو را نسبت به دیگر استراتژی ها بهبود می بخشد. از جمله این استراتژی ها می توان استراتژی فراخوانی با نام را نام برد. در مقابل ارزیابی کندرو، ارزیابی تندرو یا استراتژی موکد قرار دارد. این استراتژی ارزیابی در بیشتر زبان های برنامه نویسی مورد استفاده قرار می گیرد. به طور خلاصه، در این ارزیابی برخلاف ارزیابی کندرو، رویهٔ یک تابع، حتی اگر هیچگاه مورد استفاده قرار نگیرد و یا بعد از زمان زیادی به آن نیاز داشته باشد، به محض فراخوانی برای یک یا چندین متغیر با مقا
دیر مشخص اجرا می شود. تاریخچه. ارزیابی کندرو ابتدا برای جبر لاندا توسط کریستوفر وادزورث تعبیه شد. پیتر هندرسون و جیمز هیرام موریس و همچنین دنیل پائول فریدمن و دیوید وایس هر یک این ارزیابی را برای زبان های برنامه نویسی به طور مستقل تعریف کردند. شرح استراتژی. ارزیابی کندرو طبق شرح جان لوئیس بنتلی در گزارش "«نوشتن برنامه کارآمد»" معمولاً با مموایز کردن ترکیب می شود. در عمل به هر عبارت که معادل تابعی از یک یا چند متغیر با مقادیری مشخص می باشد، خانه ای از حافظه تعلق می گیرد که این خانه با داشتن مقادیر متغیرها قابل دسترسی است. در مموایز کردن عبارات به دو دسته تقسیم می شوند. دستهٔ اول عباراتی هستند که برای بار اول محاسبه می شوند. در این دسته پس از محاسبهٔ عبارت، مقدار حاصل را در خانهٔ مربوطه نیز ذخیره می کنند. دستهٔ دوم عباراتی هستند که برای بار دوم و یا بیشتر مورد استفاده قرار می گیرند. این عبارات از دفعهٔ دوم به بعد، به جای محاسبهٔ مجدد، به خانهٔ حافظهٔ مربوطه رجوع کرده و مقدار را از خانهٔ حافظه برمی دارند. کارکرد مموایز کردن در ارزیابی کندرو تنها این تفاوت را قائل می شود که محاسبه و ذخیره سازی عبارت را تا اولین ز
مان نیاز به مقدار عبارت، به تأخیر می اندازد. روشن است که ایجاد این تأخیر اثر مخربی بر فرایند مموایز کردن ندارد و سلسله عملکرد آن را حفظ خواهد کرد. بنابراین در ارزیابی کندرو در صورت مواجه شدن با یک عبارت، ابتدا تا اولین نیاز به مقدار آن هیچ اقدامی صورت نمی گیرد. سپس در هنگام نیاز به مقدار عبارت، ابتدا بررسی می شود که این اولین باریست که نیاز به محاسبهٔ عبارت وجود دارد یا خیر. اگر اولین بار باشد، مقدار عبارت طبق رویهٔ تابع محاسبه گردیده و مقدار آن در خانهٔ مربوطه نیز ذخیره می شود. در غیر این صورت، بدون محاسبهٔ مجدد عبارت، مقدار آن که در مراحل قبل محاسبه شده، از خانهٔ مربوطه برداشته می شود و مورد استفاده قرار می گیرد. بخشی از حافظه را که به مقادیر عبارات تعلق می گیرد، جدول مراجع می نامند. مزایا و معایب. غیرموکد بودن این ارزیابی، با توجه به این که ذخیرهٔ مقدار را تا زمان نیاز به تأخیر می اندازد، موجب کاهش حافظهٔ مورد استفاده در هر لحظه خواهد شد. اما با این حال محاسبهٔ حافظهٔ مورد استفاده در این روش ارزیابی قابل پیش بینی نیست. علاوه بر این در عبارات ساختاریافته، می توان با بخش بندی کردن عبارت، تنها بخش های مورد نی
از آن را محاسبه کرد و برای سایر بخش ها جدول مراجع و مقادیر ثابت را مورد استفاده قرار داد. با توجه به این که ایجاد لیست واسط نیز تا زمان نیاز به تعویق می افتد، استفاده از آن لزوماً هزینهٔ زیادی به همراه نخواهد داشت و این هزینه در موارد خاص چشمگیر خواهد بود. همچنین این ارزیابی، به کمک مموایز کردن، محاسبات تکراری را کاهش داده و زمان اجرای برنامه را بهبود می دهد. علی رغم وجود این مزایا، غیرموکد بودن ارزیابی موجب بهم ریختن ترتیب اجرای دستورات شده و ترکیب این ارزیابی با ویژگی های ضروری دیگر را دشوار می سازد. از جمله این ویژگی ها می توان مدیریت استثناءها و مدیریت ورودی/خروجی را نام برد. علاوه بر این ارزیابی کندرو می تواند موجب نشت حافظه شود. پیاده سازی در زبان های مختلف. برخی از زبان های برنامه نویسی به تأخیر انداختن ارزیابی عبارات را به صورت معمول انجام می دهند. در عین حال برخی زبان های دیگر از توابع و نمادهای خاصی برای این کار استفاده می کنند. برای مثال در زبان های میراندا و هاسکل این کار به طور معمول انجام می شود، در حالی که در زبان اسکیم از عباراتی معادل "تاخیر" و "اجرا" و در زبان اکمل از عباراتی معادل "کندرو" و
"اجرای کندرو" استفاده می شود. برخی دیگر از زبان ها، ارزیابی کندرو را تنها برای برخی از ساختمان داده ها و یا تنها بر روی یک ساختمان دادهٔ خودساخته تعریف می کنند. برای مثال در زبان پرل۶ ارزیابی کندرو بر روی لیست ها تعریف شده، در حالی که این ارزیابی در این زبان برای محاسبهٔ توابع و عملگرهای ریاضی تعبیه نشده است. شرح یک مثال در زبان پایتون نیز عملکرد استراتژی را واضح تر خواهد ساخت. تابع formula_1 در زبان پایتون را در نظر بگیرید. این تابع لیستی از اعداد را در بازه ای خاص و با فاصلهٔ گام های مشخص ایجاد می کند. این زبان برای این تابع ارزیابی کندرو را مورد استفاده قرار داده است. این ارزیابی برای فراخوانی تابع در بازه های بزرگی از اعداد، موجب کاهش زمان اجرا خواهد شد. همچنین حافظهٔ مورد استفاده نیز کاهش خواهد یافت. چرا که تمامی مقادیر بازه در یک لحظه مورد استفاده قرار نمی گیرند و نیاز به دسترسی همزمان به تمام اعداد در یک لحظه نخواهد بود.
تجزیه کننده کاهشی بازگشتی (انگلیسی: Recursive descent parser) یک تجزیه کننده بالا به پایین است که درخت تجزیه را از بالا و حالت اولیه می سازد تا به رشته ورودی کاربر برسد. این تجزیه کننده به صورت بازگشتی، ورودی را تجزیه می کند تا درخت تجزیه را بسازد. با ساختن درخت از بالا به پایین، احتمال وجود عقب گردی در مراحل وجود دارد که نقطه ضعف این روش برشمرده می شود ولی با نوع پیش بینی کننده آن که از رشته ورودی برای پیش بینی اقدام بعدی استفاده می کند، می توان عقب گرد را از میان برداشت. این تجزیه کننده از گرامر مستقل از متن که به صورت بازگشتی پیاده سازی می شود، استفاده می کند.تجزیه کننده های کاهشی بازگشتی نمی توانند دستور زبان های چپ گرد را تجزیه کنند (چون در حلقهٔ بی نهایت گیر می کنند) و ابتدا باید این چپ گردی را رفع نمود. نحوه اعمال تجزیه. برای این تجزیه ابتدا برای همهٔ نمادهای ناپایانی گرامر از جمله نماد شروع، یک تابع در نظر می گیریم، که در واقع روند تجزیه آن را شامل می شود و همهٔ رشته هایی که از آن مشتق می شوند در این تابع صدق می کنند. در صورتی که نماد ناپایانی N فقط یک قاعده را در گرامر داشته باشد (در سمت چپ فقط یک قا
عده آمده باشد) تابع تجزیه آن آسان خواهد بود و به ترتیب نمادها در سمت راست آن پیش می رویم. اگر نماد بعدی یک نماد پایانی بود آن را نماد پیش رو در ورودی چک می کنیم اگر یکسان بود، هم در ورودی و هم در سمت راست قاعده یک نماد جلوتر می رویم و اگر نبود، خطا گزارش می دهیم. اگر نماد بعد یک نماد ناپایانی بود، تابع متناظر آن نماد ناپایانی را فراخوانی می کنیم به این صورت ادامه می دهیم تا به انتهای سمت راست قاعده می رسیم. در صورتی که نماد ناپایانی N، در سمت چپ چندین قاعده بود، تابع تجزیه نیاز دارد تصمیم بگیرد از کدام قاعده استفاده کند. در صورتی که تجزیه ما پیش بینی کننده نباشد یکی از قاعده ها را انتخاب می کند و در صورت بروز خطا در اجرای این قاعده، قاعده دیگر مشتق شده از N را فراخوانی می کند. تا زمانی که یکی از قاعده ها به درستی اجرا شود. ولی در صورتی که تجزیه کننده ما قابلیت پیش بینی کنندگی داشته باشد، براساس نماد پیش رو ورودی و مقایسه قواعد مشتق شده، یک قاعده را انتخاب می کند. شبه کد. N() { if (lookahead can start first rule for N) { match rule 1 for N else if (lookahead can start second rule for N) { match rule 2 for N ..
. ... else if (lookahead can start n'th rule for N) { match rule n for N else { error(); پیاده سازی در جاوا. مثال ساده روبرو که گرامر ساده ای از عبارت های ریاضی با پرانتز کامل، را نشان می دهد. در ادامه طبق تجزیه کننده کاهشی بازگشتی به زبان جاوا پیاده سازی شده است. &lt;expression&gt; ::= &lt;number&gt; | "(" &lt;expression&gt; &lt;operator&gt; &lt;expression&gt; ")" &lt;operator&gt; ::= "+" | "-" | "*" | "/" در تکه کد زیر، تابع ParseError جهت نمایش خطاهای نحوی در رشته ورودی کاربر تعریف شده است. private static class ParseError extends Exception { ParseError(String message) { super(message); تکه کد زیر برای این پیاده سازی شده که کاراکتر بعدی در ورودی را بخواند و اگر از جمله عملیات های مجاز بود، آن را برگرداند در غیر این صورت خطا دهد. static char getOperator() throws ParseError { TextIO.skipBlanks(); char op = TextIO.peek(); if ( op == '+' || op == '-' || op == '*' || op == '/' ) { TextIO.getAnyChar(); return op; else if (op == ' ') throw new ParseError("Missing operator at end of line."); else throw new ParseErr
or("Missing operator. Found "" + op + "" instead of +, -, *, or /."); کد زیر برای خواندن یک عبارت ریاضی از یک خط ورودی و برگرداندن مقدار نهایی آن است. در صورت که ورودی دارای خطای نحوی باشد، خطا برمی گرداند. این تابع تا زمانی اجرا می شود که به یک عدد نهایی منجر شود. سپس به صورت بازگشتی به توابع فراخوانی کننده اش، نتیجه نهایی هر عبارت در هر مرحله را برمی گرداند private static double expressionValue() throws ParseError { TextIO.skipBlanks(); if ( Character.isDigit(TextIO.peek()) ) { return TextIO.getDouble(); else if ( TextIO.peek() == '(' ) { TextIO.getAnyChar(); double leftVal = expressionValue(); char op = getOperator(); double rightVal = expressionValue(); TextIO.skipBlanks(); if ( TextIO.peek() != ')' ) { throw new ParseError("Missing right parenthesis."); TextIO.getAnyChar(); switch (op) { case '+': return leftVal + rightVal; case '-': return leftVal - rightVal; case '*': return leftVal * rightVal; case '/': return leftVal / rightVal; default: return 0; else { throw new ParseError("Encountered unexpected c
haracter, "" + TextIO.peek() + "" in input."); پیاده سازی در پایتون. گرامر محاسبه گر روبرو نیز توسط تجزیه کننده کاهشی بازگشتی به زبان پایتون که در ادامه آمده، حل شده است. exp ::= term | exp + term | exp - term term ::= factor | factor * term | factor / term factor ::= number | ( exp ) پیاده سازی در پایتون: class Calculator(): def __init__(self, tokens): self._tokens = tokens self._current = tokens[0] def exp(self): result = self.term() while self._current in ('+', '-'): if self._current == '+': self.next() result += self.term() if self._current == '-': self.next() result -= self.term() return result def factor(self): result = None if self._current[0].isdigit() or self._current[-1].isdigit(): result = float(self._current) self.next() elif self._current is '(': self.next() result = self.exp() self.next() return result def next(self): self._tokens = self._tokens[1:] self._current = self._tokens[0] if len(self._tokens)&gt; 0 else None def term(self): result = self.factor() while self._current in ('*', '/'): if se
lf._current == '*': self.next() result *= self.term() if self._current == '/': self.next() result /= self.term() return result if __name__ == '__main__': while True: lst = list(raw_input('&gt; ').replace(' ', ")) tokens = [] for i in range(len(lst)): if lst[i].isdigit() and i&gt; 0 and (tokens[-1].isdigit() or tokens[-1][-1] is '.'): tokens[-1] += lst[i] elif lst[i] is '.' and i&gt; 0 and tokens[-1].isdigit(): tokens[-1] += lst[i] else: tokens.append(lst[i]) print Calculator(tokens).exp()
در علوم رایانه، کد ریسه ای به روش برنامه نویسی گفته می شود که در آن، تمامی کد، یا حداقل اکثریت آن، از فراخوانی رویه ها تشکیل شده است. در حالت کلی این روش از برنامه نویسی در کامپایلرهایی یافت می شود که یا خروجی کامپایل آنها به صورت کد ریسه ای باشد و یا آنکه پیاده سازی کد خود کامپایلر به صورت ریسه ای صورت گرفته باشد. یکی از مهم ترین بده بستان ها در علوم و مهندسی کامپیوتر، مسئله تقابل زمان و حافظه می باشد. در این روش سعی بر آن است تا چگالی و حجم کد اشغال شده در ازای افزایش زمان اجرای آن، کاهش پیدا کند. در برخی مواقع این کاهش حجم کد منجر به آن می شود که کل کد در حافظه نهان پردازنده قرار گیرد و در این صورت، استفاده از کد ریسه ای و کاهش حجم کد منجر به کاهش زمان اجرای آن می شود. از کد ریسه ای در طراحی کامپایلر برخی زبان های برنامه نویسی از جمله Forth و همچنین برخی از پیاده سازی های بیسیک و کوبول استفاده می شود. تاریخچه و معرفی. رایانه های اولیه حافظه به نسبت بسیار کمی داشتند که بعضاً در حد چند کیلوبایت می بود و در نتیجه معضل اساسی، یافتن روشی برای کم حجم تر کردن کد بود تا بتواند در حافظه ذخیره گردد. از طرف دیگر سرعت
رایانه ها بسیار کم بود و زمان اجرای کد ماشین بسیار سریعتر از تفسیر عادی کد بود. در اینجا بود که نوشتن توابع و زیرروال ها فراگیر شد و به برنامه نویسان امکان این را می داد که ضمن کاستن از حجم کد، اجرای برنامه ها را از طریق کد ماشین تسریع بخشند. چارلز مور روش ریسه ای غیرمستقیم را در سال ۱۹۷۰ ابداع کرد ولی جیمز بل در سال ۱۹۷۳ نخستین مقاله مرتبط با این روش را به چاپ رسانید. اصلی ترین کاربرد این روش در تولید و پیاده سازی مفسرهای ماشین مجازی می باشد. عملکرد. در این بخش عملکرد کد ریسه ای را به صورت کلی و به همراه مثال توضیح می دهیم. فرض کنید کدی داریم که شامل سه دستورالعمل ماشین مجازی B ،A و C باشد. می توانیم برای اجرای این سه دستورالعمل، توابع سطح ماشینی به نام های Br ،Ar و Cr را تعریف کنیم و با استفاده از آنها کد مقابل را تولید کنیم. call Ar call Br call Cr حال با حذف تابع call کد را بازنویسی می کنیم. Ar Br Cr کد جدید در واقع دنباله ای از آدرس های کدی می باشد که دستورالعمل های ماشین را نشان می دهند. در نتیجه برای اجرای این تکه کد، نمی توانیم به ابتدای آن پرش کنیم. همچنین باید با استفاده از اشاره گری در یک ثبات، جد
ا از ثبات های PC و RA، به دستورالعمل فعلی اشاره کنیم. برای این منظور از ثبات اشاره گر دستورالعمل (IP) استفاده می کنیم. فرض می کنیم که IP به کلمه ای از دنباله ی کدها که دقیقاً بعد از کلمه دستورالعمل فعلی قرار دارد اشاره می کند. حال کافی است تا تنها کلمه را بارگیری کرده و با افزایش مقدار آن به اندازه یک کلمه، به رویه ای که بدان اشاره می کند پرش کنیم. کد مقابل نحوه این عملیات را در زبان اسمبلی MIPS نشان می دهد. lw $2,0($4) #get next instruction, $4=inst.ptr. addu $4,$4,4 #advance instruction pointer j $2 #execute next instruction به متد بالا NEXT نیز گفته می شود. این روش اولین بار در مقاله ای توسط بل ارائه شد. مدل های ریسیدن. در اینجا به تبیین برخی از مدل های کد ریسه ای می پردازیم. ریسیدن مستقیم. آدرس های موجود در ریسه همان آدرس های زبان ماشین می باشند. این مدل در عین سادگی می تواند سربارهایی نیز داشته باشد زیرا از آنجایی که تنها از آدرس های ماشین در آن استفاده شده است، برای ورودی دادن پارامترها، باید آنها را به صورت غیرمستقیم از حافظه بارگیری کرد. خروجی این مدل حجمی در حدود یک هشتم تا یک چهارم کد کامپایل شده دا
رد. ریسیدن غیرمستقیم. در این مدل از اشاره گرهایی استفاده می شود که به مکان هایی اشاره می کنند که آن مکان ها خود به کد ماشین اشاره می کنند. ممکن است بعد از اشاره گر غیرمستقیم تعدادی عملوند که به جای ریسه در بلوک غیرمستقیم ذخیره شده اند، نیز قرار داشته باشد. کدهای تولید شده توسط این روش معمولاً کم حجم تر از کدهای تولید شده توسط روش ریسیدن مستقیم می باشند اما غالباً کندتر نیز می باشند. ریسیدن زیرروالی. در این مدل توالی ای از دستورالعمل های call در زبان ماشین استفاده می شود. کامپایلرهای اولیه برای زبان هایی همچون فورترن و کوبول از این مدل استفاده می کردند. خروجی این مدل و دو مدل ارائه شده در بالا، حجمی در حدود یک هشتم تا یک چهارم کد کامپایل شده دارد. ریسیدن نشانه ای. در این مدل لیستی از نمایه های ۸ یا ۱۲ بیتی به جدولی از اشاره گرها استفاده می شود. این روش معمولاً فشرده شده می باشد و حجم کد تولیدی آن چیزی حدود ۵۰ تا ۷۵ درصد کد تولید شده توسط مدل های دیگر می باشد. ویژگی های مشترک. به صورت معمول در یک ماشین مجازی ریسه ای چهار ثبات وجود دارند که عبارتند از: