SICP 연습문제 1.41 친절한 풀이

문제

인자 하나 받는 프로시저를 인자로 받아, 그 프로시저를 두번 연거푸 계산하는 프로시저를 내놓도록, 프로시저 double을 짜 보자. 예컨데, inc가 인자에 1을 더하는 프로시저라면 (double inc)는 2를 더하는 프로시저가 된다. 다음 식의 값은 얼마인가?

(((double (double double)) inc) 5)

문제로 부터 얻은 것

((a b) x) = (a (b x))가 항상 성립하지는 않습니다.
함수끼리 연산된 함수에 값을 입력하는 경우에는, 함수를 먼저 정확히 계산해 주어야 합니다.

문제풀이

이문제의 풀이는 꼭꼭 씹어드셔야 이해하기 쉽습니다.
한줄 한줄 씹어먹어 주시기 바랍니다.

처음 이 문제를 봤을때, 단순한 저의 생각은 아래와 같았습니다.

double이 두번 실행하는 것이라면,
(double double)은 네번 실행하는 것이고,
(double (double double))은 여덟번 실행하는 것이므로,
(((double (double double)) inc) 5) = 5+8 =13이 아닐까?

인생이란 원래 자기 생각대로 되지 않는 법이란다.





시간을 들여서 깊게 고민해 보았습니다.
하지만 만족스러운 결론을 얻지 못했습니다.
차선책으로, 아래의 코드를 실행시켜서 사고의 힌트를 얻고자 했습니다.
편의를 위해서 double은 d로 치환하였습니다.
테스트 케이스들





위 실험에서 제가 주목한 부분은 아래의 두 식입니다.
(d ((d d) inc)) = (lambda (x) (+ x 8))
((d (d d)) inc) = (lambda (x) (+ x 16))
괄호를 어떻게 묶느냐에 따라서 결과가 달라진다는 것을 보여준 결과입니다.





위의 결과에서 깨달은 중요한 사실이 있습니다.
제가 은연중에 아래의 식을 당연하게 생각하고 있었다는 것입니다.
((d (d d)) f) = (d ((d d) f))

즉, 람다함수 a b에 대해
((a b) f) = (a (b f))가 성립한다고 당연하게 생각하고 있었던 것입니다.

그 생각은 당연히 틀렸습니다.





((d (d d)) f)식을 제대로 정리하면 다음과 같습니다.

(d (d d))
= (lambda (x) ((d d) ((d d) x)))
위의 식은 (d f)의 정의에 맞게 f 인자에 (d d)를 넣은 결과입니다.

((d (d d)) f)
= ((lambda (x) ((d d) ((d d) x))) f)
= ((d d) ((d d) f))





((d (d d)) f)
= ((d d) ((d d) f))

(d f) = (lambda (x) (f (f x)))이므로,
(d d) = (lambda (x) (d (d x)))입니다.
((d d) f) = (d (d f))

((d (d d)) f)
= ((d d) ((d d) f))
= (d (d ((d d) f)))
= (d (d (d (d f))))





((d (d d)) inc)
= (d (d (d (d inc))))
= (lambda (x) (+ x 16))

함수끼리 연산된 함수에 값을 입력하는 경우에는, 함수를 먼저 정확히 계산해 주어야 합니다.
난해한 글 읽어주셔서 감사합니다.