함수는 토파즈에서 1등 시민입니다. 변수에 저장하고, 다른 함수의 매개변수로 전달하고, 함수에서 반환할 수 있습니다. 🚀
🔧 기본 함수 선언
표준 함수 선언
// 기본 함수 구조
function 인사하기(이름: string) -> string {
return "안녕하세요, {이름}님!"
}
// 매개변수 없는 함수
function 현재시간() -> string {
return "2024년 12월 20일"
}
// 반환값 없는 함수 (void)
function 로그출력(메시지: string) {
print("로그: {메시지}")
}
// 사용 예시
let 인사말 = 인사하기("토파즈 개발자")
print(인사말) // "안녕하세요, 토파즈 개발자님!"
매개변수와 기본값
// 기본 매개변수값
function 계산하기(a: int, b: int = 10, 연산: string = "더하기") -> int {
match 연산 {
case "더하기" => return a + b
case "빼기" => return a - b
case "곱하기" => return a * b
case _ => return 0
}
}
// 다양한 호출 방식
let 결과1 = 계산하기(5) // 5 + 10 = 15
let 결과2 = 계산하기(5, 3) // 5 + 3 = 8
let 결과3 = 계산하기(5, 3, "곱하기") // 5 * 3 = 15
// 명명된 매개변수
let 결과4 = 계산하기(a: 8, 연산: "빼기", b: 3) // 8 - 3 = 5
가변 매개변수
// 가변 매개변수 (...rest)
function 합계구하기(숫자들: ...int) -> int {
let mut 합계 = 0
for 숫자 in 숫자들 {
합계 += 숫자
}
return 합계
}
let 총합1 = 합계구하기(1, 2, 3, 4, 5) // 15
let 총합2 = 합계구하기(10, 20) // 30
// 배열과 추가 매개변수 결합
function 통계계산(레이블: string, 값들: ...float) -> string {
let 개수 = 값들.length()
let 평균 = 값들.sum() / 개수
return "{레이블}: 개수={개수}, 평균={평균}"
}
print(통계계산("시험점수", 85.5, 92.0, 78.5, 89.0))
➡️ 화살표 함수
기본 화살표 함수
// 간단한 화살표 함수
let 제곱 = (x: int) => x * x
let 인사 = (이름: string) => "안녕, {이름}!"
// 매개변수 없는 화살표 함수
let 랜덤숫자 = () => Math.random() * 100
// 복잡한 화살표 함수
let 사용자정보 = (이름: string, 나이: int) => {
let 상태 = if 나이 >= 18 { "성인" } else { "미성년자" }
return {
이름: 이름,
나이: 나이,
상태: 상태
}
}
// 사용 예시
print(제곱(5)) // 25
print(인사("김토파즈")) // "안녕, 김토파즈!"
let 정보 = 사용자정보("이개발", 25)
print(정보) // { 이름: "이개발", 나이: 25, 상태: "성인" }
배열 메서드와 화살표 함수
let 숫자들 = [1, 2, 3, 4, 5]
// map 변환
let 제곱들 = 숫자들.map(x => x * x)
print(제곱들) // [1, 4, 9, 16, 25]
// filter 필터링
let 짝수들 = 숫자들.filter(x => x % 2 == 0)
print(짝수들) // [2, 4]
// reduce 축약
let 합계 = 숫자들.reduce((누적, 현재) => 누적 + 현재, 0)
print(합계) // 15
// 복잡한 체이닝
let 결과 = 숫자들
.filter(x => x > 2)
.map(x => x * 3)
.reduce((a, b) => a + b, 0)
print(결과) // (3 + 4 + 5) * 3 = 36
🎯 고차 함수
함수를 매개변수로 받기
// 고차 함수 정의
function 연산적용(배열: [int], 연산함수: function(int) -> int) -> [int] {
let 결과 = []
for 요소 in 배열 {
결과.push(연산함수(요소))
}
return 결과
}
// 함수를 매개변수로 전달
let 숫자들 = [1, 2, 3, 4]
let 제곱결과 = 연산적용(숫자들, x => x * x)
let 두배결과 = 연산적용(숫자들, x => x * 2)
print(제곱결과) // [1, 4, 9, 16]
print(두배결과) // [2, 4, 6, 8]
// 조건부 처리 함수
function 조건부처리(값: int, 조건: function(int) -> bool, 처리함수: function(int) -> int) -> int {
if 조건(값) {
return 처리함수(값)
}
return 값
}
let 결과 = 조건부처리(15, x => x > 10, x => x * 2)
print(결과) // 30 (15 > 10이므로 15 * 2)
함수를 반환하기
// 함수 팩토리
function 곱셈기만들기(배수: int) -> function(int) -> int {
return function(x: int) -> int {
return x * 배수
}
}
let 두배만들기 = 곱셈기만들기(2)
let 세배만들기 = 곱셈기만들기(3)
print(두배만들기(5)) // 10
print(세배만들기(4)) // 12
// 설정 가능한 검증 함수
function 범위검사기만들기(최소: int, 최대: int) -> function(int) -> bool {
return function(값: int) -> bool {
return 값 >= 최소 && 값 <= 최대
}
}
let 성인나이검사 = 범위검사기만들기(18, 65)
let 학생나이검사 = 범위검사기만들기(5, 18)
print(성인나이검사(25)) // true
print(학생나이검사(16)) // true
print(학생나이검사(25)) // false
�� 클로저
기본 클로저
function 카운터만들기(초기값: int) -> function() -> int {
let mut 카운트 = 초기값 // 클로저에 의해 캡처됨
return function() -> int {
카운트 += 1 // 외부 변수에 접근
return 카운트
}
}
let 카운터1 = 카운터만들기(0)
let 카운터2 = 카운터만들기(100)
print(카운터1()) // 1
print(카운터1()) // 2
print(카운터2()) // 101
print(카운터1()) // 3
복잡한 클로저 패턴
// 상태를 가진 클로저
function 계좌만들기(초기잔액: float) -> { 입금: function(float) -> float, 출금: function(float) -> float, 잔액확인: function() -> float } {
let mut 잔액 = 초기잔액
return {
입금: function(금액: float) -> float {
잔액 += 금액
return 잔액
},
출금: function(금액: float) -> float {
if 금액 <= 잔액 {
잔액 -= 금액
}
return 잔액
},
잔액확인: function() -> float {
return 잔액
}
}
}
let 내계좌 = 계좌만들기(1000.0)
print(내계좌.입금(500.0)) // 1500.0
print(내계좌.출금(200.0)) // 1300.0
print(내계좌.잔액확인()) // 1300.0
// 환경 캡처하는 클로저
function 설정관리자(기본설정: { [string]: any }) -> function(string, any) -> any {
let mut 현재설정 = 기본설정.copy()
return function(키: string, 값: any = null) -> any {
if 값 != null {
현재설정[키] = 값
}
return 현재설정[키]
}
}
let 앱설정 = 설정관리자({
테마: "다크",
언어: "한국어",
알림: true
})
print(앱설정("테마")) // "다크"
앱설정("테마", "라이트")
print(앱설정("테마")) // "라이트"
🔄 재귀 함수
기본 재귀
// 팩토리얼 계산
function 팩토리얼(n: int) -> int {
if n <= 1 {
return 1
}
return n * 팩토리얼(n - 1)
}
print(팩토리얼(5)) // 120
// 피보나치 수열
function 피보나치(n: int) -> int {
if n <= 1 {
return n
}
return 피보나치(n - 1) + 피보나치(n - 2)
}
print(피보나치(8)) // 21
// 배열 합계 (재귀적)
function 배열합계(배열: [int]) -> int {
if 배열.length() == 0 {
return 0
}
if 배열.length() == 1 {
return 배열[0]
}
return 배열[0] + 배열합계(배열.slice(1))
}
print(배열합계([1, 2, 3, 4, 5])) // 15
꼬리 재귀 최적화
// 꼬리 재귀 팩토리얼
function 꼬리팩토리얼(n: int, 누적: int = 1) -> int {
if n <= 1 {
return 누적
}
return 꼬리팩토리얼(n - 1, 누적 * n) // 꼬리 호출
}
// 꼬리 재귀 피보나치
function 꼬리피보나치(n: int, a: int = 0, b: int = 1) -> int {
if n == 0 {
return a
}
return 꼬리피보나치(n - 1, b, a + b) // 꼬리 호출
}
print(꼬리팩토리얼(10)) // 3628800
print(꼬리피보나치(10)) // 55
⚡ 함수형 프로그래밍 패턴
커링 (Currying)
// 커링된 함수
function 더하기(a: int) -> function(int) -> int {
return function(b: int) -> int {
return a + b
}
}
let 5더하기 = 더하기(5)
print(5더하기(3)) // 8
// 다중 매개변수 커링
function 곱하고더하기(곱수: float) -> function(float) -> function(float) -> float {
return function(더할수: float) -> function(float) -> float {
return function(값: float) -> float {
return 값 * 곱수 + 더할수
}
}
}
let 두배하고십더하기 = 곱하고더하기(2.0)(10.0)
print(두배하고십더하기(5.0)) // 20.0 (5 * 2 + 10)
함수 합성
// 함수 합성 유틸리티
function 합성하기<T, U, V>(f: function(U) -> V, g: function(T) -> U) -> function(T) -> V {
return function(x: T) -> V {
return f(g(x))
}
}
let 제곱 = (x: int) => x * x
let 두배 = (x: int) => x * 2
let 문자열로 = (x: int) => x.toString()
// 함수들을 합성
let 제곱후두배 = 합성하기(두배, 제곱)
let 제곱후두배후문자열 = 합성하기(문자열로, 제곱후두배)
print(제곱후두배(3)) // 18 (3² * 2)
print(제곱후두배후문자열(4)) // "32" (4² * 2 → 문자열)
메모이제이션
// 메모이제이션 데코레이터
function 메모이제이션<T, R>(함수: function(T) -> R) -> function(T) -> R {
let mut 캐시 = {}
return function(입력: T) -> R {
let 키 = 입력.toString()
if 키 in 캐시.keys {
return 캐시[키]
}
let 결과 = 함수(입력)
캐시[키] = 결과
return 결과
}
}
// 느린 피보나치를 메모이제이션으로 최적화
let 메모피보나치 = 메모이제이션(function(n: int) -> int {
if n <= 1 return n
return 메모피보나치(n - 1) + 메모피보나치(n - 2)
})
// 이제 큰 숫자도 빠르게 계산
print(메모피보나치(40)) // 빠르게 계산됨
🛡️ 함수 최적화와 베스트 프랙티스
1. 순수 함수 사용
// 좋은 예: 순수 함수
function 세금계산(소득: float, 세율: float) -> float {
return 소득 * 세율
}
// 피해야 할: 부작용이 있는 함수
let mut 전역카운터 = 0
function 나쁜함수(값: int) -> int {
전역카운터 += 1 // 부작용!
return 값 * 2
}
// 좋은 대안: 상태 반환
function 좋은함수(값: int, 현재카운터: int) -> { 결과: int, 새카운터: int } {
return {
결과: 값 * 2,
새카운터: 현재카운터 + 1
}
}
2. 함수 이름과 구조화
// 명확한 함수 이름
function 사용자이메일유효성검사(이메일: string) -> bool {
return 이메일.contains("@") && 이메일.contains(".")
}
function 주문총액계산(주문항목들: [{ 가격: float, 수량: int }]) -> float {
return 주문항목들
.map(항목 => 항목.가격 * 항목.수량)
.reduce((합계, 금액) => 합계 + 금액, 0.0)
}
// 단일 책임 원칙
function 사용자데이터파싱(원시데이터: string) -> { 이름: string, 나이: int } {
// 파싱만 담당
let 부분들 = 원시데이터.split(",")
return {
이름: 부분들[0].trim(),
나이: parseInt(부분들[1].trim())
}
}
function 사용자데이터검증(사용자: { 이름: string, 나이: int }) -> bool {
// 검증만 담당
return 사용자.이름.length() > 0 && 사용자.나이 > 0
}
3. 에러 처리
// Result 타입을 사용한 안전한 함수
function 안전한나누기(분자: float, 분모: float) -> Result<float, string> {
if 분모 == 0.0 {
return Err("0으로 나눌 수 없습니다")
}
return Ok(분자 / 분모)
}
// 사용 시
match 안전한나누기(10.0, 2.0) {
case Ok(결과) => print("결과: {결과}")
case Err(오류) => print("오류: {오류}")
}
함수와 클로저는 토파즈의 핵심 기능입니다. 이들을 효과적으로 활용하면 재사용 가능하고 테스트 가능한 코드를 작성할 수 있습니다! 🎯