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

هش کردن فرایندی است که در طی آن یک رشته یا Hash از یک پیام مفروض با استفاده از یک تابع ریاضی ساخته میشود. این تابع ریاضی به نام «تابع هش رمزشناختی» (Cryptographic Hash Function) نامیده میشود. با این که چند تابع هش هم اینک وجود دارند، اما توابعی که برای هش کردن رمزهای عبور طراحی شدهاند باید چهار مشخصه زیر را داشته باشند تا امن محسوب شوند:
یک تابع هش که هر چهار مشخصه فوق را داشته باشد، گزینه خوبی برای هش کردن رمزهای عبور است، زیرا مجموعه مشخصههای فوق موجب میشوند که مهندسی معکوس رمز عبور از روی هش دشوار باشد.
ضمناً، تابعهای هش کردن رمزهای عبور باید کُند باشند، چون الگوریتمهای سریع مستعد حملات brute force هستند که در طی آن مهاجمان تلاش میکنند یک رمز عبور را با هش کردن و مقایسه میلیاردها یا تریلیونها رمز عبور بالقوه در طی هر ثانیه حدس بزنند.
برخی تابعهای هش عالی که همه معیارهای فوق را دارند شامل PBKDF2 ،BCrypt و SCrypt هستند. اما ابتدا باید برخی الگوریتمهای قدیمیتر را بررسی کنیم تا ببینیم چرا استفاده از آنها دیگر توصیه نمیشود.
نخستین تابع هش که بررسی میکنیم، الگوریتم «چکیده پیام» (message-digest) به نام MD5 است که در سال 1992 توسعه یافته است. متد MessageDigest جاوا امکان محاسبه آن را ساده کرده است و هنوز در برخی موارد میتواند مفید باشد. با این وجود، در طی سالهای اخیر ثابت شده است که MD5 مشخصه چهارم هش کردن رمزهای عبور را که در بخش قبلی اشاره کردیم ندارد. چون از نظر محاسباتی تولید تصادم بسیار راحت است. علاوه بر آن MD5 یک الگوریتم سریع است و از این رو در برابر حملههای brute-force کارایی ندارد.
در این بخش نگاهی به SHA-512 خواهیم داشت که بخشی از خانواده الگوریتم هش امن است. این خانواده با معرفی SHA-0 در سال 1993 آغاز شده است.
از آنجا که توان رایانهها به صورت مداوم افزایش مییابد و هر روز آسیبپذیریهای جدید را کشف میکنیم، محققان نسخههای جدیدی از SHA را طراحی کردهاند. نسخههای جدیدتر به تدریج طول بیشتری دارند و در برخی موارد محققان نسخه جدید را بر مبنای الگوریتم قبلی منتشر میسازند.
SHA-512 طولانیترین کلید را در نسل سوم از الگوریتمهای هش دارد. با این که در حال حاضر نسخههای امنتری از SHA ارائه شدهاند، اما SHA-512 قویترین نسخهای است که در جاوا پیادهسازی شده است.
در این بخش به بررسی پیادهسازی الگوریتم هش کردن SHA-512 در جاوا میپردازیم. ابتدا باید مفهوم Salt را درک کنیم. Salt به بیان ساده یک دنباله تصادفی است که برای هر هش جدید تولید میشود.
با استفاده از این خصوصیت تصادفی بودن میزان آنتروپی هش بالا میرود و پایگاه داده خود را در برابر لیستهای از قبل تهیه شده از هشها که به نام «جداول رنگینکمان» (rainbow tables) شناخته میشوند مقاومتر میسازیم. تابع هش جدید ما به طور تقریبی به صورت زیر است:
برای استفاده از Salt از کلاس SecureRandom از java.security استفاده میکنیم:
سپس از کلاس MessageDigest برای پیکربندی تابع SHA-512 با Salt خود کمک میگیریم:
بدن ترتیب اینک میتوانیم از متد digest برای تولید رمز عبور هش شده جدید خود بهره بگیریم:
حتی زمانی که از Salt استفاده میکنیم، SHA-512 همچنان یک گزینه متوسط محسوب میشود، اما گزینههای قویتر و کُندتری نیز وجود دارند. ضمناً گزینههای دیگر که در ادامه بررسی خواهیم کرد، یک ویژگی مهم به نام «قدرت قابل پیکربندی» (Configurable Strength) دارند.
PBKDF2 ،BCrypt و SCrypt سه الگوریتم هش کردن هستند که استفاده از آنها توصیه میشود.
هر کدام از این الگوریتمها کند هستند و هر کدام یک ویژگی مشترک عالی به نام قدرت قابل پیکربندی دارند. این بدان معنی است که با افزایش توان محاسباتی رایانهها میتوانیم الگوریتم را از طریق تغییر دادن ورودیها کندتر کنیم.
اکنون Salt-ها مفهومی بنیادی در زمینه هش کردن رمزهای عبور محسوب میشوند و از این رو باید یکی از آنها را برای PBKDF2 نیز استفاده کنیم:
سپس یک PBEKeySpec و یک SecretKeyFactory ایجاد میکنیم که با استفاده از الگوریتم PBKDF2WithHmacSHA1 وهلهسازی میشوند:
پارامتر سوم (65536) به طرز مؤثری پارامتر قدرت است. این پارامتر تعیین میکند که این الگوریتم برای چند بار تکرار خواهد شد و بدین ترتیب زمان مورد نیاز برای تولید هش افزایش مییابد. در نهایت میتوانیم از SecretKeyFactory برای تولید هش استفاده کنیم:
تا به این جا مشخص شد که BCrypt و SCrypt هنوز در جاوا پشتیبانی نمیشوند؛ اما برخی کتابخانههای جاوا از آنها پشتیبانی میکنند. یکی از این کتابخانهها Spring Security نام دارد.
با این که جاوا به صورت بومی از هر دو الگوریتم هش کردن PBKDF2 و SHA پشتیبانی میکند، اما الگوریتمهای BCrypt و SCrypt همچنان پشتیبانی نمیشوند.
خوشبختانه کتابخانه Spring Security از طریق اینترفیس PasswordEncoder از هر سه این الگوریتمهای توصیه شده پشتیبانی میکند، بنابراین:
انکودرهای رمز عبور برای PBKDF2 ،BCrypt و SCrypt همگی از پیکربندی قدرت مطلوب هش رمز عبور پشتیبانی میکنند. حتی بدون وجود یک اپلیکیشن مبتنی بر Spring Security میتوان از این انکودرها به صورت مستقیم استفاده کرد. همچنین اگر از سایت خود با استفاده از Spring Security حفاظت میکنید در این صورت میتوانید انکودر رمز عبور مطلوب خود را از طریق DSL آن یا از طریق تزریق وابستگی پیکربندی کنید.
برخلاف مثالهای قبلی که مطرح شدند، این الگوریتمها Salt را برای ما به صورت داخلی تولید میکنند. الگوریتم Salt را درون هش خروجی برای استفادههای بعدی در زمان اعتبارسنجی رمز عبور ذخیره میکند.
بدین ترتیب در این مطلب بررسی نسبتاً عمیقی در مورد روشهای هش کردن رمز عبور داشتیم و به کاوش مفاهیم و کاربردهای آنها پرداختیم. همچنین نگاهی به برخی تابعهای قدیمی هش کردن داشتیم و برخی انواع که هم اینک پیادهسازی شدهاند را پیش از کدنویسی در جاوا بررسی کردیم. در نهایت دیدیم که کتابخانه Spring Security با کلاسهای رمزنگاری خاص خود، طیفی از تابعهای هش کردن مختلف را در جاوا پشتیبانی میکند.
منبع: فرادرس
در بخش قبلی این سری مقالات آموزش ساخت یک اپلیکیشن برای آیفون سه آیتم برگهای با عناوین «News» ،«Second» و «Chat» داشتیم، اما تاکنون به آیتم برگهای «Second» نپرداختهایم. در این مطلب قصد داریم این را به بخش «Products» اپلیکیشن خود تبدیل کنیم که در آن کاربران اپلیکیشن میتوانند برخی آیتمها را خریداری کند. اکنون که با بخش Attributes Inspector در Xcode آشنا شدهایم، میتوانیم اقدام به تکمیل گردش ناوبری بین صحنههای مختلف (Senses) بکنیم. پیش از شروع و برای مطالعه بخش قبلی مطلب میتوانید به لینک زیر مراجعه کنید:
اگر اپلیکیشن در حال حاضر در شبیهساز اجرا نشده است، آن را اجرا کنید. در اپلیکیشن در حال اجرا در شبیهساز، روی سلول فوقانی در صحنه News ضربه بزنید. همان طور که میبینید این سلول هایلایت میشود، اما هیچ اتفاقی نمیافتد. کاربر ممکن است انتظار داشته باشد که با ضربه زدن روی این سلول موجب شود تا یک انتقال در اپلیکیشن صورت بگیرد و به یک صحنه برود که جزییات بیشتری در مورد آن مقاله خبری دارد. در این بخش میخواهیم به شیوه ساخت آن صحنه تفصیلی بپردازیم.
به Xcode بازگردید و به فایل Main.storyboard بروید. روی دکمه Library کلیک کنید. یک عنصر جدید Table View Controller را به سمت راست صحنه News کشیده و رها کنید.

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

