데이터와 제어

패턴과 제어 흐름

if, match, for를 상황에 맞게 고르고 패턴으로 데이터를 검사하고 꺼내는 방법.

하나의 참·거짓 조건을 판단할 때는 if, 값의 형태나 종류에 따라 결과를 고를 때는 match, 각 항목에 같은 작업을 반복할 때는 for를 사용합니다. matchfor 안의 패턴은 데이터를 검사하면서 필요한 부분에 이름도 붙여 줍니다.

예제 실행하기

다음 코드를 data-control.tpz로 저장합니다.

TOPAZ
record StudyTask {
    title: string,
    minutes: int,
    done: bool = false,
}

function pace(task: StudyTask) -> string {
    if task.done {
        return "done"
    }
    match task.minutes {
        case 1..15 => "quick"
        case 16..30 => "focused"
        case _ => "deep"
    }
}

let tasks: Array<StudyTask> = [
    StudyTask { title: "Run first program", minutes: 10, done: true },
    StudyTask { title: "Build application", minutes: 25 },
]
let mut remaining = 0
for task in tasks {
    if !task.done {
        remaining = remaining + task.minutes
    }
    print("{task.title}: {pace(task)}")
}
print("remaining: {remaining} minutes")

먼저 검사한 뒤 실행합니다.

BASH
topaz check data-control.tpz
topaz run data-control.tpz

결과는 배열의 순서를 따릅니다.

출력
Run first program: done
Build application: focused
remaining: 25 minutes

코드가 내리는 세 가지 결정

반복문은 배열의 각 항목을 task라는 이름에 연결합니다. 안쪽의 if는 “이 작업이 아직 끝나지 않았는가?”라는 하나의 참·거짓 질문만 판단합니다. 여러 경우를 분류하거나 값의 내부를 나눠 꺼낼 필요는 없습니다.

두 결과가 모두 필요하면 if condition { ... } else { ... }로 씁니다. 두 분기가 호환되는 값을 만들면 if 전체도 값이 됩니다.

pace는 완료된 작업을 먼저 if로 처리합니다. 이어지는 matchtask.minutes를 한 번 계산한 뒤 case를 위에서부터 차례로 검사합니다. 1..1516..30은 범위 패턴입니다. 마지막 _는 나머지 모든 정수를 받으므로 어떤 입력에도 결과가 생깁니다.

선택알맞은 상황만들어지는 결과
if하나의 조건으로 분기할 때각 분기가 호환되는 값을 내면 그 값, 아니면 효과만 있는 문장
match값에 의미 있는 형태나 종류가 여러 개일 때선택된 가지의 값과 그 가지 안에서 쓸 수 있는 패턴 바인딩
for정해진 순서로 모든 항목을 방문할 때문장으로 쓰면 효과, 식으로 쓰면 본문 결과를 모은 Array

패턴은 검사와 바인딩을 함께 한다

리터럴 패턴과 범위 패턴은 값이 맞는지 검사합니다. task처럼 이름만 쓴 패턴은 맞은 값을 그 이름에 연결합니다. Some(value)Err(message) 같은 생성자 패턴은 OptionResult의 내부 값을 꺼냅니다. 레코드 패턴은 이름을 지정한 필드를 선택합니다.

배열 패턴의 나머지 표시는 ...가 아니라 ..입니다. case [head, ..tail] =>는 첫 항목을 head에, 남은 항목을 Arraytail에 연결합니다. 나머지 값을 사용할 필요가 없다면 _로 받습니다.

if let pattern = value { ... }은 성공하는 형태 하나와 선택적인 실패 경로만 필요할 때 유용합니다. while let은 패턴이 더 이상 맞지 않을 때까지 같은 검사를 반복합니다. 두 형식에서 만든 바인딩은 변경할 수 없으며 성공 본문 안에서만 보입니다.

흔한 실수

패턴에 [head, ...tail]을 쓰면 안 됩니다. ...는 배열이나 함수 호출에서 값을 펼칠 때 사용합니다. 목록 패턴의 올바른 나머지 표기는 [head, ..tail]입니다.

열거형을 만들 때와 패턴으로 검사할 때의 표기도 다릅니다. 값은 Status.Paused("review")처럼 열거형 이름을 붙여 만들지만, 패턴에서는 case Paused(reason) =>처럼 변형 이름만 씁니다.

선택에 영향을 주는 정확한 동작

  • 값을 만드는 ifmatch의 분기 결과는 서로 호환되어야 합니다.
  • 닫힌 열거형을 match하면 모든 변형을 다루는지 검사합니다. 가드가 붙은 가지는 완전성 판단에 포함되지 않습니다.
  • 검사기가 모든 경우를 다뤘다고 증명할 수 없다면 _ 가지를 명시합니다.
  • 패턴은 왼쪽에서 오른쪽으로 검사합니다. 실패한 대안에서 만든 바인딩은 다음 대안으로 새지 않습니다.
  • 컬렉션 컴프리헨션에서 실패 가능한 패턴이 맞지 않으면 항목을 건너뛰는 것이 아니라 실행 오류가 납니다. 항목을 거르려면 if 절을 명시합니다.
  • Array, 정수 범위, Set, map.keys에는 정해진 순회 순서가 있습니다. 문자열은 직접 순회하지 않습니다.
  • while과 문장 위치의 for는 값을 만들지 않습니다. 식으로 사용한 for는 본문 결과를 소스 순서대로 모읍니다.

사용자 정의 패턴과 반복자, 생성기, 실행 오류를 잡는 제어 형식, while이나 for에 붙이는 레이블은 현재 언어 범위에 포함되지 않습니다.

학습 과정의 데이터와 제어 예제에서 이어서 살펴볼 수 있습니다. 패턴이 여는 데이터 형태는 레코드와 명목 데이터, 순회와 필터링은 컬렉션과 컴프리헨션을 참고하세요.