트랙
/
Factor
Factor
/
연습 문제
/
라자냐 거장
라자냐 거장

라자냐 거장

학습 연습 문제

소개

짧은 단어라면 스택을 이리저리 다루는 방식으로 충분해요. 하지만 단어에 입력이 여러 개 있고 그것들을 이름으로 가리켜야 할 때는 Factor가 로컬을 제공해요. 로컬은 ::(단어 정의용)나 [| | ](인라인 람다용)로 도입하는 이름 있는 바인딩이에요.

:: ( inputs -- outputs )

:를 ::로 바꾸면 입력 이름이 본문 안의 지역 변수가 돼요:

USING: locals ;

:: hypotenuse ( a b -- c )
    a a *
    b b *
    + sqrt ;

스택을 뒤섞는 버전과 비교해 봐요:

! DOCTEST: SKIP   (shown for comparison — would re-define hypotenuse)
: hypotenuse ( a b -- c )
    [ sq ] bi@ + sqrt ;

두 방법 모두 유효해요. 로컬은 같은 입력을 두 번 이상 참조할 때, 또는 연산 순서가 스택 순서와 자연스럽게 맞지 않을 때 빛을 발해요.

:>로 중간 값 바인딩하기

:: 본문(또는 인라인 [| ... |] 람다) 안에서 :>는 데이터 스택에서 값을 하나 꺼내 새로운 로컬에 바인딩해요:

value :> name      ! immutable binding
value :> name!     ! mutable binding (name! is its setter)

불변 형태가 일반적이에요. 중간 값에 이름을 붙이는 데 사용하면 본문이 위에서 아래로 읽혀요:

USING: locals math ;

:: average-3 ( x y z -- avg )
    x y + z + :> sum
    sum 3 / ;

!가 붙은 형태는 로컬을 변경 가능으로 선언해요. 본문 뒤쪽에서 value name!(콜론도 :>도 없이)로 값을 다시 할당해요:

USING: kernel locals math math.order ;

:: clamped ( x lo hi -- y )
    x :> v!
    v lo < [ lo v! ] when
    v hi > [ hi v! ] when
    v ;

변경 가능한 로컬은 나중 연습 문제에서 만나게 될 반복 단어(예: while) 안에서 빛을 발해요.

