Python itertools.accumulate: เก็บสถานะสะสมโดยไม่สร้างลูปข้างเคียง
running total, ยอดคงเหลือ และ state ที่เดินตาม event มักเขียนเป็นลูปพร้อมตัวแปร mutable ซึ่งอ่านได้ แต่เปิดช่องให้ update กับ append หลุดคนละจังหวะ itertools.accumulate คืนค่ากลางทุกขั้นแบบ lazy จึงเหมาะกับการต่อ pipeline และหยุดอ่านก่อนจบ stream ได้ครับ
import unittest
from itertools import accumulate
def balances(opening, movements):
return list(accumulate(movements, initial=opening))
class AccumulateTests(unittest.TestCase):
def test_running_balance_includes_opening(self):
self.assertEqual(balances(100, [20, -15, 5]), [100, 120, 105, 110])
def test_empty_stream_still_has_opening(self):
self.assertEqual(balances(40, []), [40])
if __name__ == "__main__":
unittest.main()
เมื่อใส่ initial ผลลัพธ์จะยาวกว่า input หนึ่งค่า เพราะมีสถานะก่อน event แรก จุดนี้ดีสำหรับกราฟและ audit trail แต่ทำให้ zip(input, accumulate(..., initial=...)) เหลื่อมหนึ่งตำแหน่ง ต้องเลือกว่าจะจับ event กับสถานะก่อนหรือหลังอย่างตั้งใจ ฟังก์ชันสะสมยังรับ callable สอง argument จึงทำ running maximum หรือ state transition ได้ ไม่ได้จำกัดแค่การบวก
Recap
- ใช้กับ state ที่ต้องการผลทุกขั้น ไม่ใช่แค่ผลสุดท้าย
- จำว่า
initialเพิ่ม output หนึ่งรายการ - ทดสอบ input ว่างและค่าติดลบ
- คง accumulator ให้เป็นฟังก์ชันบริสุทธิ์เมื่อทำได้