رسیدیم به بخش دوم که تلگراف چی مهربون میخواد به ما کمک کنه بهتر جاوا اسکریپت یاد بگیریم. اگر قسمت قبل رو ندیدید حتما حتما قبل از این بخش بخونید (مخصوصا اگر مبتدی هستید) چون چند تا متد مهم رو معرفی کردم (متد های split, map , trim , join) و در ضمن متدی نوشتیم که تقریبا نصف جواب این چالشه. خلاصه این پست در ادامه پست قبلی است. و البته
این مسئله یک قسمت سوم رو هم داره که واقعا سخت میشه و میخوایم صفر و یک های مربوط به یه آدم مبتدی و ××بی نظم×× رو ترجمه کنیم.
یادآوری regexp:
از regexp یا regex یا که مخفف regular expression هست برای پیدا کردن یک الگو در متن استفاده میکنیم. به این صورت که الگو رو به شکل تعدادی از کاراکتر های با معنی بین /.../ قرار میدیم و با استفاده از متد match دنبال این الگو ها میگردیم. این متد یک ارایه از قسمت هایی از متن که با الگو مطابقت داره رو برمیگردونه. اگه دوست دارید کامل تر باهاش اشنا بشید این منبع رو شدیدا توصیه میکنم.
تعدادی از کاراکتر های با معنی که در این قسمت نیاز داریم:
"+" : یعنی اگر یکی یا بیشتر از کاراکتر قبلش وجود داشت با regex مطابقت داره. مثلا /+x/ قسمت هایی از متن که در اون یک یا چند x پشت سر هم وجود داره رو انتخاب میکنه.
'^' و '$': '^' اول خط و "$" آخر خط رو نشون میده. به این ترتیب که اگه دنبال یک الگو در اول متن باشیم از '^' و اگر دنبال یک الگو در اخر متن باشیم از '$' استفاده میکنیم. پس /+0^/ یعنی اگر اول متن یکی یا بیشتر "0" دیدی و /$+0/ یعنی اگر آخر خط یکی یا بیشتر '0' دیدی اون ها رو انتخاب کن یا به عبارتی این بخش ها با regex مطابق هستند.
'|' : این کاراکتر وجود اختیار رو بیان میکنه. به طوری که هر کدوم از regex های چپ و راست اون منطبق شد همون رو اجرا میکنه. یه جور هایی یعنی " یا ".
نکته: از آپشن g معنی global هم استفاده کردیم تا همه قسمت هایی که با regex سازگاری دارند رو انتخاب کنه. در غیر این صورت متد match اولین مطابقت رو انتخاب میکنه و فرایند جست و جو متوقف میشه.
و چیز های ریگه که به مرور توضیح میدم...
تلگراف برقی بر اساس دو سیم و یک دکمه کار میکنه، به طوری که وقتی دکمه فشار داده میشه، سیم ها به هم برخورد میکنند و این برخورد جریان الکتریکی ای ایجاد میکنه که از طریق کابل به سایر نقاط جهان منتقل میشه. در واقع چیزی که اون طرف دنیا دریافت میشه ترکیبی از صفر و یک هاست:
1100110011001100000011000000111111001100111111001111110000000000000011001111110011111100111111000000110011001111110000001111110011001100000011
یک ها مربوط به زمانیه که دکمه فشار داده شده و صفر ها مربوط به زمانیه که دکمه رها شده.
مدت زمانی که این دکمه فشار داده میشه مشخص میکنه که نقطه است یا خط(دش). به این صورت که یک فشار کوتاه، نقطه ترجمه میشود و یک فشار طولانی، خط.
برای فرستادن کد مورس از طریق تلگراف برقی قوانینی وجود داره :
به این ترتیب میتونیم کد مورس رو از طریق صفر و یک ارسال کنیم. ولی چیزی که توی استاندارد مشخص نشده "واحد" یا "ضریب" است. برای یک فرد حرفه ای این واحد میتونه نیم ثانیه باشه و برای یک مبتدی که سرعتش کند تره میتونه یک ثانیه باشه. چالش اصلی ما برای حل این مسئله پیدا کردن واحده.
پس باید دو تا تابع بسازیم. اول تابع (decodeBits(bits که اول واحد رو پیدا میکنه و صفر و یک ها رو به درستی به نقطه و خط تبدیل میکنه و string حاوی کد مورس رو تحویل میده.
نکته ای که باید بهش توجه کنید اینه که اول و آخر هر پیام ممکنه تعدادی صفر پشت سر هم باشه که در معنای پیام تاثیر نداره و قبل از انجام هر کاری باید اونها رو حذف کرد.
متد دوم که decodeMorse باشه رو توی قسمت قبل نوشتیم و کارش اینه که کد مورس رو به زبان ادمی زاد ترجمه کنه.
بریم سراغ راه حل.
این مسئله فرصت خیلی خوبی برای تمرین regular expressions هست.
متد decodeBits به این صورت خواهد بود:
function decodeBits(bits) { // Trim zeros const message = bits.replace(/^0+|0+$/g, "").match(/0+|1+/g); // Find transmission rate const rate = Math.min(...message.map(x => x.length)); // Convert to morse code bits = bits .replace(new RegExp('(?:111){' + rate + '}(?:0{' + rate + '}|$)', 'g'), '-') .replace(new RegExp('1{' + rate + '}(?:0{' + rate + '}|$)', 'g'), '.') .replace(new RegExp('(?:000000){' + rate + '}', 'g'), ' ') .replace(new RegExp('(?:00){' + rate + '}', 'g'), ' ') return bits; }
خط به خط اون رو بررسی میکنیم:
const message = bits.replace(/^0+|0+$/g, "").match(/0+|1+/g);
اول صفر های اضافی رو حذف میکنیم. بعد با متد match صفر ها و یک های پشت سر هم رو به عنوان یک ارایه برمیگردونیم و توی message ذخیره میکنیم. محتوا message چیزی شبیه به این خواهد بود:
['11', '111111', '0000', '1111', '00']
const rate = Math.min(...message.map(x => x.length));
اول با طول اعضا message یک ارایه ساختیم و بعد با متد Math.min کوتاه ترین اون رو انتخاب کردیم که واحد یا ضریب ما خواهد بود. در مورد این که چطور ضریب رو پیدا کردیم خودتون فکر کنید(انتظار ندارید که همه چیز رو من بگم. درسته؟)
کاربرد '...' : از '...' کاربرد های زیادی دارد که مهم ترین آن کپی کردن اعضا یک ارایه دیگر است. به عنوان مثال اگر بخواهیم اعضا آرایه arr1 را در بین آرایه arr2 کپی کنیم:
const arr1 = [2, 3, 5, 6, 7]; const arr2 = [1, ...arr1, 8, 9]; // arr2 => [1, 2, 3, 4, 5, 6, 7, 8, 9];
بقیه کاربرد های ... در اینجا توضیح داده شده.
تنها نکته اینه که چرا از '...' قبل از message استفاده کردیم و چرا از خودش استفاده نکردیم؟ جواب اینه که اگر تنها از message استفاده میکردیم خروجی یک آرایه بود در صورتی که متد min ورودی آرایه نمیگیرد و باید تعدادی عنصر مجزا در ان قرار قیرد تا کوچکترین ان را انتخاب کند. با استفاده از ... با جای خود آرایه اعضا اون رو به متد min میدیم.
bits = bits .replace(new RegExp('(?:111){' + rate + '}(?:0{' + rate + '}|$)', 'g'), '-') .replace(new RegExp('1{' + rate + '}(?:0{' + rate + '}|$)', 'g'), '.') .replace(new RegExp('(?:000000){' + rate + '}', 'g'), ' ') .replace(new RegExp('(?:00){' + rate + '}', 'g'), ' ') return bits;
برای اینکه این رو بفهمیم اول باید درک بهتری از واحد به دست بیاریم. فرض کنید یک فرد حرفه ای حرف 'T'بخواد بنویسه. فرض کنیم این شکلی میشه:
111 یعنی به اندازه سه واحد دکمه رو فشار داده
ولی برای یک فرد مبتدی که نصف سرعت حرفه ای ها رو داره میشه:
111111
میبینیم که تعداد یک ها دو برابر شد. پس واحد اینجا میشه ۲ چون دو برابر وقت برای تایپ یک کاراکتر استفاده شده و در نتیجه تعداد یک ها هم دو برابر شده. اگر بخواهیم با regexp این رو نشون بدیم میشه:
/{ضریب}111/
یادتون هست که بین {...} تعداد دقیق رخداد یک کاراکتر رو مشخص میکردیم. مثلا /{3}0/ یعنی 000.
سوال مهم دوم اینه که چرا به جای فرم /.../ از شی RegExp استفاده کردیم؟
جواب اینه که اگر از فرم /.../ استفاده میکردیم نمیتونستیم در اون از متغیر استفاده کنیم و همونطور که میبینید استفاده از متغیر rate برای حل این سوال ضروریه پس از شی RegExp استفاده کردیم. پارامتر اول regexp و پارامتر دوم آپشن ها هستند.
بقیه ماجرا فقط regex هست و خودتون یکم بهش فکر کنید.
این از نصف جواب. نصف بقیه جواب رو هم قبلا نوشتیم: متدی که کد مورس رو به زبان آدم ترجمه میکنه:
decodeMorse = function(morseCode){ function decodeMorseLetter(letter) { return MORSE_CODE[letter]; } function decodeMorseWord(word) { return word.split(' ').map(decodeMorseLetter).join(''); } return morseCode.trim().split(' ').map(decodeMorseWord).join(' '); }
این هم از این.
تموم شد. امیدوارم استفاده کرده باشید و چیز های زیادی یاد گرفته باشید.
این مسئله یک قسمت سوم رو هم داره که واقعا سخت میشه و میخوایم صفر و یک های مربوط به یه آدم مبتدی و ×بی نظم× رو ترجمه کنیم. منتظر باشید...