{ }codelper

学ぶ › JavaScript

さらに先へ · 22

よくある間違いとエラーメッセージ

エラーメッセージの読み方と、どの言語でも出る間違い:1 つずれ、= と ==、整数の割り算、null、範囲外の添字。

どの言語でも出る間違いがあります。
エラーメッセージの読み方と一緒に覚えましょう。
下の例は、添字が範囲内か、割る数が 0 でないかを先に確かめてから使います。

const tok = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number);let p = 0;const n = tok[p++];const a = tok.slice(p, (p += n));const q = tok[p++];const idx = tok.slice(p, (p += q));const [x, y] = tok.slice(p, p + 2); // a[7]은 오류 없이 undefined라서 직접 검사해요const get = (arr, i) => (i >= 0 && i < arr.length ? arr[i] : null);const safeDiv = (u, v) => (v === 0 ? null : Math.trunc(u / v)); // u / 0은 Infinity for (const i of idx) {  const v = get(a, i);  console.log(v === null ? `a[${i}]: out of range` : `a[${i}] = ${v}`);}const d = safeDiv(x, y);console.log(d === null ? `${x} / ${y}: cannot divide` : `${x} / ${y} = ${d}`);let sum = 0;for (let i = 0; i < n; i++) sum += a[i]; // <= n 이면 undefined를 더해 NaNconsole.log(`average ${(sum / n).toFixed(1)}`);
stdin
5
3 1 4 1 5
3
2 7 -1
7 0
stdout
a[2] = 4
a[7]: out of range
a[-1]: out of range
7 / 0: cannot divide
average 2.8

ポイント

  • エラーメッセージではエラー名・理由・行番号を探します。
    Python は一番下の行から読みます。
  • コンパイルする言語(C・C++・Java・C#)では、エラーがいくつ出ても最初のエラーから直します。
    後ろのものは最初のエラーにつられて出ていることが多いからです。
  • 添字は 0 から n - 1 までなので、ループは i < n です。
    i <= n と書くと 1 つはみ出す 1 つずれ(off-by-one)になります。
  • = は代入、== は比較です。
  • 割る前に 0 でないか、使う前に空の値(null)でないかを先に確かめます。
  • 変数は作るときに初期値を与えます(int sum = 0;)。

言語ごとの書き方

Python最後の行に IndexError: list index out of range のような名前と理由、その上に行番号。
0 で割ると ZeroDivisionError、None で計算すると TypeError、ない名前は NameError。
a[-1] はエラーではなく最後の値で、if not v: は 0 も引っかけます(v is None で確かめます)
JavaScript範囲外の添字はエラーなしで undefined、7 / 0 は Infinity。
空の値は null と undefined で、undefined + 1 は NaN、null + 1 は 1 になって黙って間違えます。
比較は ===("1" == 1 は真)
TypeScriptJavaScript と同じです
Java1 行目が ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 5、下の at Main.main(Main.java:12) が行番号。
0 で割ると ArithmeticException、null は NullPointerException(int には入らないので Integer)。
初期値がないと "might not have been initialized" のコンパイルエラー
C#範囲外は IndexOutOfRangeException、0 で割ると DivideByZeroException。
空の値は int?。
初期値がないとコンパイルエラー CS0165
C++a[i] は検査しないので範囲外は未定義動作、a.at(i) は std::out_of_range 例外。
整数の 0 での割り算も未定義動作(たいてい落ちます)。
空の値は std::optional。
int sum; はごみの値から始まります
C配列の範囲検査がないので未定義動作:でたらめな値、別の変数の書き換え、落ちる。
0 での割り算と初期化していないローカル変数は C++ と同じです
Craykon範囲外は「リストには 0〜4 番しかありません」で止まります。
7 / 0 は Infinity、数学.商(7, 0) は「0 で割れません」。
なし + 1 は「なしでは計算できません」(文字列とつなぐと 빈값 と表示)、ない名前は「まだ作られていません」

よくある間違い

i <= n で 1 つはみ出したC · C++ · Java · C# · JavaScript · TypeScript

int sum = 0;
for (int i = 0; i <= n; i++)
    sum += a[i];

→ Java・C# は範囲外の例外、JavaScript は undefined を足して NaN、C は黙ってごみの値を足します

i < n と書きます。

== の代わりに = を書いたC · C++ · JavaScript

if (x = 5) {
    puts("five");
}

→ エラーなしで x に 5 が入り、いつも真になります(gcc -Wall なら警告してくれます)

比較は ==。
Java・C# はコンパイルエラー、Python は SyntaxError(3.10 からは "Maybe you meant '=='")、Craykon は文法エラーで止めてくれます。

平均が整数になったJava · C# · C · C++

int sum = 7, n = 2;
double avg = sum / n;

→ avg は 3.0 です。7 / 2 が整数の割り算なので、小数が先に捨てられます

片方を浮動小数点数にします:(double)sum / n。

言語ごとの違い
エラーで止まるのは、むしろありがたいことです。
本当に怖いのは、C・C++ の範囲外アクセスや JavaScript の undefined のようにエラーなしで間違った値が出る場合です。

練習問題