در یک دشت سرسبز تعدادی خرگوش و یک روباه وجود دارد. خرگوشها در طی هر سال تعدادشان دوبرابر میشود و روباه نیز در پایان سال تعدادی خرگوش را شکار کرده و میخورد.
از شما میخواهیم با داشتن تعداد اولیه خرگوشها و تعدادی که هر سال توسط روباه خورده میشوند، جمعیت آنها را بعد از چند سال محاسبه کنید.
در اولین خط ورودی، تعداد اولیه خرگوشها () و تعداد خرگوشهایی که هرساله توسط روباه خورده میشوند () با یک فاصله داده میشود.
در خط دوم ورودی، انتهای سالی که جمعیت خرگوشها پس از آن مدنظر است میآید. ()
تضمین میشود که در پایان سال اول، روباه همهی خرگوشها را نمیخورد.
همچنین تعداد خرگوشها در هیچ زمانی صفر یا منفی نمیشود.
در تنها خط خروجی، تعداد خرگوشهایی که در آخر سال ام زنده هستند را چاپ کنید.
دقت کنید که در سال آخر نیز روباه خرگوش را میخورد و بعد از آن تعداد را محاسبه میکنیم.
در اینجا ۵ خرگوش اولیه داریم که در پایان سال به ۱۰ تا میرسد، سپس روباه ۲ تا از آنها را میخورد. در پایان ۱ سال، ۸ خرگوش باقیمانده داریم.
۳ خرگوش در پایان سال اول به ۵ خرگوش میرسند.
در پایان سال دوم به ۹ خرگوش میرسند.
در پایان سال سوم به ۱۷ خرگوش میرسند.
در نهایت در پایان سال چهارم به ۳۳ خرگوش میرسند.
تیم کدکاپ برای تهیه کیبوردهای مرحلهی حضوری کدکاپ به مشکل برخورد کرده است. قضیه از این قرار است که این کیبوردها بسیار بیکیفیت هستند و کلیدهایشان زود خراب میشود. بعد از اینکه یک کلید خراب میشود با فشار دادن آن هیچ اتفاقی نمیافتد.
در واقع هر کلید روی کیبورد یک عمر مشخص دارد و عمر همهی کلیدهای یک کیبورد نیز با هم برابر است. به تعداد بارهای قابل پذیرش قبل از خراب شدن یک کلید روی کیبورد، «عدد کیفیت» میگوییم. برای مثال ممکن است «عدد کیفیت» یک کیبورد برابر ۲ باشد، در این صورت هر یک از کلیدها ۲ بار کار میکند و پس از آن از کار میافتد.
یعنی در این کیبورد به ازای متن ورودی زیر:
متن زیر را دریافت میکنید:
در اینجا چند تا از حروف خراب شدند؛ مثلاً o
خراب شد و دیگر کار نکرد.
حال در این مسئله از شما میخواهیم که با داشتن «عدد کیفیت» یک کیبورد، مشخص کنید اگر کاربر بخواد با آن یک متن اولیه را وارد کنید به جایش چه متنی وارد میشود.
پروژهی اولیه را از این لینک دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
در فایل main.go
دو تابع تعریف شدهاند که شما باید آنها را کامل کنید:
این تابع قرار است یک شی از نوع Keyboard را مقداردهی کند و بازگرداند. ورودی این متد همان «عدد کیفیت» کیبورد است. ساختار داخلی Keyboard را نیز میتوانید به دلخواه طراحی و استفاده کنید.
این تابع یک رشته ورودی میگیرد و مانند این است که کاربر آن رشته را تایپ کرده است. در خروجی چیزی که واقعا تایپ میشود را برگردانید.
دقت کنید که ممکن است برای یک کیبورد چندین بار عملیات تایپ انجام شود. همچنین ممکن است در طول تایپ یک کلمه یک کلید خراب شود و در ادامهی کلمه خراب باشد.
برای سادگی فرض کنید ورودی inp فقط از کارکترهای زیر تشکیل شده است:
?!'
پس از پیادهسازی توابع خواسته شده، فایل main.go
را آپلود کنید.
سرویس Gravatar یک سرویس جهانی برای نگهداری آواتار (عکس پروفایل) افراد در سطح اینترنت است. اغلب وبسایتها (مخصوصاً وبسایتهای وردپرسی) از این سرویس برای نمایش آواتار پیشفرض کاربران استفاده میکنند. در این وبسایت به هر آدرس ایمیل یک تصویر اختصاص مییابد. تصویری که افراد در این وبسایت آپلود میکنند، در همهی وبسایتهایی که از Gravatar استفاده میکنند قابل مشاهده است.
حسن یک API ساده برای تولید لینک تصویر افراد براساس آدرس ایمیلشان طبق این راهنما نوشته بود و برای آن تعدادی تست نوشته بود، اما پس از چند روز، طی یک سانحه، کد پیادهسازیشدهی برنامه از سیستمش حذف شدند! او اکنون فقط تستهای برنامه را در دسترس دارد، اما دیگر حوصلهی پیادهسازی مجدد برنامه را ندارد. از شما میخواهیم این برنامه را برایش پیادهسازی کنید تا تستهایش پاس شوند.
پروژهی اولیه را از این لینک دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
برنامه را بهگونهای پیادهسازی کنید که همهی تستهای سؤال پاس شوند.
go test ./test/...
استفاده کنید.پس از پیادهسازی برنامه، محتویات دایرکتوری اصلی برنامه را زیپ کرده و ارسال کنید، بهطوری که هنگام باز کردن فایل زیپ، فایل main.go
(و سایر فایلها، در صورت نیاز) دیده شود.
چیزی که از شما در این سوال میخواهیم، یک حافظهی نهان (Cache) از نوع کلید و مقداری است. این حافظهی نهان، اطلاعات را داخل دیسک نگهداری نمیکند و از نوع «داخل حافظهای» است.
تا اینجا احتمالاً ذهنتان سمت Redis رفته است، اما این حافظه به شکل خاص برای زبان Go طراحی شده و از امکانات این زبان بهره میبرد. به طور خاص برای اتصال از سمت کاربر به حافظهی نهان از چنلها استفاده میشود.
روال کار استفاده از حافظهی نهان را میتوان به این شکل خلاصه کرد:
به جز Get
و Set
که قابلیتهای اولیهی یک حافظهی نهان هستند، یک عملیات دیگر نیز تعریف میشود به نام SetWithRTL
که در اینجا RTL مخفف Request to Live است. این عدد برای هر کلید تعریف میشود و به معنی تعداد درخواستهای خواندن است که پس از ذخیره شدن مقدار آن کلید میتوانیم انجام دهیم تا نهایتاً آن کلید حذف شود. برای نمونه اگر یک RTL برابر با ۳ داشته باشیم، چنین اتفاقی میتواند بیفتد: پس از ۳ بار خواندن، آن مقدار از حافظه حذف شده است، بهگونهای که اصلاً وجود نداشته.
پروژهی اولیه را از این لینک دانلود کنید. ساختار فایلهای پروژه به صورت زیر است:
در فایل cache.go
چند تابع وجود دارد که در main.go
استفاده شدهاند و شما باید آنها را کامل کنید.
در فایل main.go
که نمیتوانید محتوای آن را تغییر دهید، پیادهسازی سمت کاربر این حافظهی نهان آمده است و در فایل تست نمونه، نحوه استفاده از آن آمده است.
در فایل cache.go
فقط امضای توابع آمدهاند. شما باید مطابق خواستهی صورت سؤال توابع را کامل کرده و در صورت نیاز توابع جدید اضافه کنید.
main.go
🔗در این فایل همهی توابع پیادهسازی شدهاند و بر اساس این پیادهسازیها پروتوکل ارتباط بین کلاینت و سرور را مشاهده خواهید کرد. برای مثال، در صورتی که برای کلید مقداری وجود نداشته باشد، رشتهی خالی برمیگردد.
cache.go
🔗InitCache
🔗در این تابع یک کش جدید ساخته شده و مقدار میگیرد و اشارهگر به آن برمیگردد. همچنین در صورت نیاز میتواند عملیات دیگری نیز انجام شود.
Connect
🔗در این قسمت یک کاربر جدید با یک نام جدید (که تضمین میشود یکتاست) میخواهد به این کش متصل شود. برای این منظور یک جفت چنل درست میشود که در قالب یک CacheClientConnection
به کاربر برمیگردد. همچنین باید این دو چنل برای خود کش نیز نگه داشته شوند تا بتوان به آنها گوش داد.
پس از پیادهسازی توابع خواستهشده، فایل cache.go
را آپلود کنید.
علی بهتازگی با کتابخانهی GORM که یک ORM به زبان Go است آشنا شده. او داکیومنت این کتابخانه را مطالعه کرده و به مرور زمان در حال پی بردن به امکانات جذاب این کتابخانه است. از آنجایی که علی فرد کنجکاوی است، او تصمیم گرفته تا سورس کد این کتابخانه را مطالعه کند و دریابد که این کتابخانه چگونه کار میکند. از آنجایی که کد این کتابخانه بسیار طولانی است و علی حوصلهی خواندن آن را ندارد. به دنبال نسخهی سادهای از یک ORM مشابه GORM میگردد تا بتواند با خواندن سورس کد آن، نحوهی کارکرد آن را متوجه شود. از شما میخواهیم تا چنین کتابخانهای را برای علی پیادهسازی کنید.
پروژهی اولیه را از این لینک دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
EntityConfigurator
🔗این استراکت در فایل configurators.go
تعریف شده است. موجودیتهای برنامه میتوانند شامل اطلاعات مختفی نظیر نام جدول باشند. این اطلاعات میتوانند در این استراکت ذخیره شوند. متدی با نام Table
برای این نوع داده تعریف شده است که فیلد table
(نام جدول) مربوط به موجودیت را مقداردهی میکند. در صورت نیاز، میتوانید ساختار این استراکت را تغییر دهید، اما نباید امضای متد Table
را تغییر دهید.
Driver
🔗قرار است ORM ما بتواند به دیتابیسهای مختلف متصل شود. درایورها، جزئیات پیادهسازی مربوط به DBMS های مختلف را مشخص میکنند. استراکت Driver
در فایل driver.go
تعریف شده است. همچنین یک استراکت بدون نام در متغیر Drivers
تعریف شده که شامل دو درایور در قالب فیلدهای PostgreSQL
و SQLite3
است. نام درایور PostgreSQL
را برابر با postgres
و نام درایور SQLite3
را برابر با sqlite3
قرار دهید. یک فیلد با نام PlaceHolderGenerator
نیز وجود دارد که مقدار آن یک تابع است که با دریافت تعداد پارامترهای موجود در یک کوئری SQL ، آرایهای از رشتهها که بیانگر placeholder مقادیر موجود در کوئری هستند را برمیگرداند. این تابع را برای دو درایور تعریفشده بهصورت زیر پیادهسازی کنید:
PostgreSQL
، تابع PlaceHolderGenerator
باید با دریافت n
، مجموعهای از رشتهها به فرم $i
(بهطوری که i
از 1
تا n
است) را برگرداند. برای مثال، اگر مقدار n
برابر با 3
باشد، خروجی باید بهترتیب شامل رشتههای $1
، $2
و $3
باشد.SQLite3
، تابع PlaceHolderGenerator
باید با دریافت n
، یک آرایه از رشتهها بهطول n
که مقادیر آن همگی برابر با ?
هستند برگرداند.Entity
🔗این اینترفیس در فایل orm.go
تعریف شده و شامل یک متد ConfigureEntity(e *EntityConfigurator)
است. موجودیتهای برنامه باید این اینترفیس را پیادهسازی کنند. متد ConfigureEntity
این اینترفیس، پیکربندی اولیهی یک Entity
را انجام میدهد (مثلاً نگهداری نام جدول مربوط به موجودیت) که به ازای هر Entity
توسط برنامهنویس آن پیادهسازی میشود.
ConnectionConfig
🔗این استراکت شامل پوینتری به یک sql.DB
، پوینتری به یک درایور و آرایهای از موجودیتهای برنامه است. از این ConnectionConfig
ها در تابع SetupConnections
استفاده میشود.
SetupConnections
🔗این تابع با دریافت تعداد نامشخصی ConnectionConfig
، اتصال مربوط به کانکشنها را برقرار میکند و مقداردهیهای اولیهی لازم برای کانکشنها را انجام میدهد.
Find
🔗این تابع با دریافت کلید اصلی (عددی) یک موجودیت از نوع T
(جنریک)، اطلاعات مربوط به سطر موردنظر از دیتابیس را باید در قالب یک آبجکت از نوع T
برگرداند. نام ستونها در جدول بهصورت snake_case
هستند، اما نام فیلدهای نوع دادهی T
بهصورت PascalCase
است. برای مثال، اگر در یک موجودیت، فیلدی با نام BodyText
داشته باشیم، در جدول متناظر با موجودیت، ستونی با نام body_text
وجود خواهد داشت. تضمین میشود که فیلدها برای ستونهای مختلف جدول تعریف شدهاند.
All
🔗این تابع، اطلاعات مربوط به سطرهای جدول موردنظر از دیتابیس (با توجه به نوع دادهی جنریک) را باید در قالب آرایهای از T
ها برگرداند.
First
🔗این تابع باید اطلاعات اولین سطر (بر اساس کلید اصلی جدول) در جدول موردنظر (با توجه به نوع دادهی جنریک) را در قالب آبجکتی از نوع T
برگرداند.
Last
🔗این تابع باید اطلاعات آخرین سطر (بر اساس کلید اصلی جدول) در جدول موردنظر (با توجه به نوع دادهی جنریک) را در قالب آبجکتی از نوع T
برگرداند.
QueryBuilder
🔗این استراکت شامل اطلاعات یک کوئری برای تبدیل به فرمت SQL است که در فایل query.go
تعریف شده. آن را به دلخواه پیادهسازی کنید.
تابع NewQueryBuilder
را بهگونهای پیادهسازی کنید که یک نمونهی جدید از نوع QueryBuilder
برگرداند.
متدهای زیر برای استراکت QueryBuilder
تعریف شده که باید آنها را پیادهسازی کنید:
Get
: این متد باید کوئری مدنظر (با توجه به متدهای فراخوانیشده روی QueryBuilder
) را اجرا کرده و نتیجه را در قالب یک آبجکت از نوع OUTPUT
(بهصورت جنریک) برگرداند. در اینصورت، تنها اولین نتیجه برمیگردد.All
: این متد باید کوئری مدنظر (با توجه به متدهای فراخوانیشده روی QueryBuilder
) را اجرا کرده و همهی نتایج را در قالب آرایهای از OUTPUT
ها (بهصورت جنریک) برگرداند.OrderBy
: این متد با دریافت نام یک ستون و نحوهی ترتیب (ASC
یا DESC
، بهمعنای صعودی یا نزولی) باید یک بخش ORDER BY
به کوئری اضافه کند.Where
: این متد میتواند حداقل دو آرگومان دریافت کند:AND
شود.=
) و سومین آرگومان مقدار مدنظر خواهد بود. شرط واردشده باید به کوئری اضافه شود. در صورتی که شرطی از قبل به کوئری اضافه شده باشد، شرط فعلی باید با شرطهای قبلی AND
شود.IN
باشد، آرگومان اول برابر با نام ستون موردنظر خواهد بود. آرگومان سوم به بعد، مقادیری هستند که مقدار ستون موردنظر باید حداقل برابر با یکی از آنها باشد.WhereIn
: عملکرد این متد، مشابه حالت سوم متد Where
است.AndWhere
: عملکرد این متد، مشابه متد Where
است.OrWhere
: عملکرد این متد، مشابه متد Where
است، با این تفاوت که اگر شرطی از قبل در کوئری موجود باشد، شرط جدید با شرطهای قبلی OR
خواهد شد.Limit
: این متد با دریافت یک عدد، limit موجود در کوئری را مشخص میکند.Offset
: این متد با دریافت یک عدد، offset موجود در کوئری را مشخص میکند.Table
: این متد با دریافت نام جدول در قالب یک رشته، نام جدولی که کوئری باید روی آن اجرا شود را مشخص میکند.GroupBy
: این متد با دریافت تعداد نامشخصی رشته، یک عبارت group by به کوئری اضافه میکند. در صورتی که این متد چند بار فراخوانی شود، ستونها باید بهترتیب به بخش group by کوئری اضافه شوند.Select
: این متد با دریافت تعداد نامشخصی رشته، مشخص میکند که مقدار کدام ستونها از جدول دیتابیس دریافت شوند. اگر این متد فراخوانی نشود، همهی ستونها (*
) باید از جدول موجود در دیتابیس دریافت شوند. اگر این متد چند بار فراخوانی شود، ستونها باید بهترتیب به کوئری اضافه شوند.SetDriver
: این متد با دریافت یک Driver
، باید موارد موردنیاز برای کوئری بیلدر (نظیر PlaceHolderGenerator
) را به موارد موجود در Driver
تغییر دهد.ToSql
: این متد باید کوئری SQL تولیدشده را در قالب یک رشته و مقادیر پارامترهای موجود در کوئری را در قالب آرایهای از interface{}
ها برگرداند. اگر اولین فراخوانی روی یک آبجکت جدید QueryBuilder
مربوط به متد ToSql
بود، مقدار خروجی error
را برابر با یک رشتهی غیر از خالی قرار دهید. در غیر اینصورت، error
را nil
برگردانید.نکته: در صورتی که چند شرط AND
و OR
شوند، صرفاً کافی است تا آنها را با استفاده از عملگرهای مربوطه (بین پرانتز) کنار یکدیگر قرار دهید. مثال:
کوئریهای تولیدشده توسط QueryBuilder
باید مطابق با مثالهای زیر باشند (فاصله و کوچکی و بزرگی حروف مهم است):
کوئری ایجادشده:
کوئری ایجادشده در درایور SQLite3:
کوئری ایجادشده در درایور PostgreSQL:
go.mod
و go.sum
را نیز در فایل زیپ ارسالی خود قرار دهید.پس از پیادهسازی برنامه، محتویات دایرکتوری اصلی برنامه را زیپ کرده و آپلود کنید، بهطوری که وقتی آن را باز میکنیم، با فایل driver.go
و سایر فایلهای برنامه مواجه شویم.