데이터와 제어

패턴과 제어 흐름

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에 붙이는 레이블은 현재 언어 범위에 포함되지 않습니다.

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