بدین ترتیب Xcode مانند تصویر فوق فهرستی بازشدنی از انواع Seuge-های مختلف نمایش میدهد.

توجه کنید که بخشهای Selection Segue و Accessory Action وجود دارند که هر دو لیست مشابهی از آیتمها را دارند.
گزینه show را زیر عنوان نخست یعنی Selection Segue انتخاب کنید تا Xcode صحنهها را به هم متصل سازد. از آنجا که گزینه نمایش seuge انتخاب شده است، Xcode میداند که باید یک صحنه جدید را در همان کنترلر ناوبری قبلی مربوط به صحنه لیست اخبار نمایش دهد. از این رو Xocde همان نوار ناوبری را در پیشنمایش استوریبورد نمایش میدهد. همچنین دکمه بازگشت را به صورت خودکار نمایش میدهد و عنوان آن همان عنوان صحنه قبلی است.

دکمه بازگشت News آبی رنگ است که روی نوار ناوبری قرمز اصلاً خوانا نیست. ما باید آن را به سفید تغییر دهیم.
بدین منظور باید نوار ناوبری را طوری تنظیم کنیم از رنگ سفید به عنوان «روشنای رنگ» (Tint Color) استفاده کند و آن را جایگزین رنگ پیشفرض آبی قرار دهد. در این حالت همه آیتمهای دکمههای نوار ناوبری مانند دکمه بازگشت از همان روشنای رنگ استفاده خواهند کرد. روی کنترلر ناوبری News اسکرول کنید و نوار ناوبری را انتخاب کنید.

پنل Attributes Inspector اینک عنوان Navigation Bar را در بخش فوقانی نمایش میدهد. به سمت پایین و عنوان View اسکرول کنید.
مانند اغلب کامپوننتهای نمایشی، نوار ناوبری یک زیرکلاس خاص از یک «نما» (View) یا به عبارت دقیقتر یک UIView است. به همین دلیل است که علاوه بر خصوصیتهایی که مختص یک نوار ناوبری هستند، پنل Attributes Inspector همه خصوصیتهای عمومیتر که برای نماها وجود دارند را نیز زیر بخش View نمایش میدهد. ما در مورد زیرکلاسها در ادامه و در بخش نهم این سری مقالات آموزشی صحبت خواهیم کرد. فعلاً باید Tint Color را از مقدار Default به White color تغییر دهید.

در ادامه مجدداً به بخش canvas اسکرول کنید تا این بار جزییات جدیدی از نمای News را ببینید. در این زمان دکمه بازگشت باید به جای آبی، سفید شده باشد. با این وجود، Xcode بهظاهر یک باگ دارد که در این وضعیت رفرش نمیشود. برای این که آن را وادار به رفرش بکنید، میتوانید با کلیک روی چیزی در صحنه جدید آن را انتخاب کنید.

این صحنه جدید یک آیتم ناوبری دارد. آیتمهای ناوبری قبلی که استفاده میکردیم، همگی از سوی Xcode و زمان اتصال دادن نمای اول به کنترل ناوبری ایجاد شده بودند. اما هنگامی که یک صحنه جدید را از طریق Show seuge اتصال میدهیم، Xcode آیتم ناوبری برای صحنه ایجاد نمیکند. در برخی موارد به چنین چیزی نیاز هم نداریم؛ اما این بار به یک آیتم ناوبری نیاز داریم تا بتوانیم Large Title را برای این صحنه غیر فعال کنیم.
روی دکمه library کلیک کنید و عبارت nav را در نوار جستجو وارد کنید. Xcode سه بخش از گردش کار کنترل ناوبری را که قبلاً بررسی کردیم نمایش میدهد.

گزینه Navigation Item را از Library به صحنه جزییات News بکشید. در این حالت، مهم نیست که آن را در این صحنه به کجا میکشید، چون Xcode میداند که تنها میتواند در بخش فوقانی و درون نوار ناوبری ظاهر شود.

عنوان پیشفرض آیتم جدید ناوبری Title است. در این حالت ما نیازی به عنوان نداریم، چون سلول عنوان مقاله خبری خاص را نمایش خواهد داد. از این رو به فضای عنوان بزرگی هم نیاز نداریم. آیتم ناوبری جدید از قبل در پنل Attributes Inspector انتخاب شده است، بنابراین متن Title را انتخاب و حذف کنید. منوی بازشدنی Large Title را از Automatic به Never تغییر دهید.

اینک نوبت آن رسیده است که سلولهای کانتینر را برای سلول جزییات خبر تنظیم کنید. یک بار روی ناحیه خاکستری Table View در سلول News کلیک کنید. در پنل Attributes Inspector، مقدار Content را از Dynamic Prototypes به Static Cells تغییر دهید.

ما فعلاً سلول نخست را خالی میگذاریم. در ادامه تصویر بزرگی را به آن اضافه خواهیم کرد. روی سلول دوم (میانی) کلیک کنید. کلید Shift را نگه داشته و روی سلول سوم کلیک کنید تا هر دو مورد انتخاب شوند. در پنل Attributes Inspector مقدار Style را به Basic تغییر دهید.

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

اگر در حال حاضر اپلیکیشن را اجرا کنیم و به صحنه جزییات خبر رفته و روی این سلولها ضربه بزنیم، خواهیم دید که هایلایت میشوند. بدین ترتیب ممکن است این تصور نادرست در کاربر ایجاد شود که با ضربه زدن روی این سلولها اقدامی صورت میگیرد. بنابراین باید این وضعیت را اصلاح کنیم. روی پسزمینه بوم (و نه هیچ کدام از صحنهها) کلیک کنید تا همه چیز از حالت انتخاب در بیاید. یک بار روی یکی از سلولها در صحنه جزییات خبر کلیک کنید. در منوی Edit گزینه Select All را انتخاب کنید.

همان طور که میبینید سه سلول انتخاب شدهاند. در پنل Attributes Inspector مقدار Selection را از Default به None تغییر دهید.

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

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

صحنه Second را با ضربه زدن روی کلید Delete کیبورد حذف کنید. بدین ترتیب Xcode صحنه Seocnd و seuge-های آن را حذف میکند.

یک مستطیل روی بوم بکشید که روی همه صحنهها در گردش News قرار گیرد.

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

همزمان با گرفتن کلید Control کیبورد، با استفاده از ماوس کنترلر نوار برگهای را گرفته و بکشید تا کنترلر ناوبری کپی شود.

از لیست suege-ها گزینه view controllers را در زیر عنوان elationship Segue انتخاب کنید.

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

روی عنوان آیتم نوار برگهای News در این نسخه کپی شده دو بار کلیک کنید و عنوان را به Products تغییر دهید. دکمه اینتر یا Return را بزنید تا ذخیره شود.

دقت کنید که در کنترلر نوار برگهای، آیتم برگه Prosducts گزینه آخر است، چون آخرین موردی است که به کنترلرهای نمای خود افزوده شده است. ما باید آن را به حالت قبل بازگردانیم تا Chat به عنوان آخرین گزینه نمایش یابد.
در کنترلر نوار برگهای، آیتم Chat را به سمت راست آیتم Products بکشید.

