변수, 방정식, 그리고 순수함수
변수는 placeholder이고, 방정식은 문제 명세서이고, 함수는 입력→출력 매핑이다. 수학의 함수가 순수함수와 정확히 같다는 것을 확인한다.
변수, 방정식, 그리고 순수함수
Stage 1에서는 구체적인 수를 다뤘어. 3 + 5 = 8, 2³ = 8. 근데 여기서 한 단계 더 추상화할 수 있어.
"어떤 수에 5를 더하면 8이 된다. 그 수는?"
이걸 매번 말로 쓰면 귀찮으니까, 모르는 수를 기호로 놓는 거야: x + 5 = 8. 이게 대수(Algebra)의 시작이야.
변수: 수학 vs 프로그래밍
변수는 **"아직 모르는 값의 placeholder"**야. 프로그래밍에서 변수와 비슷하지만 결정적인 차이가 있어.
수학의 변수: x + 5 = 8이면 x는 3일 수밖에 없음 값이 "변하는" 게 아니라 정해져 있는데 우리가 모를 뿐 → "미지수" (unknown)
// 프로그래밍의 변수: let x = 3; x = 5; // 재할당 가능! // 값을 담는 상자 — 바뀔 수 있음 // const가 수학의 변수에 더 가까움 const x = 3; // 한번 정해지면 안 변함
수학의 변수는 "변하는 수"라기보다 "미지수" -- 값이 정해져 있는데 우리가 아직 모를 뿐이야. (나중에 함수에서는 진짜 변하는 의미로도 쓰이긴 해.)
방정식: 문제 명세서
방정식은 "이 조건을 만족하는 x를 찾아라"라는 스펙이야. x + 5 = 8은 "더해서 8이 되는 수를 찾아라"는 문제 명세서지.
방정식을 푼다는 건, 역연산을 연쇄적으로 적용해서 x를 혼자 남기는 과정이야:
x + 5 = 8
x + 5 - 5 = 8 - 5 ← 덧셈의 역연산(뺄셈)을 양변에
x = 3
Stage 1에서 배운 "모든 연산에는 역연산이 있다"가 여기서 바로 써먹히지.
좀 더 복잡한 것도 같은 원리야:
3x + 7 = 22
3x = 15 ← 뺄셈 (덧셈의 역)
x = 5 ← 나눗셈 (곱셈의 역)
양변에 같은 연산을 하면 등호가 유지된다 -- 이게 방정식 풀이의 유일한 규칙이야. 나머지는 전부 이 규칙에서 나와.
반복 테마: 연산 ↔ 역연산
방정식을 푸는 건 결국 역연산을 순서대로 적용하는 것이야. 이 패턴은 Stage 5의 가우스 소거법(행렬 방정식)까지 동일하게 이어져.
함수: 입력과 출력의 관계
함수의 본질
함수는 한 문장으로 정의할 수 있어:
"입력 하나에 출력 하나가 정해지는 관계"
그게 전부야. f(x) = 2x + 1이면 f(3) = 7, f(0) = 1, f(-2) = -3. 같은 입력을 넣으면 항상 같은 출력이 나와. 이게 함수의 핵심 조건이야.
수학 함수 = 순수함수
수학의 함수:
f(x) = x² + 1
- 부수효과(side effect) 없음
- 같은 입력 → 항상 같은 출력 (참조 투명성)
- "계산 과정"은 신경 안 씀. 입력-출력 매핑만 중요
- 시간 개념 없음
수학의 함수는 프로그래밍의 **순수함수(pure function)**에 정확히 대응해. 사실 함수형 프로그래밍이 수학의 함수 개념을 그대로 가져온 거야.
순수성 ≠ 멱등성 (중요한 구분)
흔한 혼동
"수학의 함수는 멱등성이 보장된다" -- 이건 틀려.
- 순수성: 같은 입력 → 항상 같은 출력. 수학 함수는 항상 만족.
- 멱등성:
f(f(x)) = f(x). 한 번 적용 = 두 번 적용. 일부만 만족.
f(x) = x²는 순수함수이지만, f(f(3)) = f(9) = 81 ≠ 9. 순수하지만 멱등하지 않아.
정확한 명제: "수학의 함수는 순수성이 항상 보장되고, 프로그래밍에서는 순수함수만 이에 해당한다."
정의역, 공역, 치역 = 타입 시그니처
f: ℝ → ℝ, f(x) = x² 정의역 = ℝ (입력 가능한 값) 공역 = ℝ (출력이 속하는 범위) 치역 = [0, ∞) (실제 출력 값)
function square(x: number): number {
return x * x;
}
// 입력 타입: number (정의역)
// 리턴 타입: number (공역)
// 실제 출력: 0 이상만 (치역)정의역(domain)은 입력 타입, 공역(codomain)은 출력 타입이야. 그리고 실제로 출력되는 값들의 집합이 치역(range)이고.
f(x) = x²에서 입력이 모든 실수라도, 출력은 0 이상만 나오잖아. 공역은 ℝ 전체지만 치역은 [0, ∞)인 거지. 리턴 타입은 number지만 실제로는 음수가 절대 안 나오는 것과 같은 이치야.
함수 합성 = 파이프라인
함수를 연결할 수 있어:
f(x) = 2x "두 배로"
g(x) = x + 1 "1 더하기"
g(f(3)) = g(6) = 7 ← f 먼저, 그 다음 g
프로그래밍의 파이프라인이랑 완전 같지:
const result = pipe(3, double, addOne); // 3 → 6 → 7
그리고 합성 순서가 중요해:
g(f(3)) = g(6) = 7
f(g(3)) = f(4) = 8 ← 다른 결과!
교환법칙이 안 되는 거야. Article 1에서 "행렬곱은 교환법칙이 안 된다"고 살짝 언급했는데, 그 이유가 여기에 있어. 행렬은 함수이고, 행렬곱은 함수 합성이고, 함수 합성은 순서가 중요하니까.
일차함수: 변화율의 첫 만남
f(x) = ax + b
이게 전부야. 입력에 뭔가를 곱하고(a), 뭔가를 더한다(b). 그래프로 그리면 직선이야.
b는 시작점, a가 진짜 중요해
b는 쉬워 -- f(0) = b니까 y절편, 즉 **초기값(initial value)**이야.
a가 핵심 -- 기울기:
f(x) = 2x일 때:
x=0 → 0
x=1 → 2 (x가 1 늘 때 출력이 2 늘었음)
x=2 → 4 (x가 1 늘 때 출력이 2 늘었음)
x=3 → 6 (x가 1 늘 때 출력이 2 늘었음)
입력이 1 변할 때 출력이 얼마나 변하는가 -- 이게 기울기야. 일차함수에서는 이 변화율이 어디서든 일정해.
기울기 = (출력의 변화량) / (입력의 변화량) = Δy / Δx
기울기 = 변화의 속도. 이 직관이 Stage 4 미적분에서 미분으로 직결돼. 미분은 "기울기가 일정하지 않은 함수에서 순간 기울기를 구하는 것"이거든.
두 점이면 기울기를 구할 수 있다
점 (1, 3)과 (4, 9)를 지나는 직선의 기울기:
a = (9 - 3) / (4 - 1) = 6 / 3 = 2
현실에서는 함수의 식을 모르는 경우가 대부분이야. 데이터 포인트만 있지. 그때 두 점 사이의 기울기를 구하면 **"이 구간에서 평균적으로 얼마나 변했는가"**를 알 수 있어.
이 "두 점 사이의 평균 변화율"을 구간을 극한까지 좁히면 어떻게 될까? 그게 미분이야. 아직 안 가지만, 씨앗이 여기 심겨진 거야.