یک کارمند گوگل چگونه مشکلات کدنویسی را حل می‌کند؟

ترجمه و تالیف : عرفان کاکایی
تاریخ انتشار : 13 خرداد 98
خواندن در 5 دقیقه
دسته بندی ها : آموزشی

در این مقاله، من شما را با استراتژی خود برای رفع مشکلات برنامه‌نویسی را از ۰ تا ۱۰۰ آشنا خواهم کرد، که از آن هم در کار روزانه خود در Google، و هم به همراه برنامه‌نویسانی از سطوح مختلف استفاده می‌کنم. اعمال کردن این روند ساختاربندی شده، فرایند ناامید کننده اشکال‌زدایی را به حداقل می‌رساند و به یک کد تمیزتر و صحیح‌تر، در مدت زمان کمتر ختم می‌شود.

قدم به قدم

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

مشکل: «با دو رشته داده شده، یعنی sourceString و searchString، اولین ورودی‌ای که در آن searchString داخل sourceString ظاهر می‌شود را برگردان. اگر searchString داخل sourceString ظاهر نمی‌شود، مقدار -1 را برگردان.»

۱) مشکل خود را ترسیم کنید

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

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

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

تصاویری را ترسیم کنید. از پیکان‌ها استفاده کنید. اعداد را در جعبه‌های کوچک قرار دهید. هر کاری که به شما در بصری‌سازی مشکل کمک می‌کند، آن را انجام دهید. هدف این است که مشکل را رفع کنید، و شما در کاغذ و قلم آزادی کامل دارید؛ بدون هیچ کدام از محدودیت‌های یک کیبورد.

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

بیایید فرض کنیم که رشته‌های مورد نظر این مقادیر را دارند:

sourceString: "abcdyesefgh"
searchString: "yes"

افکار من به طور صریح:

«خب، من می‌توانم ببینم که searchString داخل sourceString است. اما من چگونه این کار را انجام دادم؟ خب، من از ابتدای sourceString شروع کردم و همینطور آن را خواندم، تا این که به انتهای آن رسیدم، و در این حین بررسی کردم که آیا هر قطعه ۳ کاراکتری موجود در آن با کلمه «yes» تطابق داشت، یا نه. برای مثال، «abc»، «bcd»، «cde» و... وقتی که به ورودی ۴ رسیدم، کلمه «yes»را یافتم، و پی بردم که یک تطابق برای آن وجود داشته، و در ورودی ۴ شروع می‌شود.»

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

بیایید با یک جفت رشته دیگر، مجددا تلاش کنیم:

sourceString: "abcdyefg"
searchString: "yes"

«در اینجا، ما از ابتدای sourceString شروع کردیم و همینطور خواندیم تا به انتهای آن رسیدیم، و در این حین بررسی کردیم که آیا هر قطعه ۳ کاراکتری موجود در آن با کلمه «yes» تطابق داشت، یا نه. وقتی که به ورودی ۴ رسیدیم، «yef» را پیدا کردیم، که تقریبا یک تطابق بود، اما به طور کامل هم با کلمه ما مطابق نبود؛ زیرا سومین کاراکتر آن متفاوت بود. پس ما همینطور ادامه دادیم، تا به انتهای رشته رسیدیم و پی بردیم که هیچ تطابقی در آن وجود نداشت؛ پس مقدار -1 را برگرداندیم.»

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

۲) مشکل خود را به زبان انگلیسی بنویسید

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

۱. از ابتدای رشته شروع کن.

۲. به هر مجموعه سه کاراکتری (یا هر تعداد کاراکتر که در sreachString‌ وجود دارد) نگاه کن.

۳. اگر هر کدام از آن‌ها برابر با searchString هستند، ورودی صحیح را برگردان.

۴. اگر بدون هیچ گونه تطابق به انتهای رشته رسیدیم، مقدار -1 را برگردان.

خوب به نظر می‌رسد!

۳) یک شبه کد بنویسید

