SICP 연습문제 2.23 친절한 풀이

문제

for-each 프로시저는, map처럼 프로시저와 리스트를 하나씩 인자로 받지만, 결과 값으로 리스트를 내놓는 게 아니라, 리스트의 원소마다 (왼쪽에서 오른쪽으로) 프로시저를 적용한 결과만 내놓는다. 원소마다 프로시저를 적용했을 때 얻은 값은 아예 쓰지 않는다. 그러므로 리스트의 원소를 차례대로 화면에 찍는 등 똑같은 명령을 처리해야 하는 프로시저를 짜는 데 쓴다. 아래는 그 보기다.

(for-each (lambda (x) (newline) (display x))
(list 57 321 88))
>>> 57
>>> 321
>>> 88

(위에 나타내진 않았지만) for-each 프로시저의 결과 값은 (참처럼) 뭐가 되어도 상관없다. 그러니, for-each 프로시저를 정의해 보라.

문제로 부터 얻은 것

반환값이 없는 함수를 구현할 때 사용할 수 있는 여러가지 테크닉을 배울 수 있었습니다.

문제풀이

스킴 커뮤니티에 가보시면 정말 다양한 솔루션들이 있습니다. 좋아보이는 것 몇가지를 소개해 드리려고 합니다.

우선 제가 문제를 읽자마자 무지성으로 만들어본 코드입니다. if문의 else절에는 하나의 프로시저만 실행 가능하기 때문에, 새로운 프로시저 proc-and-next를 정의해서 proc를 실행하고 다음으로 실행할 리스트를 넘겨줬습니다.

(define (for-each proc items)
(define (proc-and-next items)
(proc (car items))
(for-each proc (cdr items)))
(if (null? items)
#t
(proc-and-next items)))



다음으로 살펴볼 프로시저는 and를 이용한 프로시저입니다. and를 이용하면, 하나의 프로시저밖에 들어갈 수 없는 if문의 else절에 여러 프로시저를 넣을 수 있다는 것을 알았습니다. for-each는 애초에 반환값이 없기 때문에 이런 구현이 가능합니다.

(define (for-each proc items)
(if (null? items)
#t
(and (proc (car items))
(for-each proc (cdr items)))))



다음으로 살펴볼 프로시저는 iter를 사용한 프로시저입니다. 특이한 점은 iter의 인자인 evaluate는 기존의 iter와는 달리 값을 전달하기 위한 목적이 아니라 프로시저를 실행하는 목적으로 쓰인다는 것입니다. 이 또한 반환값이 없기 때문에 구현할 수 있는 것입니다.

(define (for-each proc items)
(define (iter items evaluate)
(if (null? items)
#t
(iter (cdr items) (proc (car items)))))
(iter items #t))



다음으로 살펴볼 프로시저는 연습문제 2.21에서 사용한 map 프로시저를 이용한 프로시저입니다. map과 for-each의 차이점은 단순히 반환값이 있냐 없냐이기 때문에 이런식으로 구현이 가능한 것입니다. 천재적입니다.

(define (for-each proc items) 
(map proc items))



여러 천재들의 정답을 보자 하니 제 코드가 초라해지는 하루였습니다. 읽어주셔서 감사합니다.