またしてもTcl9.0の非互換性にハマりましたw
設定ファイルから読み出した値(str)が数字だけからなる文字列かどうかテストするとき、
string is integer $str
を実行したところ、Tcl9.0では真(1)が返されました。ところがTcl8.6では偽(0)が返されました。
実はそもそも「integer」を使っている事自体が間違いで、本来は
string is digit $str
でテストするべきであり、それにもっと早く気が付いていればTcl9.0の非互換性に混乱させられることもなかったのですが、まぁそこは貴重な投稿ネタが見付かったのでよしとしましょうw
閑話休題(?)、先程の違いをもう少し詳しく比較してみました。
最近はWindowsにはTcl9.0をインストールしているのですが、今回は最終的にはLinuxサーバで実行する予定で、LinuxサーバにはTcl8.6が入っていました。(使用しているディストロの公式パッケージはまだtcl9.0を提供していませんでした)
tkconで
% set str 0123456789
% puts $str
0123456789
% puts [string is integer $str]
1
% expr $str
123456789
などと実験してみながら適当にコーディングしていたのですが、「ふ~ん、整数の先頭のゼロは無視されるんだ~」などと勝手に解釈していました。
ここで、「あれ~、先頭がゼロの整数は8進数表記じゃなかったっけ? Tclでは10進数なん?」という疑問が一瞬頭をよぎりましたが、テスト目的が厳密には整数かどうが問題なのではなく、数字だけで構成された文字列かどうかを調べたかっただけだったので、それ以上深くは考えませんでした。
その後、Linuxサーバ(Tcl8.6)で実行したところ、設定値「0123456789」の書式が間違っている!というエラーが表示されました。
そこでTcl8.6で実験してみると、Tcl9.0とは違う結果になりました。
% set str 0123456789
% puts $str
0123456789
% puts [string is integer $str]
0
% puts [string is integer [string range $str 0 end-1]]
0
% puts [string is integer [string range $str 0 end-2]]
1
% puts [string range $str 0 end-2]
01234567
% expr [string range $str 0 end-2]
342391
つまりTcl8.6では「0123456789」と「012345678」は整数ではなく、「01234567」は整数だけど、10進数では「342391」だというのです。
げ!、またTcl9.0の非互換性仕様だったか!
ということで調べてみました。
まずTcl8.6のヘルプを読んでみたところ、
- 「string is integer」は32ビット整数かどうかをテストし、オーバーフローする場合は偽を返す。
- 「expr」は先頭がゼロの数値は8進数表現と見なす。
と書かれていました。
なんだやっぱ8進数表現なんじゃん。え? でもTcl9.0は10進数として扱ってるよ?
次にTcl9.0のヘルプを読んでみたところ、
- 「string is integer」は任意のサイズの整数値を表す有効な文字列形式かどうかをテストする。
- 「expr」のヘルプのどこにも先頭がゼロの数値をどう扱うかの説明が無くなってる!
さらにリリースノートを読んでみると
- 先頭がゼロの数値(0NNN)は8進数ではなくなりました。 0oNNN形式を使いなさい。
とありました。(ただ0NNN形式をどう扱うように仕様変更したかは書かれていませんでしたが)
Tcl8.6では
- 16進数: 0xNNN
- 10進数: NNN
- 8進数: 0oNNNまたは0NNN
- 2進数: 0bNNN
だったのが、Tcl9.0では
- 16進数: 0xNNN
- 10進数: 0dNNNまたはNNN
- 8進数: 0oNNN
- 2進数: 0bNNN
とし、よく使う10進数以外は2文字プレフィックスだけになったわけですが、(要否は別として)新規仕様追加の0dNNNは別にいいですが、慣習的に使われてきた0NNNが別の値になってしまう破壊的な仕様変更はいかがなものでしょうか。
そこまでする意義があるのか、甚だ疑問に感じました。