KR_Functional - somaz94/python-study GitHub Wiki
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก๊ทธ๋จ์ ์์ฑํ๋ ํจ๋ฌ๋ค์์ด๋ค.
# ์์ ํจ์์ ์
def add(x, y):
return x + y
# ์์ํ์ง ์์ ํจ์์ ์
total = 0
def add_to_total(x):
global total
total += x
return total
# ๋ถ์์ฉ์ด ์๋ ๋ฐฉ์์ผ๋ก ์์ฑํ๊ธฐ
def create_adder(initial=0):
def add(x):
return initial + x
return add
add_to_5 = create_adder(5)
print(add_to_5(10)) # 15
โ
ํน์ง:
- ์์ ํจ์
- ๋ถ์์ฉ ์์
- ์ฐธ์กฐ ํฌ๋ช ์ฑ
- ์ํ ๋ณ๊ฒฝ ์ต์ํ
- ์ ์ธ์ ํ๋ก๊ทธ๋๋ฐ ์คํ์ผ
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์์ ์์ฃผ ์ฌ์ฉ๋๋ ๊ณ ์ฐจ ํจ์๋ค์ด๋ค.
from functools import reduce
# map: ๋ชจ๋ ์์์ ํจ์ ์ ์ฉ
numbers = [1, 2, 3, 4, 5]
squares = list(map(lambda x: x**2, numbers))
print(squares) # [1, 4, 9, 16, 25]
# filter: ์กฐ๊ฑด์ ๋ง๋ ์์๋ง ์ ํ
evens = list(filter(lambda x: x % 2 == 0, numbers))
print(evens) # [2, 4]
# reduce: ์์๋ค์ ํ๋๋ก ์ค์
product = reduce(lambda x, y: x * y, numbers)
print(product) # 120
# ๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์
์ ํ์ฉํ ๋์
squares_comp = [x**2 for x in numbers]
evens_comp = [x for x in numbers if x % 2 == 0]
# ์ฌ๋ฌ ํจ์๋ฅผ ์กฐํฉํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
result = reduce(
lambda acc, x: acc + x,
filter(lambda x: x > 10,
map(lambda x: x**2, numbers))
)
print(result) # 16 + 25 = 41
โ
ํน์ง:
- ๋ฐ์ดํฐ ๋ณํ
- ์์ ํํฐ๋ง
- ์ง๊ณ ์ฐ์ฐ
- ์ง์ฐ ํ๊ฐ (map๊ณผ filter๋ ์ดํฐ๋ ์ดํฐ ๋ฐํ)
- ์ฒด์ด๋ ๊ฐ๋ฅ
- ์ ์ธ์ ์คํ์ผ
ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๊ฑฐ๋ ํจ์๋ฅผ ๋ฐํํ๋ ํจ์์ด๋ค.
def multiply_by(n):
def multiplier(x):
return x * n
return multiplier
times_two = multiply_by(2)
times_three = multiply_by(3)
print(times_two(5)) # 10
print(times_three(5)) # 15
# ํจ์๋ฅผ ์ธ์๋ก ๋ฐ๋ ๊ณ ์ฐจ ํจ์
def apply_twice(func, arg):
return func(func(arg))
def add_five(x):
return x + 5
print(apply_twice(add_five, 10)) # 10 + 5 + 5 = 20
# ๋ฐ์ฝ๋ ์ดํฐ๋ ๊ณ ์ฐจ ํจ์์ ์ผ์ข
์ด๋ค
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"ํจ์ {func.__name__} ํธ์ถ๋จ")
return func(*args, **kwargs)
return wrapper
@log_function_call
def greet(name):
return f"์๋
, {name}!"
print(greet("์ฒ ์")) # "ํจ์ greet ํธ์ถ๋จ" ์ถ๋ ฅ ํ "์๋
, ์ฒ ์!" ๋ฐํ
โ
ํน์ง:
- ํจ์๋ฅผ ์ธ์๋ก ์ ๋ฌ
- ํจ์๋ฅผ ๋ฐํ
- ํด๋ก์ ํ์ฉ
- ์ฝ๋ฐฑ ํจ์ ์ง์
- ํจ์ ํฉ์ฑ ๊ฐ๋ฅ
- ์ถ์ํ ์์ค ์ฆ๊ฐ
๋ฐ์ดํฐ๋ฅผ ์์ฑํ ํ ๋ณ๊ฒฝํ์ง ์๋ ์์น์ด๋ค.
from typing import NamedTuple
import copy
class Point(NamedTuple):
x: int
y: int
def move_point(point, dx, dy):
return Point(point.x + dx, point.y + dy)
p1 = Point(1, 2)
p2 = move_point(p1, 3, 4) # ์๋ก์ด ๊ฐ์ฒด ์์ฑ
print(p1) # Point(x=1, y=2) - ์๋ณธ ๋ณ๊ฒฝ ์์
print(p2) # Point(x=4, y=6) - ์ ๊ฐ์ฒด ๋ฐํ
# ๋ถ๋ณ ๋ฆฌ์คํธ ํ๋ด๋ด๊ธฐ
def append_to_list(lst, item):
# ์๋ณธ ๋ฆฌ์คํธ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ์ ๋ฆฌ์คํธ ๋ฐํ
new_list = lst.copy()
new_list.append(item)
return new_list
original = [1, 2, 3]
modified = append_to_list(original, 4)
print(original) # [1, 2, 3] - ์๋ณธ ๋ณ๊ฒฝ ์์
print(modified) # [1, 2, 3, 4] - ์ ๋ฆฌ์คํธ ๋ฐํ
# ์ค์ฒฉ ๋ฐ์ดํฐ ๊ตฌ์กฐ์์์ ๋ถ๋ณ์ฑ
def update_user_email(user_dict, new_email):
# ๋์
๋๋ฆฌ ๊น์ ๋ณต์ฌ
new_user = copy.deepcopy(user_dict)
new_user['email'] = new_email
return new_user
user = {'name': 'ํ๊ธธ๋', 'email': '[email protected]', 'settings': {'theme': 'dark'}}
updated_user = update_user_email(user, '[email protected]')
print(user['email']) # [email protected]
print(updated_user['email']) # [email protected]
โ
ํน์ง:
- ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ
- ์ํ ๋ณ๊ฒฝ ๋ฐฉ์ง
- ์์ ํ ๋ณ๋ ฌ ์ฒ๋ฆฌ
- ์์ธก ๊ฐ๋ฅํ ๋์
- ๋๋ฒ๊น ์ฉ์ด์ฑ
- ์๊ฐ ์ฌํ ๋๋ฒ๊น ๊ฐ๋ฅ
- ๋ถ์์ฉ ๊ฐ์
์ฌ๋ฌ ํจ์๋ฅผ ์กฐํฉํ์ฌ ์๋ก์ด ํจ์๋ฅผ ๋ง๋๋ ๊ธฐ๋ฒ์ด๋ค.
def compose(f, g):
return lambda x: f(g(x))
def double(x):
return x * 2
def increment(x):
return x + 1
# ํจ์ ํฉ์ฑ
double_then_increment = compose(increment, double)
increment_then_double = compose(double, increment)
print(double_then_increment(3)) # 3*2 + 1 = 7
print(increment_then_double(3)) # (3+1)*2 = 8
# ์ฌ๋ฌ ํจ์ ํฉ์ฑํ๊ธฐ
def compose_all(*functions):
def compose_two(f, g):
return lambda x: f(g(x))
if not functions:
return lambda x: x # ํญ๋ฑ ํจ์
return functools.reduce(compose_two, functions)
def square(x):
return x * x
# square(double(increment(x)))
composed = compose_all(square, double, increment)
print(composed(3)) # square(double(increment(3))) = square(double(4)) = square(8) = 64
# ๋ถ๋ถ ์ ์ฉ ํจ์
from functools import partial
def add(x, y):
return x + y
add_five = partial(add, 5) # x=5๋ก ๊ณ ์
print(add_five(10)) # 15
โ
ํน์ง:
- ํจ์ ์กฐํฉ
- ์ฝ๋ ์ฌ์ฌ์ฉ
- ๋ชจ๋ํ
- ์ถ์ํ ์์ค ์์น
- ํ์ดํ๋ผ์ธ ์์ฑ ๊ฐ๋ฅ
- ๋ถ๋ถ ํจ์ ์ ์ฉ
- ์ปค๋ง(Currying) ์ง์
ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ค์ ์ํฉ์ ์ ์ฉํ๋ ๋ฐฉ๋ฒ์ด๋ค.
import functools
def pipeline(*funcs):
def wrapper(x):
result = x
for f in funcs:
result = f(result)
return result
return wrapper
# ๋ฐ์ดํฐ ์ฒ๋ฆฌ ํจ์๋ค
clean = lambda x: x.strip().lower()
split_words = lambda x: x.split()
count_words = lambda x: len(x)
# ํ์ดํ๋ผ์ธ ์์ฑ
word_counter = pipeline(clean, split_words, count_words)
text = " Hello World "
print(word_counter(text)) # 2
# ๋ฐ์ดํฐ ๋ณํ ํ์ดํ๋ผ์ธ
def transform_data(data):
# ํจ์ํ ์ ๊ทผ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ ๋ณํ
return (
data
| pipe(filter_invalid_entries)
| pipe(normalize_fields)
| pipe(enrich_with_external_data)
| pipe(calculate_statistics)
)
# ํ์ดํ ์ฐ์ฐ์ ํ๋ด๋ด๊ธฐ
def pipe(func):
def wrapper(data):
return func(data)
return wrapper
# ํจ์ํ ์๋ฌ ์ฒ๋ฆฌ - Maybe ๋ชจ๋๋ ํจํด
class Maybe:
def __init__(self, value=None):
self.value = value
@classmethod
def just(cls, value):
return cls(value)
@classmethod
def nothing(cls):
return cls(None)
def bind(self, func):
if self.value is None:
return self
try:
result = func(self.value)
if isinstance(result, Maybe):
return result
return Maybe.just(result)
except:
return Maybe.nothing()
def get_or_else(self, default):
return self.value if self.value is not None else default
# Maybe ๋ชจ๋๋ ์ฌ์ฉ ์์
def safe_divide(x, y):
return Maybe.just(x / y) if y != 0 else Maybe.nothing()
def safe_sqrt(x):
return Maybe.just(x ** 0.5) if x >= 0 else Maybe.nothing()
# ์์ ํ ๊ณ์ฐ ํ์ดํ๋ผ์ธ
def calculate(x, y):
return (Maybe.just(x)
.bind(lambda v: safe_divide(v, y))
.bind(safe_sqrt)
.get_or_else("๊ณ์ฐ ๋ถ๊ฐ"))
print(calculate(16, 4)) # 2.0
print(calculate(16, 0)) # "๊ณ์ฐ ๋ถ๊ฐ"
print(calculate(-16, 4)) # "๊ณ์ฐ ๋ถ๊ฐ"
โ
ํน์ง:
- ํ์ดํ๋ผ์ธ ํจํด
- ํจ์ ์ฒด์ด๋
- ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์๋ํ
- ๋ชจ๋๋ ํจํด
- ์๋ฌ ์ฒ๋ฆฌ ์ถ์ํ
- ์ ์ธ์ ๋ฐ์ดํฐ ๋ณํ
- ๋ณต์กํ ๋ก์ง ๋จ์ํ
๋ฐ๋ณต ๋์ ์ฌ๊ท๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ด๋ค.
# ์ผ๋ฐ์ ์ธ ์ฌ๊ท - ํฉํ ๋ฆฌ์ผ ๊ณ์ฐ
def factorial(n):
if n <= 1:
return 1
return n * factorial(n-1)
print(factorial(5)) # 120
# ๊ผฌ๋ฆฌ ์ฌ๊ท(Tail Recursion) - ์คํ ์ค๋ฒํ๋ก์ฐ ๋ฐฉ์ง
def factorial_tail(n, acc=1):
if n <= 1:
return acc
return factorial_tail(n-1, n*acc)
print(factorial_tail(5)) # 120
# ํผ๋ณด๋์น ์์ด - ๋ฉ๋ชจ์ด์ ์ด์
์ผ๋ก ์ฑ๋ฅ ๊ฐ์
def fibonacci(n, memo=None):
if memo is None:
memo = {}
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo)
return memo[n]
print(fibonacci(10)) # 55
# ํธ๋จํด๋ฆฐ(Trampoline) ํจํด - ๊ผฌ๋ฆฌ ์ฌ๊ท ์๋ฎฌ๋ ์ด์
def trampoline(func, *args, **kwargs):
result = func(*args, **kwargs)
while callable(result):
result = result()
return result
def factorial_trampoline(n, acc=1):
if n <= 1:
return acc
return lambda: factorial_trampoline(n-1, n*acc)
print(trampoline(factorial_trampoline, 5)) # 120
โ
ํน์ง:
- ๋ฐ๋ณต๋ฌธ ๋์ฒด
- ์์ฐ์ค๋ฌ์ด ๋ฌธ์ ํด๊ฒฐ ๋ฐฉ์
- ์ํ ๋ณ์ด ์์
- ๋ฉ๋ชจ์ด์ ์ด์ ๊ธฐ๋ฒ
- ๊ผฌ๋ฆฌ ์ฌ๊ท ์ต์ ํ
- ํธ๋จํด๋ฆฐ ํจํด
- ์คํ ์ค๋ฒํ๋ก์ฐ ๋ฐฉ์ง ๊ธฐ๋ฒ
Python์์ ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์ง์ํ๋ ๋๊ตฌ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ด๋ค.
# ๋ด์ฅ ํจ์ ํ์ฉ
from functools import reduce, partial
import itertools
import operator
data = [1, 2, 3, 4, 5]
# ๋ถ๋ถ ์ ์ฉ ํจ์
multiply = lambda x, y: x * y
double = partial(multiply, 2)
# ์ฐ์ฐ์ ๋ชจ๋ ํ์ฉ
total = reduce(operator.add, data)
product = reduce(operator.mul, data)
# itertools ํ์ฉ
combinations = list(itertools.combinations(data, 2))
permutations = list(itertools.permutations(data, 2))
# ๋ฌดํ ์ํ์ค ์์ฑ๊ณผ ์ฌ๋ผ์ด์ฑ
infinite = itertools.count(1)
first_10 = list(itertools.islice(infinite, 10))
# ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ - toolz
# pip install toolz
try:
from toolz import compose, curry, pipe
def add(x, y):
return x + y
def double(x):
return x * 2
# ์ปค๋ง
curried_add = curry(add)
add_5 = curried_add(5)
# ํฉ์ฑ
transform = compose(double, add_5)
# ํ์ดํ
result = pipe(10, add_5, double)
except ImportError:
print("toolz ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ด ์์ง ์์ต๋๋ค.")
# ํจ์ํ ๋ฐ์ดํฐ ๊ตฌ์กฐ - pyrsistent
# pip install pyrsistent
try:
import pyrsistent as pyr
# ๋ถ๋ณ ๋ฆฌ์คํธ
v = pyr.pvector([1, 2, 3])
v2 = v.append(4) # ์ ๋ฒกํฐ ๋ฐํ
# ๋ถ๋ณ ๋งต
m = pyr.pmap({'a': 1, 'b': 2})
m2 = m.set('c', 3) # ์ ๋งต ๋ฐํ
except ImportError:
print("pyrsistent ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ค์น๋์ด ์์ง ์์ต๋๋ค.")
โ
ํน์ง:
- ๋ด์ฅ ํจ์ํ ๋๊ตฌ
- ์ฐ์ฐ์ ๋ชจ๋์ ๊ธฐ๋ฅ
- ์ดํฐ๋ ์ดํฐ ๋๊ตฌ
- ์ธ๋ถ ํจ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
- ๋ถ๋ณ ๋ฐ์ดํฐ ๊ตฌ์กฐ
- ์ปค๋ง๊ณผ ๋ถ๋ถ ์ ์ฉ ์ง์
- ํจ์ ํฉ์ฑ ๋๊ตฌ
โ
๋ชจ๋ฒ ์ฌ๋ก:
- ์์ ํจ์ ์ฌ์ฉํ๊ธฐ
- ์ํ ๋ณ๊ฒฝ ํผํ๊ธฐ
- ํจ์๋ฅผ ์ผ๊ธ ๊ฐ์ฒด๋ก ๋ค๋ฃจ๊ธฐ
- ๊ณ ์ฐจ ํจ์ ํ์ฉํ๊ธฐ
- ๋ถ๋ณ์ฑ ์ ์งํ๊ธฐ
- ๋ถ์์ฉ ์ต์ํํ๊ธฐ
- ์ฌ๊ท ํ์ฉํ๊ธฐ (์คํ ์ ํ ์ฃผ์)
- ํฉ์ฑ ํจ์ ํ์ฉํ๊ธฐ
- map, filter, reduce ์ ์ ํ ์ฌ์ฉํ๊ธฐ
- ๋ฆฌ์คํธ ์ปดํ๋ฆฌํจ์ ๊ณผ ์ ๋๋ ์ดํฐ ํํ์ ํ์ฉ
- ์ดํฐ๋ ์ดํฐ์ ์ง์ฐ ํ๊ฐ ํ์ฉํ๊ธฐ
- ๋ช ๋ นํ๊ณผ ํจ์ํ ์คํ์ผ ์ ์ ํ ํผํฉํ๊ธฐ
- ๋ฉ๋ชจ์ด์ ์ด์ ์ผ๋ก ์ฑ๋ฅ ์ต์ ํํ๊ธฐ
- ๋ฉํฐ์ค๋ ๋ฉ/๋ฉํฐํ๋ก์ธ์ฑ๊ณผ ํจ๊ป ํ์ฉํ๊ธฐ
- ํจ์ํ ์ค๋ฅ ์ฒ๋ฆฌ ํจํด ๊ณ ๋ คํ๊ธฐ
- ๊ฐ๋ ์ฑ ๊ท ํ ์ ์งํ๊ธฐ