أحيانًا تريد دمج تسلسل في قيمة واحدة، وأحيانًا تريد رؤية كل قيمة وسيطة ينتجها الدمج على طول الطريق. ويقسم Factor هذا إلى أداتين: reduce (في sequences) من أجل الطيّ إلى قيمة واحدة، والعائلة التراكمية في math.statistics من أجل الصيغة الجارية.
reduce: الطيّ العامreduce ( seq init quot: ( prev elt -- next ) -- result )
يمرّ reduce على تسلسل عنصرًا عنصرًا، حاملًا معه نتيجة جارية (المراكم) ويمرّرها إلى اقتباس من وسيطين. يستقبل الاقتباس المراكم الجاري والعنصر التالي، وأيًّا كان ما يتركه على المكدّس يصبح المراكم الجديد.
USING: math sequences ;
{ 1 2 3 4 } 0 [ + ] reduce . ! => 10
{ 1 2 3 4 } 1 [ * ] reduce . ! => 24
القيمة الأولية غير الصفرية ودالة الدمج المخصّصة هما ما لا تستطيع sum وproduct بلوغه في reduce. على سبيل المثال، أكبر قيمة في تسلسل، مع قيمة افتراضية إن لم يتفوّق عليها أي عنصر:
USING: math.order ;
{ 3 1 -4 5 -2 } 0 [ max ] reduce . ! => 5
{ -3 -1 -4 } 0 [ max ] reduce . ! => 0
تشارك القيمة الأولية 0 في المقارنة: فهي تعمل كنتيجة عندما يخسر كل عنصر، لذا يُنتج تسلسل كل قيمه سالبة 0 بدلًا من أصغر قيمة اعتباطية.
أحيانًا تريد كل النتائج الوسيطة، لا النتيجة النهائية فقط. وتُرجع العائلة التراكمية في math.statistics تسلسلًا بالطول نفسه الذي للمدخل، حيث يكون كل موضع هو الاختزال على البادئة المنتهية عند ذلك الموضع:
cum-sum ( seq -- newseq ) ! running total
cum-product ( seq -- newseq ) ! running product
cum-min ( seq -- newseq ) ! running minimum
cum-max ( seq -- newseq ) ! running maximum
USING: math.statistics ;
{ 3 1 4 1 5 9 2 6 } cum-sum . ! => { 3 4 8 9 14 23 25 31 }
{ 1 2 3 4 } cum-product . ! => { 1 2 6 24 }
{ 3 1 4 1 5 9 2 6 } cum-min . ! => { 3 1 1 1 1 1 1 1 }
{ 3 1 4 1 5 9 2 6 } cum-max . ! => { 3 3 4 4 5 9 9 9 }
من الأنماط المفيدة ربط الاختزالات التراكمية ببعضها: فمخرجات إحداها تسلسل في نفسه، جاهز للتغذية في أخرى. وهذا يجعل «ملخّص جارٍ لملخّص جارٍ» قابلًا للتعبير عنه بكلمتين. والتركيبات مرنة، فاقرن بينها بناءً على ما يلخّصه كل خطوة.
produce: النشريستهلك reduce تسلسلًا ليصير قيمة. أما produce (في sequences) فيسير في الاتجاه المعاكس: يُنشئ تسلسلًا من قيمة أولية عبر الاختبار والتقدّم بصورة متكررة:
produce ( pred quot -- seq )
تُشغّل كل تكرار أولًا pred على الحالة الراهنة، وإذا أرجعت قيمة تُعتبر صحيحة، يُستدعى quot لإنتاج العنصر التالي وتحديث الحالة. وعندما تُرجع pred القيمة f، يتوقف التكرار وتُرجَع العناصر المجمّعة.
ومن الأمثلة الكلاسيكية متتالية فيبوناتشي (كل عدد هو مجموع العددين السابقين). والحالة الجارية هي الزوج (a, b). وتُصدر كل خطوة b، ثم تستبدل الزوج بـ (b, a + b):
USING: kernel math sequences ;
! Fibonacci numbers strictly below 100:
0 1 [ dup 100 < ] [ tuck + over ] produce 2nip .
! => { 1 1 2 3 5 8 13 21 34 55 89 }
تمتد الحالة الجارية على قيمتين، لذا يستخدم الجسم tuck (في kernel)، وهو الخلط الثلاثي الذي ينسخ العنصر الأعلى أسفل العنصر الثاني، لتقديم الزوج، ويستخدم 2nip (وهو أيضًا في kernel، النظير الثنائي لـ nip) للترتيب في النهاية. وإذا قرأنا الاستدعاء من اليسار إلى اليمين:
[ dup 100 < ] إلى أعلى الزوج (وهو العدد التالي الذي سيُصدر) ويستمر ما دام لا يزال دون الحد.[ tuck + over ] الحالة إلى (b, a + b) ويُصدر b، تاركًا ثلاث قيم على المكدّس: الزوج الجديد في الأسفل، والعدد المُصدر في الأعلى.produce، تُهمَل القيمتان الزائدتان (الزوج الأخير) بـ 2nip، فلا يبقى إلا التسلسل الناتج.produce هو النظير التام لـ reduce: فبينما يطوي reduce تسلسلًا حتى يصير قيمة، ينشر produce قيمة حتى تصير تسلسلًا.
أنت أمين المكتبة، وتُمسك بدفتر حسابات المستعيرين. يصل إلى مكتبك كل أسبوع نوعان من العمل:
في كل أسبوع تُجري الجرد: رصيد نهائي بعد تنفيذ الطلبات، ورصيد جارٍ يومي من المعاملات، وحد أدنى جارٍ لرصد الفترات التي ارتفعت فيها الغرامات.
عرّف protected-balance لتأخذ رصيد opening ومصفوفة من requests (مبالغ بإشارة) وتُرجع الرصيد النهائي بعد تنفيذ كل طلب على التوالي. السحب الذي قد يخفض الرصيد إلى ما دون الصفر يُنفَّذ فقط بمقدار المبلغ المتاح، بحيث يستقر الرصيد الجاري عند الصفر.
100 { 50 -200 30 } protected-balance .
! => 30
500 { 100 -300 -250 } protected-balance .
! => 50
0 { -10 50 } protected-balance .
! => 50
عرّف running-balance لتأخذ مصفوفة من transactions وتُرجع متتالية بالطول نفسه، حيث يكون عنصرها ذو الفهرس i هو الرصيد بعد أول i+1 من المعاملات (نسبةً إلى رصيد ابتدائي صفر).
{ 50 -30 -20 100 } running-balance .
! => { 50 20 0 100 }
عرّف least-balance-so-far لتأخذ مصفوفة من transactions وتُرجع متتالية بالطول نفسه، حيث يكون عنصرها ذو الفهرس i هو أدنى رصيد جارٍ لوحظ حتى الموضع i ضمناً. هذا هو الحد الأدنى الجاري، وهو مفيد لرصد الأيام التي بدا فيها الحساب محفوفًا بالمخاطر.
{ 50 -30 -20 100 } least-balance-so-far .
! => { 50 20 0 0 }
{ 200 -50 -100 -200 } least-balance-so-far .
! => { 200 150 50 -150 }
تُشغّل المكتبة برنامج عفو عن الغرامات: يُنصَّف الرصيد المستحق للمستعير في كل فترة سداد حتى يصل إلى حد التسامح أو ينخفض عنه. عرّف halve-until لتأخذ principal وtarget، وتُرجع متتالية القيم المنصَّفة (باستخدام القسمة الصحيحة) بدءًا من أول تنصيف، وتستمر ما دامت القيمة الجارية لا تزال أكبر تمامًا من target. وستكون آخر قيمة مُصدَرة هي أول قيمة تنخفض إلى target أو عنه.
100 5 halve-until .
! => { 50 25 12 6 3 }
64 1 halve-until .
! => { 32 16 8 4 2 1 }
3 5 halve-until .
! => { }
سجّل في Exercism لتتعلّم وتتقن Factor عبر 47 مفهومًا163 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.