در این بخش از سری مقالات آموزش ساخت اپلیکیشن آیفون از چند سازوکار استفاده میکنیم تا امکان رشد محتوا را فراهم سازیم و به سلولها امکان دهیم که ار تنظیم خودکار اندازه بهره بگیرند. در بخش قبلی در مورد خصوصیتهای سلول News اپلیکیشن خود صحبت کردیم و چنان که دیدیم هنگامیکه سلول را در زمان اجرا مشاهده میکنیم، متن و تصویر تفصیلی، هر دو برش پیدا میکنند و محتوای کامل خود را نمایش نمیدهند. برای مشاهده بخش قبلی این مجموعه مطلب آموزشی میتوانید به لینک زیر مراجعه کنید:
زمانی که لیآوت NewsTableViewCell را ایجاد کردیم، قیدی برای حاشیههای فوقانی و تحتانی آن در نظر گرفتیم. بدین ترتیب سلول میتواند ارتفاعی را که برای نمایش محتوایش نیاز دارد (یعنی اندازه ثابت ذاتی) بداند. اگر متن یا تصویر بزرگتر از این شوند، سلول میداند که باید ارتفاع بیشتری پیدا کند.
یک نمای جدولی به صورت پیشفرض و به طور خودکار ارتفاع سلولهایش را برابر با محتوایش تنظیم میکند. هر دو سلول را در صحنه News انتخاب کنید. یک روش برای انجام این کار کلیک کردن روی یک سلول و نگهداشتن کلید Shift و کلیک روی سلول دیگر است. Size Inspector (آیکون خط کش) را انتخاب کنید.

در کنار برچسب Row Height در بخش Size Inspector کادر انتخاب Custom را غیر فعال کنید.

بدین ترتیب فیلد Row Height به صورت Default درمیآید. بنابراین ارتفاع سلولها در این بوم تا مقدار 44 پوینت کاسته میشود و دیگر نمیتوانید محتوا را ببینید. اکنون اپلیکیشن را اجرا کنید.

در زمان اجرا، نمای جدولی به صورت دینامیک ارتفاع سلولها را طوری تنظیم میکند که با محتوا مطابقت یابند. اما در زمان طراحی سلولها تنها 44 پوینت ارتفاع دارند و پیشنمایش کاملی ایجاد نمیشود.
این وضعیت بغرنجی است. اگر از یک ارتفاع سفارشی استفاده کنیم، سلولها در زمان طراحی ارتفاع مفیدی دارند، اما در زمان اجرا تنظیم نمیشوند. اگر از ارتفاع پیشفرض استفاده کنیم، محتوای سلول در زمان طراحی فشرده میشود، اما در زمان اجرا به صورت دینامیک تنظیم میشود. خوشبختانه میتوانیم ارتفاع سلولها را در «سازنده اینترفیس» (Interface Builder) یعنی در زمان طراحی به هر مقداری که مناسب به نظر میرسد تغییر دهیم و کنترلر نما را به صورت دینامیک برای ارتفاع هر سلول تنظیم کنیم تا با محتوا در زمان اجرا مطابقت پیدا کند.
چنان که پیشتر گفتیم، هر نما یک کنترلر نما دارد که شامل کدی است که آن را اجرا میکند. کنترلر نما، چنان که از نامش برمیآید، به کنترل همه نماهای درون صحنه در زمان اجرا میپردازد و این مسئله شامل ارتفاع سلول نیز میشود. ما درون کنترلر برای تنظیم دینامیک ارتفاع سلولها به کد نیاز داریم. خوشبختانه این کد در فریمورک BFWControl از قبل برای ما نوشته شده است.
قبل از هر چیز باید سلولها را در سازنده اینترفیس به حالت ارتفاع پیشنمایش مفید بازگردانیم. به xcode بروید و با انتخاب دو سلول در بخش Size Inspector مقدار 220 را برای Row Height وارد کنید.
ما میتوانیم ارتفاع ردیف را طوری وارد کنیم که پیشنمایش معقولی از سلولها در استوریبورد به دست بدهد. همچنین میتوانیم ارتفاع آنها را با کشیدن دستگیره تحتانی سلول چنان که قبلاً انجام دادیم تنظیم کنیم.

در ادامه به کنترلر نما اعلام میکنیم که ارتفاع هر سلول را در زمان اجرا طوری تنظیم کند که با ارتفاع ذاتی محتوایش مطابقت داشته باشد. به این منظور کافی است نمای کنترلر نما را به صورت AdjustingTableViewController تغییر داده و خصوصیت intrinsicHeightCells را فعال کنیم.
کنترلر نمای جدولی را با کلیک کردن روی آیکون زردرنگ در نوار عنوان انتخاب کنید. در ادامه در سمت پنل سمت راست Identity Inspector را انتخاب کنید.

در Identity Inspector در فیلد Class عبارت Adj را وارد کرده و گزینه AdjustingTableViewController را از منوی بازشدنی انتخاب کنید. Return یا Tab را بزنید. در این مرحله Xcode به صورت خودکار عبارت Module را به مقدار BFWControls تنظیم میکند چون جایی است که کلاس در آن قرار دارد.

به پنل Attributes Inspector بروید. در این زمان با یک بخش جدید مواجه میشوید که عنوان آن Adjusting Table View Controller است. روی منوی بازشدنی کنار Intrinsic Height Cells کلیک کرده و آن را از Default به On تغییر دهید.

اپلیکیشن را اجرا کنید.

چنان که مشاهده میکنید، کنترلر نما به صورت خودکار ارتفاع هر سلول را بر مبنای محتوای آن تنظیم کرده است. این همان نتیجهای است که در زمان تنظیم Row Height هر سلول به صورت Default به دست میآید. با این حال اکنون ما مقدار Row Height را روی 220 تنظیم کردهایم و در زمان اجرا آن را به ارتفاع ذاتی هر سلول override کردهایم.
متن درون هر سلول در حال حاضر تنها یک خط اشغال میکند. بدیهی است که متن تفصیلی در هر سلول به فضای بیشتری برای جای گرفتن نیاز دارد و این مسئله به وسیله (…) مورد اشاره قرار گرفته است. در این بخش برچسبهای متنی را تغییر میدهیم تا در بیش از یک خط قرار گیرند.
چنان که میدانید، هر سلول در صحنه News وهلهای از کلاس NewsTableViewCell است. برچسبهای متنی درون آن کلاس و لیآوت، تحت کنترل NewsTableViewCell.xib هستند. هر برچسب یک مشخصه numberOfLines دارد که در بخش Attributes Inspector به صورت Lines ظاهر میشود. مقدار پیشفرض 1 است، اما میتوانیم آنها را به 2، 3 یا هر مقداری که خطوط ما برای آن برچسب نیاز دارند تغییر دهیم. برای این که تعداد خطوط بیشینه متن نامحدود باشند میتوانید مقدار 0 وارد کنید.
فایل NewsTableViewCell.xib را در Project Navigator انتخاب کنید. یکبار روی برچسب [Detail Text] (برچسب انتهایی) کلیک کنید و Attributes Inspector را انتخاب نمایید. در فیلد Lines مقدار 1 را به 0 تغییر دهید و دکمه Return را بزنید.

به Main.storyboard بازگردید. اینک باید ببینید که برچسبها خطوط متنی بیشتری را نمایش میدهند. اگر چنین نیست، در منوی Editor گزینه Refresh All Views را انتخاب کنید.

