2019년 1월 3일 목요일

[on lisp] 3.2 Imperative Outside-In 명령형 뒤집기

3.2 Imperative Outside-In

함수형 프로그래밍의 목적은 명령형 프로그래밍과 대조될 때 더 명확하게 드러난다.
함수형 프로그램은 당신이 무엇을 원하는지 말한다. 명령형 프로그램은 당신이 무엇을 해야 하는지 말한다.

비교를 해보자면
함수형 프로그램은 "Return a list of a and the square of the first element of x"라고 말한다.
(defun fun (x)
  (list 'a (expt (car x) 2)))
명령형 프로그램은 "Get the first element of x, then square it, then return a list of a and the square"라고 말한다.
(defun imp (x)
  (let (y sqr)
    (setq y (car x))
    (setq sqr (expt y 2))
    (list 'a sqr)))

명령형 에서 함수형으로 변환하는 트릭이 있다.
그 트릭은 명령형 프로그램은 함수형 프로그램이 안에서 밖으로 변형된 것이며
함수형 프로그램은 명령형 프로그램이 밖에서 안으로 변형된 것이라 해보자.

첫번째로 우리가 주목해야 하는 것은 let 내부에 있는 y, sqr의 생성이다.
이것은 나쁜일이 따라올 거라는 징조다. 런타임에 eval을 하는 것처럼 초기화 되지 않은 변수들은 거의 필요하지 않다.
하여 일반적으로 프로그램 내 질병의 징후로 취급해야 한다.

이러한 변수들은 프로그램을 자연스러운 모양(natural shape)으로 되지 않도록 고정시키는 핀처럼 쓰인다.
일단 이것들은 넘어가고 함수의 마지막으로 가보자.
명령형에서 가장 마지막에 실행되는 녀석은 함수형에서는 가장 바깥쪽에서 실행된다.

그러므로 우리가 할 일의 첫번째 단계는, 마지막 호출을 잡은 채로 프로그램의 나머지를 안에 집어 넣는 것이다!
마치 셔츠를 뒤집는 것처럼. 해보자.
마지막에 있는 값 (list 'a sqr)을 잡은 채로 나머지 내용을 안에 넣는 것이다.
(list 'a sqr)
;; sqr을 안에 넣는다.
(list 'a (expt y 2))
;; 이제 y도 안에 넣는다.
(list 'a (expr (car x) 2))
;; 명령형이 함수형으로 변경되었다.
필자 말로는 이 최종결과가 이전 초기버전보다 낫다고 한다.
기존 명령형 코드를 보면, 우리는 리턴을 하는 마지막 표현식 (list 'a sqr)을 만나게 되는데
sqr이 어디서 왔는지 알 수 없기 때문에 즉각적으로 명확히 할 수 없다.
이제 변형된 리턴코드는 로드맵처럼 제시되어 있다.

이 기법은 더 큰 기능에 적용되면서 가치가 높아진다.
사이드이팩트를 수행하는 함수에조차 그렇지 않은 부분을 정리할 수 있다.

댓글 없음:

댓글 쓰기