次のJavaScriptコードの実行結果はどうなるでしょうか?
[1,11,3] > [1,2,4]
答えはfalse。配列が文字列化されて比較される。JSでの順序比較は ToPrimitive という処理を行い、その戻り値を使って比較するらしい。
あの分かりにくいECMAScript仕様書を読むと、順序比較時の ToPrimitive は以下の流れになっているようだ。
-
@@toPrimitive
メソッドを取得。取得できれば呼び出して、その結果を返す。 -
valueOf
メソッドを呼ぶ。戻り値がObjectでなければ、その結果を返す -
toString
メソッドを呼び、その結果を返す
ArrayにはSymbol.toPrimitiveがなく、valueOfはArray自身を返す。ArrayはObjectなので、toStringが呼ばれて文字列となるようだ。
で、他の言語ではどうなるのか気になったので色々な言語で配列の大小比較をしてみた。
Ruby
[1, 11, 3] > [1, 2, 4]
例外がスローされる。Rubyは演算子もメソッドであり、Arrayは > メソッドを実装していないのでNoMethodError。
Java
public class Hoge {
public static void main(String[] args) {
int[] x = new int[1];
int[] y = new int[2];
System.out.println(x > y);
}
}
コンパイルエラー、エラー: 二項演算子'>'のオペランド型が不正です
Golang
package main
import "fmt"
func main() {
a := []int{1, 11, 3}
b := []int{1, 2, 4}
fmt.Println(a > b)
}
コンパイルエラー、invalid operation: a > b (operator > not defined on slice)
Swift
let a = [1, 11, 3]
let b = [1, 2, 4]
print(a > b)
コンパイルエラー、error: binary operator '>' cannot be applied to two '[Int]' operands
Rust
fn main() {
let a = [1, 11, 3];
let b = [1, 2, 4];
println!("{}", a > b);
}
$ ./main
true
先頭の要素から順番に比較しているようだ。
RustではOrdトレイトを実装すると大小比較が可能になる。スライスもOrdを実装している。2つのスライスの長さが異なるとコンパイルエラーになるが、可変長のvecは長さが違っても比較可能であった。
Python
>>> [1, 11, 3] > [1, 2, 4]
True
Rustと同じっぽい
PHP
<?php
echo [1,11,3] >[1,2,4];
?>
1
boolean型ないんでしたね。おそらくRust、Pythonと同じ結果でしょう
C++
#include <iostream>
int main() {
int a[3] = {1, 2, 3};
int b[3] = {1, 11, 3};
std::cout << (a > b) << std::endl;
}
警告が出ます。aとbの定義位置を入れ替えると結果が変わります。これはポインタのアドレスの比較のようです。
まとめ
言語 | 結果 |
---|---|
JavaScript | toStringして比較 |
Ruby | 実行時エラー |
Java | コンパイルエラー |
Go | コンパイルエラー |
Swift | コンパイルエラー |
Rust | 要素の先頭から比較 |
Python | 要素の先頭から比較 |
PHP | 要素の先頭から比較 |
C++ | 不明 |