3방향 비교

컴퓨터 과학에서 3방향 비교(three-way comparison)는 전순서를 가진 타입에 속하는 두 값 A와 B를 가져와 수학적 삼분법의 법칙에 따라 단일 연산으로 A < B, A = B, 또는 A > B인지 여부를 결정한다.

이는 함수 (C의 strcmp와 같은), 메서드 (자바의 compareTo와 같은), 또는 연산자 (펄, PHP, C++의 우주선 연산자 <=>와 같은)로 구현될 수 있다.

 기계 수준 계산 
많은 프로세서에는 기본 타입에 대한 이러한 연산을 지원하는 명령어 집합이 있다. 일부 머신은 부호-크기 또는 1의 보수 표현(부호화된 숫자 표현 참조)에 기반한 부호 있는 정수를 가지며, 둘 다 구별되는 양수 및 음수 0을 허용한다. 이는 일관된 전순서가 채택되는 한 삼분법을 위반하지 않는다: −0 = +0 또는 −0 < +0 모두 유효하다. 그러나 일반적인 부동소수점 타입은 삼분법에 대한 예외가 있다: 특수 값 "NaN" (숫자가 아님)이 있어 모든 부동소수점 값 x(NaN 자체 포함)에 대해 x < NaN, x > NaN, x = NaN이 모두 거짓이다.

 고급 언어 
 기능 
C에서 strcmp 및 memcmp 함수는 각각 문자열과 메모리 버퍼 간에 3방향 비교를 수행한다. 첫 번째 인수가 두 번째 인수보다 사전적으로 작으면 음수를 반환하고, 인수가 같으면 0을 반환하며, 그렇지 않으면 양수를 반환한다. 이러한 "차이의 부호"를 반환하는 규칙은 표준 정렬 함수 qsort에 의해 임의의 비교 함수로 확장되며, qsort는 비교 함수를 인수로 받아들이고 이를 준수하도록 요구한다.

펄 (숫자 비교에만 해당하며, cmp 연산자는 문자열 사전 비교에 사용됨), PHP (버전 7부터), 루비, Apache Groovy에서 "우주선 연산자" <=>는 A < B, A = B, 또는 A > B인지 여부에 따라 각각 −1, 0, 또는 1의 값을 반환한다. 파이썬 2.x의 cmp(3.x에서 제거됨), OCaml compare, 코틀린 compareTo 함수는 동일한 것을 계산한다. 하스켈 표준 라이브러리에서 3방향 비교 함수 compare는 Ord 클래스의 모든 타입에 대해 정의된다; 이 함수는 Ordering 타입을 반환하며, 그 값은 LT (작음), EQ (같음), 및 GT (큼)이다:

data Ordering = LT | EQ | GT

많은 객체 지향 프로그래밍 언어에는 객체와 다른 주어진 객체 간에 3방향 비교를 수행하는 3방향 비교 함수가 있다. 예를 들어, 자바에서 Comparable 인터페이스를 구현하는 모든 클래스에는 음의 정수, 0, 또는 양의 정수를 반환하거나 NullPointerException을 던지는 (객체 중 하나 또는 둘 다 null인 경우) compareTo 메서드가 있다. 마찬가지로, .NET 프레임워크에서 IComparable 인터페이스를 구현하는 모든 클래스에는 이러한 CompareTo 메서드가 있다. C++에서 3방향 비교가 가능한 모든 클래스는 std::compare_three_way, std::strong_order, std::weak_order, 또는 std::partial_order의 인스턴스의 매개변수가 될 수 있다.

자바 버전 1.5부터는 아래에 언급된 산술 오버플로와 같은 계산 문제 없이 차이를 알 수 있다면 Math.signum 정적 메서드를 사용하여 동일한 것을 계산할 수 있다. 많은 컴퓨터 언어는 함수의 정의를 허용하므로 compare(A,B)를 적절하게 고안할 수 있지만, 문제는 내부 정의가 어떤 종류의 3방향 구문을 사용할 수 있는지 또는 반복 테스트에 의존해야 하는지 여부이다.

3방향 비교 연산자나 메서드가 이미 없는 경우 3방향 비교를 구현할 때 A = B 및 A < B, 또는 A < B 및 A > B와 같이 두 가지 비교를 결합하는 것이 일반적이다. 원칙적으로 컴파일러는 이 두 표현식을 하나의 비교 다음에 여러 결과 테스트로 대체할 수 있다고 추론할 수 있지만, 이 최적화에 대한 언급은 해당 주제에 대한 텍스트에서 찾을 수 없다.

어떤 경우에는 A와 B를 빼고 결과의 부호를 검사하여 3방향 비교를 시뮬레이션할 수 있으며, 숫자의 부호를 검사하는 특수 명령어를 활용할 수 있다. 그러나 이 경우 A와 B의 타입이 잘 정의된 차이를 가져야 한다. 고정 폭 부호 있는 정수는 뺄셈 시 오버플로될 수 있고, 부동소수점 숫자는 정의되지 않은 부호를 가진 NaN 값을 가지며, 문자열은 전체 순서에 해당하는 차이 함수가 없다. 기계 수준에서는 오버플로가 일반적으로 추적되며 뺄셈 후 순서를 결정하는 데 사용될 수 있지만, 이 정보는 일반적으로 상위 수준 언어에서는 사용할 수 없다.

프로그래밍 언어에서 제공하는 3방향 조건문의 한 경우, 포트란의 현재는 더 이상 사용되지 않는 3방향 산술 IF 문은 산술 표현식의 부호를 고려하고 결과의 부호에 따라 이동할 세 개의 레이블을 제공한다:

 IF (expression) negative,zero,positive

C 및 관련 언어의 일반 라이브러리 함수 strcmp는 문자열에 대한 3방향 사전식 비교이지만, 이러한 언어는 다른 데이터 타입에 대한 일반적인 3방향 비교를 지원하지 않는다.

우주선 연산자
숫자에 대한 3방향 비교 연산자 또는 "우주선 연산자"는 펄, 루비, Apache Groovy, PHP, Eclipse Ceylon, C++에서 <=>로 표기되며 우주선 연산자라고 불린다.

C++에서는 C++20 개정판이 우주선 연산자 <=>를 추가하여, 두 값이 같은지, 작은지, 큰지, 또는 순서가 없는지를 인코딩하는 값을 반환하며, 비교의 엄격성에 따라 다른 타입을 반환할 수 있다.

이 이름의 유래는 랜들 L. 슈왈츠가 HP BASIC 스타 트렉 게임의 우주선을 연상시켰기 때문이라고 한다. 다른 코더는 스타워즈 사가에 나오는 다스 베이더의 타이 파이터와 비슷하게 생겼기 때문에 그렇게 명명되었다고 제안했다.

PHP 예시:
echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

C++ 예시:
1 <=> 1; // std::strong_ordering::equal로 평가됨
1 <=> 2; // std::strong_ordering::less로 평가됨
2 <=> 1; // std::strong_ordering::greater로 평가됨

복합 데이터 타입

3방향 비교는 2방향 비교와 달리 기본이 아닌 데이터 타입에 대한 사전식 비교를 쉽게 구성하고 구축할 수 있는 속성을 가지고 있다.

다음은 펄의 구성 예시이다.
sub compare($$) {
 my ($a, $b) = @_;
 return $a->{unit} cmp $b->{unit}
 || $a->{rank} <=> $b->{rank}
 || $a->{name} cmp $b->{name};
}
펄에서 cmp는 문자열에 사용되며, <=>는 숫자에 사용된다는 점에 유의하라. 2방향 동등 코드는 덜 간결하지만 반드시 덜 명확한 것은 아니다. 위 코드는 || 연산자의 단락 평가와 펄에서 0이 거짓으로 간주된다는 사실을 활용한다. 결과적으로 첫 번째 비교가 동일하면 (0으로 평가되므로) 두 번째 비교로 "넘어가고", 비영(non-zero) 값을 찾거나 끝에 도달할 때까지 계속된다.

파이썬, 루비, 하스켈 등을 포함한 일부 언어에서는 목록 비교가 사전식으로 이루어지므로, 위 예시처럼 값을 원하는 순서로 목록에 넣으면 비교 체인을 만들 수 있다. 예를 들어 루비에서는 다음과 같다.
[a.unit, a.rank, a.name] <=> [b.unit, b.rank, b.name]C++에서는:std::tie(a.unit, a.rank, a.name) <=> std::tie(b.unit, b.rank, b.name)

SQL 조인 연산자
일부 SQL 방언에서는 <=>가 둘 다 null인 경우 피연산자를 일치시키는 null-안전 조인 연산자로 사용된다.

 같이 보기 
 부호함수
 3치 논리

 내용주 

 각주 

분류:조건문
분류:연산자 (프로그래밍)