이번 연습 문제에서는 조건문을 소개해요. 조건문은 값에 따라 둘 이상의 행동 중에서 하나를 고르는 거예요. Annalyn's Infiltration에서 만난 불리언과 Currency Conversion에서 다룬 정수 연산을 바탕으로 해요.
이 단어들은 모두 math에 있어요 (=는 kernel에 있고요):
= ( x y -- ? ) ! equal
< ( x y -- ? ) ! less than
<= ( x y -- ? ) ! less than or equal
> ( x y -- ? ) ! greater than
>= ( x y -- ? ) ! greater than or equal
3 3 = . ! => t
2 3 < . ! => t
3 3 <= . ! => t
3 4 = not . ! => t (inequality: combine = with not)
zero?는 (math에 있어요) 흔히 쓰는 0 = 검사를 줄여 놓은
거예요. 숫자를 소비하고 그 숫자가 0이었는지 여부를 푸시해요:
zero? ( n -- ? ) ! t when n is 0
between?는 (math.order에 있어요) 어떤 값이 양 끝을
포함하는 범위 안에 들어가는지 검사해요. 어떤 행동이 숫자가 어느 구간에
속하는지에 따라 달라질 때 유용해요:
between? ( x lo hi -- ? ) ! lo <= x <= hi (inclusive)
5 1 10 between? . ! => t
0 1 10 between? . ! => f
10 1 10 between? . ! => t (inclusive at both ends)
값 하나가 아니라 범위로 분기를 고를 때 cond의 조건식으로
dup 1 4 between?처럼 쓰이는 걸 자주 보게 될 거예요.
위의 비교 단어들은 불리언을 만들어내요. 불리언에 따라 행동하려면 조건문에
쿼테이션을 하나 이상 넘겨줘요. 쿼테이션은 대괄호 [ ... ]로 감싼 코드
조각이에요. 쿼테이션을 쓰면 코드가 실행되는 대신 값으로서 스택에 푸시돼요.
그러면 if 같은 단어가 어느 쿼테이션을 실행할지 정해요.
[ neg ] ! a quotation that negates the top of the stack
[ ] ! the empty quotation — does nothing
쿼테이션은 뒤에 나오는 연습 문제에서 자세히 다뤄요. 지금은 [ ... ]를
"이 분기에서 실행할 코드"로 읽으면 돼요.
if, when, unless
if는 (kernel에 있어요) 불리언과 쿼테이션 두 개를 받아요.
불리언이 참이면 첫 번째 쿼테이션을, 거짓이면 두 번째 쿼테이션을 실행해요:
if ( ? then-quot else-quot -- )
: abs ( x -- y ) dup 0 < [ neg ] [ ] if ;
when은 불리언이 참일 때만 쿼테이션을 실행하고, unless는 거짓일 때만
실행해요:
when ( ? quot -- )
unless ( ? quot -- )
if*, when*, unless*
커널에 있는 이 세 가지 변형은 불리언이 참일 때 그 불리언을 값으로서 남겨
둬요. 어떤 단어가 "그 값, 아니면 f"를 반환할 때 유용해요:
if* ( ? true false -- ) ! truthy: true is called WITH ? on stack
when* ( ? true -- ) ! truthy: true is called WITH ? on stack
unless* ( ? false -- ) ! falsy: false runs and pushes a default
if*는 두 갈래 형태예요. 참일 때 실행되는 갈래는 값이 스택에 남아 있는
채로 호출되고, 거짓일 때 실행되는 갈래는 값 없이 호출돼요:
42 [ ] [ "nothing" ] if* . ! prints 42
f [ ] [ "nothing" ] if* . ! prints "nothing"
unless*는 "값 또는 기본값"을 나타내는 대표적인 관용구예요. 값이 참이면
그대로 두고, f이면 값을 버리고 쿼테이션을 실행해서 대체할 값을 푸시해요:
"hello" [ "anonymous" ] unless* . ! => "hello"
f [ "anonymous" ] unless* . ! => "anonymous"
when*는 if*의 한 갈래 형태예요. 값이 참일 때만 값이 스택에 남아 있는
채로 쿼테이션을 실행하고, 값이 f이면 그냥 버려요. "그 값, 아니면 f"라는
결과를 f인 경우를 굳이 처리하지 않고 누적 값에 합쳐 넣고 싶을 때 쓰면
돼요:
0 41 [ + ] when* . ! => 41 (truthy: 41 added to the running total)
0 f [ + ] when* . ! => 0 (falsy: f dropped, total left untouched)
cond여러 대안 중에서 골라야 할 때는 combinators에 있는 cond가
딱 맞아요. { predicate body } 형태의 쌍으로 이루어진 배열을 받아서, 참을
만들어내는 첫 번째 조건의 본문을 실행해요:
USING: combinators ;
: classify ( n -- label )
{
{ [ dup 0 < ] [ drop "negative" ] }
{ [ dup 0 = ] [ drop "zero" ] }
[ drop "positive" ]
} cond ;
몇 가지 눈여겨볼 점이 있어요:
dup ... <test> 형태를 써요.drop한 뒤 결과를 푸시하는 것으로 시작해요.이 연습 문제에서는 자동차 공장 조립 라인의 생산량을 분석하는 코드를 작성해요. 조립 라인의 속도는 0(꺼짐)부터 10(최대)까지 조절할 수 있어요.
0이 아닌 가장 낮은 속도(1)에서는 시간당 221대의 자동차가 생산돼요. 생산량은 속도에 따라 선형적으로 증가하기 때문에, 속도 4에서는 시간당 4 * 221 = 884대의 자동차를 생산해요. 하지만 속도가 높아질수록 결함이 있는 자동차가 생산될 가능성도 커지고, 그런 자동차는 폐기해야 해요.
네 가지 작업이 있어요. 각 작업은 스택에서 조립 라인의 속도, 즉 정수 매개변수 하나를 가져와요.
production-status를 정의해서 속도가 0이면 "stopped"를, 그 밖의 속도에서는 "running"을 반환하게 해요:
0 production-status .
! => "stopped"
3 production-status .
! => "running"
success-rate를 정의해서 제품이 오류 없이 생산될 확률을 반환하게 해요:
0: 0.0
1부터 4까지: 1.0
5부터 8까지: 0.9
9: 0.8
10: 0.77
10 success-rate .
! => 0.77
production-rate-per-hour를 정의해서 성공률을 고려한 조립 라인의 시간당 생산량을 반환하게 해요.
먼저 base-speed를 정의해야 해요. 값은 상수 221이에요.
6 production-rate-per-hour .
! => 1193.4
반환되는 값은 실수예요.
working-items-per-minute를 정의해서 분당 정상 작동하는 자동차가 몇 대 생산되는지 반환하게 해요. 결과는 정수예요. 일부만 완성된 자동차는 세지 않아요.
6 working-items-per-minute .
! => 19
Exercism에 가입하고 Factor 트랙을 개념 47개연습 문제 163개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.