سایت خبرکاو

جستجوگر هوشمند اخبار و مطالب فناوری

مهارت های کدنویسی خود را با ساختن یک برنامه به روش های مختلف تمرین کنید

در حالی که در سال‌های دیگر 365 روز داریم، امسال (2024) ویژه است زیرا یک روز «اضافی» دارد. پس با روحیه روز کبیسه، اجازه دهید مقداری کدنویسی را تمرین کنیم تا جنبه های مختلف برنامه نویسی را درک کنیم. ما روی یک برنامه تمرکز خواهیم کرد اما از دیدگاه های متفاوت. برنامه مثال ما راه های مختلفی را تحلیل می کند که بتوانید برنامه ای را کدنویسی کنید که تعیین می کند یک سال معین سال کبیسه ...

در حالی که در سال‌های دیگر 365 روز داریم، امسال (2024) ویژه است زیرا یک روز «اضافی» دارد.

پس با روحیه روز کبیسه، اجازه دهید مقداری کدنویسی را تمرین کنیم تا جنبه های مختلف برنامه نویسی را درک کنیم. ما روی یک برنامه تمرکز خواهیم کرد اما از دیدگاه های متفاوت.

برنامه مثال ما راه های مختلفی را تحلیل می کند که بتوانید برنامه ای را کدنویسی کنید که تعیین می کند یک سال معین سال کبیسه است یا خیر. در روزهای دیگر کد می‌زنیم. اما امروز، بیایید آنچه را که انجام می‌دهیم رمزگشایی کنیم و دانش اضافی را از آن فرآیند بدست آوریم.

فهرست مطالب

الزامات و پیش نیازهای برنامه

رویکردهای منطقی برای حل مسئله

رویکرد ساده لوحانه من

تخصیص مجدد و بیانیه بازگشت واحد

جابجایی به Switch-Case از If-Else

کسر منطقی و زیر مجموعه ها برای ساختار بهتر

عملگرهای منطقی که همه شرایط واقعی را ترکیب می کنند

استفاده از نیترو با اپراتور سه تایی

تبدیل آن به یک تابع پیکان خطی

تغییر پارادایم: برنامه نویسی اعلانی

توابع با عوارض جانبی

اطلاعات بیشتر درباره برنامه نویسی تابعی

ردیابی جانبی: اتصال کوتاه!

کپسوله سازی و برنامه نویسی اعلامی

بالاتر و فراتر از کیفیت کد

اعتبارسنجی: فراتر از مشخصات اولیه

تست کردن آن از بیرون

پینوشت

الزامات و پیش نیازهای برنامه

ابتدا، بیایید الزامات را مورد بحث قرار دهیم و مشخصات را تنظیم کنیم. برنامه باید بتواند یک سال (انتظار یک عدد، یک عدد صحیح را مشخص باشد) به عنوان آرگومان دریافت کند و بسته به اینکه سال کبیسه باشد یا خیر، درست یا نادرست (یک بولی) را برمی گرداند.

از طریق مثال‌ها، به جای زبان (سینتکس) بر منطق برنامه (معناشناسی) تمرکز خواهیم کرد.

در طول سال‌ها، من اغلب از جاوا اسکریپت استفاده کرده‌ام، پس از این زبان برای پروژه استفاده خواهیم کرد. اگر از زبان دیگری استفاده می کنید، جای نگرانی نیست زیرا بسیاری از مفاهیم بین زبان های برنامه نویسی مشترک هستند. به عنوان مثال، در این مقاله از تابع arrow استفاده می کنیم که مشابه تابع لامبدا است که در برخی از زبان های برنامه نویسی دیگر مانند پایتون استفاده می شود.

پس ، به عنوان پیش نیاز، شما باید دانش اولیه برنامه نویسی داشته باشید و با مفاهیم توابع (روش های مختلف برای تعریف و فراخوانی توابع، مقادیر برگشتی و غیره) و منطق شرطی (اگر غیر از این، سوئیچ مورد، و غیره). اگر می‌خواهید این کد را برای خودتان بخوانید و امتحان کنید، در اکثر موارد، این کافی است.

فقط در آخرین بیت، ما همچنین تست واحد کد خود را انجام می دهیم. اگر با تست واحد آشنا نیستید، در اینجا یک تازه‌سازی خوب در مورد نحوه نوشتن تست‌های واحد در جاوا اسکریپت با جست وجود دارد.

