لنفترض أننا نريد جمع عناصر مصفوفة من الأعداد. وهناك طرق عديدة لتحقيق هذا الهدف.
في كثير من اللغات، تُصاغ هذه المسألة على هيئة حلقة مع متغير مراكم.
ويمكن كتابة هذه المسألة على هيئة دالة عودية. وبصيغة الكود الزائف، قد يكون لدينا ما يلي.
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] كمدخل، واتخذنا الصفر حالةً ابتدائية، فهذا ما تبدو عليه كل خطوة.
| # | الحالة | العنصر | الدمج | النتيجة |
|---|---|---|---|---|
| 1 | 0 | 10 | 0 + 10 | 10 |
| 2 | 10 | 20 | 10 + 20 | 30 |
| 3 | 30 | 30 | 30 + 30 | 60 |
| 4 | 60 | 40 | 60 + 40 | 100 |
في صيغة 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"]
أنت مدرّس. في نهاية العام، أنشأت درجة رقمية لكل طالب من طلابك. والآن عليك تحويلها إلى تقدير حرفي، وإحصاء عدد الطلاب الذين حصلوا على كل تقدير حرفي
دالة letter_grade تأخذ درجة رقمية كمدخل، وتُرجع التقدير الحرفي.
استخدم هذه النطاقات:
| التقدير | الدرجة |
|---|---|
| A | 90% - 100% |
| B | 80% - 89% |
| C | 70% - 79% |
| D | 60% - 69% |
| F | 0% - 59% |
مثال:
75 | letter_grade # => "C"
الدالة count_letter_grades تأخذ كائنًا يربط أسماء الطلاب بدرجاتهم.
وستُرجع كائنًا يربط كل تقدير حرفي بعدد الطلاب الحاصلين على ذلك التقدير.
مثال:
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
هناك عدة أساليب لحل هذه المسألة.
استخدم المرشّح reduce للتدريب.
سجّل في Exercism لتتعلّم وتتقن jq عبر 12 مفهومًا75 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.