포괄적인 Racket 및 함수형 프로그래밍 (Functional Programming) 치트 시트
요약
Racket 언어의 핵심 문법과 함수형 프로그래밍의 기초를 정리한 치트 시트입니다. 접두사 표기법, 데이터 타입, 조건문, 함수 정의 및 스코프 관리 방법을 다룹니다.
핵심 포인트
- Racket의 접두사 표기법(Prefix Notation)과 괄호 사용 규칙 설명
- 불변성을 특징으로 하는 4가지 원자적 데이터 타입 소개
- if, cond를 활용한 조건부 연산 및 논리 제어 방식
- 이름 있는 함수, lambda 익명 함수, let/let* 스코프 차이점
지난 주말, 저는 Lisp의 세계로 잠시 우회하여 Racket을 만났습니다. 제가 정리한 내용은 다음과 같습니다.
1단계: 구문 (Syntax) 및 핵심 산술 연산
Racket은 실행 괄호로 둘러싸인 접두사 표기법 (prefix notation, (operator arg1 arg2))을 사용합니다. 여는 괄호 (는 실행 트리거 역할을 합니다. 평가는 가장 안쪽 괄호에서 가장 바깥쪽 괄호 순으로 진행됩니다.
핵심 예시
;; 기본 산술 연산 (Basic Arithmetic)
(+ 10 5 2) ;; 17을 반환
(* 10 5 2) ;; 100을 반환
...
괄호의 황금률
명령, 연산자 또는 함수를 호출할 때만 괄호를 사용하세요.
(+ 5 (10))는 충돌(CRASH)이 발생합니다 (숫자 10을 함수로 실행하려고 시도함).((+ 5 5))는 충돌(CRASH)이 발생합니다 (10으로 평가된 후, 숫자 10을 실행하려고 시도함).
2단계: 핵심 데이터 구조 및 변수
전역 바인딩 (Global bindings)은 define을 사용하여 생성됩니다. 값은 불변(immutable)이며 시간이 지나도 변경될 수 없습니다.
네 가지 원자적 데이터 타입 (Atomic Data Types)
- 숫자 (Numbers): 정수 (
45), 소수 (3.14), 또는 분수 (1/3). - 문자열 (Strings): 큰따옴표로 감싸진 텍스트 (
"Hello"). - 불리언 (Booleans): 참 (
#t) 및 거짓 (#f). - 심볼 (Symbols): 작은따옴표가 접두사로 붙은 가볍고 불변하는 식별자 토큰 (
'success).
핵심 예시
(define radius 5)
(define pi 3.14)
(define status 'success)
3단계: 조건문 및 논리
조건부 연산은 단일 반환 값으로 평가되는 표현식입니다.
핵심 연산자 및 흐름 제어 (Flow Control)
and/or/not: 표준 논리 단락 평가 (short-circuiting) 접두사 연산자.if: 정확히 세 개의 인수를 받습니다:(if condition true-branch false-branch).else키워드는 없습니다.cond: 여러 분기를 순차적으로 평가합니다. 가독성을 위해[...]를 사용할 수 있습니다.
핵심 예시
(and (> 15 10) (< 15 20)) ;; #t를 반환
(if (> temperature 30) 'hot 'cold)
...
4단계: 함수 및 스코프 (Scope)
함수는 명시적인 return 키워드 없이 본문 표현식의 값을 자동으로 반환합니다.
이름 있는 함수, 익명 함수 및 스코프 블록
- 이름 있는 함수 (Named Functions): 이름과 매개변수를 괄호 안에 그룹화하여 정의합니다:
(define (name args) body). - 익명 함수 (
lambda(Anonymous Functions)): 즉석에서 만들어 사용하는 일회용 함수입니다:(lambda (args) body). let(병렬 (Parallel)): 지역 변수들을 동시에 생성합니다. 변수들은 설정 과정 동안 서로를 참조할 수 없습니다.let*(순차 (Sequential)): 지역 변수들을 차례대로 생성합니다. 나중에 생성된 변수는 이전에 생성된 변수를 참조할 수 있습니다.
핵심 예제 (Core Examples)
;; 이름 있는 함수 (Named Function)
(define (double n) (* n 2))
...
5단계: 리스트 및 현대적 리스트 연산 (Lists & Modern List Operations)
리스트는 순서가 있는 순차적 컬렉션 (sequential collections)입니다. 리스트는 전통적인 Lisp 관례 또는 현대적인 별칭 (aliases)을 사용하여 처리됩니다.
생성 및 추출 (Creation & Extraction)
list: 인자들을 평가하여 순차적인 리스트로 만듭니다.'(): 리터럴 형태의 빈 리스트 (empty list)를 나타냅니다.cons: 기존 리스트의 맨 앞에 단일 요소를 추가합니다.- 첫 번째 항목 (First Item):
car(전통적 방식) 또는first(현대적 방식)를 통해 추출합니다. - 나머지 리스트 (Remaining List):
cdr(전통적 방식) 또는rest(현대적 방식)를 통해 추출합니다.
핵심 예제 (Core Examples)
(define my-list (list 100 #t 'hello)) ;; '(100 #t hello)를 생성
(cons 'apples '(bananas cherries)) ;; '(apples bananas cherries)를 반환
...
6단계: 반복 및 고차 함수 (Iteration & Higher-Order Functions)
데이터를 제자리에서 변경하는 루프 (loops)를 사용하는 대신, 함수형 프로그래밍 (functional programming)은 불변 컬렉션 (immutable collections)을 처리하기 위해 고차 함수 (Higher-Order Functions)에 의존합니다.
핵심 4요소 (The Big Four)
map: 리스트를 순회하며 각 항목을 변환 함수 (transformation function)에 통과시키고, 새로운 리스트를 반환합니다.filter: 리스트를 순회하며 술어 조건 (predicate condition)에 대해 #t로 평가되는 항목은 유지하고, 나머지는 버립니다.foldl(Fold-Left): 요소를 왼쪽에서 오른쪽으로 (앞에서 뒤로) 처리하여 리스트를 하나의 값으로 축소 (reduce)합니다.foldr(Fold-Right): 요소를 오른쪽에서 왼쪽으로 (뒤에서 앞으로) 처리하여 리스트를 하나의 값으로 축소합니다.cons를 사용하여 재구성할 때 리스트 구조를 보존합니다.
핵심 예제 (Core Examples)
(map (lambda (x) (* x 2)) '(5 10 15 20)) ;; '(10 20 30 40)를 반환
(filter (lambda (n) (= n 5)) '(2 5 7 5 9 1)) ;; '(5 5)를 반환
...
Phase 7: 재귀 (Recursion) 및 꼬리 호출 최적화 (Tail Call Optimization, TCO)
재귀 (Recursion)는 전통적인 루프 (loops)를 대체합니다. 적절한 재귀 함수는 기저 사례 (Base Case, 종료 절)와 재귀 단계 (Recursive Step, 더 작은 인자를 사용한 자기 호출)를 필요로 합니다.
메모리 최적화 규칙 (Memory Optimization Rules)
- 표준 재귀 (Standard Recursion): 재귀 호출을 다른 함수(예:
+또는append) 내부에 가두어, 호출 스택 (call stack) 메모리가 선형적으로 확장되도록 강제합니다 (O(N) 공간 복잡도). - 꼬리 호출 최적화 (Tail Call Optimization, TCO): 재귀 호출이 꼬리 위치 (tail position, 평가되는 절대적인 마지막 표현식)에 있다면, Racket은 동일한 메모리 프레임을 재사용하여 상수 공간 (O(1) 공간 복잡도)에서 실행합니다.
- 누산기 패턴 (Accumulator Pattern): 진행 중인 합계를 인자로 전달하는 것은 표준 재귀를 꼬리 위치 최적화로 전환하기 위해 사용되는 주요 전략입니다.
핵심 예제 (Core Examples)
;; ❌ 표준 재귀 (Standard Recursion) (TCO 미적용 - 메모리 확장)
(define (sum-list lst)
(if (empty? lst)
...
Phase 8: 고급 생태계 엔지니어링 (Advanced Ecosystem Engineering)
1. 해시 맵 (Hash Maps) 및 유니크 세트 (Unique Sets)
#hash: 키-값 쌍 (key-value pairings)을 저장합니다.hash-ref와 같은 조회 도구에 전달되는 키워드는 컴파일러 네임스페이스 충돌을 방지하기 위해 반드시 따옴표로 묶어야 합니다 ('#:key).#hash내부에서 표준 심볼 (symbols)을 사용하는 경우에는 내부 따옴표를 생략합니다.set: 요소의 유일성 (uniqueness)을 보장하는 컬렉션입니다.set-member?를 통해 테스트하고set-add를 통해 확장할 수 있습니다.
(define user #hash((#:name . "Alice")))
(hash-ref user '#:name) ;; "Alice"를 반환
...
2. 상태 및 가변성 (State & Mutability, box)
box: 가변 데이터 (mutable data)를 감싸는 참조 래퍼 (reference wrapper)를 생성합니다.unbox를 통해 읽고set-box!를 통해 변경합니다. 느낌표(!)가 붙은 함수는 구조적 변경 (structural mutation)을 나타냅니다.begin: 순차적인 부수 효과 (side-effect) 연산들을 위에서 아래로 연결하며, 마지막 표현식의 평가 결과만을 반환합니다.
(define health (box 100))
(define (take-damage!)
(begin
...
3. 타입 체크 및 캐스팅 (Type Checking & Casting)
- 술어 (Predicates,
?): 런타임 타입 (예:string?,number?,symbol?)을 검증합니다. - 캐스팅 (Casting,
->): 데이터 형식을 변환합니다.string->number는 유효하지 않은 텍스트 입력이 주어지면 안전하게 #f를 반환합니다.
(if (string? "50")
(* (string->number "50")
2) 'error) ;; 100을 반환
4. 모듈 및 네임스페이스 (Modules & Namespaces)
- provide: 파일 시스템 파일의 어떤 부분이 공개적으로 내보내지는지(exported) 선언합니다.
- require: 상대적인 문자열 파일 경로를 로드함으로써 외부 샌드박스로부터 공개된 기능들을 가져옵니다.
;; file-a.rkt 내부
(provide double)
(define (double x) (* x 2))
...
5. 매크로 (Macros, define-syntax-rule)
- 매크로는 새로운 키워드를 주입하기 위해 컴파일 타임 (compile-time)에 평가되지 않은 원시 소스 코드를 처리합니다.
- Racket 매크로는 위생적 (hygienic)입니다. 즉, 컴파일러가 매크로 식별자를 자동으로 격리하여 사용자의 변수를 실수로 덮어쓰거나 충돌하지 않도록 합니다.
(define-syntax-rule (swap! box1 box2)
(let ([temp (unbox box1)])
(begin
...
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기