2P by neo 24일전 | favorite | 댓글 1개

David Beazley와 SICP 강의 후기: 1주간의 경험

2022년 말 David Beazley의 SICP 강의에 참여한 경험을 공유함. 여러 무료 자료가 있지만, Dave의 강의는 특정 주제를 선택하고 심도 있게 설명함으로써 매우 효과적이었음.

시작점

SICP 강의는 Scheme 언어로 진행되었으며, 여기서는 Python으로 간단한 Scheme 해석기를 구현하여, 기초 개념인 대입(substitution) 모델을 설명함.

Scheme 언어의 기본

  • 프리미티브(Primitive): 기본 값들 (예: 정수)
  • 연산자: +, -, *, / 등의 기본 연산을 접두어 표기법으로 사용
  • define: 변수 정의
> (define x 2)  
> (+ x 3) ; 결과: 5  
  • if: 조건문
  • lambda: 익명 함수 정의
> ((lambda (x) (* x x)) 3) ; 결과: 9  

Python에서의 Scheme 해석기

Python을 사용하여 Scheme 코드를 평가하는 간단한 해석기 구현. 기본 연산은 Python 함수로 정의.

definitions = {  
    "+": lambda x, y: x + y,  
    "*": lambda x, y: x * y,  
}  

예시:

> evaluate(("+", 2, 3)) # 결과: 5  

definelambda의 구현, 그리고 조건문 if 처리까지 포함.

대입 모델(Substitution Model)

대입 모델은 간단한 프로그램 해석 방식으로, 변수를 값으로 대체하며 프로그램을 평가함. 그러나 **할당(assignment)**이 포함되면 이 모델은 실패함.

상태(State)

대입 모델이 깨지는 예로 **할당(assignment)**을 들 수 있음. 예를 들어 은행 계좌 잔고를 모델링할 때 set!을 사용하여 변수를 업데이트함.

(define balance 100)  
  
(define (withdraw amount)  
  (set! balance (- balance amount))  
  balance)  

이 경우 대입 모델은 이전과 이후의 잔고 상태를 구분하지 못함.

환경(Environment) 모델이 필요해짐. 변수는 환경 내에서 정의되고, 각 절차는 자신만의 환경을 가짐.

스트림(Streams)

상태를 모델링하는 또 다른 방식으로 스트림이 있음. 스트림은 지연 평가(lazy evaluation)를 통해 미래의 값도 모델링할 수 있음.

무한 루프와 평가 순서

평가 순서의 차이: 대부분의 언어는 **적용 순서 평가(applicative-order evaluation)**를 사용하여 인자를 먼저 평가함.

> (square (+ 1 2)) ; 결과: 9  

하지만 **정상 순서 평가(normal-order evaluation)**는 인자가 실제로 필요할 때까지 평가를 지연시킴. 이로 인해 무한 루프를 피할 수 있음.

> (define (p) (p))  
> (define (test x y) (if (= x 0) 0 y))  
> (test 0 (p)) ; 정상 순서에서는 0 반환, 적용 순서에서는 무한 루프  

람다 계산법과 Church 숫자

Church 인코딩을 통해 숫자를 절차(procedure)로 표현할 수 있음. 이는 함수형 프로그래밍의 중요한 개념임.

(define (zero f) (lambda (x) x))  
(define (increment n) (lambda (f) (lambda (x) (f ((n f) x)))))  
  • zero는 인자를 그대로 반환하는 함수 (identity 함수).
  • increment는 함수 호출을 한 번 더 적용함.

예시

> ((zero (lambda (x) (+ x 1))) 0) ; 결과: 0  
> (((increment zero) (lambda (x) (+ x 1))) 0) ; 결과: 1  

반복 vs 재귀

Scheme은 for 루프 대신 재귀를 사용하여 반복 작업을 수행함.

재귀 예시: 팩토리얼

(define (factorial n)  
  (if (= n 1)   
    1   
    (* n (factorial (- n 1)))))  

이 재귀 호출은 스택을 사용하여 메모리를 많이 차지할 수 있음.

꼬리 재귀 최적화(Tail-call optimization)

Scheme은 꼬리 재귀 최적화를 통해 메모리 사용을 줄임. 이로 인해 반복적(iterative) 프로세스처럼 작동하게 됨.

(define (factorial n)  
  (define (iter product counter)  
    (if (> counter n)  
        product  
        (iter (* product counter) (+ counter 1))))  
  (iter 1 1))  

마무리

David Beazley의 강의는 SICP의 주요 개념을 선택하여 깊이 있게 다룸. 특히 함수형 프로그래밍, 람다 계산법, 평가 순서 등 다양한 프로그래밍 패러다임을 이해하는 데 도움을 줌.

Knuth의 인용

이론만 공부한다면 실천적인 부분에 집중할 때가 되었음을 의미하고, 실천만 한다면 이론적인 부분에 집중할 때가 되었음을 의미함.

Hacker News 의견
  • SICP 강의는 정보 밀도가 높지만, 학생들의 Q&A와 칠판 사용 등으로 시간이 많이 소요됨. 강의 순서도 재구성 가능성이 있음. 개인적으로 새로운 비디오 시리즈를 계획 중임

    • SICP 강의는 Python 같은 현대 언어를 사용하면서도 본래의 취지를 유지하고 있음
    • Python은 다중 패러다임 언어로서 표현력이 뛰어남
  • 상태를 순수 함수로 인코딩하는 방법을 소개함. 다양한 데이터에 대한 순수 함수적 인코딩이 존재함

    • 인코딩은 혼란스러울 수 있지만 우아하고 간결함
    • JavaScript에서 Maybe 모나드를 함수형으로 구현한 예시를 보여줌
  • 블로그 포스트의 URL 앵커/해시로 인해 포스트스크립트로 바로 이동하여 혼란스러웠음

  • cons/car/cdr를 람다로 구현한 것은 처음 봤을 때 마법 같았음. 이는 언어 런타임이 키/값 사전을 구현해야 함을 보여줌

  • David Beazley는 Python 세계에서 전설적인 인물이며, 이 강의는 처음엔 놀라웠지만 곧 완벽한 조합이라고 생각하게 됨. 다음 강의에 등록함

    • 이는 소프트웨어 엔지니어의 지속적인 교육이 어떻게 진행될지를 보여줌
  • 귀납적 데이터 타입이 필요하다는 개념을 접했음. Church 인코딩만으로는 0 != 1 같은 정리 증명을 할 수 없음

    • 관련 내용을 Notion에 게시함
  • 기사 자체는 흥미로웠지만, 페이지 탐색이 어려웠음. 키보드 화살표로 스크롤이 안 됨

  • "대체 모델" 섹션의 코드에 오타가 있음. fib 대신 fibonacci를 사용해야 함. GitHub 저장소의 코드는 올바름

  • 책에 대한 논의가 진행 중인 링크가 있음. 링크가 페이지 하단의 논의로 바로 연결되는 이유가 궁금함. 다른 논의에 통합될 수 있는지 궁금함

  • YouTube 링크가 제공됨