스킴은 함수형 프로그래밍 언어지만 다양한 프로그래밍 패러다임을 수용할 수 있다. 여기서는 스킴으로 명령형 프로그래밍을 하는 방법을 알아보자.
set!
프로그래밍에서 시간에 따라 바뀌는 것을 '상태(state)'라고 한다. 명령형 프로그래밍은 상태를 다루는 데 초점을 맞춘다. 다음과 같은 예를 보자.
> (define state 1) > (define (inc!) (set! state (+ state 1))) > state 1 > (inc!) > state 2 > (inc!) > state 3
set! 함수는 변수의 값을 바꾼다. 처음에 state는 1이었으나 inc! 함수를 한 번 사용할 때마다 값이 1씩 증가한다. set!처럼 변수의 상태를 조작하는 함수의 이름에는 느낌표를 붙이는 것이 관례다.
비슷한 함수로 string-set!이 있다. string-set!은 string, string-append, make-string 함수로 만든 문자열 중에 특정 위치에 있는 글자를 바꾼다. 말로 설명해봐야 기니까 예제를 보자.
> (define 바뀌는 (string #\h #\e #\l #\l #\o)) > 바뀌는 "hello" > (string-set! 바뀌는 1 #\a) > 바뀌는 "hallo"
그냥 큰 따옴표를 쳐서 만든 문자열은 string-set!으로 바꿀 수 없다.
> (define 안바뀌는 "hello") > 안바뀌는 "hello" > (string-set! 안바뀌는 1 #\a) . string-set!: expects type <mutable string> as 1st argument, given: "hello"; other arguments were: 1 #\a
큰 따옴표 친 문자열을 string-set!으로 바꿀 수 있게 하려면 다음 예제처럼하면 된다. mutable-string 함수는 문자열을 받아서 리스트로 바꾼 다음 string함수를 이용해서 다시 문자열로 만든다.
> (define (mutable-string str) (apply string (string->list str))) > (define 원래안바뀌지만 (mutable-string "hello")) > (string-set! 원래안바뀌지만 4 #\u) > 원래안바뀌지만 "hellu"
상태는 시간에 따라 달라지기 때문에 상태를 다루는 함수의 의미도 시간에 따라 달라진다. "영희는 결혼을 하고 아이를 낳았다"와 "영희는 아이를 낳고 결혼을 했다."는 전혀 다른 이야기가 된다. 함수들을 순서대로 실행시키려고 할 때는 begin을 이용한다.
> (begin
(display "첫째 줄")
(newline)
(display "둘째 줄")
(newline))
첫째줄
둘째줄
newline은 줄을 바꿔주는 함수다. 만약 display 함수의 실행 순서가 바뀌면 첫째줄,둘째줄이 아니라 둘째줄,첫째줄이 될 것이다. begin의 사용법은 아래와 같다.
(begin (명령) (명령) .. (명령))
define으로 함수를 정의할 때는 begin을 따로 쓸 필요가 없다.
> (define (two-lines)
(display "첫째 줄")
(newline)
(display "둘째 줄")
(newline))
> (two-lines)
첫째줄
둘째줄
조건이 참인 경우에만 실행되는 프로그램을 짜고 싶으면 if와 begin을 함께 사용한다.
> (if #t (begin (display "안녕?") (newline))) 안녕 > (if #f (begin (display "안녕?") (newline)))
PLT 스킴에서는 이 표현을 좀 더 간단히 쓸 수 있도록 when과 unless가 있다. when은 위의 if..begin과 똑같다. 사용법은 아래와 같다.
> (when #t (display "안녕") (newline)) 안녕 > (when #f (display "안녕") (newline)) >
when은 조건이 참일 때만 실행하지만 unless는 반대로 거짓일 때만 실행한다.
> (unless #t (display "안녕") (newline)) > (unless #f (display "안녕") (newline)) 안녕
for-each는 map과 비슷하게 함수를 리스트의 각 원소에 적용해준다. 차이점은 map은 반환값이 있는 함수를 적용해서 새로운 리스트를 만들지만 for-each는 반환값이 없는 함수를 적용한다.
> (for-each display '(1 2 3)) 123
다음은 1부터 10까지 줄을 바꿔가며 출력하는 예다.
> (define (range from to)
(cond
((> from to) ())
(else (cons from (range (+ from 1) to)))))
> (for-each (lambda (n) (display n) (newline)) (range 1 10))
1
2
3
4
5
6
7
8
9
10