مسیرها
/
jq
jq
/
تمرین‌ها
/
آمار نمرات
آمار نمرات

آمار نمرات

تمرین یادگیری

مقدمه

کاهش

فرض کنید می‌خواهیم اعداد یک آرایه را با هم جمع بزنیم. راه‌های زیادی برای رسیدن به این هدف وجود دارد.

  • در بسیاری از زبان‌ها، این مسئله به شکل یک حلقه با یک متغیر انباشتگر بیان می‌شود.

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

    function Add(X, Sum=0):
      if X is empty then
        return Sum
      else
        return Add(rest(X), Sum + first(X))
      end
    end
    

    این روش تقسیم مسئله به قطعات کوچک‌تر را می‌توان «کاهش به سمت حالت پایه» هم توصیف کرد.

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

در jq، این فرایند در فیلتر reduce پیاده‌سازی شده است. در زبان‌های دیگر، ممکن است fold، fold-left، inject یا aggregate نامیده شود.

عبارت reduce در jq به این شکل است.

reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
  • STREAM_EXPRESSION یک «جریان» از عناصر است که هر کدام به نوبت در متغیر $var ذخیره می‌شود.
    • به یاد بیاورید که برای جریان دادن یک آرایه، از فیلتر تکرارگر .[] استفاده کنید: $myArray | .[].
  • INITIAL_VALUE مقدار شروع نتیجه‌ی انباشته‌شده است (که به آن «انباشتگر» می‌گویند).
  • UPDATE_EXPRESSION مقدار جاری ($var) را در انباشتگر ترکیب («فولد») می‌کند.
    • در بافت این عبارت، . مقدار انباشتگر است.
    • خروجی این عبارت در انباشتگر ذخیره می‌شود تا در تکرار بعدی به کار برود.
    • پس از آخرین تکرار، نتیجه‌ی انباشته‌شده خروجی reduce است.

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

اگر [10, 20, 30, 40] را به عنوان ورودی در نظر بگیریم و صفر را حالت اولیه بگیریم، هر مرحله به این شکل است.

# حالت عنصر کاهنده نتیجه
۱ ۰ ۱۰ ۰ + ۱۰ ۱۰
۲ ۱۰ ۲۰ ۱۰ + ۲۰ ۳۰
۳ ۳۰ ۳۰ ۳۰ + ۳۰ ۶۰
۴ ۶۰ ۴۰ ۶۰ + ۴۰ ۱۰۰

در نحوه‌ی نگارش jq، این به شکل کد زیر است.

0 + 10 | . + 20 | . + 30 | . + 40

می‌توانیم آن را با فیلتر reduce بیان کنیم.

[10, 20, 30, 40] | reduce .[] as $n (0; . + $n)     # => 100
Note

فیلتر درونی add در واقع با reduce پیاده‌سازی شده است، اما null را به عنوان حالت اولیه به کار می‌برد (هر نوع داده‌ای را می‌توان به null اضافه کرد).

def add: reduce .[] as $x (null; . + $x);

چند نکته که باید به خاطر بسپارید

  • در عبارت کاهش، . همان انباشتگر است. اگر ورودی شیئی باشد که لازم است داخل تابع کاهش به آن ارجاع دهید، باید آن را در یک متغیر ذخیره کنید.

    {"apple": 10, "banana": 16, "carrot": 4}
    | . as $obj
    | reduce (keys | .[]) as $key (0; . + $obj[$key])     # => 30
    
  • انباشتگر می‌تواند از هر نوع داده‌ای باشد. برای مثال ممکن است بخواهید ترتیب یک آرایه را برعکس کنید.

    ["A", "B", "C", "D"]
    | reduce .[] as $elem ([]; [$elem] + .)       # => ["D", "C", "B", "A"]
    

دستورالعمل‌ها

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

1. تبدیل نمره‌ی عددی به نمره‌ی حرفی

تابع letter_grade یک نمره‌ی عددی را به عنوان ورودی می‌گیرد و حرف را به عنوان خروجی برمی‌گرداند. از این بازه‌ها استفاده کنید:

حرف نمره
A ۹۰٪ - ۱۰۰٪
B ۸۰٪ - ۸۹٪
C ۷۰٪ - ۷۹٪
D ۶۰٪ - ۶۹٪
F ۰٪ - ۵۹٪

مثال:

75 | letter_grade   # => "C"

2. شمارش تعداد دانش‌آموزان برای هر نمره‌ی حرفی

تابع count_letter_grades یک شیء می‌گیرد که اسم دانش‌آموزان را به نمره‌هایشان نگاشت می‌کند. خروجی یک شیء خواهد بود که هر نمره‌ی حرفی را به تعداد دانش‌آموزانی با آن نمره نگاشت می‌کند.

مثال:

{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}

راه‌های متفاوتی برای حل این مسئله وجود دارد. برای تمرین از فیلتر reduce استفاده کنید.

ویرایش از طریق GitHub این لینک در پنجره یا زبانه‌ی جدیدی باز می‌شود
jq Exercism

آماده‌اید آمار نمرات را شروع کنید؟

در Exercism ثبت‌نام کنید تا jq را همراه با 12 مفهوم75 تمرین و مربی‌گری انسانی واقعی یاد بگیرید و در آن استاد شوید، همه‌ی این‌ها رایگان.