رویکردهای منطقی برای حل مسئله

رویکرد ساده لوحانه من

این بر اساس سبک آموزشی تعیین یک سال کبیسه است که من در کودکی یاد گرفتم که چگونه اعداد را تقسیم کند. اگر یک سال (عددی که آن را نشان می دهد) بر 4 بخش پذیر باشد، به طور کلی یک سال کبیسه است. اما نه همیشه. وقتی آن سال با دو صفر به پایان می رسد (یعنی وقتی عدد بر 100 بخش پذیر است) باید بر 400 نیز بخش پذیر باشد تا یک سال کبیسه باشد.

فلوچارت-LeapYear
نحوه تعیین اینکه آیا یک سال یک سال کبیسه است - همانطور که در بالا توضیح داده شد

به عنوان یک برنامه نویس مبتدی، افکار من مانند آنچه در فلوچارت بالا می بینید جریان یافت. در نتیجه، من آن منطق را به این شکل به برنامه خود تبدیل کردم:

 function isLeapYear(year) { if (year % 4 == 0) { if (year % 100 == 0) { if (year % 400 == 0) { return true; } else { return false; } } else { return true; } } else { return false } } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

این باعث می شود که برنامه به راحتی قابل درک باشد. اما با گذشت زمان، همانطور که من در سفر برنامه نویسی خود بیشتر پیش رفتم، این نوع کد به دلیل بسیاری از تحلیل های شرطی تو در تو زشت به نظر می رسد. بد نیست، اما به دلیل سطوح تو در تو، مغز من باید سخت کار کند تا منطق را از عکس فوری کد به سرعت دریافت کند.

تخصیص مجدد و بیانیه بازگشت واحد

برای اجتناب از حلقه‌های تودرتو، بسیاری از برنامه‌نویسان از استراتژی شرط‌های if متوالی پیروی می‌کنند و از شرایط else اجتناب می‌کنند (مانند نحوه نشان دادن کایل کوک از Web Dev Simplified در این ویدیو با مثال‌ها ). قطعا خوانایی را بهبود می بخشد.

