صورت جلسه


حمید پس از نوشتن صورت جلساتش در اسنپ به زبان انگلیسی، متوجه شده که همه‌ی اعداد را شمارشی نوشته، اما باید ترتیبی باشند! برای مثال، به‌جای 1 باید می‌نوشته 1st و به‌جای 22 باید می‌نوشته 22nd. از آن‌جایی که تعداد صورت جلسات حمید بسیار زیاد است، او از شما خواسته تا این تبدیل را به‌صورت خودکار برایش انجام دهید.

ورودی🔗

در تعدادی خط از ورودی استاندارد (stdin) که تعدادشان نامشخص است، یکی از صورت جلسات حمید وارد می‌شود که ممکن است شامل اعداد مختلفی باشد. اعداد حداکثر ۷ رقمی هستند.

خروجی🔗

متن صورت جلسه را پس از تغییر اعداد به اعداد ترتیبی چاپ کنید.

مثال🔗

ورودی نمونه ۱🔗

This is the 1 text. I'm writing this text in my 23 daily.
s12napp
Plain text

خروجی نمونه ۱🔗

This is the 1st text. I'm writing this text in my 23rd daily.
s12thnapp
Plain text

ورودی نمونه ۲🔗

1, we should pay attention to the 2 point!
2, we should pay attention to the 1 point!
And finally, we should pay attention to the 3 point!
Plain text

خروجی نمونه ۲🔗

1st, we should pay attention to the 2nd point!
2nd, we should pay attention to the 1st point!
And finally, we should pay attention to the 3rd point!
Plain text

اروررررر


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

n, err := f.Read(data)
if err != nil {
      if err == io.EOF {
          break
      }
      fmt.Println(err)
      return
}
Go

در نتیجه انتظار دارند پکیج مربوطه، همه ارور‌هایی که ممکن است برگردانده شوند را به شکل فیلد در خود داشته باشد.

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

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

خوشبختانه این کتاب‌خانه، سورس‌کد قابل خواندن (و نه تغییر) دارد و می‌توان از آن به منظور توسعه‌ی پکیج کمکی استفاده کرد.

جزئیات پروژه🔗

  • سورس‌کد فعلی شرکت را از این لینک دانلود کنید.
  • فایل helper.go در پکیج helper را به گونه‌ای تغییر دهید تا فیلد‌های ارورها در آن مقداردهی شوند.
  • باید با تغییرات جزئی در کتاب‌خانه، برنامه شما دچار مشکل نشود. مثلا متن ارورها ممکن است تغییرات جزئی بکند.
  • در پکیج helper می‌توانید به صورت زیر از کتابخانه‌ی the_lib استفاده کنید.
package helper
import (
    "snapp/the_lib"
)
// the_lib.LoadData()
Go

آن‌چه باید آپلود کنید🔗

فایل helper.go تغییریافته‌ی خود را آپلود کنید.

SIMD


اسنپ برای افزایش کارایی کد در سرور‌های خود، می‌خواهد از تکنیک SIMD استفاده کند. این تکنیک به این صورت عمل می‌کند که به جای اینکه در آن واحد یک عملیات روی دیتای ۳۲ بیتی انجام شود، ۴ عملیات روی ۴ دیتای ۸ بیتی بدون علامت انجام می‌شود. به این ترتیب به Parallelism دست‌ پیدا می‌کنیم. اما نکته مهم اینکه اینجا به جای استفاده از قابلیت سخت‌افزاری پردازنده‌ها برای SIMD، از امکانات هم‌روندی زبان گو استفاده می‌کنیم.

جزئیات پروژه🔗

پروژه اولیه را از این لینک دانلود کنید.

در این سوال شما باید تابع Simd را پیاده سازی کنید. تابع شما ورودی‌های زیر را دارد:

  • یک تابع به عنوان عملیات
  • یک uint32 به عنوان ورودی
  • یک uint32 به عنوان خروجی محاسبات

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته شده، فایل main.go را آپلود کنید. در صورتی که از dependency خاصی استفاده کرده‌اید، فایل‌های go.mod و go.sum را به‌همراه فایل main.go زیپ کرده و آن را آپلود کنید.

باگ در تخفیف‌ها


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

گزارش وی به شرح زیر است:

فوری فوری

باگ در ماژول تخفیف

این باگ می‌تواند منجر به ضرر شرکت شود

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

سورس‌کد باگ‌دار (اولیه) را از این لینک دانلود کنید.

نکات🔗

  • در حین برطرف کردن ایرادها، امضای متدهای پابلیک را عوض نکنید.
  • فیلد‌های پابلیک ساختارها را نیز تغییر ندهید.
  • امکان حذف و تغییر و اضافه کردن متدهای پرایوت را دارید.
  • فقط یک باگ در ماژول وجود دارد و شما باید فقط همان را برطرف کنید.
  • شی SnappSystem از سمت سرور می‌آید و دیتای آن حتما صحیح است.
  • شی TripRequest را سیستم کاربر ارسال می‌کند و مستقیم به این متد می‌رسد.
  • مبدا مختصات را همان بالا چپ فرض کنید.
  • باگ در سیستم بررسی تاریخ است.

