فرض کنید میخواهیم اعداد یک آرایه را با هم جمع کنیم. راههای زیادی برای رسیدن به این هدف وجود دارد.
در بسیاری از زبانها، این مسئله به شکل یک حلقه با یک متغیر انباشتگر بیان میشود.
این مسئله را میتوان به شکل یک تابع بازگشتی نوشت. در شبهکد، ممکن است چیزی شبیه این داشته باشیم.
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
فیلتر توکار 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"]