حل مسئله «آزمون تستی» کوئرا با سی‌شارپ

بسم الله الرحمن الرحیمبه امید خدا توی این مطلب می‌خوایم مسئله «آزمون تستی» که در سایت کوئرا هست رو حل کنیم.حل این مسائل، علاوه براینکه باحاله و سرگرمی :) تمرینی هستن برای افزایش مهارت حل مسئله ما. منم اینجا سعی میکنم بیشتر روش حل مسائل رو توضیح بدم.برای دیدن مجموعه سوالاتی که از سایت کوئرا حل کردم می‌تونید این مخزن رو ببینید. پیش‌نیازها:آشنایی با متدهاآشنایی با Listمسئله: آزمون تستیصورت مسئله کامل رو می‌تونید در سایت کوئرا ببینید. به صورت خلاصه:یه آزمون با n سوال تستی (۴ گزینه‌ای) برگزار شده، می‌خوایم سامانه‌ای برای تشخیص نمره افراد بنویسیم. کلید آزمون و پاسخ افراد بهمون داده میشه، ما باید نمره افراد رو محاسبه کنیم و نمایش بدیم.کلید آزمون از حروف C، B، A یا D تشکیل شده که به ترتیب نشون میده گزینه ۱ یا ۲ یا ۳ یا ۴ صحیح هستن. مثلا اگر توی کلید حرف A بود می‌دونیم گزینه اول درست بوده و اگر حرف D بود یعنی گزینه ۴ درست بوده. طبیعتا اگر nتا سوال داشته باشیم باید کلید ما هم یه رشته nتایی باشه. مثلا اگر کلید یه آزمون ۳ تایی AAC باشه میدونیم جواب سوال اول و دوم گزینه اول هستش و جواب سوال آخر گزینه ۳. پاسخ‌برگ هم یه ماتریس n * ۴ هستش. هر سوال ۴ تا کاراکتر داره که یا # هستش یا O. اگر # بود یعنی اون گزینه علامت خورده و اگر O بود یعنی علامت نخورده. شرایط درستی یا نادرستی هم به صورت زیر بیان شده:زمانی پاسخ یک سوال «درست» داده شده که فقط گزینه درست علامت خورده باشد.زمانی پاسخ یک سوال «نزده» در نظر گرفته می‌شود که هیچ گزینه‌ای علامت نخورده باشد.در صورتی که پاسخ یک سوال نه درست باشد نه نزده، پاسخ سوال «نادرست» در نظر گرفته می‌شود. (یعنی اگر گزینه‌ی نادرست انتخاب شود یا چندگزینه علامت خورده باشد، نادرست در نظر گرفته می‌شود.)نمره افراد چطور محاسبه میشه؟ اگر تعداد پاسخ‌های «درست» یک پاسخ‌برگ برابر t و تعداد پاسخ‌های «نادرست» برابر f باشد، نمره این پاسخ‌برگ برابر است با:3 × t − fراه‌حل با زبان سی‌شارپراه‌حل کامل #C رو می‌تونید از این لینک به صورت رنگی(!) ببینید.ابتدا ورودی‌های ساده و اولیه رو می‌گیریم. یعنی تعداد سوالات، کلید و تعداد پاسخ‌برگ‌ها(تعداد افراد):var numOfQuestions = Convert.ToInt32(Console.ReadLine()); var keys = Console.ReadLine(); var numOfPersons = Convert.ToInt32(Console.ReadLine());برای گرفتن ورودی از Console.ReadLine استفاده می‌کنیم که خروجیش string هستش. برای تبدیل string به int می‌تونیم از Convert.ToInt32 استفاده کنیم.حالا باید پاسخ‌برگ‌ها رو از ورودی بگیریم بعد برای هرکدوم نمرش رو حساب کنیم و در خروجی چاپ کنیم.با توجه به اینکه پاسخ‌برگ‌ها چندتا هستن از یک حلقه استفاده می‌کنیم.for (var i = 0; i < numOfPersons; i++) { }داخل این حلقه باید یه دونه پاسخ‌برگ بگیریم، بعد نمرش رو حساب کنیم و همینجا چاپ کنیم. (اینجا اشکالی نداره ولی توی پروژه‌های واقعی هیچوقت قیمه‌ها رو توی ماست‌ها نریزید! و بالعکس! اینجا برای سادگی و اینکه فهم قضیه سخت نشه از این مورد چشم پوشی می‌کنیم)هر پاسخ‌برگ شامل تعدادی رشته هستش. درواقع لیستی از رشته‌ها هستش به تعداد سوالات. پس برای گرفتن کل پاسخ‌برگ (لیست رشته‌ها) لازمه به تعداد سوالات یه حلقه بزنیم و اطلاعات رو توی لیست ذخیره کنیم.var personAnswers = new List<string>(numOfQuestions); for (var j = 0; j < numOfQuestions; j++) personAnswers.Add(Console.ReadLine());خب حالا که پاسخ‌برگ رو داریم کلید هم داریم می‌تونیم یه متد بنویسیم که این دوتا پارامتر رو بگیره و برامون نمره رو حساب کنه. اسم این متد رو میذاریم CalculateScore. بعدش هم می‌تونیم نمره رو با Console.WriteLine چاپ کنیم.var score = CalculateScore(personAnswers, keys); Console.WriteLine(score);حالا وقتشه برسیم به پیاده‌سازی متد اصلی. امضای متد ما به این شکل هستش:private static int CalculateScore(this List<string> personAnswers, string keys) { }پارامتر اول، پاسخ‌برگ هستش و پارامتر دومی کلید این آزمون. خروجی هم int هستش بخاطر اینکه نمرات ما صحیح هستن نه اعشاری.فرض کنید کلید ما AACB هستش و پاسخ‌برگ یکی از افراد به صورت زیر:OOOO O#OO OO#O O##Oهمونطور که از کلید و پاسخ‌برگ مشخصه ۴تا سوال داشتیم. جواب سوال اول گزینه اول هستش (که دانشجو علامتی نزده) جواب سوال دوم هم گزینه اول هستش (که دانشجو اشتباه زده) جواب سوال سوم گزینه سوم هستش (که دانشجو درست زده) و چواب سوال چهارم هم گزینه دوم هستش (که دانشجو دوتا گزینه رو انتخاب کرده و طبق صورت سوال، غلط درنظر گرفته میشه) اگر کلید و پاسخ‌برگ رو به شما بدن چطوری ارزیابی می‌کنید؟حرف اول کلید رو با سطر اول پاسخ‌برگ مقایسه می‌کنیدبعد حرف دوم کلید رو با سطر دوم پاسخ‌برگ مقایسه می‌کنیدبعد حرف nام کلید رو با سطر nام پاسخ‌برگ مقایسه می‌کنیدخب اینجا هم همینکار رو می‌کنیم. یه حلقه می‌زنیم به تعداد حروف کلید و برای اینکه تعداد پاسخ‌های درست و غلط رو داشته باشیم دوتا متغیر تعریف می‌کنیم به نام correctAnswers و wrongAnswers:var correctAnswers = 0; var wrongAnswers = 0; for (var i = 0; i < keys.Length; i++) { }در داخل این حلقه باید personAnswers[i] رو با keys[i] مقایسه کنیم.برای هر ردیف لازمه بفهمیم چندتا گزینه علامت خورده.اگر هیچ گزینه‌ای علامت نخورده بود: نه جواب درسته و نه غلط (ازش رد می‌شیم)اگر بیشتر از یک گزینه علامت خورده بود: جواب رو غلط در نظر می‌گیریماگر فقط و فقط یک گزینه علامت خورده بود: باید ببینیم با توجه به کلید، گزینه درستی علامت خورده یا نهبیایین همین رو پیاده‌سازی کنیم.برای اینکه تعداد گزینه‌های علامت خورده رو به دست بیاریم می‌تونیم از متد Count استفاده کنیم.for (var i = 0; i < keys.Length; i++) { var markedCount = personAnswers[i].Count(c => c == '#'); if (markedCount < 1) continue; if (markedCount > 1) { wrongAnswers++; continue; } }توی شرط‌ها، اینکه هیچ گزینه‌ای علامت نخورده باشه یا چند گزینه علامت خورده باشه رو بررسی کردیم. حالا در ادامه می‌دونیم فقط و فقط یک گزینه علامت خورده. باید ببینیم آیا گزینه‌ای که علامت خورده با کلید یکی هست یا نه. اگر یکی بود درست درنظر می‌گیریم و اگر نه غلط حساب میشه.توی کلید اگر:حرف A بود باید خونه ۰ علامت خورده باشهحرف B بود باید خونه ۱ علامت خورده باشهحرف C بود باید خونه ۲ علامت خورده باشهحرف D بود باید خونه ۳ علامت خورده باشهاگر بتونیم یه جوری این تبدیل‌ها (مثلا تبدیل A به ۰) رو انجام بدیم توی یک خط می‌تونیم شرط رو ارزیابی کنیم. اما چطوری؟توی کامپیوتر هر حرف کد استانداردی داره. مثلا کد A، عدد ۶۵ هستش. به جدول زیر نگاه کنید:جدول ASCIIبا توجه به این نکته خیلی راحت می‌تونیم تبدیل مورد نظر رو انجام بدیم:if (personAnswers[i][keys[i] - 'A'] != '#') wrongAnswers++; else correctAnswers++;مثلا اگر حرف کلید ما B باشه، میاد کد A (که ۶۵ هست) رو از کد B (که ۶۶ هست) کم میکنه و خروجی میشه ۱. اینطوری چک میکنه که آیا خونه ۱ علامت خورده یا نه. اگر علامت خورده باشه یعنی جواب درسته وگرنه غلطه.در پایان هم با توجه به اینکه تعداد جواب‌های درست و غلط رو داریم می‌تونیم نمره فرد رو محاسبه کنیم:private static int CalculateScore(this IReadOnlyList<string> personAnswers, string keys) { var correctAnswers = 0; var wrongAnswers = 0; for (var i = 0; i < keys.Length; i++) { var markedCount = personAnswers[i].Count(c => c == '#'); if (markedCount < 1) continue; if (markedCount > 1) { wrongAnswers++; continue; } if (personAnswers[i][keys[i] - 'A'] != '#') wrongAnswers++; else correctAnswers++; } return (3 * correctAnswers) - wrongAnswers; }می‌تونید کد کامل رو از این لینک ببینید.امیدوارم براتون مفید بوده باشه. :)اگر دوست داشتین لایک کنیدلینک مطلب رو برای کسایی که فکر می‌کنید به دردشون میخوره بفرستیناگر سوالی هم داشتین همینجا بپرسیدان‌شاءالله در پناه خدا همیشه صبور، شاد و موفق باشین.