از آنجا که سلولها در سازنده اینترفیس (زمان طراحی) ارتفاع ثابتی دارند، Xcode ارتفاع نمایان نمای تصویر را کاهش داده تا فضای بیشتری برای متن باز شود.
اگر دوست دارید میتوانید ارتفاع سلولها را در سازنده اینترفیس تغییر دهید، چون ارتفاع زمان اجرا اکنون به طور مستقل و از سوی کنترلر نما تعیین میشود. اینک اپلیکیشن را اجرا کنید.

توجه کنید که روی شبیهساز iPhone XR، تعداد خطوط مورد نیاز برای هر برچسب متن تفصیلی متفاوت است و کنترلر نما به صورت دینامیک اندازه سلولها را مستقل از هم تعیین میکند.
تا به اینجا برچسبهای متنی ما همگی از استایل فونت پیشفرض بهره میگیرند. در ادامه مقداری تمایز بین برچسبها ایجاد میکنیم.
به Xocde بازگردید. فایل NewsTableViewCell.xib را انتخاب کنید. برچسب [Text] را انتخاب کرده و به Attributes Inspector بروید.

در Attributes Inspector روی آیکون T در فیلد Font کلیک کنید. در فیلد Font که باز میشود گزینه Headline را از بخش Text Styles انتخاب کنید.

تعداد Lines را روی 2 قرار دهید. بدین ترتیب عنوان میتواند بسته به نیاز یک یا دو خط اشغال کند.

برچسب [Tertiary Text] را انتخاب کرده و گزینه استایل متنی Caption 1 را انتخاب کنید.

همزمان با انتخاب برچسب [Tertiary Text] در بخش Attributes Inspector منوی بازشدنی Color را به Light Gray Color تغییر دهید.

اپلیکیشن را اجرا کنید.

چنان که ملاحظه میکنید، هر وهله از NewsTableViewCell استایل متنی جدیدی دارد. ارتفاع برچسبها، نماهای پشتهای و سلولها هر کدام محتوای با اندازه اندکی متفاوت دارند.
همانند بخشهای قبلی کارهای زیر را برای کامیت کردن تغییرات پروژه انجام میدهیم:
ews: auto adjusting cell heights; text styles
اکنون سلولهای News ما استایلهای متنی دارند، اندازه فونتشان متفاوت است و به صورت خودکار ارتفاع سلول را تنظیم میکنند. در بخش بعدی این سری مقالات آموزشی از همه آنچه تاکنون آموختهایم بهره میگیریم تا سلولهای با تنظیم خودکار برای صحنه Products بسازیم.
منبع: فرادرس
در این مقاله، شیوه ایجاد یک اپلیکیشن کوچک و کارآمد را بررسی میکنیم که تاریخچه هزینههای مالی شما را نگهداری میکند. این اپلیکیشن مدیریت هزینه های مالی امکان گردآوری همه رسیدها را در یک پوشه «دراپباکس» (Dropbox) فراهم میسازد و سپس میتوانید با کلیک روی یک دکمه آنها را به صورت ماهانه تنظیم کنید.
اپلیکیشنی که قصد ساخت آن را داریم به طور خاص زمانی مفید است که میخواهید هزینههای شخصی خود را حسابداری کنید چون به صورت معمول این کار را به شکل ماهانه انجام میدهیم. اینکه همه هزینههای مالی انجام شده طی یک ماه را یک جا و در یک پوشه واحد گردآوری کنیم، موجب صرفهجویی زیادی در زمانمان میشود.
در این راهنما موارد زیر بررسی شدهاند:
برای ساختن اپلیکیشنی بر مبنای دراپباکس، ابتدا باید یک حساب دراپباکس (+) داشته باشید. پس از این که در این وب سایت ثبت نام کردید، به بخش توسعهدهندگان (+) بروید. در این بخش در منوی سمت چپ داشبورد گزینه My apps را انتخاب کرده و روی Create app کلیک کنید.
تنظیمات زیر را انتخاب کرده و نام یکتایی برای اپلیکیشن خود انتخاب کنید.

در داشبورد، به بخش OAuth 2 زیر Generated access token بروید و روی دکمه Generate کلیک کنید تا یک accessToken برای API به دست آورید. این توکن دسترسی را برای استفاده آتی ذخیره کنید.

اکنون میتوانیم اپلیکیشن دسکتاپ دراپباکس (+) را نصب کنیم. با استفاده از اطلاعات احراز هویت جدید که به دست آوردید وارد اپلیکیشن شوید تا بتوانید پوشهای را که همان نام اپلیکیشن اخیراً ایجادشده را دارد مشاهده میکنید. ما در این مقاله از نام ExpenseOrganizer استفاده کردهایم.

برخی رسیدها و فاکتورهای خود را در این پوشه قرار دهید تا بتوانید از طریق API به آنها دسترسی داشته باشید و در زمان اتمام پروژه آنها را در پوشههای مشخصی دستهبندیشدهای به صورت زیر داشته باشید:

اکنون باید کدبیس خود را راهاندازی کنیم. ما از سادهترین ساختار ممکن استفاده میکنیم که یک فایل index.html با لینکهای به فایل جاوا اسکریپت و یک استایلشیت است. همچنین باید یک نام برای اپلیکیشن خود در تگ <title> قرار دهیم.

نکته: کد نهایی این راهنما را میتوانید در این صفحه (+) مشاهده کنید و میتوانید در صورت علاقه آن را کلون کنید. با این حال باید کلید API دراپباکس خود را به پروژه اضافه کنید تا کار کند.
اکنون باید کتابخانه دراپباکس را در پروژه خود نصب کنیم. به طور معمول به این منظور باید کاری مانند زیر انجام دهیم:
npm install dropox # or yarn add dropbox
با این حال در کد Scrimba که در بخش قبل معرفی کردیم، ما صرفاً کتابخانه دراپباکس را به صورت یک وابستگی در نوار کناری چپ اضافه کردهایم چون روش افزودن پکیجهای npm در Scrimba چنین است.
گام بعدی ایمپورت کردن دراپباکس و ایجاد یک وهله از کلاس Dropbox است. ما آن را dbx مینامیم و توکن خود را به آن ارسال میکنیم و کتابخانه منتخبمان را که در این مورد fetch است واکشی میکنیم. اگر ترجیح میدهید از axios با هر کتابخانه واکشی دیگر استفاده کنید، میتوانید از آن استفاده کنید و هیچ مشکلی وجود ندارد.
در این بخش مراحلی که برای دریافت و نمایش هزینهها مورد نیاز است را مورد بررسی قرار میدهیم.
برای نمایش دادهها در اپلیکیشن باید ابتدا آنها را واکشی کنیم. بدین منظور رسیدهایی که در پوشه دراپباکس خود داریم را دریافت میکنیم.
به این منظور میتوانیم از متد ()filesListFolder استفاده کنیم. این متد نام یک پوشه را میگیرد و یک Promise بازگشت میدهد که وقتی resolve شود، محتوای پوشه را در اختیار ما قرار میدهد. البته این متد یک فوت کوزهگری دارد، زیرا برای تعیین یک مسیر ریشه (پوشه مبنایی که در آن هستیم) باید یک رشته خالی به صورت ‘ ‘ بنویسیم و نوشتن آن به صورت ‘/’ صحیح نیست.
زمانی که این متد را برای بازیابی فایلها از حساب دراپباکس فراخوانی کنیم، باید چیزی مانند تصویر زیر ببینیم:

بنابراین ما یک آرایه entries داریم که آرایهای از شیءها است. هر شیء در آرایه entries، فایل ما (و در ادامه برخی از آنها میتوانند پوشههایی باشند که دادههایمان را در آنها سازماندهی میکنیم) به همراه تگهای tag ،name ،id و مشخصههای زیاد دیگر ارائه شده است. اینک تابعی مینویسیم که هر فایل را نمایش میدهد. tag مشخصهای است که نوع مدخل بازیابی شده را مشخص میکند که یک file یا یک folder است.
زمانی که همه فایلها را واکشی کردیم، آنها را در اپلیکیشن خود ذخیره میکنیم. ما میتوانیم شیئی به نام state برای انجام این کار بسازیم. همچنین میتوانیم آرایهای داشته باشیم که files را نگهداری کند و یک رشته نیز بسازیم که رد rootPath ما را ذخیره کند.
یکی از روشهای رندر کردن دادهها این است که یک لیست نامرتب <ul> به کد HTML خود اضافه کنیم، آن را با جاوا اسکریپت انتخاب کرده و با فایلهای واکشی شده از دراپباکس مقداردهی کنیم. در این مرحله یک placeholder به صورت Loading… به فایل index.html خود اضافه میکنیم که به محض نمایش فایلهای واکشی شده بازنویسی خواهد شد.
اینک میتوانیم fileListElem را بسازیم که لیست نامرتب را انتخاب میکند.
ما میتوانیم همه فایلهای مرتبسازی شده به صورت الفبایی را به fileListElem.innerHTML اضافه کنیم تا مطمئن شویم که پوشهها را ابتدا قرار دادهایم. سپس هر پوشه و فایل را با استفاده از (”)join به یک <id> نگاشت (map) میکنیم تا از رندر کردن آرایه به جای رشته جلوگیری کنیم.
اما در این مرحله هیچ چیز روی صفحه نمایش نمییابد. دلیل این مسئله آن است که باید دادهها را واکشی کرده و سپس فایلها را با ()renderFiles رندر کنیم. در ادامه یک تابع کمکی ()init به این منظور میسازیم.
میتوان حالت را استخراج و بهروزرسانی کرده و ()renderFiles را در یک متد جداگانه درج کرد.
اکنون ()init را به انتهای فایل index.js اضافه میکنیم تا کل فرایند راهاندازی شده و فایلها نمایش پیدا کنند. بدین ترتیب لیست فایلهای ما نمایش مییابد گرچه کمی خلوت به نظر میرسد.
میتوان آن را بهبود بخشید و برای این لیست آیتمها یک آیکون پوشه و فایل پیشفرض قرار داد.
برای این که همه چیز زیباتر به نظر برسد، یک فایل به نام icon.js میسازیم و آیکونهای base64 SVG را آنجا اضافه میکنیم. دلیل استفاده از این نوع آیکون آن است که کاربرد آنها در این راهنما راحتتر است و دیگر نیازی به رفتن به جای دیگر و دانلود کردن آنها وجود ندارد.
اینک میتوانیم آیکونها را در فایل index.js ایمپورت کنیم:
در این مرحله map. را در renderFiles خود بهروزرسانی میکنیم تا آیکونهای جدید را شامل شود.
اکنون فایل ما زیباتر به نظر میرسد.
قابلیت اصلی اپلیکیشن ما در این است که با یک کلیک همه فایلها درون پوشه جابجا میشوند و بر حسب سال و سپس درون هر پوشه بر حسب ماه سازماندهی میشوند. قبل از هر چیز باید یک دکمه در فایل index.js بسازیم.
در وهله دوم مقداری کد جاوا اسکریپت به آن اضافه میکنیم.
ما در انتهای این راهنما مجدداً به سراغ این دکمه میآییم تا تابعی که فایلها را جابجا میکند را تکمیل کنیم. برای جابجایی فایلها میتوانیم از ()filesMoveBatchV2 استفاده کنیم. این متد فایلها را به صورت دستهای از یک پوشه به پوشه دیگر جابجا میکند. در این مورد ما میخواهیم فایلها را از پوشه root به پوشههای با نام مجزا جابجا کنیم.
این متد زمانی که به صورت بخشی از تابع async استفاده شود، بهترین پیادهسازی خود را خواهد داشت.
این متد entries را میپذیرد که آرایهای از شیءها است و شامل مشخصههای from_path و to_path است.
()filesMoveBatchV2 اگر فراخوانی بیدرنگ موفق باشد و در واقع فایلهای معدودی برای پردازش ارائه شده باشند، مقدار success بازگشت میدهد.
با این حال در مورد حجم جابجایی بالا، شیئی به همراه یک مشخصه به نام async_job_id بازگشت میدهد و معنی آن این است که فراخوانی شما اجرا شده است و باید آن را در مرحله بعدی یعنی زمانی که تکمیل شده و دیگر در حالت in_progress نیست، با فراخوانی filesMoveBatchCheckV2 بررسی کنیم.
اینک مسیرهای صحیح را برای شیء entries پیادهسازی میکنیم و آن را به ()moveFiles اضافه میکنیم.
برای تکمیل این قابلیت باید ()moveFiles را به دکمه Organise وصل کنیم. همچنین خوب است که متن دکمه را طوری بهروزرسانی کنیم که نشان دهد پردازش جابجایی فایلها آغاز شده و در زمان اتمام کار به حالت عادی بازگردانیم.
اکنون وقتی روی دکمه کلیک کنیم، میبینیم که تغییریافته و پیام in_progress در لاگ console دیده میشود. این صرفاً برای ما است تا ببینیم که دراپباکس فایلها را جابجا میکند.
زمانی که کار پایان یافت، پوشه سالانه به دست میآید.
چنان که میبینید همه رسیدهای مثال ما مربوط به یک سال هستند. برای این که بتوانید به داخل پوشه آنگاه کنید باید بتوانید روی آنها کلیک کنید و به این منظور باید قابلیت ناوبری را تکمیل کرده باشیم. در ادامه به پیادهسازی بخش ناوبری اپلیکیشن خود میپردازیم.
ناوبری در دراپباکس عملاً شبیه به ناوبری در پوشههای فایل اکسپلورر در ویندوز یا نرمافزار Finder روی سیستمهای مک است. تنها چیزی که لازم داریم این است که پوشهای که در آن قرار داریم را عوض کنیم. در ادامه ابتدا این فرایند را به صورت دستی بررسی میکنیم و سپس کد آن را نیز مینویسیم تا پردازش به صورت خودکار اجرا شود.
اگر rootPath را در state تغییر دهیم و صفحه را بارگذاری مجدد کنیم:
همه فایلهای آوریل 2019 را به دست میآوریم:
یک بار دیگر state را به صورت دستی بهروزرسانی میکنیم تا به پوشه آوریل یعنی ماه شماره 4 برویم و صفحه را بهروزرسانی میکنیم:
بدین ترتیب همه رسیدها را مشاهده میکنید:
برای این که این فرایند کمی سادهتر شود، باید یک فیلد input اضافه کنیم، به طوری که بتوانیم نام پوشهای که میخواهیم به آن برویم را وارد کنیم و نیاز نباشد که هر بار از hard-code استفاده کنیم. بدین ترتیب فیلد input را درون یک عنصر <form> قرار میدهیم که در ادامه مشاهده میکنید.
همچنین در ادامه صفحه خود را با طراحی یک هدر زیبا در index.html زیباتر میسازیم.
اینک صفحه اپلیکیشن ما به صورت زیر در آمده است:
چنان که مشاهده میکنید، یک دکمه نیز برای تأیید مسیری که میخواهیم برویم تعبیه کردهایم. درون فایل index.js تلاش میکنیم state را به همان چیزی که بود بازگردانیم.
همچنین کمی کد جاوا اسکریپت اضافه میکنیم تا rootPath را با مقادیری از فیلد input بهروزرسانی کنیم:
بدین ترتیب کار ما به پایان رسیده و ناوبری اپلیکیشن عملیاتی شده است.
بدین ترتیب ما موفق شدیم با پیگیری مراحل معرفی شده در این راهنما اپلیکیشنی بسازیم که به رسیدهای مالی ما سر و سامان میبخشد. این موفقیت بزرگی محسوب میشود. اگر همچنان حس میکنید که موفق نشدهاید همه مفاهیم مطرحشده در این راهنما را به خوبی متوجه نشدهاید پیشنهاد میکنیم به صفحه کد منبع این اپلیکیشن (+) نیز سری بزنید تا توضیحات را با تفصیل بیشتری مشاهده کنید.
منبع: فرادرس
در این مقاله به بررسی روش پیادهسازی درخت دودویی در جاوا میپردازیم. ما در این راهنما از یک درخت دودویی مرتب استفاده میکنیم که شامل مقادیر عدد صحیح (int) است.
درخت دودویی یک ساختمان داده بازگشتی است که در آن هر گره حداکثر میتواند دو فرزند داشته باشد. نوع رایجی از درخت دودویی به نام درخت جستجوی دودویی شناخته میشود که در آن هر گره مقداری دارد که بزرگتر یا مساوی مقادیر گره زیردرخت چپ خود است و کمتر یا مساوی مقادیر گره در زیردرخت راستش است.
در زیر بازنمایی مختصری از این نوع درخت دودویی را میبینید:

ما برای پیادهسازی این درخت از یک کلاس کمکی Node استفاده میکنیم که مقادیر int را ذخیره میکند و ارجاعی به هر فرزند نگه میدارد:
در ادامه گره آغازین درخت را که «ریشه» (Root) نامیده میشود اضافه میکنیم:
در این بخش برخی از رایجترین عملیات که میتوان روی یک درخت دودویی اجرا کرد را بررسی میکنیم.
نخستین عملیاتی که بررسی میکنیم درج گرههای جدید در درخت دودویی است. ابتدا باید مکانی را بیابیم که میخواهیم گره جدید را به صورت ترتیبی اضافه کنیم تا کل درخت مرتب باقی بماند. برای درج گره جدید قواعد زیر را با آغاز از گره ریشه پیگیری میکنیم:
ابتدا یک متد بازگشتی برای انجام این عملیات درج ایجاد میکنیم:
سپس متد عمومی را ایجاد میکنیم که فرایند بازگشتی را از گره ریشه آغاز میکند:
اکنون بررسی میکنیم که چگونه میتوانیم از این متد برای ایجاد درخت در مثال خود استفاده کنیم:
در این بخش یک متد اضافه میکنیم تا بررسی کنیم آیا درخت شامل مقدار خاصی است یا نه. همانند عملیات قبل ابتدا یک متد بازگشتی مینویسیم تا درخت را پیمایش کنیم:
در این متد با مقایسه مقدار مورد نظر با مقادیر گره جاری به جستجوی آن میپردازیم و سپس بسته به نتیجه مقایسه، ادامه کار را در فرزندان سمت چپ یا راست ادامه میدهیم.
سپس متد عمومی را ایجاد میکنیم که از گره ریشه آغاز میشود.
اکنون میتوانیم یک تست ساده بنویسیم که تأیید کند آیا درخت واقعاً شامل عنصر درج شده است یا نه.
همه گرههای اضافه شده باید در درخت موجود باشند.
عملیات رایج دیگری که روی درختهای دودویی استفاده میشود، حذف یک گره از درخت است. ابتدا باید گرهی را که میخواهیم حذف کنیم به روشی که قبلاً معرفی کردیم بیابیم:
زمانی که گره مورد نظر را یافتیم، 3 حالت متفاوت وجود دارد:
در ادامه شیوه پیادهسازی حالت نخست را میبینید که در آن گره مورد نظر یک برگ است:
اکنون به بررسی حالتی میپردازیم که گره یک فرزند دارد:
در کد فوق یک فرزند غیر تهی بازگشت میدهیم تا بتواند به گره والد انتساب یابد.
در نهایت باید حالتی را مدیریت کنیم که گره دو فرزند دارد. در این حالت ابتدا باید گرهی را که جایگزین گره حذف شده خواهد شد بیابیم. به این منظور از کوچکترین گرهی که در زیردرخت سمت راست گره مورد نظر قرار دارد استفاده میکنیم:
سپس کمترین مقدار را به گرهی که باید حذف شود انتساب میدهیم و سپس آن را از زیردرخت راست حذف میکنیم.
در نهایت متد عمومی را ایجاد میکنیم که فرایند حذف را از ریشه آغاز میکند:
اکنون بررسی میکنیم که آیا عملیات حذف مطابق انتظار پیش میرود یا نه.
در این بخش روشهای مختلف پیمایش یک درخت را بررسی میکنیم و جستجوهای «عمق-اول» و «سطح-اول» را بررسی میکنیم. ما از همان درختی که قبلاً استفاده کردیم بهره میگیریم و ترتیب پیمایش را در هر حالت نمایش میدهیم.
جستجوی عمق-اول نوعی از پیمایش است که ابتدا تا حد امکان به عمق درخت میرود همه فرزندان را بررسی میکند و سپس به کاوش همنیاها میپردازد. چند روش برای اجرای جستجوی عمق-اول وجود دارد که به صورت پیشترتیبی، میانترتیبی و پسترتیبی نامیده میشوند. پیمایش میانترتیبی شامل بازدید زیردرخت چپ در وهله اول است و سپس گره ریشه بازدید میشود و در نهایت زیردرخت راست مورد بازدید قرار میگیرد.
اگر این متد را فراخوانی کنیم، پیمایش میانترتیبی در کنسول به صورت زیر نمایش پیدا میکند:
3 4 5 6 7 8 9
در پیمایش پیشترتیبی ابتدا گره ریشه مورد بازدید قرار میگیرد، سپس زیردرخت چپ و در نهایت زیردرخت راست بازدید میشود:
خروجی پیمایش پیشترتیبی در کنسول به صورت زیر خواهد بود:
6 4 3 5 8 7 9
در روش پیمایش پسترتیبی، ابتدا زیردرخت چپ مورد بازدید قرار میگیرد و سپس زیردرخت راست و در نهایت گره ریشه بازدید میشود:
خروجی پیمایش پسترتیبی گرهها به صورت زیر است:
3 5 4 7 9 8 6
این نوع جستجو نیز یکی دیگر از روشهای رایج پیمایش درخت است و در آن ابتدا همه گرههای یک سطح مورد بازدید قرار میگیرند و سپس به سطح بعدی مراجعه میشود. این نوع از پیمایش به نام پیمایش ترتیب سطح نیز نامید میشود و در آن همه سطوح درخت با آغاز از ریشه و از چپ به راست مورد بازدید قرار میگیرد.
برای پیادهسازی این نوع جستجو از «صف» (Queue) استفاده میکنیم تا گرهها را در هر سطح به ترتیب حفظ کنیم. به این منظور هر گره را از لیست استخراج و مقدار آن را پرینت میکنیم، سپس فرزندانش را به صف اضافه میکنیم:
در این حالت، ترتیب گرهها به صورت زیر خواهد بود:
6 4 8 3 5 7 9
در این مقاله با شیوه پیادهسازی درخت دودویی در جاوا و رایجترین عملیات مربوط به آن آشنا شدیم. مانند همیشه همه کدهای معرفی شده در این مقاله را میتوانید در این آدرس گیتهاب (+) مشاهده کنید.
منبع: فرادرس
امروزه شاهد هستیم که منطق تجاری وباپلیکیشنها به مرور از بکاند به سمت فرانتاند در حال جابجایی است و از این رو خبرگی در زمینه «مهندسی فرانتاند» از هر زمان دیگری اهمیت بیشتری یافته است. مهندسان فرانتاند امروزه به کتابخانههای view از قبیل React وابسته هستند تا بهرهوری را بالا ببرند. کتابخانههای view به نوبه خود به کتابخانههای «حالت» (State) مانند Redux وابسته هستند تا دادهها را مدیریت کنند. در مجموع ریاکت و ریداکس در پارادایم برنامهنویسی واکنشی مشارکت دارند که در آن UI در واکنش به تغییرهای داده اقدام به بهروزرسانی ریاکت میکند. در این مطلب به بررسی ساختمان های داده در جاوا اسکریپت خواهیم پرداخت.
در عصر حاضر بکاند به طور فزایندهای صرفاً به عنوان یک سرور API عمل کرده و نقاط انتهایی لازم برای بازیابی و بهروزرسانی دادهها را عرضه میکند. در واقع، بکاند صرفاً پایگاه داده را به فرانتاند فوروارد میکند و انتظار دارد که مهندسان فرانتاند همه منطق کنترلر را مدیریت کنند. محبوبیت رو به تزاید میکروسرویسها و GraphQL گواهی بر این روند رو به رشد است.
در حال حاضر انتظار میرود که مهندسان فرانتاند علاوه برداشتن درکی زیباییشناسانه از HTML و CSS، بر جاوا اسکریپت نیز مسلط باشند. از آنجا که ذخیره دادهها در سمت کلاینت به عنوان نسخههایی تکراری از پایگاه داده روی سرور تلقی میشود، کسب دانش دقیق از ساختمانهای داده رایج به امری ضروری تبدیل شده است. درواقع، سطح خبرگی یک مهندس را میتوان از توانایی وی برای تمییز زمان و چرایی استفاده از یک ساختمان داده، استنباط کرد.
برنامه نویسان بد از کد نگران هستند، اما برنامه نویسان خوب در مورد ساختمان داده و روابط آن دغدغه دارند.
– لینوس تروالدز، خالق لینوکس و گیت
در سطوح بالا اساساً سه نوع ساختمان داده وجود دارد: «پشته» (Stack)، «صف» (Queue) و ساختارهای شبیه آرایه که تنها تفاوتشان در شیوه درج و حذف آیتمها است. لیستهای پیوندی، درخت و گراف ساختمانهایی با گره هستند که به گرههای دیگر ارجاع میدهند. جداول هَش برای ذخیره و مکانیابی دادهها به تابعهای هش وابسته هستند.
برحسب پیچیدگی، پشتهها و صفها سادهترین ساختمانهای داده هستند و میتوانند از لیستهای پیوندی ساخته شوند. درختها و گرافها پیچیدهترین ساختمانهای داده هستند که مفهوم لیست پیوندی را بسط دادهاند. جداول هش نیاز دارند از این ساختمانهای داده برای کارکرد پایدار بهره بگیرند. برحسب کارایی، لیستهای پیوندی بهینهترین ساختمان داده برای ثبت و ذخیره دادهها محسوب میشوند، در حالی که جداول هش برای جستجو و بازیابی دادهها بالاترین کارایی را دارند.
در این مقاله برای توضیح علت و روشن ساختن زمان استفاده از هر کدام از این ساختمانهای داده از ترتیب این وابستگیها تبعیت خواهیم کرد.
بدیهی است که مهمترین پشته در جاوا اسکریپت «پشته فراخوانی» (call stack) است که هر زمان یک تابع را اجرا میکنیم آن را به دامنه تابع ارسال میکنیم. از نظر برنامهنویسی پشته فراخوانی صرفاً یک آرایه با دو عملیات اصلی یعنی push و pop است. عملیات push عناصری را به ابتدای آرایه اضافه میکند در حالی که عملیات pop عناصر را از همان مکان حذف میکند. به بیان دیگر پشته از پروتکل «ورودی اول، خروجی اول» به اختصار FIFO تبعیت میکند.
در ادامه مثالی از یک پشته را در کد جاوا اسکریپت میبینید. دقت کنید که میتوانیم ترتیب پشته را معکوس کنیم و انتهای پشته به ابتدا بیاید و ابتدا به انتها برود. در چنین حالتی میتوانیم از متدهای unshift و shift به ترتیب به جای عملیات push و pop استفاده کنیم.
زمانی که تعداد آیتمها افزایش پیدا میکند، push/pop به طور فزایندهای کارایی بیشتری نسبت به unshift/shift پیدا میکنند، زیرا در حالت دوم هر آیتم باید مجدداً اندیسگذاری شود، اما در حالت اول چنین الزامی وجود ندارد.
جاوا اسکریپت یک زبان برنامهنویسی «رویداد-محور» (event-driven) است که امکان پشتیبانی از عملیات غیر مسدودساز را فراهم میسازد. مرورگر به صورت داخلی تنها یک نخ دارد که کل کد جاوا اسکریپت را روی آن اجرا میکند و از «صف رویداد» (event queue) برای صفبندی شنوندهها و از «حلقه رویداد» (event loop) برای گوش دادن به رویدادهای ثبت شده استفاده میکند. برای پشتیبانی از ناهمگامی در یک محیط تک نخی (برای صرفهجویی در منابع پردازنده و بهبود تجربه وب) تابعهای شنونده از صف خارج میشوند و تنها زمانی اجرا میشوند که پشته فراخوانی خالی شود. Promise-ها به این معماری رویداد-محور وابسته هستند که امکان اجرای کد ناهمگام به «سبک همگام» را فراهم میسازد و دیگر عملیات را مسدود نمیکند.
از نظر برنامهنویسی، صفها صرفاً آرایهای هستند که دو عملیات عمده یعنی unshift و pop دارند. unshift آیتمها را در انتهای آرایه صفبندی میکند در حالی که pop آنها را از ابتدای آرایه از صف خارج میکند. به بیان دیگر صفها از پروتکل «ورودی اول، خروجی اول» یا FIFO تبعیت میکنند. اگر جهت عوض شود میتوانیم unshift و pop را به ترتیب با push و shift عوض کنیم.
مثالی از کدنویسی صف در عمل به صورت زیر است:
لیستهای پیوندی نیز همانند آرایهها به ذخیرهسازی عناصر داده با ترتیب متوالی میپردازند. اما لیستهای پیوندی به جای این که دادهها را اندیسگذاری کنند، از اشارهگر برای اشاره به عناصر دیگر بهره میگیرند. نخستین گره به نام head خوانده میشود در حالی که گره آخر tail نام دارد. در یک لیست پیوندی یکطرفه، هر گره تنها یک اشارهگر به گره بعدی دارد. در این وضعیت head جایی است که مسیر حرکت ما تا انتهای لیست از آنجا آغاز میشود. در لیست پیوندی دوطرفه، یک اشارهگر به گره قبلی نیز نگهداری میشود. از این رو میتوانیم از tail نیز آغاز کنیم و در جهت معکوس به سمت head حرکت کنیم.
در لیستهای پیوندی عملیات درج و حذف دارای زمان ثابتی است، زیرا میتواند صرفاً اشارهگرها را عوض کرد. اجرای عملیات مشابه در آرایهها دارای پیچیدگی زمانی خطی است، زیرا آیتمهای بعدی نیز باید جابجا شوند. ضمناً لیستهای پیوندی میتوانند تا جایی که فضا اجازه میدهد بسط یابند. با این وجود حتی آرایههای دینامیک که به طور خودکار تغییر اندازه میدهند نیز ممکن است به طور غیرمترقبهای پرهزینه شوند. البته همواره تعادلی بین این مزایا و معایب وجود دارد. برای گشتن به دنبال یک عنصر و ویرایش کردن آن در لیست پیوندی ممکن است لازم باشد کل طول لیست را بپیماییم که از نظر زمانی دارای پیچیدگی خطی خواهد بود. در حالی که با وجود اندیس در آرایهها چنین عملیاتی بسیار سادهتر است.
لیستهای پیوندی نیز همچون آرایهها میتوانند به صورت پشته عمل کنند. این کار به سادگی اجرا میشود بدین ترتیب که head به عنوان تنها مکان برای درج و حذف عناصر استفاده میشود. لیستهای پیوندی میتوانند به صوت صف نیز مورد استفاده قرار گیرند. این وضعیت با استفاده از یک لیست پیوندی دوطرفه میسر میشود که در آن عملیات درج در tail رخ میدهد و عملیات حذف در head انجام مییابد و یا برعکس. در مورد تعداد بالای عناصر، این روش پیادهسازی صف بسیار کارآمدتر از استفاده از آرایه است، زیرا عملیات shift و unshift در ابتدای آرایه زمان خطی دارند و نیازمند اندیسگذاری مجدد همه عناصر بعدی هستند.
لیستهای پیوندی در هر دو سمت کلاینت و سرور مفید هستند. در سمت کلاینت کتابخانههای مدیریت «حالت» (State) مانند ریداکس منطق میانافزاری خود را به روشی مانند لیست پیوندی سازماندهی میکنند. زمانی که اکشن ارسال میشود این کتابخانه آن را از یک میانافزار به دیگری pipe میکند و همین طور تا آخر میرود تا این که پیش از رسیدن به «کاهندهها» (Reducers) همه میانافزارها را بازدید کرده باشد. در سمت سرور فریمورکهای وب مانند Express نیز منطق میانافزاری خود را به روش مشابهی سازماندهی میکنند. زمانی که یک درخواست دریافت میشود، از یک میانافزار به دیگری pipe میشود تا این که یک پاسخ صادر شود.
نمونهای از لیست پیوندی دوطرفه را در مثال زیر ملاحظه میکنید:
«درخت» (Tree) شبیه به لیست پیوندی است به جز این که هر آیتم در آن به گرههای فرزند زیادی ارجاع میدهد و ساختاری سلسله مراتبی دارد. به بیان دیگر هر گره نمیتواند بیش از یک والد داشته باشد. «مدل شیء سند» (Document Object Model) یا به اختصار DOM چنین ساختاری است که ریشه آن گره html است که به گرههای body و head تقسیم میشود و سپس به همه تگهای آشنای خانواده html انشعاب مییابد. وراثت پروتوتایپی و ترکیببندی با استفاده از کامپوننتهای ریاکت نیز در پس زمینه، ساختارهای درخت را بازتولید میکنند. البته DOM مجازی ریاکت به عنوان یک بازنمایی درون حافظهای از DOM نیز ساختاری درختی دارد.
درخت جستجوی دودویی درخت خاصی است، زیرا در آن هر گره نمیتواند بیش از دو فرزند داشته باشد. فرزند چپ باید مقداری کمتر یا برابر با والد خود داشته باشد، در حالی که فرزند راست باید مقدار بزرگتری داشته باشد. درخت جستجوی دودویی به این ترتیب سازماندهی یافته و متعادل شده است و بنابراین میتوانیم در طی یک زمان لگاریتمی به دنبال هر آیتمی بگردیم، زیرا میتوانیم در هر تکرار نیمی از انشعاب باقیمانده را نادیده بگیریم. درج و حذف نیز میتواند در زمان لگاریتمی اجرا شود. به علاوه کوچکترین و بزرگترین مقدار میتواند به سادگی به ترتیب در برگهای انتهای سمت چپ و انتهای سمت راست مشاهده شود.
پیمایش درخت به دو شیوه افقی و عمودی اجرا میشود. «پیمایش عمق-اول» (Depth-First Traversal) یا به اختصار DFT در جهت عمودی صوت میگیرد و یک الگوریتم بازگشتی مناسبتر از نوع تکراری است. گرهها میتوانند به روشهای پیشترتیبی، میانترتیبی یا پسترتیبی پیمایش شوند. اگر لازم باشد ریشهها را پیش از بازرسی برگها بررسی کنیم، باید روش پیشترتیبی را انتخاب کنیم. اما اگر نیاز باشد که برگها قبل از ریشه بررسی شوند، باید از روش پسترتیبی استفاده کنیم. روش میانترتیبی نیز چنان که از نامش مشخص است امکان پیمایش گرهها به روش متوالی را فراهم میسازد. این مشخصه موجب شده است که درخت جستجوی دودویی برای مرتبسازی بهینه باشد.
در روش «پیمایش سطح-اول» (Breadth-First Traversal) که به اختصار BFT نامیده میشود، از جهتگیری افقی استفاده میشود و رویکرد تکرار مناسبتر از رویکرد بازگشتی است. این پیمایش نیازمند استفاده از صف برای ردگیری همه گرههای فرزند در هر تکرار است. با این حال، حافظه مورد نیاز برای چنین صفی ممکن است سنگین باشد. اگر شکل درخت بیشتر عریض است تا عمیق، BFT گزینه بهتری نسبت به DFT محسوب میشود. ضمناً مسیری که BFT بین هر دو گره طی میکند، کوتاهترین مسیر ممکن است. نمونهای از کد درخت جستجوی دودویی را در ادامه ملاحظه میکنید:
اگر در یک درخت امکان این باشد که هر گره بیش از یک والد داشته باشد، آن را گراف مینامیم. یالی که گرهها را در گراف به هم وصل میکند، میتواند جهتدار یا غیر جهتدار، وزندار یا بیوزن باشد. یالهای دو جهته و وزندار، شبیه بُردار هستند.
وراثتهای چندگانه به شکل Mixin و اشیای داده که روابط چند به چند دارند ساختمانهای داده گراف را ایجاد میکنند. یک شبکه اجتماعی و خود اینترنت نیز گراف محسوب میشوند. پیچیدهترین گراف برحسب ماهیت، مغز انسان است که الگوریتمهای شبکه عصبی تلاش میکنند با شبیهسازی آن به ماشینها نوعی فراهوشمندی بدهند.
نمونهای از کد گراف به صورت زیر است:
جداول هش ساختمانهای شبه دیکشنری هستند که از جفت کلید/مقدار استفاده میکنند. مکان حافظه برای هر جفت بر اساس تابع هش تعیین میشود که یک کلید میپذیرد و آدرسی که جفت باید درج و بازیابی شود را بازمیگرداند. در صورتی که دو یا چند کلید به آدرس یکسانی تبدیل شوند، تصادم رخ میدهد. برای افزایش پایداری باید getter و setter، این رویدادها را پیشبینی کنند و اطمینان حاصل کنند که همه دادهها میتوانند بازیابی شوند و هیچ دادهای بازنویسی نمیشوند. به طور معمول linked lists سادهترین راهحل هستند. داشتن جداول بسیار بزرگ نیز در این زمینه کمک زیادی خواهد کرد.
اگر بدانیم که آدرسهای ما توالیهای عدد صحیح هستند، میتوانیم صرفاً از Arrays برای ذخیرهسازی جفتهای کلید-مقدار استفاده کنیم. برای نگاشتهای آدرس پیچیدهتر میتوانیم از Maps یا Objects استفاده کنیم. جداول هش به طور میانگین زمان ثابتی برای درج و جستجو دارند. به دلیل تصادم و تغییر اندازه، این هزینه ناچیز میتواند به صورت زمان خطی رشد کند. با این حال در عمل میتوانیم تصور کنیم که تابعهای هش به قدر کافی هوشمند هستند که تصادم و تغییر اندازه نادر و ارزان است. اگر کلیدها نماینده آدرسها باشند و زمانی که به هیچ هش کردن نیاز نباشد، میتوان از یک object literal استفاده کرد. البته همواره تعادلی بین مزیتها و معایب وجود دارند. تناظر ساده بین کلید و مقدار و ارتباط ساده بین کلیدها و آدرسها، روابط بین دادهها را قربانی میکند. از این رو جداول هش برای مرتبسازی دادهها اصلاً بهینه نیستند.
اگر کفه تصمیم شما به سمت بازیابی سنگینتر است تا ذخیرهسازی، هیچ ساختمان داده دیگری نمیتواند سرعتی که جداول هش برای گشتن، درج، و حذف ارائه میکنند در اختیار شما قرار دهد. در نتیجه هیچ جای شگفتی نیست که از جداول هش تقریباً در همه جا استفاده میشود. از پایگاه داده تا سرور و کلاینت، جداول هش و به طور خاص تابعهای هش برای عملکرد و امنیت اپلیکیشنهای نرمافزاری حیاتی هستند. سرعت کوئریهای پایگاه داده تا حدود زیادی به نگهداری اندیسهای جدولی که به رکوردهای مرتبشده اشاره میکنند وابسته است. بدین ترتیب جستجوهای دودویی میتوانند در زمان لگاریتمی اجرا شوند که ارتقای عملکرد عظیمی به خصوص در حوزه کلانداده محسوب میشود.
در هر دو سمت کلاینت و سرور کتابخانههای محبوب زیادی وجود دارند که از memorization برای بیشینهسازی عملکرد بهره میگیرند. با حفظ سوابق ورودیها و خروجیها در جدول هش، تابعها برای ورودیهای یکسان صرفاً یک بار اجرا میشوند. کتابخانه محبوب Reselect از این راهبرد کَش کردن برای بهینهسازی تابعها در اپلیکیشنهایی که از ریداکس استفاده میکنند بهره میگیرد. در واقع موتور جاوا اسکریپت در پس زمینه از جداول هش به نام heap برای ذخیرهسازی همه variables و primitives که ایجاد کردیم بهره میگیرد. این موارد از طریق اشارهگرها به پشته فراخوانی مورد دسترسی قرار میگیرند.
خود اینترنت برای عملکرد صحیحش بر مبنای الگوریتمهای هش کردن بنا شده است. ساختار اینترنت به طوری است که هر رایانهای میتواند با رایانه دیگر از طریق شبکهای از دستگاههای به هم متصل ارتباط برقرار کند. هر زمان که یک دستگاه وارد اینترنت میشود خود به یک روتر تبدیل میشود که جریان دادهها میتوانند از آن بگذرند. اما این یک شمشیر دو لبه است. معماری نامتمرکز به این معنی است که هر دستگاهی در شبکه میتواند بستههای داده را مورد شنود قرار دهد و آنها را که رله میکند دستکاری نماید. تابعهای هش مانند MD5 و SHA256 نقشی حیاتی در جلوگیری از چنین حملههای «مرد میانی» (Man-in-the-Middle) دارند. تجارت الکترونیک در بستر HTTPS تنها به این جهت امن است که این تابعهای هش مورد استفاده قرار میگیرند.
فناوری بلاکچین که از اینترنت الهام گرفته به دنبال این است که ساختار وب را در سطح پروتکل، متنباز کند. بلاکچین با بهرهگیری از تابعهای هش برای ایجاد «اثرانگشتهای تغییرناپذیر» (immutable fingerprints) برای هر بلوک داده کاری کرده است که اساساً کل پایگاه داده امکان حضور آزادانه روی وب برای همه افرادی که قصد مشارکت دارند پیدا کند. بلاکچین از نظر ساختاری صرفاً لیستهای پیوندی یکطرفهای از درختهای دودوییِ هشهای رمزنگاریشده است.
هش کردن چنان جنبه رمزنگاریشدهای دارد که با استفاده از آن میتوان یک پایگاه داده از تراکنشهای مالی را طوری ایجاد و بهروزرسانی کرد که هر کس به آن دسترسی آزاد داشته باشد. نتیجه ضمنی خارقالعاده این وضعیت قدرت شگفتانگیز خلق پول بوده است. کاری که روزگاری صرفاً تحت سلطه دولتها و بانکهای مرکزی بود؛ اما امروزه هر کس میتواند پول خاص خود را خلق کند. این همان بینش کلیدی بود که بنیانگذار «اتریوم» (Ethereum) و مبدع ناشناس بیتکوین درک کردهاند.
همچنان که رفتهرفته پایگاههای داده بیشتری باز میشوند، نیاز به مهندسان فرانتاند خبره که بتوانند همه سطوح پیچیدگیهای رمزنگارانه سطح پایین را تجزیه کرده و همچنین ترکیب کنند، بیشتر حس میشود. در چنین آیندهای عامل تمایز اصلی تجربه کاربری خواهد بود.
در کد زیر میتوانید نمونهای از جدول هش را ملاحظه کنید:
همچنان که منطق تجاری به صورت فزایندهای در حال جابجایی از سمت سرور به سمت کلاینت است، لایه داده در فرانتاند رواج بیشتری مییابد. مدیریت صحیح این لایه نیازمند چیرگی بر ساختمانهای داده است که منطق تجاری بر مبنای آن شکل میگیرد. هیچ ساختمان دادهای برای همه موقعیتها کامل محسوب نمیشود، چون بهینهسازی برای یک مشخصه عموماً موجب ضعف در مشخصه دیگر میشود. برخی ساختارها در ذخیرهسازی دادهها کارآمدتر هستند، در حالی که برخی دیگر برای جستجوی دادهها کارایی بیشتری دارند. به طور معمول، یکی از اینها قربانی دیگری میشود.
در یک انتها، لیستهای پیوندی برای ذخیرهسازی بهینه هستند و میتوان پشته و صف را با زمان خطی ساخت. در سمت دیگر هیچ ساختمان دیگری نمیتواند با سرعت جستجوی جدول هش (زمان ثابت) برابری کند. ساختمانهای درخت در میانه این طیف (زمان لگاریتمی) قرار میگیرند و تنها یک گراف میتواند ماهیت پیچیدهترین ساختار طبیعی یعنی مغز انسان را (با زمان چندجملهای) نمایش دهد. کسب مجموعه مهارتهای مورد نیاز برای تمییز زمان و توضیح چرایی استفاده از یک ساختمان داده نشانهای از تواناییهای یک مهندس کاملاً خبره است.
نمونههایی از این ساختمانهای داده را میتوانید هر کجا از پایگاه داده، تا سرور، تا کلاینت و حتی خود زبان جاوا اسکریپت ببینید. این ساختمانهای داده سوییچهای روشن و خاموش روی تراشههای سیلیکونی را به اشیای شبه زندهای تبدیل میکنند. گرچه این اشیا صرفاً دیجیتالی هستند اما تأثیری که روی جامعه دارند کاملاً شگرف است. این که شما امکان مطالعه این مقاله به صورت امن و رایگان را به دست آوردهاید، مدیون معماری خارقالعاده اینترنت و سازماندهی دادههای آن است. با این حال این تنها یک شروع است. هوش مصنوعی و بلاکچینهای نامتمرکز در دهههای آتی ماهیت انسان و نقش موسسههایی که بر زندگی ما حکمرانی میکنند را بازتعریف خواهند کرد. بینشهای وجودگرایانه و واسطهزدایی از نهادها جزء خصوصیات اینترنت هستند که نهایتاً در حال رسیدن به بلوغ هستند.
در این مقاله هر آن چه باید در مورد تابع های ++C بدانید را آموزش میدهیم. از جمله این که چه نوع تابعهایی در زبان برنامهنویسی ++C وجود دارند و مثالهایی از شیوه استفاده از آنها ارائه میکنیم. در برنامهنویسی منظور از تابع گزارهای است که کدها را برای اجرای وظیفه خاصی گروهبندی میکند. برای مطالعه بخش قبلی این مجموعه مقالات آموزشی میتوانید به لینک زیر رجوع کنید:
بسته به این که تابع از قبل تعریف شده باشد یا از سوی برنامهنویس ایجاد شود دو نوع تابع وجود دارد:
تابعهای کتابخانه تابعهای داخلی زبان برنامهنویسی ++C هستند. برنامهنویس میتواند با فراخوانی مستقیم تابع از این تابعهای کتابخانه استفاده کند و نیازی نیست که آنها را خودش بنویسد.
Enter a number: 26 Square root of 26 = 5.09902
در مثال فوق، تابع کتابخانه ()sqrt برای محاسبه ریشه مربع یک عدد استفاده میشود. به کد <include <cmath# در برنامه فوق توجه کنید. در این کد cmath یک فایل «هدر» (Header) است. تعریف تابع ()sqrt یعنی بدنه تابع در فایل هدر cmath قرار دارد. زمانی که فایل cmath را با استفاده از دستور <include <cmath# در برنامه خود بگنجانید، میتوانید از همه تابعهای تعریف شده در آن استفاده کنید.
++C به برنامهنویس امکان میدهد که تابع خاص خود را تعریف کند. تابع تعریف شده از سوی کاربر به گروهبندی کد میپردازد تا وظیفه خاصی را اجرا کند و این گروه کد یک نام (شناسه) دارد. زمانی که تابع از هر بخش از برنامه فراخوانی شود، کدهای تعریف شده در بدنه تابع را اجرا میکند.