شبه کد در واقع کد نیست، اما ساختار کد را تقلید می‌کند. من یک شبه کد از الگوریتم بالا را به این صورت می‌نویسم:

for each index in sourceString,
    there are N characters in searchString
    let N chars from index onward be called POSSIBLE_MATCH
    if POSSIBLE_MATCH is equal to searchString, return index
at the end, if we haven't found a match yet, return -1.

من می‌توانم با نوشتن آن به این صورت، کمی نزدیک‌تر شوم:

for each index in sourceString,
    N = searchString.length
    POSSIBLE_MATCH = sourceString[index to index+N]
    if POSSIBLE_MATCH === searchString:
        return index
return -1

این که شبه کد شما چقدر به کد شما شباهت دارد، به خودتان بستگی دارد، و با گذر زمان درک خواهید کرد که چگونه برای شما بهترین نتیجه را دارد.

۴) چیزی که می‌توانید را به کد ترجمه کنید

نکته: برای مشکلات آسان‌تر، این قدم می‌تواند با قدم بالا ترکیب شود.

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

function findFirstMatch(searchString, sourceString) {
    let length = searchString.length;
    for (let index = 0; index < sourceString.length; index++) {
        let possibleMatch = <the LENGTH chars starting at index i>
        if (possibleMatch === searchString) {
            return index;
        }
    }
    return -1;
}

دقت کنید که من بخشی از این کد را خالی گذاشتم. این کار از روی عمد بوده است! و درباره سینتکس مربوط به تقسیم‌بندی رشته‌ها در JavaScript مطمئن نبودم؛ پس در قدم بعد به دنبال آن خواهم گشت.

۵) حدس نزنید

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

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

اما ۲ چیز؟ در اینجا ۳ احتمال وجود دارد (چیز اول مشکل دارد، چیز دوم مشکل دارد یا این که هر دوی آن‌ها مشکل دارند). ۳ چیز؟ حال ۷ احتمال وجود دارند. این مشکل به سرعت از کنترل خارج می‌شود.

نکته جانبی: فرمول مربوط به تعداد راه‌هایی که برنامه شما می‌تواند اشتباه باشد، به این صورت است که: a(n) (2^n)-1.

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

در قدم قبلی، من درباره شیوه انتخاب یک بخش از یک رشته در JavaScript مطمئن نبودم. پس اگر به گوگل بروم و عبارت انگلیسی «how to select part of a string in javascript» را جستجو کنم، این نتیجه به من نمایش داده می‌شود:

«متد slice() بخش‌هایی از رشته را استخراج کرده، و بخش‌های استخراج شده را در یک رشته جدید بر می‌گرداند. از پارامترهای شروع (start) و پایان (end) برای مشخص کردن آن بخش از رشته که می‌خواهید استخراج کنید، استفاده نمایید. اولین کاراکتر موقعیت 0 را دارد، دومین کاراکتر موقعیت 1 را دارد و...»

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

بر حسب این نتایج، به نظرم باید هر بار از substr(index, searchString.Length) برای استخراج بخش مورد نظر از sourceString استفاده کنم. اما این یک فرض بوده، و چیزی بیشتر از آن نیست. پس در ابتدا، من یک مثال کوچک برای آزمایش این رفتار می‌سازم.

>> let testStr = "abcdefghi"
>> let subStr = testStr.substr(3, 4);  // simple, easy usage
>> console.log(subStr);
"defg"
>> subStr = testStr.substr(8, 5);   // ask for more chars than exist
"i"

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

و حال من می‌توانم آخرین بخش برنامه خود را وارد کنم.

function findFirstMatch(searchString, sourceString) {
    let length = searchString.length;
    for (let index = 0; index < sourceString.length; index++) {
        let possibleMatch = (
            sourceString.substr(index, length));
        if (possibleMatch === searchString) {
            return index;
        }
    }
    return -1;
}

نتیجه گیری

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

منبع

دیدگاه‌ها و پرسش‌ها

برای ارسال نظر لازم است ابتدا وارد سایت شوید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید