토파즈는 다양한 연산자를 제공하여 데이터를 조작하고 계산할 수 있습니다. 모든 연산자의 동작 방식과 우선순위를 정확히 이해하는 것이 중요합니다. ⚡
🔢 산술 연산자
기본 산술 연산
// 기본 산술 연산자
let a = 10
let b = 3
let 더하기 = a + b // 13
let 빼기 = a - b // 7
let 곱하기 = a * b // 30
let 나누기 = a / b // 3.333...
let 나머지 = a % b // 1
let 거듭제곱 = a ** b // 1000
print("더하기: {더하기}")
print("빼기: {빼기}")
print("곱하기: {곱하기}")
print("나누기: {나누기}")
print("나머지: {나머지}")
print("거듭제곱: {거듭제곱}")
부호 연산자
let 양수 = +42 // 42 (명시적 양수)
let 음수 = -42 // -42 (부호 반전)
let 숫자 = 15
let 반전 = -숫자 // -15
print("원래 값: {숫자}")
print("부호 반전: {반전}")
증감 연산자
토파즈 v4에서는 ++/-- 대신 명시적 재할당을 사용합니다.
let mut 카운터 = 5
카운터 = 카운터 + 1
카운터 = 카운터 - 1
function 증가(값: int) -> int { 값 + 1 }
function 감소(값: int) -> int { 값 - 1 }
카운터 = 증가(카운터)
카운터 = 감소(카운터)
print("카운터 최종값: {카운터}")
복합 할당 연산자
let mut 값 = 10
값 += 5 // 값 = 값 + 5 = 15
값 -= 3 // 값 = 값 - 3 = 12
값 *= 2 // 값 = 값 * 2 = 24
값 /= 4 // 값 = 값 / 4 = 6
값 %= 4 // 값 = 값 % 4 = 2
값 **= 3 // 값 = 값 ** 3 = 8
print("최종 값: {값}")
🔍 비교 연산자
기본 비교 연산
let x = 10
let y = 20
let z = 10
// 동등성 비교
let 같음 = x == z // true
let 다름 = x != y // true
// 크기 비교
let 작음 = x < y // true
let 작거나같음 = x <= z // true
let 큼 = y > x // true
let 크거나같음 = z >= x // true
print("x == z: {같음}")
print("x != y: {다름}")
print("x < y: {작음}")
print("y > x: {큼}")
타입 안전 비교
// Topaz는 타입 안전 비교를 강제
let 정수 = 42
let 실수 = 42.0
let 문자열 = "42"
// 같은 타입끼리만 비교 가능
let 정수비교 = 정수 == 42 // true
let 실수비교 = 실수 == 42.0 // true
// 다른 타입 비교는 명시적 변환 필요
let 타입변환비교 = 정수.toString() == 문자열 // true
let 숫자변환비교 = 정수 == parseInt(문자열) // true
print("타입 변환 후 비교: {타입변환비교}")
특수 비교 연산자
토파즈 v4에는 <=> 연산자가 없습니다.
🧠 논리 연산자
기본 논리 연산
let 참 = true
let 거짓 = false
// 논리 AND
let AND결과 = 참 && 거짓 // false
let AND결과2 = 참 && 참 // true
// 논리 OR
let OR결과 = 참 || 거짓 // true
let OR결과2 = 거짓 || 거짓 // false
// 논리 NOT
let NOT결과 = !참 // false
let NOT결과2 = !거짓 // true
print("AND 결과: {AND결과}")
print("OR 결과: {OR결과}")
print("NOT 결과: {NOT결과}")
단축 평가 (Short-circuit)
function 부작용있는함수() -> bool {
print("함수가 호출되었습니다!")
return true
}
// AND 단축 평가 - 첫 번째가 false면 두 번째 실행 안함
let 단축AND = false && 부작용있는함수() // 함수 호출 안됨
// OR 단축 평가 - 첫 번째가 true면 두 번째 실행 안함
let 단축OR = true || 부작용있는함수() // 함수 호출 안됨
print("단축 AND: {단축AND}")
print("단축 OR: {단축OR}")
null 병합 연산자
// null 병합 연산자 (??)
let 값1: Option<string> = None
let 값2: Option<string> = Some("기본값")
let 값3 = "대체값"
let 결과1 = 값1 ?? "없음" // "없음"
let 결과2 = 값2 ?? "없음" // "기본값"
let 결과3 = 값1 ?? 값2 ?? 값3 // "기본값"
print("null 병합 결과: {결과1}")
// Option과 함께 사용
let 사용자 = { 이름: "김토파즈", 나이: Some(25) }
let 나이 = match 사용자.나이 { case Some(v) => v; case None => 0 }
print("나이: {나이}")
null 병합 할당 (??=)
현재 값이 None/null일 때만 초기화하는 문장 전용 할당입니다. 표현식 값을 생성하지 않으며 우결합입니다.
let mut 이름: Option<string> = None
이름 ??= Some("guest") // 이름은 Some("guest")가 됨
이름 ??= Some("override") // 이미 Some 이므로 변화 없음
// 우결합
a ??= b ??= Some(1) // a ??= (b ??= Some(1)) 과 동일
🔧 비트/시프트 연산자
토파즈 v4에는 비트/시프트 연산자가 포함되지 않습니다. 또한 >>는 비트 시프트가 아니라 함수 합성 연산자로 예약되어 있습니다(자세한 내용은 아래 함수 합성(>>) 섹션 참고).
🎯 특수 연산자
타입 연산자
토파즈 v4에는 typeof와 instanceof가 포함되지 않습니다. 타입 확인이 필요하다면 패턴 매칭이나 라이브러리 제공 유틸리티를 사용하세요.
범위 연산자
// 범위 연산자 (.. 포함, ..< 상한 제외)
let 포함범위 = 1..5 // 1, 2, 3, 4, 5
let 제외범위 = 1..<5 // 1, 2, 3, 4
for i in 포함범위 { print(i) }
for i in 제외범위 { print(i) }
멤버십 연산자 (in)
// 배열/리스트/집합
let ok1 = 3 in [1, 2, 3] // true
let ok2 = "a" in Set.of("a", "b") // true
// 맵 키 (keys 뷰)
let hasId = "id" in 사용자맵.keys // bool
// 범위
let inside = 5 in 1..10 // true
let outside = 10 in 1..<10 // false
스프레드 연산자
토파즈 v4 문법에 포함되지 않습니다.
옵션-안전 옵셔널 체이닝 (?.)
Option<T> 또는 T | null 같은 null 포함 유니온일 때만 파싱되는 연산자입니다. 일반 객체 체이닝은 아닙니다. 일반 멤버 접근은 .을 사용하세요.
let 사용자: Option<{ 이름: string, 프로필: Option<{ 도시: string }> }> =
Some({ 이름: "Ann", 프로필: Some({ 도시: "Seoul" }) })
let 이름옵션 = 사용자?.이름 // Option<string>
let 도시옵션 = 사용자?.프로필?.도시 // Option<string>
let 도시 = 사용자?.프로필?.도시 ?? "Unknown" // string
let 없음사용자: Option<{ 이름: string }> = None
let 기본이름 = 없음사용자?.이름 ?? "guest" // "guest"
// 내부적으로 Option.map/flatMap 조합으로 환원됩니다
// 사용자?.프로필?.도시 ≃ 사용자 |> Option.flatMap(_, u => u.프로필) |> Option.map(_, p => p.도시)
파이프 연산자
// 파이프 연산자 (|>)
let 결과 = 10
|> (x => x * 2) // 20
|> (x => x + 5) // 25
|> (x => x.toString()) // "25"
// 함수 체이닝에 유용
let 데이터 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|> (arr => arr.filter(x => x % 2 == 0)) // [2, 4, 6, 8, 10]
|> (arr => arr.map(x => x * x)) // [4, 16, 36, 64, 100]
|> (arr => arr.reduce((a, b) => a + b)) // 220
print("파이프 결과: {결과}")
print("체이닝 결과: {데이터}")
⚡ 연산자 우선순위 (토파즈 v4)
v4// 높음 → 낮음; 결합성 표시
1) 호출/인덱스/멤버: () [] . ?. (좌결합)
2) 거듭제곱: ** (우결합)
3) 단항: + - ! (우결합)
4) 곱셈/나눗셈/나머지: * / % (좌결합)
5) 덧셈/뺄셈: + - (좌결합)
6) 범위: .. ..< (좌결합) // 산술보다 낮고 비교보다 높음
7) 비교: < <= > >= == != in (좌결합)
8) 논리 AND: && (좌결합)
9) 논리 OR: || (좌결합)
10) null 병합: ?? (좌결합)
11) 함수 합성: >> (우결합)
12) 파이프라인: |> (좌결합)
13) 할당: = …, ??= (우결합)
// 합성 vs 파이프라인
let h = f >> g
let x = 입력 |> h // 입력 |> (f >> g) 와 동일
함수 합성 (>>)
let 정규화 = trim >> toLowerCase >> collapseSpaces
print(정규화(" Hello WORLD ")) // "hello world"
🔄 연산자 오버로딩
사용자 정의 타입 연산자
// 구조체에 연산자 구현
struct 벡터 {
x: float,
y: float
}
impl 벡터 {
// + 연산자 오버로딩
function +(other: 벡터) -> 벡터 {
return 벡터 { x: self.x + other.x, y: self.y + other.y }
}
// * 연산자 오버로딩 (스칼라 곱)
function *(scalar: float) -> 벡터 {
return 벡터 { x: self.x * scalar, y: self.y * scalar }
}
// == 연산자 오버로딩
function ==(other: 벡터) -> bool {
return self.x == other.x && self.y == other.y
}
// 문자열 변환
function toString() -> string {
return "벡터({self.x}, {self.y})"
}
}
// 사용 예제
let 벡터1 = 벡터 { x: 3.0, y: 4.0 }
let 벡터2 = 벡터 { x: 1.0, y: 2.0 }
let 합 = 벡터1 + 벡터2 // 벡터(4.0, 6.0)
let 배수 = 벡터1 * 2.0 // 벡터(6.0, 8.0)
let 같은가 = 벡터1 == 벡터2 // false
print("벡터 합: {합}")
print("벡터 배수: {배수}")
print("벡터 같음: {같은가}")
🛡️ 안전한 연산자 사용법
1. 오버플로우 처리
// 체크된 산술 연산
let 큰수1 = 2147483647 // int 최대값
let 큰수2 = 1
// 안전한 덧셈 (오버플로우 검사)
match 큰수1.checkedAdd(큰수2) {
case Some(결과) => print("안전한 덧셈: {결과}")
case None => print("오버플로우 발생!")
}
// 포화 연산 (최대/최소값으로 클램핑)
let 포화덧셈 = 큰수1.saturatingAdd(큰수2) // int 최대값 유지
print("포화 덧셈: {포화덧셈}")
2. 0으로 나누기 방지
function 안전한나누기(분자: float, 분모: float) -> Result<float, string> {
if 분모 == 0.0 {
return Err("0으로 나눌 수 없습니다")
}
return Ok(분자 / 분모)
}
// 사용 예제
match 안전한나누기(10.0, 0.0) {
case Ok(결과) => print("나누기 결과: {결과}")
case Err(오류) => print("오류: {오류}")
}
3. 타입 안전성
// 명시적 타입 변환으로 안전성 확보
let 문자열숫자 = "123"
let 실제숫자 = match parseInt(문자열숫자) {
case Ok(숫자) => 숫자
case Err(_) => {
print("숫자 변환 실패")
0 // 기본값
}
}
let 결과 = 실제숫자 + 100
print("안전한 계산 결과: {결과}")
📊 성능 최적화 팁
1. 비싼 연산 최적화
// 거듭제곱 대신 곱셈 사용 (작은 지수)
let 제곱 = 값 * 값 // x ** 2 대신
let 세제곱 = 값 * 값 * 값 // x ** 3 대신
// 나누기 대신 곱셈 사용
let 반값 = 값 * 0.5 // 값 / 2 대신
// 모듈로 연산 최적화 (2의 거듭제곱)
let 나머지 = 값 & 7 // 값 % 8 대신 (8 = 2^3)
2. 단축 평가 활용
// 비싼 함수 호출을 뒤로
let 결과 = 간단한조건() && 복잡한함수()
// null 체크 최적화
let 값 = 객체?.속성?.하위속성 ?? 기본값
토파즈의 연산자들을 정확히 이해하고 적절히 활용하면 효율적이고 안전한 코드를 작성할 수 있습니다! 연산자 우선순위와 타입 안전성을 항상 염두에 두세요. 🎯