MaxScript で文字列を数値変換するときの注意

MaxScript で文字列を数値に変換することを考えます。なにを今さら、と思いますか?
私もまさか、こんな罠があるとは知りませんでした。今さら罠を発見したので紹介したいと思います。

基本の文字列→数値変換

s = "123"
s as integer -->123
s = "123.4"
s as float -->123.4

このように、整数への変換は as integer 、浮動小数点数への変換は as float を使います。

変換できないものを渡すとundefined になります。

s = "abc"
s as integer --> undefined

問題点

さてここからが問題です。

s = "t"
s as integer -->0
s as float -->0.0

はて?

文字列"t" を as integer するとundefined ではなく0 が戻ってきました。つまり0 に変換できたということになります。

そこで調べてみました。

-- アルファベットの配列を作る
alphabets = #("A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W",  "X", "Y", "Z")

-- その整数変換を試す
for c in alphabets do (
    format "%: %\n" c (c as integer)
    format "%: %\n" (toLower c) ((toLower c) as integer)
)

すると出力が

A: undefined
a: undefined
B: undefined
b: undefined
C: undefined
c: undefined
D: undefined
d: undefined
E: undefined
e: undefined
F: undefined
f: 0
G: undefined
g: undefined
H: undefined
h: undefined
I: undefined
i: undefined
J: undefined
j: undefined
K: undefined
k: undefined
L: 0
l: undefined
M: undefined
m: 0
N: undefined
n: undefined
O: undefined
o: undefined
P: 0
p: undefined
Q: undefined
q: undefined
R: undefined
r: undefined
S: undefined
s: 0
T: undefined
t: 0
U: undefined
u: undefined
V: undefined
v: undefined
W: undefined
w: undefined
X: undefined
x: undefined
Y: undefined
y: undefined
Z: undefined
z: undefined

つまり大文字のL、P、小文字のf、m、s、t が数値変換できてしまい、0 になるということがわかります。

さらに謎

もう少し実験。

"L" as integer -->0
"LL" as integer -->undefined
"LP" as integer -->undefined

"ff" as integer -->0
"fm" as integer -->0
"fmst" as integer -->0

どうやら大文字は連結するとundefined で変換できなくなり、小文字は連結しても0 に変換されます。

なぜなんだ?

文字列が数字かどうか識別する

さて、なぜこんな話を紹介しているかというと、皆さん文字列が数字かどうか判別したいときどうしてますか? というところにつながるのです。

function isNumber s = (
  return ((s as integer) != undefined)
)

こんな感じでチェックしたり、してませんか?

私はしてました。ところが上記の問題により、この関数はバグのもとになります。

isNumber "ttt" --> true

ということになるからです。この問題を踏まえて正しく動作するisNumber を作ると次のようになります。

function isNumber s = (
    -- as float が undefined になるものは数字ではない
    if (s as float) == undefined do return false
    -- as float がundefined にならない場合はすべての文字が数字かどうか確認する
    -- "." as float は0.0 なので小数点は許容する
    local numbers = #("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", ".")
    for i=1 to s.count do(
        if (findItem numbers s[i] == 0) do return false
    )
    return true
)

そんな面倒なことをしなくてもPython を使えばいいのでは?

もちろんその通りです。

function isNumber s = (
    local py = python.import "__builtin__"
    try (
    -- python でfloat に変換
        py.float s
        return true
    ) catch (
    -- 変換できなければfalse を返す
        return false
    )
)

まとめ

  • MaxScript の文字列→数値変換は意外な挙動をするので要注意