هنگامی که آن را رها کنید، آیکونهای آیتم نوار برگهای باید در جای خود ثابت شود.

آیتمهای نوار برگهای در حال حاضر دارای رنگ آبی پیشفرض هستند. در ادامه رنگ آنها را به رنگ قرمز اپلیکیشن تغییر میدهیم. به این منظور روی نوار برگهای در کنترلر نوار برگهای کلیک کنید.

در پنل Attributes Inspector مقدار Image Tint را به رنگ قرمز که قبلاً برای نوار ناوبری انتخاب کردهایم، تغییر دهید.


صحنههای Products همچنان چند برچسب دارند که دارای متن تکراری از News هستند و باید آنها را اصلاح کنیم.

در گردش کار کپی شده، روی عنوان آیتم ناوبری News کلی کنید تا متن آن انتخاب شود و عبارت Products را وارد کنید تا جایگزین آن شود.

دکمه اینتر یا Return را بزنید؛ اینک میبینید که نهتنها عنوان آیتم ناوبری در این صحنه؛ بلکه عنوان دکمه بازگشت در صحنه بعدی نیز تغییر یافتهاند.

متن موجود در برچسبهای هر سلول را انتخاب کرده و برخی متون برای محصولهای ساختگی از خودتان وارد کنید.

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

در پنل Attributes Inspector اقدامات زیر را انجام دهید:

صحنه Chat تنها شامل یک سلول است که خالی است. در این بخش از آن چه تاکنون آموختهایم استفاده میکنیم و یک متن ساده برای آن تعیین میکنیم. به این منظور سلول را انتخاب کرده و Style را از Custom به Subtitle تغییر دهید.

مقدار Selection را برای سلول از Default به None تغییر دهید تا در زمان تپ کردن کاربر هایلایت نشود.

نمای جدولی را انتخاب کرده و مقدار Content آن را به صورت سلولهای Static تعیین کنید:

متن Chat سادهای را برای هر سلول وارد کنید که نام در بخش عنوان و توضیح در بخش عنوان فرعی قرار میگیرد.

اپلیکیشن را اجرا کنید و از وجود موارد زیر مطمئن شوید:
به Xcode بازگردید و مانند قبل کارهای زیر را انجام دهید:
Added detail scene to News. Added Products list with detail scene. Set tab bar tint color. Added chat messages.
گردش کار اپلیکیشن ما اینک به صورت زیر و کاملاً مناسب به نظر میرسد.