به تصویر فوق توجه کنید. زمانی که یک برنامه شروع به اجرا میکند، سیستم تابع ()main را فراخوانی میکند، یعنی سیستم شروع به اجرای کد از تابع ()main میکند. زمانی که کنترل برنامه به ()function_name درون ()main برسد، به ()void function_name انتقال مییابد و همه کدهای درون آن را اجرا میکند. سپس کنترل برنامه مجدداً به تابع main بازمیگردد و چنان که در تصویر فوق مشخص است، کد پس از فراخوانی ()function_name اجرا میشود.
برنامه ++C زیر، دو عدد صحیح را با هم جمع میکند. یک تابع به نام ()add ایجاد میکنیم تا اعداد صحیح را با هم جمع کند و مجموع را در تابع ()main نمایش دهد.
Enters two integers: 8 -4 Sum = 4
اگر یک تابع تعریف شده از سوی کاربر پس از تابع ()main تعریف شود، کامپایلر خطایی نمایش خواهد داد. دلیل این امر آن است که کامپایلر از وجود تابع تعریف شده از سوی کاربر، نوع آرگومانهای ارسالی به تابع و نوع بازگشتی آن ناآگاه است.
پروتوتایپ تابع در ++C یک اعلان از تابع بدون بدنه است که اطلاعاتی را در مورد تابع تعریف شده از سوی کاربر در اختیار کامپایلر قرار میدهد. پروتوتایپ تابع در مثال فوق به صورت زیر است:
int add(int، int);
چنان که ملاحظه میکنید، در پروتوتایپ تابع هیچ بدنهای ندارد. ضمناً تنها نوع بازگشتی آرگومانها ذکر شده و خبری از خود آرگومانها نیست. پروتوتایپ تابع را میتوان به شکل زیر نیز اعلان کرد، اما لزومی به نوشتن آرگومانها وجود ندارد:
int add(int a، int b);
نکته: در صورتی که تابع تعریف شده کاربر پیش از تابع ()main قرار داشته باشد، اعلان پروتوتایپ ضروری است.
برای اجرای کد بدنه تابع، تابع تعریف شده از سوی کاربر باید احضار یا فراخوانی شود. در برنامه فوق دستور زیر:
درون ()main تابع تعریف شده کاربر را فراخوانی میکند. بدین ترتیب تابع یک عدد صحیح بازگشت میدهد که در متغیر add ذخیره میشود.
خود تابع به صورت تعریف تابع مورد ارجاع قرار میگیرد. تعریف تابع در برنامه فوق به صورت زیر است:
زمانی که تابعی فراخوانی میشود، کنترل به گزاره اول بدنه تابع انتقال مییابد. سپس گزارههای دیگر در بدنه تابع به ترتیب اجرا میشوند. هنگامی که همه کدهای درون تعریف تابع اجرا شدند، کنترل برنامه به برنامه فراخوانی کننده بازگشت مییابد.
پارامتر آرگومان در برنامهنویسی، اشاره به دادههایی دارد که در زمان فراخوانی تابع به آن ارسال میشوند. در مثال فوق، دو متغیر num1 وnum2 در طی فراخوانی تابع به آن ارسال میشوند. این آرگومانها به نام آرگومانهای واقعی شناخته میشوند. مقدار num1 و num2 به ترتیب با متغیرهای a و b مقداردهی میشوند. این آرگومانهای a و b به نام آرگومانهای صوری شناخته میشوند.
این وضعیت در شکل زیر نمایش یافته است:

