CODE Polyglot / 入門 / 第11回

数として読む — 入力が整数かを確かめる

受け取った文字列が整数なら2倍にして出し、整数でなければ「整数ではありません」と出します。「数に直す」関数が何を整数とみなすかは、言語によって驚くほど違います。

画面にこう出れば正解

どの言語で書いても、同じ入力なら画面に出る文字は1文字も違わない、というのが正解の決め方です(例は Python で動かしたときのコマンドです)。

基本

$ python3 parseint.py 42
42 の2倍は 84

マイナス

$ python3 parseint.py -7
-7 の2倍は -14

先頭の0

$ python3 parseint.py 007
7 の2倍は 14

文字

$ python3 parseint.py abc
整数ではありません

小数

$ python3 parseint.py 12.0
整数ではありません

前に空白

$ python3 parseint.py " 12"
整数ではありません

全角の数字

$ python3 parseint.py 12
整数ではありません

空

$ python3 parseint.py ""
整数ではありません

プラス記号

$ python3 parseint.py +5
整数ではありません

16桁の数

$ python3 parseint.py 9007199254740993
9007199254740993 の2倍は 18014398509481986

範囲を少し超える

$ python3 parseint.py 1000000000000000001
大きすぎます

20桁

$ python3 parseint.py 99999999999999999999
大きすぎます

マイナス0

$ python3 parseint.py -0
0 の2倍は 0

下線入り

$ python3 parseint.py 1_000
整数ではありません

16進数の書き方

$ python3 parseint.py 0x1A
整数ではありません

10言語のプログラム

どれもそのまま保存して動かせます。毎朝、上の正解の例をすべて実際に動かして確かめています。

parseint.php8.4.26 で 15 / 15 件の出力が正解と一致

<?php
// 受け取った文字列が整数なら2倍にして出す。整数でなければ、そう伝える
$s = $argv[1];
// is_numeric は前の空白・「+」・小数も数とみなすので、先に「-? と半角数字だけ」かを確かめる
if (!preg_match('/\A-?[0-9]+\z/', $s)) {
    echo "整数ではありません\n";
    exit;
}

// PHP の整数に入らない数は (int) で別の数に変わるので、先に桁数で「大きすぎる」かを決める(10の18乗は19桁)
$digits = ltrim(ltrim($s, '-'), '0');
if (strlen($digits) > 19 || (strlen($digits) === 19 && $digits !== '1000000000000000000')) {
    echo "大きすぎます\n";
    exit;
}

$n = (int) $s;
echo $n . " の2倍は " . ($n * 2) . "\n";

動かし方(ファイルを保存したフォルダで) ・ PHP を入れる

$ php parseint.php 42

言語でつまずきやすいところ

初心者がよく書く形(各言語で最初に習う書き方をそのまま使ったもの)を、同じ正解表で動かした結果です。言語を出した答えごとにまとめ、赤い字はその言語が実際に出した間違った行です。

入力初心者がよく書く形の答え(10言語)
基本42

✓10言語すべて正解

マイナス-7

✓10言語すべて正解

先頭の0007

✓10言語すべて正解

文字abc

✓10言語すべて正解

小数12.0
  • PHP・JavaScript12 の2倍は 24
  • Python・Go・Java・C#・Rust・Ruby・Kotlin・Swift✓ 正解
前に空白" 12"
  • PHP・Python・JavaScript・C#・Ruby12 の2倍は 24
  • Go・Java・Rust・Kotlin・Swift✓ 正解
全角の数字12
  • Python・Java・Kotlin12 の2倍は 24
  • PHP・Go・JavaScript・C#・Rust・Ruby・Swift✓ 正解
空""
  • JavaScript0 の2倍は 0
  • PHP・Python・Go・Java・C#・Rust・Ruby・Kotlin・Swift✓ 正解
プラス記号+5
  • PHP・Python・Go・JavaScript・Java・C#・Rust・Ruby・Kotlin・Swift5 の2倍は 10