ما یک seuge نمایشی را برای کاربر اتصال دادهایم تا بتواند به جزییات مطالب دسترسی پیدا کند و یک دکمه بازگشت برای رجوع به صفحه قبلی تدارک دیدهایم. یک انیمیشن نرم نیز بین صحنهها وجود دارد که از راهنماییهای اینترفیس انسانی اپل (+) پیروی میکند. شاید اینک اگر کمی تأمل کنید، متوجه شوید که روشهای مستقیمتری نیز برای ایجاد برخی چیزها وجود داشته است؛ اما تلاش ما در این سری مقالات این بوده است که تکنیکهای مختلف را معرفی کنیم.
شما میتوانید هر گونه نظر یا پیشنهاد خود را در بخش نظرات در ادامه مطرح کنید. در بخش بعدی این سری مقالات در مورد استفاده از آیکونهای سفارشی برای آیتمهای نوار برگهای و تصاویر برای سلولها صحبت خواهیم کرد. برای مطالعه بخش بعدی این سر مقالات آموزشی به لینک زیر مراجعه کنید:
منبع: فرادرس
تصادفی بودن، بخش مهمی از «پیکربندی» (Configuration) و «ارزیابی» (Evaluation) الگوریتمهای «یادگیری ماشین» (Machine Learning) را تشکیل میدهد. از مقداردهی اولیه تصادفی وزنها در «شبکه عصبی مصنوعی» (Artificial Neural Network) تا بخشبندی دادهها به صورت تصادفی به دو دسته «مجموعه تست» (Test Set) و «مجموعه آموزش» (Train Set) و همچنین، «برُ زدن تصادفی» (Random Shuffling) مجموعه داده تست در «گرادیان نزولی تصادفی»، همه و همه نیاز به تسلط بر مهارت تولید اعداد تصادفی و استفاده از تصادفی بودن دارند. در این مطلب، روش تولید اعداد تصادفی در پایتون مورد بررسی قرار خواهد گرفت. در ادامه، مباحث زیر بررسی خواهند شد:
مفاهیم بیان شده، در سه بخش کلی مولدهای اعداد شبه تصادفی، اعداد تصادفی با پایتون و اعداد تصادفی با «نامپای» (NumPy) شرح داده میشوند.
منبع تصادفی بودنی که در برنامهها و الگوریتمها تزریق میشود، ترفند ریاضیاتی است که مولد اعداد تصادفی نامیده میشود. مولد اعداد تصادفی، سیستمی است که اعداد تصادفی را از یک منبع درست از تصادفی بودن تولید میکند. معمولا، یک ابزار اندازهگیری پدیده فیزیکی مانند «شمارشگر گایگر مولر» (Geiger-Müller Counter)، جایی است که نتایج به اعداد تصادفی مبدل میشوند. در یادگیری ماشین، به تصادفی بودن نیازی نیست. بلکه، از اعداد «شبه تصادفی» (Pseudorandom) استفاده میشود. اعداد شبه تصادفی، نمونهای از اعداد محسوب میشوند که بسیار شبیه به اعداد تصادفی هستند، اما با استفاده از یک فرایند قطعی تولید شدهاند.
در بُر زدن دادهها و مقداردهی اولیه ضرایب با مقادیر تصادفی از مولدهای اعداد شبه تصادفی استفاده میشود. این برنامههای کوچک، اغلب توابعی هستند که پس از فراخوانی شدن، یک عدد تصادفی را باز میگردانند. در صورت فراخوانی مجدد، این برنامهها یک عدد تصادفی جدید را باز میگردانند. توابع پوششی (Wrapper Functions) نیز موجود هستند و به کاربر این امکان را میدهند که عدد تصادفی را به صورت «عدد صحیح» (Integer)، «ممیز شناور» (Floating Point)، با یک توزیع احتمالی خاص و یا در یک طیف خاص دریافت کند.
ارقام مربوط به اعداد تصادفی طبق یک توالی ایجاد میشوند. توالی قطعی است و با یک مقدار اولیه «دانه» (seed) آغاز میشود. اگر کاربر صراحتا به مولد اعداد تصادفی دانه ندهد، امکان دارد مولد از زمان سیستم به ثانیه یا میلیثانیه به عنوان دانه استفاده کند. مقدار دانه اهمیتی ندارد. کاربر میتواند هر آنچه میخواهد را انتخاب کند. آنچه اهمیت دارد این است که دانه دادن یکسان به فرایند، منجر به توالی مشابهی از اعداد تصادفی میشود. در ادامه، مثالهایی در این مورد ارائه میشوند.
کتابخانه استاندارد پایتون، ماژولی با عنوان random فراهم میکند که دارای دستهای از توابع برای تولید اعداد تصادفی است. پایتون از یک مولد اعداد تصادفی مستحکم و محبوب به نام Mersenne Twister بهره میبرد که یک توالی از اعداد تقریبا تصادفی را تولید میکند. این مولد، پارامتری را برای آغاز توالی دریافت میکند که «دانه» (seed) نامیده میشود. تابع قطعی است، بدین معنا که دادن یک دانه مشابه، هر بار منجر به تولید توالی مشابهی از اعداد میشود. انتخاب دانه اهمیتی ندارد.
تابع ()seed به مولد اعداد تصادفی، دانه میدهد و یک عدد صحیح مانند ۱ یا ۷ را به عنوان آرگومان دریافت میکند. اگر تابع ()seed پیش از استفاده از تابع تصادفی بودن فراخوانی نشود، به طور پیشفرض، از زمان سیستم به میلیثانیه از مبدا تاریخ موجود در بایوس (۱۹۷۰) برای تولید اعداد تصادفی استفاده میشود. مثال زیر، چگونگی دانه دادن به مولد عدد تصادفی را نشان میدهد که بر اثر آن، اعداد تصادفی تولید میشوند. همچنین، نشان میدهد که دانه دادن مجدد به مولدها منجر به تولید توالی مشابهی از اعداد تصادفی میشود.
با اجرای مثال بالا، به مولد اعداد تصادفی دانه با مقدار ۱ داده میشود؛ مولد، ۳ عدد تصادفی تولید میکند، مجددا به مولد دانه داده میشود و همانطور که مشهود است، سه عدد تصادفی مشابه تولید میشوند.
تنظیم کردن دانه، برای کنترل تصادفی بودن و حصول اطمینان از اینکه کد هر بار از توالی مشابهی استفاده میکند، درست مانند مدل تولید، بسیار مفید خواهد بود. برای اجرای آزمایشهایی که در آنها تصادفی بودن برای کنترل متغیرهای مداخلهگر مورد استفاده قرار میگیرد، امکان دارد یک دانه متفاوت برای هر اجرای آزمایشی مورد استفاده قرار بگیرد.
مقادیر ممیز شناور تصادفی، با استفاده از تابع ()random قابل استفاده هستند. مقادیر در بازه بین صفر و یک و به ویژه در بازه (0,1] تولید میشوند. مقادیر از یک «توزیع یکنواخت» (Uniform Distribution) حاصل میشوند، بدین معنا که هر مقدار دارای شانس برابری برای تولید شدن دارد. مثال زیر، ۱۰ مقدار تصادفی ممیز شناور را تولید میکند.
با اجرای قطعه کد بالا، مقادیر تصادفی ممیز شناور، تولید و چاپ میشوند.
0.1343642441124012 0.8474337369372327 0.763774618976614 0.2550690257394217 0.49543508709194095 0.4494910647887381 0.651592972722763 0.7887233511355132 0.0938595867742349 0.02834747652200631
مقادیر ممیز شناور را میتوان با ضرب کردن آنها با اندازه دامنه تغییرات (range) و افزودن مقدار کمینه (min) به آن، تغییر مقیاس داد.
در کد بالا، min و max، به ترتیب مقادیر کمینه و بیشینه از یک طیف دلخواه هستند، و value مقداری است که توسط روند تولید اعداد تصادفی در فاصله ۰ تا ۱ تولید شده است.
مقادیر صحیح تصادفی را میتوان با تابع ()randint تولید کرد. این تابع، دو آرگومان دریافت میکند؛ شروع و پایان طیف برای مقادیر صحیح تولید شده، دو آرگومان دریافتی هستند. اعداد صحیح تصادفی در طیف مقادیر شروع و پایان و با احتساب خود این مقادیر (بازه بسته [start, end]) تولید میشوند. مثال زیر، ۱۰ عدد تصادفی را بین ۰ و ۱۰ تولید میکند.
با اجرای قطعه کد بالا، ده عدد تصادفی صحیح تولید میشود.
مقادیر ممیز شناور تصادفی را میتوان از یک توزیع گاوسی با استفاده از تابع ()gauss ایجاد کرد. این تابع، دو آرگومانی که متناظر با پارامترهای توزیع نرمال (گاوسی) یعنی میانگین و انحراف معیار هستند را دریافت میکند.. مثال زیر، ۱۰ عدد تصادفی از توزیع گاوسی را با میانگین ۰.۰ و انحراف معیار ۱.۰ تولید میکند. توجه به این نکته لازم است که این پارامترها محدودیتی روی مقادیر ایجاد نمیکنند و توزیع مقادیر بر اساس شکل زنگی (زنگولهای)، توزیع کنترل میشود؛ از آنجایی که شکل توزیع نرمال، متقارن است، برای مقادیر مثبت و منفی (بزرگتر و کوچکتر از صفر) احتمال تولید عدد تصادفی یکسان است.
با اجرای قطعه کد مثال بالا، ۱۰ مقدار گاوسی تصادفی ایجاد میشود.
1.2881847531554629 1.449445608699771 0.06633580893826191 -0.7645436509716318 -1.0921732151041414 0.03133451683171687 -1.022103170010873 -1.4368294451025299 0.19931197648375384 0.13337460465860485
اعداد تصادفی برای انتخاب تصادفی یک آیتم از لیست نیز قابل استفاده هستند. برای مثال، اگر یک لیست از حروف a, b, c, d در اختیار داشته باشیم و بخواهیم به شکل تصادفی یک کلمه ۱۰ حرفی (با جایگذاری) ایجاد کنیم از تابع ()choice استفاده خواهیم کرد. انتخابها با «توزیع یکنواخت» (Uniform Likelihood) انجام میشوند. قطعه کدی که در زیر آمده، این کار را انجام داده است و حروف مربوط به یک کلمه ۱۰ حرفی را از لیست حروف داده شده، ایجاد کرده است.
با اجرای مثال بالا، ابتدا یک لیست از مقادیر صحیح چاپ میشود، سپس، پنج مقدار تصادفی از میان آیتمهای این لیست انتخاب و چاپ میشوند. البته از آنجایی مقدار (1)seed داده شده، با تکرار برنامه همین حروف ایجاد خواهد شد.
کاربر ممکن است تمایل به تکرار فرایند انتخاب مقادیر تصادفی از لیست داشته باشد تا یک زیرمجموعه انتخاب شده به صورت تصادفی بسازد. مهمتر آنکه، هنگامی که یک عنصر از لیست انتخاب و به زیر مجموعه اضافه شد، مجددا نباید اضافه شود. به این کار، انتخاب بدون جایگذاری گفته میشود، زیرا هنگامی که یک عنصر از لیست برای زیر مجموعه انتخاب شد، مجددا به لیست اصلی اضافه نمیشود (برای انتخاب مجدد موجود نیست). این رفتار، توسط تابع ()sample ارائه میشود که طی آن، لیست و اندازه زیرمجموعه به select به عنوان آرگومان داده میشود. توجه به این نکته لازم است که آیتمها از لیست اصلی حذف نشدهاند، بلکه، تنها در یک کپی از لیست انتخاب شدهاند. مثال زیر چگونگی انتخاب یک زیر مجموعه حاوی سه عنصر را از لیستی حاوی چهار حرف اولی لاتین، نشان میدهد.
با اجرای قطعه کد بالا، ابتدا یک لیست از مقادیر صحیح چاپ و سپس، یک نمونه تصادفی از آن انتخاب و چاپ میشود.
از تصادفی بودن میتوان برای بُر زدن تصادفی یک لیست از عناصر مانند بُر زدن ورقهای بازی استفاده کرد. از تابع ()shuffle میتوان برای انجام این کار، استفاده کرد. shuffle دادهها را در جای خودشان بُر میزند، بدین معنا که لیست به عنوان یک آرگومان برای تابع ()shuffle ارائه، بر زده و باز گردانده میشود. در حقیقت این تابع لیست بر زده شده را به جای کپی در یک لیست جدید، به شکل درجا ایجاد میکند. مثال زیر چگونگی بر زدن تصادفی یک لیست از اعداد صحیح را نشان میدهد. همانطور که میبینید تابع (shuffle(sequence به متغیر دیگری نسبت داده نشده و حاصل اجرای این تابع (یعنی بُر زدن) در همان لیست sequence دیده خواهد شد.
با اجرای مثال بالا، یک لیست از اعداد صحیح چاپ میشود، سپس همان لیست به صورت تصادفی بُر میخورد.
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] [11, 5, 17, 19, 9, 0, 16, 1, 15, 6, 10, 13, 14, 12, 7, 3, 8, 2, 18, 4]
در یادگیری ماشین، از کتابخانههایی مانند «سایکیتلِرن» (Scikit-Learn) و «کرس» (Keras) استفاده میشود. این کتابخانهها مبتنی بر «نامپای» (NumPy) هستند و در واقع، از کتابخانه نامپای در پس خود استفاده میکنند. NumPy کتابخانهای است که قادر به انجام محاسبات بسیار کارآمد روی بردارها و ماتریسها است. نامپای، پیادهسازی اختصاصی از مولد اعداد تصادفی را بوسیله توابع پوششی دارد. همچنین، این کتابخانه مولد اعداد شبهتصادفی Mersenne Twister را پیادهسازی میکند. در ادامه، مثالهایی پیرامون تولید اعداد تصادفی و استفاده از تصادفی بودن با آرایههای NumPy ارائه میشود.
مولد اعداد تصادفی NumPy از کتابخانه استاندارد پایتون متفاوت است. مهمتر آنکه، دانه دادن به مولد اعداد شبهتصادفی پایتون، مولد اعداد تصادفی NumPy را تحت تاثیر قرار نمیدهد. این دو باید به طور جداگانه دانه داده و استفاده شوند. تابع ()seed را میتوان برای دانه دادن به مولد اعداد شبه تصادفی NumPy استفاده کرد که یک مقدار صحیح را به عنوان دانه دریافت میکند. مثال زیر نشان میدهد که چگونه به مولد دانه داده میشود و چطور، دانه دادن مجدد مولد منجر به توالی مشابهی از اعداد تصادفی تولید شده میشود.
با اجرای قطعه کد بالا، به مولد اعداد شبه تصادفی دانه داده میشود و یک توالی از اعداد تصادفی چاپ میشود؛ سپس، مجددا به مولد دانه داده میشود و دقیقا اعداد تصادفی مشابهی با آنچه پیشتر تولید شده بود، نمایش داده میشوند.
[4.17022005e-01 7.20324493e-01 1.14374817e-04] [4.17022005e-01 7.20324493e-01 1.14374817e-04]
یک آرایه از مقادیر ممیز شناور تصادفی را میتوان با استفاده از تابع نامپای ()rand تولید کرد. اگر هیچ آرگومانی فراهم نشود، یک مقدار تصادفی مجرد ساخته میشود، در غیر این صورت، اندازه آرایه را میتوان تعیین کرد. مثال زیر، یک آرایه از ۱۰ عدد تصادفی ممیز شناور با توزیع یکنواخت میسازد.
با اجرای قطعه کد بالا، آرایه نامپای از اعداد تصادفی ممیز شناور تولید و چاپ میشود.
[4.17022005e-01 7.20324493e-01 1.14374817e-04 3.02332573e-01 1.46755891e-01 9.23385948e-02 1.86260211e-01 3.45560727e-01 3.96767474e-01 5.38816734e-01]
یک آرایه از مقادیر تصادفی صحیح را میتوان با استفاده از تابع پایتون ()randint تولید کرد. این تابع، سه آرگومان کمترین مقدار بازه، بیشترین مقدار بازه و تعداد اعداد صحیح یا اندازه آرایه اعداد صحیح را دریافت میکند. اعداد صحیح از یک توزیع یکنواخت از کمترین مقدار تا بیشترین مقدار بازه پیروی میکنند (بازه به صورت (lower, upper] که شامل مقدار upper نیست). در مثال زیر، چگونگی ساختن یک آرایه از اعداد صحیح تصادفی نشان داده شده است.
با اجرای قطعه کد بالا، آرایهای شامل ۲۰ عدد صحیح بین ۰ و ۱۰ ساخته میشود.
[5 8 9 5 0 0 1 7 6 9 2 4 5 2 4 2 4 7 7 9]
یک آرایه از مقادیر گاوسی تصادفی را میتوان با استفاده از تابع ()randn نامپای ساخت. این تابع یک آرگومان یکتا را به منظور تعیین اندازه آرایه حاصل شده دریافت میکند. مقادیر گاوسی از توزیع استاندارد گاوسی گرفته شدهاند؛ این توزیع دارای میانگین ۰.۰ و انحراف معیار ۱.۰ است. قطعه کد زیر، نشان میدهد که چطور میتوان یک آرایه از اعداد تصادفی گاوسی را تولید کرد.
با اجرای قطعه کد بالا، یک آرایه از ۱۰ مقدار تصادفی از توزیع استاندارد گاوسی تولید میشود.
[ 1.62434536 -0.61175641 -0.52817175 -1.07296862 0.86540763 -2.3015387 1.74481176 -0.7612069 0.3190391 -0.24937038]
مقادیر حاصل از توزیع استاندارد گاوسی را میتوان با ضرب کردن در انحراف معیار و اضافه کردن میانگین به یک توزیع گاوسی با میانگین و انحراف معیار دلخواه، تغییر مقیاس داد.
در قطعه کد بالا، mean و stdev، میانگین و انحراف معیار برای یک توزیع گاوسی دلخواه هستند و value مقدار حاصل از تولید عدد تصادفی با توزیع استاندارد گاوسی است. محاسبه صورت گرفته باعث ایجاد یک عدد تصادفی از توزیع گاوسی با میانگین mean و انحراف استاندارد stdev خواهد شد.
یک آرایه نامپای را میتوان به صورت تصادفی در جای خود (بدون تهیه یک کپی از لیست) با استفاده از تابع ()shuffle نامپای بُر زد. مثال زیر نشان میدهد که چگونه میتوان یک آرایه نامپای را بُر زد.
با اجرای قطعه کد بالا، ابتدا لیستی حاوی ۲۰ عدد صحیح ساخته میشود و سپس، همان آرایه بُر زده و چاپ میشود.
در این راهنما، چگونگی تولید اعداد تصادفی و کار با آنها در زبان برنامهنویسی پایتون شرح داده شد. یکی از مباحثی که در این مطلب بیان شد، این است که اعداد تصادفی در برنامهها با استفاده از مولد اعداد تصادفی تولید میشوند. همچنین، چگونگی تولید اعداد تصادفی و استفاده از آنها با کتابخانه استاندارد پایتون تشریح شد. در پایان، چگونگی تولید آرایهای از اعداد تصادفی با استفاده از کتابخانه نامپای مورد بررسی قرار گرفت.
منبع: فرادرس
اصطلاحهای تخصصی زیادی در دنیای ریداکس وجود دارند. اکشن، اکشنساز، انواع اکشن، کاهنده، کاهندههای ریشه و مواردی از این دست. در پس همه این مجموعه واژگان پیچیده، کدهای سادهای وجود دارند که موجب بهبود زیادی در روش سازماندهی و نوشتن اپلیکیشنهای ریاکت شدهاند. در این مطلب تلاش خواهیم کرد برخی از این مفاهیم مقدماتی و مبانی ریداکس را توضیح دهیم. همچنین قصد داریم نگاهی به پکیجهای پراستفاده npm داشته باشیم که به همراه ریاکت و ریداکس استفاده میشوند.
ما در این نوشته با یک اپلیکیشن کاملاً ساده ریاکت کار میکنیم تا بتوانیم مبانی ریداکس را نشان داده و توضیح دهیم. ما قصد داریم یک کامپوننت بسازیم که یک عنصر دکمه بازگشت میدهد و کاربر میتواند روی آن کلیک کند تا مقداری از متن مشخص شود. فهرست فایلهایی که میخواهیم نگاه دقیقی به آنها داشته باشیم، شامل همه فایلهایی میشود که زیر پوشه src قرار دارند. همچنین به بررسی فایلهای rootReducer.js و App.js میپردازیم.
کار خود را با تجزیه کردن اکشنها آغاز میکنیم.

مستندات ریداکس (+) اکشنها را به عنوان «ظرفیت ترابری اطلاعات که دادهها را از اپلیکیشن به store ارسال میکند» تعریف کرده است. فعلاً لازم نیست در مورد واژه store نگران باشید، چون در ادامه همه چیز را توضیح خواهیم داد. مستندات ریداکس اکشنها را به خوبی تعریف کردهاند. اکشنها پیچیده نیستند؛ در واقع اکشن صرفاً یک شیء جاوا اسکریپت ساده است. به مثال زیر توجه کنید:
خط 4 کد فوق یک اکشن معتبر ریداکس است. چنان که میبینید این صرفاً یک شیء جاوا اسکریپت است. اکشنها در ریداکس باید یک مشخصه type داشته باشند که نشاندهنده نوع اکشنی است که قرار است اجرا شود. میتوان از اکشنها هر چیزی خواست؛ اما باید کاری که خواسته میشود توضیح داده شود. به جز این میتوان هر مشخصهای که برای یک اپلیکیشن معنیدار است به اکشنها اضافه کرد. در اغلب موارد میبینید که الگوی مهم معرفی شده در خط 1 یعنی گزاره ایمپورت به وفور در کدهای مختلف وجود دارد. توجه داشته باشید که در دایرکتوری اکشنها در پروژه، فایلی به نام type.js داریم که مانند زیر است:
فایلهایی که در بخش فوق معرفی کردیم، ثابتهای نوع اکشن هستند و دلیل این که برنامهنویسان دوست دارند از آنها استفاده کنند، این است که به سازماندهی کد کمک میکنند. بدین ترتیب همه انواع اکشن در یک فایل نگهداری میشوند و برنامهنویسان میتوانند آنها را در فایلهای دیگر ایمپورت کرده و یا به صورت موردی استفاده کنند. این وضعیت به جلوگیری از بروز باگ در زمان فراخوانی اکشنها از سوی «کاهنده» (Reducer)-ها کمک میکند. این نکته را در بخشی از ذهن خود داشته باشید تا در موقع خود توضیح دهیم که چرا این وضعیت به جلوگیری از بروز باگ در کاهندهها کمک میکند.
بنابراین اکشنهایی داریم که صرفاً اشیای ساده جاوا اسکریپت هستند. اکنون از اکشنها برای ساختن «اکشنساز» (Action Creator) استفاده میکنیم. لطفاً از این که با یک اصطلاح جدید ریداکس مواجه شدهاید، آشفته نشوید. اکشنساز نیز بسیار ساده است. در ادامه نگاهی به یک اکشنساز خواهیم داشت که مربوط به اکشن کدنویسی شده در بخش قبلی است:
همان طور که در کد فوق میبینید، یک اکشنساز صرفاً تابع ساده جاوا اسکریپت است که شیء را بازگشت میدهد. این اکشنسازها توابعی هستند که اکشنها را ایجاد میکنند (بازگشت میدهند). ما باید اکشنساز خود را اکسپورت کنیم، زیرا در نهایت میخواهیم آن را در فایل کامپوننت Toggle.js ایمپورت کنیم تا از آن برای dispatch کردن اکشن TOGGLE_MESSAGE_VISIBILITY استفاده کنیم. اما شاید کمی تند رفتیم، ابتدا باید کاهندهها را بررسی کنیم.
در اپلیکیشنهای ریداکس، «حالت» (State) اپلیکیشن به صورت یک شیء منفرد ذخیره میشود. هنگامی که یک اکشن به store (این را نیز به زودی تعریف خواهیم کرد) ارسال میشود، کاهندهها به مدیریت شیوه تغییر یافتن حالت در پاسخ به آن اکشن میپردازند. ما تنها رد 1 چیز را در حالت اپلیکیشن خود نگه میداریم و آن این است که آیا پیام ما قابل مشاهده است یا نه. در ادامه نگاهی به فایل toggle.js خواهیم داشت که در دایرکتوری کاهندهها به صورت تو در تو قرار دارد:
توضیح کد فوق را از خط 1 آغاز میکنیم. به خاطر داشته باشید که گفتیم استفاده از ثابتهای نوع اکشن به جلوگیری از باگ کمک میکنند؟ برای توضیح دلیل این مسئله کد زیر را بدون استفاده از ثابتهای نوع اکشن نوشتهایم. به جای استفاده از ثابتهای نوع اکشن، که به اکشنسازها و کاهندهها ارسال میشوند، از تنظیمات پایه استفاده میکنیم:
در مثال فوق، از ثابتهای نوع اکشن برای ساخت اکشنساز بهره نمیگیریم و به جای آن از تنظیمات پایه استفاده کردهایم. سپس در کاهنده اکشنی را که میخواهیم در ثابت سوئیچ تحریک شود به صورت اشتباه نوشتهایم (ساختار کاهندهها را نیز در ادامه توضیح خواهیم داد). مشکل کد فوق این است که کاری که ما از آن میخواهیم را انجام نمیدهد؛ اما خطایی را نیز تولید نمیکند. کد همچنان اجرا میشود، زیرا این یک رشته معتبر است. با این وجود اگر آن را با استفاده از یک ثابت نوع اکشن بارگذاری میکردیم و سپس آن ثابت را اشتباه مینوشتیم، کامپایلر خطایی صادر میکرد که به ما نشان میداد کجا اشتباه کردهایم.
اینک متوجه شدیم که چرا باید از ثابتهای نوع اکشن استفاده کنیم؛ اما در مورد باقی فایل چه میتوان گفت؟ کار خود را با توصیف آن چه که در مورد حالت ابتدایی میخواهیم، آغاز میکنیم. ما میخواهیم که پیاممان در ابتدا پنهان باشد:
این مورد نیز یک مثال کاملاً ساده محسوب میشود. شما احتمالاً در اپلیکیشنهای بزرگتر خود در حالت آغازین، مشخصههای بیشتری خواهید داشت. در نهایت کاهنده واقعی را مورد بررسی قرار میدهیم:
توضیح خود را با اکسپورت کردن تابع کاهنده آغاز میکنیم، زیرا در ادامه قصد داریم همه کاهندهها را در فایل rootReducer.js بارگذاری کنیم. سپس پارامتر حالت را به صورت شیء حالت آغازین که در بخش قبلی کدنویسی کردیم تنظیم و سپس به اکشن خود ارسال میکنیم. در خط 2، action.type را استخراج کرده و آن را در ثابت type با استفاده از روش تجزیه ساختار ES6 ذخیره میکنیم.
سپس به بدنه اصلی کاهنده میرسیم که گزاره سوئیچ است. گزاره سوئیچ ما به نوع اکشن نگاه میکند و حالت را بر اساس روشی که میخواهیم اکشن، آن را تغییر دهد تنظیم میکند. در این مثال ساده میخواهیم messageVisibility را از false به true یا برعکس تغییر دهیم و پیام خود را پنهان کنیم. در ادامه نگاهی دقیقتر به کاهنده خودمان خواهیم داشت:
به خاطر داشته باشید که ما هرگز نباید در اپلیکیشنهای ریاکت، state را به صورت مستقیم دستکاری کنیم. ساختار فوق در جاوا اسکریپت ES6 ارائه شده است و دقیقاً همان کار کد زیر را انجام میدهد:
بدین ترتیب یک کپی از حالت ایجاد میشود و مشخصهای که میخواهیم تغییر داده میشود. در ریداکس نمیتوان حالت را تغییر داد؛ بلکه به جای آن باید یک کپی از آن را بازگشت داد.
اکنون که کاهنده تنظیم شده است باید نگاهی به فایل rootReducer.js و فایل App.js داشته باشیم تا همه این موارد را جمعبندی کنیم. به فایل rootReducer.js توجه کنید:
توضیح خود را از ایمپورت کردن متد combineReducers از ریداکس آغاز میکنیم. البته این مثال تا حدودی ساختگی است، زیرا برای متد combineReducers خود، کاهندههای چندگانه نداریم؛ اما فعلاً این موضوع برایمان اهمیتی دارد. سپس کاهنده toggle را ارسال کرده و نتیجه را در یک const به نام rootReducer ذخیره میکنیم که در خط آخر اکسپورت شده است.
اکنون ما همه کاهندههای خود را به صورت ترکیبی در یک const منفرد به نام rootReducer داریم که در اختیار بخشهای دیگر اپلیکیشن قرار دارد. این امر مهمی است، زیرا از این rootReducer برای ایجاد store استفاده خواهیم کرد.
هنگامی که از ریداکس استفاده میکنیم، store به نگهداری از حالت اپلیکیشن میپردازد. هر اپلیکیشن ریداکس تنها 1 store منفرد دارد. store در ریداکس موضوع عجیبی است، زیرا امکان دسترسی به حالت اپلیکیشن را از هر کامپوننتی درون اپلیکیشن میدهد. اتصال به store ریداکس کار بسیار آسانی است. به فایل App.js زیر نگاه کنید:
ما کار خود را با ایمپورت کردن ریاکت و کامپوننت از ریاکت آغاز میکنیم که خب چیزی جدیدی در این جا وجود ندارد. سپس Provider را از react-redux ایمپورت میکنیم. ماژول npm به نام react-redux به ما کمک میکند که ریداکس را به آسانی به ریاکت اتصال دهیم. بنابراین تابع createStore را از ریداکس ایمپورت میکنیم و سپس کامپوننت Toggle و rootReducer خود را که در بخش قبلی ایجاد کردیم، ایمپورت میکنیم.
به خاطر داشته باشید، const ما به نام rootReducer همه کاهندهها را در اپلیکیشن در یک کاهنده منفرد نگهداری میکند. بنابراین store را با استفاده از تابع createStore که قبلاً ایمپورت کردهایم میسازیم و آن را به RootReducer ارسال میکنیم. در نهایت در گزاره statement مربوط به تابع render همه اپلیکیشن را درون Provider قرار میدهیم و store خود را به آن ارسال میکنیم.
بدین ترتیب به پایان این مقاله میرسیم. شاید این مقاله یک توصیف طولانی برای یک کاربرد بسیار کوچک محسوب شود؛ اما امیدواریم به تفهیم برخی از مفاهیم پایه ریداکس برای شما کمک کرده باشد. شما میتوانید هرگونه دیدگاه یا پیشنهاد خود را در بخش نظرات با ما در میان بگذارید.
منبع: فرادرس
در بخشهای قبلی این سری مقالات راهنمای جامع جاوا اسکریپت با مبانی نظری شیئهای جاوا اسکریپت و جزییات ساختار آنها آشنا شدیم و بدین ترتیب یک نقطه شروع قابل اتکا برای آغاز به دست آوریم. در این مقاله وارد یک تمرین عملی میشویم و تجربه عملی بیشتری در زمینه ساخت شیئ در جاوا اسکریپت به دست میآوریم که منجر به ساخت شیئی جالب و رنگارنگ خواهد شد.
برای مطالعه قسمت قبلی این مجموعه مطلب آموزشی میتوانید به پست کار با JSON در جاوا اسکریپت — راهنمای کاربردی مراجعه کنید.
هدف از مطالعه این مطلب، کسب تجربه عملی برای کار با اشیا و تکنیکهای شیئگرایی در دنیای واقعی است.
در این مقاله، یک دمو از «توپهای جنبنده» میسازیم تا نشان دهیم که اشیا در جاوا اسکریپت تا چه میزان میتوانند مفید باشند. توپهای کوچک ما روی صفحه به حرکت درمیآیند و زمانی که با هم برخورد کنند رنگشان تغییر پیدا میکند. نمونه کامل شده این پروژه چیزی مانند تصویر زیر خواهد بود:

در این مثال از Canvas API برای ترسیم توپها روی صفحه استفاده میکنیم و از API دیگری به نام requestAnimationFrame نیز برای متحرکسازی کل نمایش استفاده خواهیم کرد. در هر حال لازم نیست هیچ دانش قبلی از این API-ها داشته باشید. امیدواریم زمانی که این مقاله را به پایان میبرید، علاقهمند باشید این موضوعات را بیشتر کاوش کنید. در این مسیر از برخی اشیای عالی استفاده میکنیم و چند تکنیک زیبا مانند روش برخورد توپ با دیوار و جهش یافتن یا بررسی این که با هم برخورد کردهاند یا نه که به نام تشخیص تصادم نامیده میشود را معرفی خواهیم کرد.
در آغاز کار کپیهای محلی از فایلهای زیر روی سیستم خود ایجاد کنید.
این فایل یک سند کاملاً ساده HTML است که دارای یک عنصر <a>، یک عنصر <canvas> برای ترسیم توپها روی آن و عناصری برای بهکارگیری CSS و جاوا اسکریپت است.
این فایل حاوی سبکهای سادهای است که به طور عمده برای سبکبندی موقعیت <h1> و رها شدن از شر نوارهای اسکرول یا حاشیه پیرامون صفحه (جهت زیباتر ساختن آن) استفاده میشود.
در این فایل برخی کدهای جاوا اسکریپت قرار دارند که عنصر <canvas> را تنظیم میکنند و یک تابع عمومی ارائه میکند که از آن استفاده خواهیم کرد. بخش نخست اسکریپت مانند زیر است:
این اسکریپت ارجاعی به عنصر <canvas> ایجاد میکند و سپس روش ()getContext را روی آن فراخوانی کرده و ساختاری ارائه میدهد که میتوان از آن برای شروع رسم استفاده کنیم. متغیر حاصل (ctx) شیئی است که به صورت مستقیم ناحیه رسم بوم را نمایش میدهد و امکان ترسیم اشکال 2 بعدی را روی آن فراهم میسازد.
سپس متغیرهایی را که width و height نام دارند، تنظیم میکنیم و به ترتیب عرض و ارتفاع عنصر بوم را روی آنها تنظیم میکنیم. این عناصر با مشخصههای canvas.width و canvas.height نمایش مییابند و با عرض و ارتفاع صفحه مرورگر متناسب هستند. مقادیر اخیر را میتوان از مشخصههای Window.innerWidth و Window.innerHeight به دست آورد.
در ادامه خواهید دید که چندین آرگومان را به هم زنجیر میکنیم تا همه متغیرها سریعتر تعیین شوند. بخش آخر اسکریپت اولیه مانند زیر است:
این تابع دو عدد را به عنوان آرگومان میگیرد و یک عدد تصادفی در بازهای بین آن دو بازگشت میدهد.
برنامه ما شامل توپهای زیادی خواهد بود که روی صفحه به اطراف میجهند. از آنجا که این توپها رفتاری نسبتاً مشابه دارند بهتر است که آنها را با یک شیئ بازنمایی کنیم. کار خود را با افزودن سازنده زیر به انتهای کد قبلی آغاز میکنیم.
در این تابع، پارامترهایی داریم که مشخصههای هر توپ را در برنامه ما مشخص میسازند:
بدین ترتیب همه مشخصههای مورد نیاز توپها تعیین میشوند؛ اما متدها چطور؟ ما باید کاری کنیم که توپهایمان در برنامه واقعاً کاری انجام دهند.
ابتدا متد ()draw زیر را به پروتوتایپ ()Ball اضافه کنید:
با استفاده از این تابع، میتوانیم به توپ خود بگوییم که با فراخوانی یک سری از اعضای context مربوط به canvas دوبعدی که قبلاً تعریف کردیم (ctx) خود را روی صفحه رسم کند. این context مانند یک صفحه کاغذ است و اینک میخواهیم به قلم خود دستور بدهیم که چیزی را روی آن رسم کند.
شما هم اینک میتوانید شروع به آزمودن شیئ خود بکنید. کدی را که تا به اینجا نوشتهاید ذخیره کنید و فایل HTML را در یک مرورگر بارگذاری کنید.
کنسول جاوا اسکریپت مرورگر را باز کرده و سپس صفحه را رفرش کنید به طوری که اندازه بوم به اندازه Viewport کوچکترشده در زمان باز کردن کنسول درآید.
کد زیر را بنویسید تا یک وهله جدید از شیئ توپ ایجاد شود:
اعضای آن را به صورت زیر فراخوانی کنید:
هنگامی که خط آخر را وارد میکنید باید ببینید که توپ شروع به رسم کردن خود روی بوم میکند.
ما میتوانیم توپ را در موقعیت خود رسم کنیم، اما برای این که بتوانیم در ادامه شروع به متحرکسازی آن بکنیم، باید یک تابع بهروزرسانی داشته باشیم. کد زیر را در انتهای فایل جاوا اسکریپت خود وارد کرد و یک متد ()update به پروتوتایپ ()Ball اضافه کنید.
چهار بخش نخست تابع به بررسی این نکته میپردازند که آیا توپ به لبههای بوم رسیده است یا نه. اگر چنین اتفاقی افتاده باشد، جهت سرعت مرتبط معکوس میشود تا توپ در مسیر معکوس شروع به حرکت کند. برای مثال، اگر توپ به سمت بالا حرکت میکند (velY مثبت) در این صورت سرعت عمودی طوری تغییر پیدا میکند که شروع به حرکت به سمت پایین بکند، یعنی velY منفی میشود.
بنابراین چهار حالت زیر را داریم:
در هر حالت، مشخصه size توپ را در محاسبات بررسی میکنیم، زیرا مختصات x/y در مرکز توپ هستند؛ اما ما میخواهیم مختصات لبه پیرامونی توپ را بررسی کنیم. در واقع ما نمیخواهیم قبل از این که توپ از دیوار بازجهد، نیمی از آن از دیوار گذشته باشد.
دو خط آخر مقدار velx را به مختصات x و مقدار vely را به مختصات y اضافه میکنند. بدین ترتیب توپ هر بار که متد فراخوانی میشود جابجا میشود. کار ما تا به اینجا پایان یافته است، در بخش بعدی به متحرکسازی توپها میپردازیم.
اینک بخش جالب داستان فرا رسیده است. اکنون قصد داریم شروع به افزودن توپها به بوم بکنیم و آنها را به حالت متحرک درآوریم. ابتدا باید جایی را بسازیم که همه توپها در آن ذخیره شوند و سپس آنها را روی صفحه پر کنیم. کد زیر این کار را انجام خواهد داد. این کد را به انتهای کدهای فعلی اضافه کنید:
همه برنامههایی که به متحرکسازی اشیا میپردازند، به طور کلی دارای یک حلقه انیمیشن هستند که به منظور بهروزرسانی اطلاعات در برنامه استفاده میشود و سپس نتیجه نهایی هر فریم از انیمیشن رندر میشود. این طرز کار اغلب بازیها و دیگر برنامهها از این دست است. حلقه while یک وهله جدید از ()ball ما را با استفاده از مقادیر تصادفی میسازد. خود مقادیر تصادفی به وسیله تابع ()random ایجاد میشوند و سپس از ()push برای ارسال آنها به آرایه نهایی توپها استفاده میشود، اما این اتفاق تنها در زمانی رخ میدهد که تعداد توپها کمتر از 25 باشد.
بنابراین هنگامی که توپهای روی صفحه به عدد 25 برسند دیگر توپ جدیدی ایجاد نمیشود. شما میتوانید این عدد را در بخش balls.length < 25 تغییر دهید تا توپهای بیشتری روی صفحه داشته باشید. بسته به این که رایانه یا مرورگر شما چه مقدار توان محاسباتی دارد میتوانید توپ به صفحه اضافه کنید؛ اما تعیین چند هزار توپ موجب کُند شدن نسبی انیمیشن خواهد شد.
کد زیر را به انتهای کدهای فعلی خود اضافه کنید:
در تابع ()loop کارهای زیر را انجام دهید:
رنگ fill بوم را به صورت سیاه نیمه شفاف تعیین کنید و سپس یک مستطیل از آن رنگ را با استفاده از ()fillrect روی عرض و ارتفاع بوم رسم کنید. چهار پارامتر مختلف این بوم در واقع نقاط ابتدایی و عرض و ارتفاع بوم را مشخص میسازند. منظور از رسم این مستطیل، پوشش دادن فریم قبلی پیش از رسم فریم بعدی است. اگر این کار را انجام ندهید، به جای تماشای توپهایی که روی صفحه حرکت میکنند، کِرمهایی را خواهید دید که پیرامون صفحه میخزند! رنگ این مستطیل نیمه شفاف به صورت (rgba(0،0،0،0.25 انتخاب شده است تا امکان دیدن نسبی چند فریم قبلی از خلال فریم فعلی وجود داشته باشد و به این ترتیب رد حرکت توپها در زمان حرکت بر جای بماند. اگر این مقدار را از 0.25 به 1 تغییر دهید، این رد حرکت را دیگر نخواهید دید. با تغییر دادن این مقدار میتوانید تأثیر آن را در شرایط مختلف مشاهده کنید.
حلقهای روی همه توپها در آرایه balls تعریف کنید و تابع ()draw و ()update را برای هر یک از آنها روی صفحه اجرا کنید. سپس بهروزرسانیهای لازم را برای موقعیت و سرعت هر توپ در فریم بعدی انجام دهید.
تابع را بار دیگر با استفاده از متد ()requestAnimationFrame اجرا کنید. زمانی که این تابع به صورت پیوسته اجرا شود و همان نام تابع را به صورت مرتب ارسال کند، تابع به تعداد زمان تعیین شده اجرا میشود و انیمیشن روانی ایجاد میکند. این کار عموماً به روش بازگشتی اجرا میشود، یعنی تابع هر بار که اجرا شود، خود را فراخوانی میکند و بدین ترتیب برای همیشه به صورت مکرر اجرا میشود.
در گام آخر، کد زیر را به انتهای خطوط موجود کد خود اضافه کنید. بدین ترتیب کافی است تابع را یک بار در زمان آغاز انیمیشن فراخوانی کنیم:
بدین ترتیب مقدمات کار به پایان میرسد. فایل را ذخیره کرده و صفحه را رفرش کنید تا توپهای جهنده خود را بیازمایید.
اینک برای سرگرمی بیشتر، یک روش تشخیص تصادم به برنامه خود اضافه میکنیم تا توپهایمان بدانند که چه زمانی با توپ دیگر برخورد میکنند.
قبل از هر چیز، تعریف متد زیر را در زیر بخشی که متد ()update را تعریف کردهاید، اضافه کنید. منظور ما بلوک Ball.prototype.update است.
این متد کمی پیچیده است و اگر آن را به طور کامل درک نمیکنید، جای نگرانی نیست. توضیح طرز کار آن به زبان ساده چنین است: برای هر توپ باید یک بررسی در مورد همه توپهای دیگر انجام دهیم و ببینیم آیا با توپ کنونی برخورد دارند یا نه. به این منظور یک حلقه for دیگر تعریف میکنیم تا روی همه توپها در آرایه []balls اجرا شود.
در ابتدای حلقه for از گزاره if استفاده میکنیم تا بفهمیم آیا توپ کنونی که حلقه روی آن قرار دارد، همان توپی است که در حال بررسی کردنش هستیم یا نه. بدیهی است که نمیخواهیم بدانیم آیا توپی با خودش برخورد کرده است یا نه. به این منظور بررسی میکنیم که آیا توپ کنونی (یعنی توپی که متد collisionDetect آن فراخوانی شده است) همان توپی است که حلقه روی آن قرار دارد (یعنی توپی که در تکرار فعلی حلقه قرار است بررسی شود). سپس از عملگر (!) برای منفی ساختن بررسی استفاده میکنیم و بنابراین کد درون گزاره if تنها زمانی اجرا میشود که این دو توپ برابر نباشند.
سپس از یک الگوریتم رایج برای بررسی وقوع تصادم بین دو دایره استفاده میکنیم. در واقع میخواهیم بدانیم آیا مساحت دو دایره همپوشانی دارند یا نه. برای کسب اطلاعات بیشتر در مورد این محاسبه به این مقاله (+) مراجعه کنید.
اگر یک تصادم تشخیص داده شود، کد درون گزاره if درونیتر اجرا میشود. در این حالت مشخصه color هر دو دایره را به صورت یک مقدار تصادفی جدید تغییر میدهیم. البته ما میتوانیم کار پیچیدهتری مانند شبیهسازی واقعگرایانه حالت برخورد دو توپ با هم را نیز اجرا کنیم؛ اما پیادهسازی چنین مثالی بسیار پیچیدهتر است. توسعهدهندگان برای پیادهسازی چنین شبیهسازیهای فیزیکی از یک کتابخانه بازی یا فیزیک مانند PhysicsJS ،matter.js ،Phaser و غیره استفاده میکنند.
همچنین باید این متد را روی هر فریم انیمیشن فراخوانی کنید. کد زیر را در زیر خط ;()balls[i].update اضافه کنید:
فایل را ذخیره کرده و صفحه را رفرش کنید تا تغییرات رنگ توپها را در حین تصادم مشاهده کنید.
نکته: اگر مشکلی در اجرای فایل خود دارید میتوانید نسخه نهایی را در ادامه مشاهده کنید و اشکال خود را بیابید:
امیدواریم از نوشتن این مثال واقعی در مورد توپهای جهندهی تصادفی با استفاده از شیئهای مختلف و تکنیکهای گوناگون شیئگرایی که آنها را در طی این سری مقالات آموزش جامع جاوا اسکریپت معرفی کردهایم، لذت برده باشید. بنابراین تجربه عملی مفیدی در زمینه استفاده از شیئها در یک مثال از دنیای واقعی کسب کردهاید. برای مطالعه بخش بعدی این سری مفالات آموزشی روی لینک زیر کلیک کنید:
منبع: فرادرس