هر تابعی میتواند با استفاده از گزاره Return یک مقدار منفرد به برنامه فراخوانی کننده بازگشت دهد. در برنامه فوق، مقدار add از تابع تعریف شده از سوی کاربر با استفاده از گزاره زیر به برنامه فراخوانی کننده بازگشت مییابد:
return add;
تصویر زیر طرز کار گزاره return را نشان میدهد:

در برنامه فوق، مقدار add درون تابع تعریف شده کاربر به تابع فراخوانی کننده بازگشت مییابد. سپس این مقدار در متغیر sum ذخیره میشود. توجه داشته باشید که متغیر بازگشت یافته یعنی add از نوع int و متغیر sum نیز از نوع int است. ضمناً توجه کنید که نوع بازگشتی یک تابع در اعلان تابع تعریف میشود:
int add(int a، int b)
عبارت int در دستور اعلان فوق به این معنی است که این تابع باید مقداری با نوع int بازگشت دهد. اگر هیچ مقداری به تابع فراخوانی کننده بازگشت نیابد، در این صورت باید از void استفاده کنید. بدین ترتیب به پایان این بخش از سری مقالات آموزش ++C میرسیم.
برای مطالعه قسمت بعدی این مجموعه مطلب آموزشی میتوانید روی لینک زیر کلیک کنید:
منبع: فرادرس