16桁の数9007199254740993
  • JavaScript9007199254740992 の2倍は 18014398509481984
  • PHP・Python・Go・Java・C#・Rust・Ruby・Kotlin・Swift✓ 正解
範囲を少し超える1000000000000000001
  • JavaScript1000000000000000000 の2倍は 2000000000000000000
  • PHP・Python・Go・Java・C#・Rust・Ruby・Kotlin・Swift✓ 正解
20桁99999999999999999999
  • Go・Java・C#・Rust・Kotlin・Swift整数ではありません
  • PHP・Python・JavaScript・Ruby✓ 正解
マイナス0-0

✓10言語すべて正解

下線入り1_000
  • Python・Ruby1000 の2倍は 2000
  • PHP・Go・JavaScript・Java・C#・Rust・Kotlin・Swift✓ 正解
16進数の書き方0x1A
  • JavaScript・Ruby26 の2倍は 52
  • PHP・Python・Go・Java・C#・Rust・Kotlin・Swift✓ 正解

この回でやること

  • 文字列を整数に直す
  • 整数として受け付けてよい形かを、自分で決めて確かめる
  • 大きすぎる数を見分ける

この回の決まり

  • 受け付けるのは、半角の数字だけ(先頭にマイナスが1つ付いてもよい)。前後の空白・全角の数字・小数点・プラス記号・下線(_)・0x などは「整数ではありません」
  • 先頭の 0 は取って出す(007 は 7)。-0 は 0
  • 大きさが 10の18乗(100京)を超える数は「大きすぎます」

解説

「数に直す」関数は、決まりより広く受け付けることがある

どの言語にも、文字列を整数に直す関数(int()・parseInt・Integer.parseInt・strconv.Atoi など)があります。ただし、前後の空白、全角の数字、プラス記号、下線、0x の書き方などを「数」として受け付けるかどうかは、関数によって違います。空の文字列を 0 として扱う関数もあります。

各言語のいちばん普通の関数に、この回の入力を渡した結果です(「受け付けた」は、エラーにならずに数として返したもの)。

入力受け付けて数にした言語・関数
+510言語すべて(5 として)
12(前に空白)Python int()・Ruby Integer()・C# long.TryParse・PHP is_numeric+(int)・JavaScript Number()
12(全角)Python int()・Java Long.parseLong・Kotlin toLongOrNull()
12.0PHP・JavaScript(12 として)
1_000Python・Ruby(1000 として)
0x1AJavaScript・Ruby(26 として)
空の文字列JavaScript Number()(0 として)

「+5」は、どの言語の関数も受け付けました。この回の決まりで「+5」を受け付けないなら、関数に任せずに自分で確かめるしかありません。

問い合わせフォームなど、人が入力した値を受け取るときは、関数に任せる前に「受け付ける形」を決めて、その形かを確かめます。この回では、第7回 と同じように、半角の数字だけかを1文字ずつ(または正規表現で)確かめてから数に直しています。

大きい数

整数の型には、入る大きさに上限があります(よく使う64ビットの整数で、およそ 922京)。20桁の「99999999999999999999」を渡すと、Go・Java・Kotlin・C#・Rust・Swift の関数は「直せない」というエラーを返しました。エラーの種類を見ずに「整数ではありません」と出すと、「大きすぎる」のか「形が違う」のかが区別できません。

JavaScript の普通の数(Number)は、9007199254740991(2の53乗−1)を超えると、1の位まで正しく表せなくなります。「9007199254740993」を Number() に渡すと 9007199254740992 になり、「1000000000000000001」は 1000000000000000000 になって「大きすぎます」の判定をすり抜けました。大きい整数は BigInt を使います。

練習

  1. 「1,000」のようにカンマの入った数も受け付けるようにしてみましょう
  2. 0 から 120 までの数だけを受け付ける「年齢の入力チェック」を書いてみましょう