آن‌چه باید آپلود کنید🔗

پس از پیاده‌سازی موارد خواسته شده، فایل main.go را آپلود کنید.

اسنپ‌باز


تیم فنی اسنپ قصد دارد تا سیستم جدیدی برای مدیریت دستاوردهای کاربران و دادن امتیاز به آن‌ها با بهره‌گیری از gamification پیاده‌سازی کند، اما به دلیل درخواست این تیم برای جذب نیروی فنی، آن‌ها این تسک را به برخی از افرادی سپرده‌اند که در مرحله‌ی اول مصاحبه‌ی اسنپ قبول شده‌اند. مهدی که سرش شلوغ است، اما می‌خواهد در اسنپ استخدام شود، از شما می‌خواهد تا این تسک را برایش انجام دهید. نسخه‌ی اولیه‌ی این سیستم قرار است یک API ساده باشد.

جزئیات پروژه🔗

پروژه‌ی اولیه را از این لینک دانلود کنید. ساختار فایل‌های پروژه به‌صورت زیر است:

snapp_baz
├── achievements
│   └── achievements.go
├── db
│   └── connection.go
├── handlers
│   └── achievements.go
├── test
│   └── achievements_sample_test.go
├── go.mod
├── go.sum
└── main.go
Plain text

پایگاه داده🔗

در این پروژه از پایگاه داده‌ی PostgreSQL استفاده شده است. برنامه شامل جداول زیر است:

  1. کاربران (users):
نام ستون نوع تعریف ملاحضات
id BIGSERIAL شناسه‌ی کاربر PRIMARY KEY
name VARCHAR(255) نام
phone VARCHAR(255) شماره تلفن
  1. دستاوردها (achievements):
نام ستون نوع تعریف ملاحظات
id BIGSERIAL شناسه‌ی دستاورد PRIMARY KEY
title VARCHAR(255) عنوان دستاورد
  1. دستاوردهای کاربران (user_achievements):
نام ستون نوع تعریف
user_id BIGINT شناسه‌ی کاربر
achievement_id BIGINT شناسه‌ی دستاورد

دسترسی به پایگاه داده از طریق تابع db.GetConnection صورت می‌گیرد که به شکل singleton پیاده‌سازی شده است.

منطق کسب دستاوردها🔗

یک map[int](func(int) bool) به شکل singleton در برنامه تعریف شده که از طریق تابع achievements.GetMap قابل دسترسی است. این مپ، نگاشتی از شناسه‌ی دستاوردها به توابعی است که با دریافت شناسه‌ی یک کاربر، مشخص می‌کنند که آیا آن دستاورد توسط کاربر کسب شده است یا خیر.

روت‌ها🔗

نسخه‌ی اولیه‌ی این API شامل روت‌های زیر است:

  • /is_achieved: این روت به تابع handlers.IsAchieved مپ شده است که با دریافت یک user_id و یک achievement_id از query string، مشخص می‌کند که آیا دستاورد توسط کاربر ذکرشده کسب شده است یا خیر.
    • اگر پارامتر user_id مقداردهی نشده باشد، کد پاسخ باید 400 و بدنه‌ی پاسخ باید {"error": "no user id provided"} باشد.
    • اگر پارامتر user_id مقداردهی شده باشد، اما پارامتر achievement_id مقداردهی نشده باشد، کد پاسخ باید 400 و بدنه‌ی پاسخ باید {"error": "no achievement id provided"} باشد.
    • اگر مقدار پارامتر user_id عددی نباشد، کد پاسخ باید 400 و بدنه‌ی پاسخ باید {"error": "invalid user id"} باشد.
    • اگر مقدار پارامتر user_id عددی باشد، اما مقدار پارامتر achievement_id عددی نباشد، کد پاسخ باید 400 و بدنه‌ی پاسخ باید {"error": "invalid achievement id"} باشد.
    • اگر کاربری با شناسه‌ی user_id در جدول users موجود نباشد، کد پاسخ باید 404 و بدنه‌ی پاسخ باید {"error": "user not found"} باشد.
    • اگر کاربری با شناسه‌ی user_id در جدول users موجود باشد، اما دستاوردی با شناسه‌ی achievement_id در جدول achievements موجود نباشد، کد پاسخ باید 404 و بدنه‌ی پاسخ باید {"error": "achievement not found"} باشد.
    • اگر هیچ یک از شرایط فوق برقرار نباشد، کد پاسخ باید 200 و بدنه‌ی پاسخ باید {"is_achieved": true} یا {"is_achieved": false} باشد (بسته به این که کاربر دستاورد را کسب کرده است یا خیر).
  • /get_achievements: این روت به تابع handlers.GetAchievements مپ شده است که با دریافت یک user_id از query string، لیست دستاوردهای کاربر را برمی‌گرداند.
    • اگر پارامتر user_id مقداردهی نشده باشد، کد پاسخ باید 400 و بدنه‌ی پاسخ باید {"error": "no user id provided"} باشد.
    • اگر مقدار پارامتر user_id عددی نباشد، کد پاسخ باید 400 و بدنه‌ی پاسخ باید {"error": "invalid user id"} باشد.
    • اگر کاربری با شناسه‌ی user_id در جدول users موجود نباشد، کد پاسخ باید 404 و بدنه‌ی پاسخ باید {"error": "user not found"} باشد.
    • اگر هیچ یک از شرایط فوق برقرار نباشد، کد پاسخ باید 200 و بدنه‌ی پاسخ باید به فرم {"achievements": ["achievement 1 name", "achievement 2 name"]} باشد (نام دستاوردها باید به‌ترتیب صعودی شناسه‌شان باشد).
  • /refresh_achievements: این روت به تابع handlers.RefreshAchievements مپ شده است که با دریافت یک user_id از query_string، لیست دستاوردهای کاربر را به‌روز می‌کند. در واقع، دستاوردهایی که تاکنون توسط کاربر کسب نشده‌اند باید مجدداً بررسی شوند و در صورتی که دستاورد جدیدی کسب شده بود، در جدول user_achievements درج شود.
    • کد و بدنه‌ی پاسخ این روت باید مشابه روت /get_achievements باشد، با این تفاوت که دستاوردهای کسب‌نشده مجدداً بررسی می‌شوند.