[let: 어디서나 쓸 수 있는 스코프

:>는 어휘적 스코프 안에서만 동작하는데, ::와 [| ... |]가 그 스코프를 자동으로 만들어 줘요. 일반 : 단어나 리스너에서 로컬을 도입하려면 코드를 [let ... ]로 감싸요:

[let code :> name code :> name ... body ]

각 :> name은 앞 표현식의 스택 맨 위 값을 바인딩해요. 그 바인딩은 폼의 나머지 부분에서 볼 수 있어요.

USING: locals math ;

[let 3 :> x 4 :> y x x * y y * + ] .   ! => 25

[let은 스택 스타일 : 정의와 이름 있는 바인딩을 잇는 다리예요. 단어의 일부만 로컬의 이점을 볼 때 편리해요.

람다: [| inputs | body ]

인용 안에서 [| inputs | body ]는 본문이 이름으로 가리킬 수 있는 로컬을 도입해요. 이는 assoc-map처럼 인용이 key와 value를 받는 고차 단어와 함께 쓸 때 특히 유용해요:

USING: assocs locals ;

H{ { "a" 1 } { "b" 2 } }
[| key value | key value neg ] assoc-map .
! => H{ { "a" -1 } { "b" -2 } }

같은 형태가 map-index([sequences][sequences]에 있어요)에도 통하는데, 그 인용은 원소와 인덱스를 받아요. 이름 덕분에 어떤 피연산자가 어떤 역할을 하는지 분명해져요:

USING: locals sequences ;

! Divide each element by its 1-based position:
{ 10 80 270 } [| elt i | elt i 1 + / ] map-index .
! => { 10 40 90 }

람다의 스택 효과는 입력 목록과 본문이 만들어 내는 것에 따라 결정돼요.

언제 로컬을 사용할까요

다음과 같은 경우 로컬을 사용해요:

  • 같은 입력이 두 곳 이상에서 사용될 때.
  • 자연스러운 연산 순서가 스택 순서와 맞지 않아서, 이를 바로잡는 dup/swap/rot가 읽기 어려울 때.
  • 고차 단어에 전달한 인라인 람다 안에서 그 단어가 여러 값을 한꺼번에 넘겨줄 때(예: assoc-map).

본문이 깔끔한 선형 흐름일 때는 일반 : 정의가 더 짧고 읽기도 그만큼 좋아요.

assoc-map: 한 번에 항목 하나씩

assoc-map(assocs에 있어요)은 연관 컬렉션의 각 항목에 인용을 매핑해요:

assoc-map ( assoc quot: ( key value -- newkey newvalue ) -- newassoc )

로컬 람다가 그 인용을 작성하는 가장 깔끔한 방법이에요.

cond 다시 보기

f나 숫자 값에 따라 분기해야 할 때, cond가 딱 맞는 형태예요. 비교할 단일 값이 없으니 case는 필요 없어요.

지침

다시 주방으로 돌아왔어요. 이번에는 Leah's Luscious Lasagna의 라자냐 레시피를 한층 더 다듬어 볼 거예요. 여기 있는 과제들은 여러 입력을 한꺼번에 다뤄야 하는데, 바로 그럴 때 로컬, 즉 Factor의 이름 있는 바인딩이 제 역할을 톡톡히 해요.

1. 요리 상태 확인하기

cooking-status는 스택에서 타이머 값을 하나 꺼내 상태 문자열을 반환하도록 정의해요.

  • 0 → "Lasagna is done."
  • f (타이머를 설정하지 않은 경우) → "You forgot to set the timer."
  • 그 밖의 숫자 → "Not done, please wait."
12 cooking-status .   ! => "Not done, please wait."
0 cooking-status .    ! => "Lasagna is done."
f cooking-status .    ! => "You forgot to set the timer."

2. 준비 시간 예측하기

preparation-time은 층 이름이 담긴 배열과 층당 평균 분 수를 받아 총 준비 시간을 반환하도록 정의해요.

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } 3 preparation-time .
! => 18

3. 필요한 면과 소스의 양 계산하기

quantities는 층 이름이 담긴 배열을 받아 두 개의 값을 반환하도록 정의해요. 필요한 면의 양(면 층 하나당 50g)과 필요한 소스의 양(소스 층 하나당 1/5리터)이에요.

{ "sauce" "noodles" "sauce" "meat" "mozzarella" "noodles" } quantities .s
! => 100
! => 2/5

4. 비밀 재료 추가하기

친구가 재료 목록을 보내왔는데, 마지막 항목이 바로 친구의 비밀 재료예요. add-secret-ingredient는 친구의 목록과 내 목록을 받아, 비밀 재료를 뒤에 붙인 내 목록을 반환하도록 정의해요.

{ "noodles" "sauce" "mozzarella" "kampot pepper" }
{ "noodles" "meat" "sauce" "mozzarella" }
add-secret-ingredient .
! => { "noodles" "meat" "sauce" "mozzarella" "kampot pepper" }

5. 레시피 조정하기

요리책 레시피는 두 인분이 나와요. scale-recipe는 레시피(재료 이름에서 양으로 가는 해시테이블)와 목표 인분 수를 받아, 그에 맞게 조정된 새 레시피를 반환하도록 정의해요.

H{ { "noodles" 200 } { "sauce" 1/2 } { "mozzarella" 1 } { "meat" 100 } }
4 scale-recipe .
! => H{ { "noodles" 400 } { "sauce" 1 } { "mozzarella" 2 } { "meat" 200 } }
GitHub에서 편집 링크가 새 창이나 탭에서 열려요
Factor Exercism

라자냐 거장 문제를 시작해 볼 준비가 됐나요?

Exercism에 가입하고 Factor 트랙을 개념 47개연습 문제 163개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.