همچنین، به ما اجازه می‌دهد در پایان تنها از یک عبارت بازگشتی استفاده کنیم، در حالی که مقدار قابل بازگشت را مجدداً تخصیص می‌دهیم. اجازه دهید زمانی که بهتر می توانید خود کد را مشاهده کنید، بیشتر در مورد آن بحث نکنیم:

 function isLeapYear(year) { let isLeap = false; if (year % 4 == 0) { isLeap = true; } if (year % 100 == 0) { isLeap = false; } if (year % 400 == 0) { isLeap = true; } return isLeap; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

کد بالا کوتاه تر و سریع تر به نظر می رسد. اما کارایی کد را تحت تأثیر قرار می دهد، زیرا اکنون باید تمام شرایط if را در همه موارد طی کنید.

در مقابل، در رویکرد ساده‌لوحانه قبلی ما، به دلیل ساختار if-else، اگر یک سال بر 4 بخش‌پذیر نباشد (مانند سال 2023)، فقط با یک شرط اگر تحلیل می‌شود. البته این درست است که برای برنامه های کوچکی مانند این، لازم نیست بیش از حد به کارایی اهمیت دهید.

با این حال، مشکل در این رویکرد این است که باید محتاط باشید که همه شرایط if را یکی پس از دیگری اعمال کنید - استفاده از "الز اگر" مشکل ایجاد می کند، زیرا در صورت رد شدن تست شرط if قبلی، برخی از تحلیل های شرط را نادیده می گیرند.

نکته مهم دیگر این است که نظم مهم است. از آنجایی که شما با موارد عمومی‌تر سال‌ها که سال کبیسه نیستند (یعنی اجازه دهید isLeap = false؛) شروع کردید، باید از موارد نسبتاً عمومی به موارد نسبتاً خاص‌تر بروید.

پس ، اگر از سه تحلیل شرطی شما، تحلیل تقسیم پذیری بر 4 در پایان باشد، "isLeap" را حتی برای سال هایی که بر 100 بخش پذیر هستند اما بر 400 بخش پذیر نیستند صادق می کند (مانند سال های 1700، 1800، 1900، و غیره).

اگر ترتیب تحلیل های تقسیم پذیری شامل 100 و 400 را با هم عوض کنید، همان خطای منطقی رخ می دهد.

آخرین نکته ای که باید به آن اشاره کنم این است که برخی از برنامه نویسان مبتدی ممکن است فکر کنند که شما نمی توانید از چندین دستور بازگشت استفاده کنید و باید فقط یک بار در یک برنامه برگردید (و تا آن مرحله می توانید تخصیص مجدد را انجام دهید). اما برنامه نویسان باتجربه فقط می توانند این مفهوم را افسانه مبتدیان بنامند!

جابجایی به Switch-Case از If-Else

در حالی که ساختار if-else برای انتخاب بین دو گزینه استفاده می شود، شما همچنین می توانید از switch-case برای انتخاب یکی از چندین گزینه استفاده کنید. می‌توانید آن را با بلوک‌های if-else تودرتو (مانند رویکرد اول) یا مجموعه‌ای از بلوک‌های if (مانند رویکرد دوم) مقایسه کنید.

مزیت ساختار سوئیچ کیس این است که کارآمدتر است زیرا می تواند معیارهای موفقیت منطبق را در یک مرحله پیدا کند.

توجه داشته باشید که یک چیز عجیب و غریب در مورد کلید سوئیچ وجود دارد. هنگام استفاده از switch-case، پس از تطبیق یک case، همه موارد بعدی نیز اجرا می شوند مگر اینکه از دستورهای break استفاده کنید. پس ، برنامه زیر حتی اگر بسیار شبیه به نسخه قبلی کد ما باشد، درست نخواهد بود.

**کد نادرست: برای نشان دادن مشکلات مربوط به دستورات شکست از دست رفته **

 function isLeapYear(year) { let isLeap = false; switch (true) { case year % 4 == 0: isLeap = true; case year % 100 == 0: isLeap = false; case year % 400 == 0: isLeap = true; } return isLeap; }

اگر باید از ساختار switch-case استفاده کنیم، باید از دستور break استفاده کنیم. ما همچنین باید از موارد خاص ابتدا به موارد عمومی برویم. در حالی که نمی‌توان همه منطق if-else را به یک منطق سوئیچ تبدیل کرد، می‌توانیم با موفقیت تابع قبلی را مانند این تبدیل کنیم:

 function isLeapYear(year) { let isLeap = false; switch (true) { case year % 400 == 0: isLeap = true; break; case year % 100 == 0: isLeap = false; break; case year % 4 == 0: isLeap = true; break; } return isLeap; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

توجه داشته باشید که در موارد فوق، مورد «پیش‌فرض» نداریم. و این به این دلیل است که متغیر isLeap را با false مقداردهی اولیه کرده ایم. اگر به تازگی متغیر را بدون مقدار دهی اولیه اعلام کرده بودیم، می توانستیم یک حالت پیش فرض بنویسیم که مقدار false را به isLeap اختصاص می دهد.

همچنین، نسخه فوق کد سوئیچ-مورد کمی طولانی تر است زیرا ما می خواستیم در پایان از یک دستور بازگشت استفاده کنیم و تا آن زمان از انتساب استفاده کردیم. اما اگر آن را مجدداً اصلاح کنیم، یک کد کوتاه تر و سازماندهی شده تر این خواهد بود:

 function isLeapYear(year) { switch (true) { case (year % 400 === 0): return true; case (year % 100 === 0): return false; case (year % 4 === 0): return true; default: return false; } } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

توجه داشته باشید که از آنجایی که اجرای دستور بازگشت در یک تابع به طور خودکار فراخوانی تابع را پایان می دهد، برنامه خطوطی را که به دنبال آن دستور هستند نمی خواند. پس ، در این مثال، لزوماً مجبور نیستیم از دستورهای break استفاده کنیم.

کسر منطقی و زیر مجموعه ها برای ساختار بهتر

با تغییر از حالت کلیدی به منطق if-else، اجازه دهید مقداری استنتاج منطقی انجام دهیم. در منطق if-else قبلی خود، از موارد عمومی به موارد خاص رفتیم. اگر به ترتیب معکوس برویم چه؟ ما در نظر می گیریم که یک سال معین یک سال کبیسه خواهد بود مگر اینکه نفی شود.

پس ، ما با موارد محدودتر صد ساله شروع می کنیم - برای آنها قانون ساده است: برای نفی آنها باید بر 100 بخش پذیر باشند اما نه بر 400 (مانند سال هایی مانند سال های 1700، 1800، 1900).

در این فرآیند، از آنجایی که ما قبلاً پذیرفته ایم سال هایی مانند 2000 (یا سال های قابل بخش بر 400) را به عنوان یک سال کبیسه پذیرفته ایم، آنها را برای بخش پذیری بر 4 آزمایش نمی کنیم (زیرا عددی که بر 400 بخش پذیر باشد به هر حال بر 4 بخش پذیر خواهد بود. خوب).

در مرحله بعد، از آنجایی که فقط سال‌های غیر صد ساله را در نظر می‌گیریم، مواردی را که سال بر 4 تقسیم نمی‌شود (سال‌هایی مانند 2023، 1996 و غیره) به سادگی نفی می‌کنیم.

 function isLeapYear(year) { let isLeap = true; if (year % 100 == 0 && year % 400 != 0) { isLeap = false; } else if (year % 4 != 0) { isLeap = false; } return isLeap; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

در اینجا ببینید، ما ابتدا سال‌های صد ساله و سپس سال‌های غیرصدساله را در نظر می‌گیریم - پس آنها متقابلاً منحصر به فرد هستند - و به همین دلیل است که در چک شرطی دوم به جای if از «اگر-اگر» استفاده می‌کنیم. و در این فرآیند، ما مقداری کارایی را نسبت به بلوک های متوالی اگر بدست می آوریم.

از آنجایی که این رویکرد در مورد تقسیم مسیرهای احتمالی سال کبیسه بودن (یا سال کبیسه نبودن) به زیر مجموعه‌های سال است، بسته به اینکه چگونه سال‌های ممکن را به زیر مجموعه‌ها تقسیم کنیم، می‌توانیم برنامه را به صورت متناوب همانطور که نشان داده شده است بسازیم. زیر:

 function isLeapYear(year) { let isLeap = false; if (year % 400 == 0) { isLeap = true; } else if (year % 100 != 0 && year % 4 == 0) { isLeap = true; } return isLeap; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

پس ، به طور خلاصه، استنباط ما از قانون سال کبیسه این است که سال‌های بخش پذیر بر 400 (مانند 1600، 2000) سال‌های کبیسه هستند و از همه سال‌های دیگر باید بر 4 بخش‌پذیر باشند، اما برای کبیسه بودن بر 100 بخش‌پذیر نباشند. سال

در اتخاذ این رویکرد، ما شرایط ترکیبی داریم و به همین دلیل عملگرهای منطقی (&&، عملگر منطقی AND) را درگیر کردیم. این به ما کمک کرده است که طول عملکرد را کاهش دهیم. به‌جای سه بلوک شرطی، ما در حال حاضر از دو بلوک استفاده می‌کنیم - یک بلوک if و سپس یک دیگری (که در آن شرایط را بیشتر تحلیل می‌کنیم و پس آن را else-if می‌نامیم تا فقط else).

اما اکنون که تقریباً از یک ساختار «اگر-دیگر» استفاده می‌کنیم و همچنین در حال تحلیل عملگرهای منطقی هستیم، بیایید در رویکرد زیر قدرت بیشتری را از عملگرهای منطقی آزاد کنیم.

عملگرهای منطقی که همه شرایط واقعی را ترکیب می کنند

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

 function isLeapYear(year) { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { return true; } else { return false; } } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

این یکی خوب به نظر می رسد زیرا با سازماندهی شرایط مثبت با هم خوانایی را افزایش می دهد. تنها هزینه ای که در اینجا متحمل می شویم این است که شرایط موجود در بلوک if طولانی تر است.

اما با عملگرهای منطقی، از نظر بصری کوتاه تر و پیچیده تر به نظر می رسد (حداقل برای برنامه نویسانی که عادت به ترکیب عملگرهای منطقی مانند این دارند).

تشریح بیشتر، از آنجایی که در رویکرد قبلی گفتیم می‌توانیم زیرمجموعه‌ها را به دو روش مختلف بشکنیم، می‌توانیم دو نسخه مربوط به این رویکرد را نیز داشته باشیم. مورد دوم به شرح زیر است:

 function isLeapYear(year) { if ((year % 100 == 0 && year % 400 != 0) || year % 4 != 0) { return false; } else { return true; } } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

استفاده از نیترو با اپراتور سه تایی

همانطور که در سفر برنامه نویسی-یادگیری خود پیشرفت می کنید، در برخی مواقع، حتماً از کشف امکان نوشتن برنامه های فوق کوتاه خوشحال شده اید.

در حالی که عملگرهای منطقی به ما کمک می کنند تا این کار را انجام دهیم، برای فعال کردن حالت «Nitro»، باید از یک اپراتور سه گانه استفاده کنیم - که اساساً بلوک های if-else ما را یک خط واحد می کند.

 function isLeapYear(year) { return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? true : false; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

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

مدت زیادی است که از آن تمرین اجتناب کرده اید و اکنون آنچه را که باید برگردانید برمی گردانید و فضای حافظه غیر ضروری را برای متغیرهای بی فایده مصرف نکنید.

تبدیل آن به یک تابع پیکان خطی

اکنون که با Nitro تقویت شده اید، تکنیک برنامه نویسی شما مانند یک فلش در حال پیشرفت است و در مأموریتی است که بقایای ES5 را از بین ببرد و شجاعانه به دنیای پس از ES6 پرواز کند. پس از عملکردهای پیکانی با آغوش باز استقبال می کنید.

 const isLeapYear = year => (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)); // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

قبلاً متغیرها را نادیده می‌گرفتید و بلوک‌های «if-else» را نادیده می‌گرفتید. و اکنون، شما حتی می توانید به لطف تابع arrow که یک دستور واحد در بدنه خود دارد، دستور بازگشت را نادیده بگیرید. شما همچنین از پرانتز اطراف آرگومان خود رد می‌شوید زیرا یک آرگومان واحد است.

هنگام خواندن حماسه کد کوتاهتر، باید به این نکته توجه کرد که کد کوتاهتر لزوما کد بهتر نیست. همه چیز به کاربران کد شما بستگی دارد (افرادی که ممکن است آن را بخوانند و احتمالاً با آن همکاری کنند/بهبود دهند).

اگر با برنامه نویسان باتجربه کار می کنید، این سطح از خلاصه خوب است. فقط اطمینان حاصل کنید که از عرض خط فراتر از تعداد مشخصی فاصله (۸۰ کاراکتر توصیه می شود) تجاوز نکنید تا همکاران خود را با نیاز به مدیریت نوارهای اسکرول افقی آزار ندهید.

اما اگر با اعضای تیم با سطوح مختلف تجربه کار می کنید، یا معلمی هستید که با زبان آموزان کار می کند، پس باید از خوانایی کد خود برای همه آگاه باشید.

تغییر پارادایم: برنامه نویسی اعلانی

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

توابع با عوارض جانبی

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

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

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

 function isLeapYear(year) { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { console.log("leap year."); } else { console.log("not leap year."); } } // Example usage: let someValue = isLeapYear(2024); // Output: leap year. console.log(someValue); // Output: undefined

بدیهی است که از مشخصات پیروی نمی کند، زیرا باید مقداری از نوع بولی را برگرداند. البته یک تابع می تواند هر دو را انجام دهد - چاپ و برگرداندن، مانند شکل جایگزین تابع فوق.

 function isLeapYear(year) { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { console.log("leap year."); return true; } else { console.log("not leap year."); return false; } } // Example usage: let someValue = isLeapYear(2024); // Output: leap year. console.log(someValue); // Output: true

اما این واقعیت که آن دو کار را انجام می دهد - برگرداندن یک مقدار و چاپ در کنسول - مشکل است. تابعی باید ساخته شود تا یک کار را برای قابلیت استفاده مجدد مناسب انجام دهد. تابع 'isLeapYear' فقط باید تعیین کند که آیا یک سال یک سال کبیسه است یا خیر. اگر ما نیاز به چاپ چیزی در مورد آن داریم، اجازه دهید مسئولیت انجام عوارض جانبی به عهده برخی دیگر از عملکردهای لاگر باشد.

 // pure function function isLeapYear(year) { if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { return true; } else { return false; } } // functions with side effect function simpleLeapYearLogger(isLeap) { if (isLeap) { console.log("Yes, a leap year!"); } else { console.log("Sorry, not a leap year."); } } function advancedLeapYearLogger(year, isLeap) { if (isLeap) { console.log(`The year ${year} is a leap year!`); } else { console.log(`The year ${year} is not a leap year!`); } } // Example usage: let currYear = 2024; let check2024 = isLeapYear(currYear); // No Output/Side Effect, just retuned value. simpleLeapYearLogger(check2024); // Output: Yes, a leap year! advancedLeapYearLogger(currYear, check2024); // Output: The year 2024 is a leap year!

همانطور که در بالا می بینید، تابع 'isLeapYear' قابل استفاده مجددتر است - با دو مورد استفاده متفاوت در دو تابع لاگر جداگانه. همچنین، اگر در منطق تابع 'isLeapYear' اشتباهی وجود داشت، بدون لمس کد توابع لاگر، رفع آن آسان تر بود.

به طور مشابه، اگر نیاز دارید رشته ثبت شده در کنسول را به گونه ای متفاوت نمایش دهید، می توانید تابع لاگر مربوطه را بدون لمس تابع منطقی سال کبیسه تغییر دهید. پس ، عملکردی که فقط یک کاری را که قرار بود انجام دهد، قابلیت استفاده مجدد و قابلیت نگهداری آن تابع را افزایش می دهد.

اطلاعات بیشتر درباره برنامه نویسی تابعی

در بخش فوق، شما قبلاً وارد فضای برنامه نویسی تابعی شده اید. و اکنون زمان آن فرا رسیده است که عمیق‌تر بگردیم.

اگر عبارت "برنامه نویسی عملکردی" را در ویکی پدیا جستجو کنم، در خط اول آمده است

برنامه نویسی تابعی یک الگوی برنامه نویسی است که در آن برنامه ها با اعمال و ترکیب توابع ساخته می شوند.

عبارت "ترکیب تابع" به معنای ساختن توابع پیچیده از توابع ساده است. در مثال ما، تابع سال کبیسه در حال حاضر بسیار ساده است. اما برای نشان دادن مکانیسم ترکیب تابع، اجازه دهید آن را خارج از توابع جزء ایجاد کنیم.

 // component function function divisible(dividend, divisor) { return dividend % divisor == 0 } // composed function function isLeapYear(year) { let isLeap = false; divisible(year, 4) && (isLeap = true); divisible(year, 100) && (isLeap = false); divisible(year, 400) && (isLeap = true); return isLeap; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear(2023)); // Output: false console.log(isLeapYear(1900)); // Output: false console.log(isLeapYear(2000)); // Output: true

ردیابی جانبی: اتصال کوتاه!

در بالا، شما از یک تابع برای ساخت یک تابع دیگر استفاده می‌کنید - یک رویکرد مبتنی بر مؤلفه که در کتابخانه فرانت‌اند مبتنی بر React JavaScript نیز دنبال می‌کنید.

اما صبر کنید، قبل از اینکه وارد React شویم، وقتی ما از هیچ عبارت if-else استفاده نمی کنیم، «&&» در آن سه خط در تابع «isLeapYear» چه می کند؟

به ارزیابی اتصال کوتاه عملگرهای منطقی خوش آمدید. در آن فرآیند، یک عبارت به محض مشخص شدن نتیجه، ارزیابی آن متوقف می شود. پس اگر دو طرف دارای یک AND منطقی (&&) در بین آنها باشند، اگر طرف اول نادرست باشد، کل عبارت را false می کند - پس طرف دوم را نمی خواند (اجرا نمی کند).

اما اگر طرف اول درست ارزیابی شود، طرف دوم را برای ارزیابی بیشتر می خواند (اجرا می کند). و در این فرآیند، آن تخصیص را در سمت راست && در مثال ما انجام می دهد.

بیشتر بخوانید

جمشید جم درگذشت


به طور مشابه، فرآیندی که OR منطقی (||) درگیر است به این صورت است که اگر سمت چپ به عنوان درست ارزیابی شود، کل عبارت درست است (برای اینکه کل عبارت درست باشد نیاز به یک شرط دارد که برای || درست ارزیابی شود. ). سپس، طرف دوم نادیده گرفته می شود. سمت دوم فقط زمانی خوانده یا اجرا می شود که طرف اول نادرست ارزیابی شود.

شما می توانید از این نوع منطق ارزیابی به عنوان جایگزینی برای تحلیل شرط "اگر" استفاده کنید. برای مثال‌های بیشتر از نحوه کارکرد آن در سناریوهای مختلف، بخش «اتصال کوتاه عملگرهای منطقی (&& و ||)» را در پست وبلاگم بخوانید، جایی که من برخی از تفاوت‌های ظریف اپراتورهای جاوا اسکریپت را مورد بحث قرار داده‌ام.

کپسوله سازی و برنامه نویسی اعلامی

با بازگشت به REACT و کامپوننت ها، ایده ساخت توابع یا اجزای ترکیبی ریشه در نیاز به کپسوله سازی دارد. با کپسوله‌سازی، می‌توانید جزئیات پیچیده را مانند یک کپسول پنهان کنید و به طور مکرر از آن استفاده کنید، بدون اینکه در مورد پیچیدگی زیربنایی آن زحمت زیادی بکشید.

اساساً، شما فقط آنچه را که نیاز دارید را اعلام می کنید (اعلام می کنید) به جای اینکه خود را با حجم کاری و سردردی که چگونه می توانید آن را گام به گام با جملات نوع «این کار» و «آن را انجام دهید» (اجبارات) انجام دهید، تحت فشار قرار دهید.

این به طور خلاصه برای شما برنامه نویسی اعلانی است.

بالاتر و فراتر از کیفیت کد

تاکنون ساختارهای منطقی و پارادایم های برنامه نویسی را پوشش داده ایم، اما اکنون، اجازه دهید به جنبه سوم نگاه کنیم: کیفیت کد.

اعتبارسنجی: فراتر از مشخصات اولیه

الزاماتی که در ابتدا مطرح کردیم، فقط ورودی های معتبر را در نظر گرفتند. اگر تابع با آرگومان هایی فراخوانی شود که آرگومان های ایده آل نیستند - مانند یک غیر عدد، یا حتی اگر یک عدد اما غیر صحیح باشد؟

برای رسیدگی به آن، می‌توانیم منطق اعتبارسنجی بسازیم. برای ایجاد منطق اعتبارسنجی، باید به تمام راه‌های مختلفی فکر کنید که ممکن است مقدار ورودی (آگومان ارسال شده به تابع شما) برای شما قابل اجرا نباشد.

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

 function isLeapYear(year) { if (typeof year!="number" || year % 1 != 0 || year <= 0) return undefined; return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? true : false; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear("TwentyTwentyFour")); // Output: undefined console.log(isLeapYear(2023.99)); // Output: undefined console.log(isLeapYear(0)); // Output: undefined console.log(isLeapYear(-1)); // Output: undefined console.log(isLeapYear("2024")); // Output: undefined

اما اگر به دقت توجه کرده باشید، در تحلیل منطقی سال کبیسه، ما به جای برابری دقیق (===) برابری معمولی (==) را ارزیابی کرده ایم. ما نمی‌توانیم از مزایای آن برای ورودی قالب رشته‌ای برای سالی مانند "2024" بهره ببریم.

اگر قصد ما پذیرش دقیق یک عدد باشد، نوع اعتبار سنجی که نوشتیم خوب است، و پس از آن استفاده از === حتی مناسب تر است.

اما اگر از سوی دیگر، بخواهیم مقادیری مانند "2024" را بپذیریم، باید منطق اعتبار سنجی خود را مانند این افزایش دهیم:

 function isLeapYear(year) { if (isNaN(Number(year)) || year % 1 != 0 || year <= 0) return undefined; return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? true : false; } // Example usage: console.log(isLeapYear(2024)); // Output: true console.log(isLeapYear("TwentyTwentyFour")); // Output: undefined console.log(isLeapYear(2023.99)); // Output: undefined console.log(isLeapYear(0)); // Output: undefined console.log(isLeapYear(-1)); // Output: undefined console.log(isLeapYear("2024")); // Output: true

تست کردن آن از بیرون

در دو بلوک کد بالا کد خود را می نویسیم و در همان مکان تست می کنیم. اما کدی که وارد مرحله تولید می‌شود، فرصتی برای گنجاندن گزارش‌های کنسولی که ما به‌طور گسترده برای نشان دادن «مثلا استفاده» در بلوک‌های کد بالا استفاده کرده‌ایم را نخواهد داشت.

اینجاست که تست واحد وارد می شود. در تست واحد، ابتدا تابع را برای استفاده در مکان های دیگر (فایل ها) صادر می کنیم، سپس آن تابع را در یک فایل آزمایشی وارد می کنیم. در آن فایل آزمایشی جایی است که ما تست را اجرا می کنیم، موارد خود را می سازیم و در نهایت آن فایل آزمایشی را برای اجرای آن تست ها اجرا می کنیم.

من از پکیج Jest برای انجام این تست واحد استفاده کرده ام و در اینجا کد فایل ایندکس و فایل اسکریپت تست من است:

index.js

 function isLeapYear(year) { if (isNaN(Number(year)) || year % 1 != 0 || year <= 0) return undefined; return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ? true : false; } module.exports = isLeapYear;

index.test.js

 const isLeapYear = require('./index.js'); describe('Test isLeapYear', () => { it('should return true for leap year', () => { expect(isLeapYear(2020)).toBe(true); }); it('should return false for non-leap year', () => { expect(isLeapYear(2023)).toBe(false); }); it('should return undefined for invalid input', () => { expect(isLeapYear('TwentyTwentyFour')).toBe(undefined); expect(isLeapYear('2023.99')).toBe(undefined); expect(isLeapYear('0')).toBe(undefined); expect(isLeapYear('-1')).toBe(undefined); }); it('should return true for a leap year in string format', () => { expect(isLeapYear("2024")).toBe(true); }); });

من Jest را با استفاده از دستور npm i jest نصب کردم. سپس، من jest به عنوان یک مقدار برای test در شی scripts داخل فایل package.json خود اضافه کردم. سپس، همانطور که من npm test اجرا کردم، تمام موارد تست من را گذراند، مانند این:

اسکرین شات-2024-02-29-05.25.03
خروجی تست

اگر می‌خواهید این کد تست واحد را تغییر دهید و امتحان کنید، می‌توانید از این پروژه تکراری استفاده کرده و فورک کنید.

پینوشت

بسیاری از مفاهیم برنامه نویسی را در تمرین بالا تحلیل کرده ایم. و یک نکته کلیدی این است که یک برنامه را می توان به چند روش نوشت.

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

و به هر حال، اگر می‌پرسید چرا سال‌های کبیسه داریم، این برای شماست: زمانی که زمین طول می‌کشد تا یک دور به دور خورشید بچرخد دقیقاً ۳۶۵ روز (یا ۳۶۵×۲۴ ساعت) نیست، بلکه تقریباً یک چهارم است. یک روز اضافه

این فرآیند ممکن است شما را به یاد اپراتور مدول بیاندازد که با نماد % نمایش داده می شود که باقیمانده یک عملیات تقسیم را برمی گرداند. در اینجا، زمان تقریبی (بر حسب ساعت) که برای یک دور زمین صرف می شود، بر 24 ساعت (یعنی یک روز) تقسیم می شود. حدود 6 ساعت باقیمانده می دهد.

 const approxTimeHrsRev = 8766; const hrsPerDay = 24; let completedDaysEachYear; let remainderHrsPerYear = 8766 % hrsPerDay; completedDaysEachYear = (approxTimeHrsRev - remainderHrsPerYear) / hrsPerDay; console.log(`After ${completedDaysEachYear} complete days, there is still about ${remainderHrsPerYear} hours left out each year.`); // Output: After 365 complete days, there is still about 6 hours left out each year.

برای محاسبه آن ساعت‌های از دست رفته، باید تقویم‌هایمان را هر چهار سال یک‌بار تنظیم کنیم، زمانی که آن بخش‌های حذف‌شده جمع شوند تا - دوباره تقریباً - یک روز باشد.

در نهایت، چون دقیقاً 6 ساعت نیست، و کمی بیشتر از آن، باید هر 100 و 400 سال دیگر را تنظیم کنیم.

خبرکاو