ข้ามไปยังเนื้อหา

itertools & functools

เวลาต้องการ collection utility ฝั่ง TypeScript มักหยิบ Lodash มาใช้ ส่วน Python มี itertools ติดมาใน standard library อยู่แล้ว และทำงานแบบ lazy คือผลิตค่าตอนที่ต้องใช้ ไม่สร้าง list กลางทางทิ้งไว้ จุดนี้สำคัญมากเมื่อเจอ dataset ขนาดใหญ่

TypeScript
// TypeScript / Lodash
import _ from 'lodash';
const result = _.concat([1, 2], [3, 4], [5]);
// หรือ spread:
const result2 = [...[1, 2], ...[3, 4], ...[5]];
console.log(result); // [1, 2, 3, 4, 5]
Python
import itertools
result = list(itertools.chain([1, 2], [3, 4], [5]))
print(result) # [1, 2, 3, 4, 5]
# chain.from_iterable — flatten หนึ่งระดับ
nested = [[1, 2], [3, 4], [5]]
flat = list(itertools.chain.from_iterable(nested))
print(flat) # [1, 2, 3, 4, 5]
TypeScript
// TypeScript — manual counter หรือ generator
function* count(start = 0, step = 1) {
let n = start;
while (true) {
yield n;
n += step;
}
}
const gen = count(10, 2);
const result = Array.from({ length: 4 }, () => gen.next().value);
console.log(result); // [10, 12, 14, 16]
Python
import itertools
counter = itertools.count(10, 2) # infinite lazy counter
result = [next(counter) for _ in range(4)]
print(result) # [10, 12, 14, 16]
# islice — รับ N items จาก infinite iterator ได้อย่างปลอดภัย
first_five = list(itertools.islice(itertools.count(1), 5))
print(first_five) # [1, 2, 3, 4, 5]

groupby ทำงานคล้าย GROUP BY ของ SQL แต่ต้องป้อน input ที่เรียงมาแล้ว เพราะจับกลุ่มเฉพาะ key ที่เท่ากันและอยู่ติดกันเท่านั้น

TypeScript
// TypeScript / Lodash
import _ from 'lodash';
const data = [
{ cat: "a", val: 1 }, { cat: "a", val: 2 },
{ cat: "b", val: 3 }, { cat: "b", val: 4 },
];
const grouped = _.groupBy(data, "cat");
// { a: [...], b: [...] }
Python
import itertools
data = [("a", 1), ("a", 2), ("b", 3), ("b", 4)]
# Input ต้องเรียงตาม key ก่อน!
for key, group in itertools.groupby(data, key=lambda x: x[0]):
items = list(group)
print(f"{key}: {items}")
# a: [('a', 1), ('a', 2)]
# b: [('b', 3), ('b', 4)]
TypeScript
// TypeScript
const sum = [1, 2, 3, 4, 5].reduce((acc, x) => acc + x, 0);
console.log(sum); // 15
Python
import functools
result = functools.reduce(lambda acc, x: acc + x, [1, 2, 3, 4, 5])
print(result) # 15
# พร้อม initial value
result2 = functools.reduce(lambda acc, x: acc + x, [1, 2, 3], 100)
print(result2) # 106

@lru_cache คือ built-in memoization decorator ของ Python ใน TypeScript คู่เทียบคือ Map-based cache แบบ manual หรือ library อย่าง memoize-one

TypeScript
// TypeScript — manual memoization
const memo = new Map<number, number>();
function fib(n: number): number {
if (n < 2) return n;
if (memo.has(n)) return memo.get(n)!;
const result = fib(n - 1) + fib(n - 2);
memo.set(n, result);
return result;
}
console.log(fib(30)); // 832040
Python
import functools
@functools.lru_cache(maxsize=128)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print(fib(30)) # 832040
print(fib.cache_info()) # hits=28, misses=31, ...
TypeScript
// TypeScript — bind หรือ arrow wrapper
function multiply(a: number, b: number): number {
return a * b;
}
const double = multiply.bind(null, 2);
// หรือ: const double = (b: number) => multiply(2, b);
console.log(double(5)); // 10
console.log(double(10)); // 20
Python
import functools
def multiply(a: int, b: int) -> int:
return a * b
double = functools.partial(multiply, 2)
print(double(5)) # 10
print(double(10)) # 20
# partial ใช้กับ keyword arguments ได้ด้วย
def connect(host: str, port: int, ssl: bool = False) -> str:
return f"{'https' if ssl else 'http'}://{host}:{port}"
prod = functools.partial(connect, port=443, ssl=True)
print(prod("api.example.com")) # https://api.example.com:443
import itertools
import functools
# chain
merged = list(itertools.chain([1, 2], [3, 4], [5]))
print("chain:", merged)
# islice from count
first_six = list(itertools.islice(itertools.count(0, 10), 6))
print("count+islice:", first_six)
# groupby
words = ["apple", "ant", "bear", "banana", "cat"]
words.sort(key=lambda w: w[0]) # ต้อง sort ก่อน!
for letter, group in itertools.groupby(words, key=lambda w: w[0]):
print(f" {letter}: {list(group)}")
# lru_cache fibonacci
@functools.lru_cache(maxsize=64)
def fib(n: int) -> int:
if n < 2:
return n
return fib(n - 1) + fib(n - 2)
print("fib(20):", fib(20))
print("cache:", fib.cache_info())
# partial
def power(base: int, exp: int) -> int:
return base ** exp
square = functools.partial(power, exp=2)
cube = functools.partial(power, exp=3)
print("squares:", [square(x) for x in range(1, 6)])
print("cubes :", [cube(x) for x in range(1, 6)])
ทำไม `itertools.groupby` จึงต้องการ input ที่เรียงลำดับแล้ว?
`@functools.lru_cache` ทำอะไร?
`functools.partial(fn, 2)` return อะไร?
ฟังก์ชัน itertools ใดที่รับ N items จาก infinite iterator ได้อย่างปลอดภัย?