کا

کاهش در jq

2 تمرین

درباره‌ی کاهش

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

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

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

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

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

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

در 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) را در انباشتگر ترکیب («fold») می‌کند.
    • در بستر این عبارت، . مقدار انباشتگر است.
    • خروجی این عبارت در انباشتگر ذخیره می‌شود تا در تکرار بعدی به کار برود.
    • بعد از آخرین تکرار، نتیجه‌ی انباشته‌شده، خروجی 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"]
    
ویرایش از طریق GitHub این پیوند در پنجره یا زبانه‌ی جدیدی باز می‌شود

کاهش را یاد بگیرید

تمرین کردن قفل شده است

برای تمرین کاهش قفل 3 تمرین دیگر را باز کنید