توجه: مقدار هدر Content-Type در پاسخ همه‌ی درخواست‌ها باید برابر با application/json قرار داده شود.

آن‌چه باید آپلود کنید🔗

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

snapp_baz
├── handlers
│   └── achievements.go
├── go.mod (Optional)
└── go.sum (Optional)
Plain text

SnappQL


کوئری‌های شما باید روی PostgreSQL 13 قابل اجرا باشد.


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

جزئیات پروژه🔗

ساختار جداول به‌صورت زیر است:

  1. کاربران (users):
نام ستون نوع تعریف ملاحضات
id BIGSERIAL شناسه‌ی کاربر PRIMARY KEY
name VARCHAR(255) نام
phone VARCHAR(255) شماره تلفن
  1. سفرها (trips):
نام ستون نوع تعریف ملاحظات
id BIGSERIAL شناسه‌ی سفر PRIMARY KEY
user_id BIGINT شناسه‌ی مسافر
driver_id BIGINT شناسه‌ی سفیر (جدول سفیران در این مسئله وجود ندارد)
started_at TIMESTAMP زمان شروع سفر
finished_at TIMESTAMP زمان پایان سفر
  1. دعوت کاربران به اپلیکیشن (invitations):
نام ستون نوع تعریف ملاحظات
id BIGSERIAL شناسه‌ی دعوت PRIMARY KEY
inviter_user_id BIGINT شناسه‌ی کاربر دعوت‌کننده
invitee_user_id BIGINT شناسه‌ی کاربر دعوت‌شده
invited_at TIMESTAMP زمان دعوت

مطلوبات🔗

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

  1. شناسه‌ی کاربرانی که شماره‌ی آن‌ها شامل زیررشته‌ی 1400 است، به‌ترتیب صعودی برحسب شناسه
  2. شناسه‌ی سفرهایی که زمان شروع‌شان بین ساعت ۲۲ تا ۱ بامداد است، به‌ترتیب صعودی برحسب شناسه
  3. شناسه، نام، شماره موبایل و جزء صحیح تعداد ساعات سفرهای کاربران (مجموع اختلاف زمان شروع و پایان سفرهای‌شان)، به‌ترتیب نزولی برحسب تعداد ساعات (اگر تعداد ساعات دو کاربر یکسان باشد، کاربری باید زودتر بیاید که شناسه‌اش کوچک‌تر است.)
  4. شناسه و شماره تلفن افرادی که حداقل در سه ماه مختلف میلادی، در هر یک از این ماه‌ها حداقل سه نفر را به اسنپ دعوت کرده‌اند، به‌ترتیب صعودی برحسب شناسه

نکته: نام ستون‌های خروجی مهم نیست، اما ترتیب آن‌ها مهم است.

آن‌چه باید آپلود کنید🔗

کوئری‌های خود را در قالب زیر، در یک فایل با پسوند .sql قرار داده و آن را ارسال کنید (فایل را زیپ نکنید):

-- Section1
  your 1st query here
-- Section2
  your 2nd query here
-- Section3
  your 3rd query here
-- Section4
  your 4th query here
SQL