何かを書き留める何か

数学や読んだ本について書く何かです。最近は社会人として生き残りの術を学ぶ日々です。

『スラスラわかるPython』の査読を担当しました

Python初心者向け本

2017年8月7日に翔泳社から『スラスラわかるPython』が発売される。

www.shoeisha.co.jp

この度、査読者の1人として参加させていただいた。 きっかけはPython mini hack-a-thonで『スラスラわかるPython』の話があり、手を挙げたからである。 『スラスラわかるPython』のレビューはDropbox上で査読者がコメントを書く形式でとても新鮮であった。

『スラスラわかるPython』の特色は、全くの初心者を対象に基本的な部分から説明があること、レビュワー陣に初心者の方が入り幅広い目線で文章が検討されていること、である。 目次を見ると「クラス」という言葉がない。 なんと、クラスの説明がないのである。 そこまで割り切った中でWebスクレイピングまで体験できるのはすごいことである。

当然、この本だけですべてマスターすることは意図されておらず、『Python チュートリアル』や別の本へステップアップが意図されている。 『Python チュートリアル』はPythonを始めた人がまず読むべきドキュメントであるが、全くの初心者にはよくわからない概念も多い。 『スラスラわかるPython』で概念に慣れることができれば『Python チュートリアル』も読みこなすことができると思う。

float(".384")と".384".isdigit()の振る舞いの違い

str.isdigit()浮動小数点数を判定できない

野球の人としてよく知られている@shinyorkeさんが興味深いツイートをしていた。

恐らくC言語レベルで違う処理が行われていると思い、調べてみた。

float()の挙動

組み込み函数float()に文字列を渡すとfloat型が返される。 渡せる文字列の規則はfloatのドキュメント 及び浮動小数点数リテラルに記述されている。 打率を意味する".384"という文字列は規則を満たす文字列である。

cpython/Objects/floatobject.cは次のようになっている。

PyObject *
PyFloat_FromString(PyObject *v)
{
    const char *s;
    PyObject *s_buffer = NULL;
    Py_ssize_t len;
    Py_buffer view = {NULL, NULL};
    PyObject *result = NULL;

    if (PyUnicode_Check(v)) {
        s_buffer = _PyUnicode_TransformDecimalAndSpaceToASCII(v);
        if (s_buffer == NULL)
            return NULL;
        s = PyUnicode_AsUTF8AndSize(s_buffer, &len);
        if (s == NULL) {
            Py_DECREF(s_buffer);
            return NULL;
        }
    }
    // 中略
    else {
        PyErr_Format(PyExc_TypeError,
            "float() argument must be a string or a number, not '%.200s'",
            Py_TYPE(v)->tp_name);
        return NULL;
    }
    result = _Py_string_to_number_with_underscores(s, len, "float", v, v,
                                                   float_from_string_inner);
    PyBuffer_Release(&view);
    Py_XDECREF(s_buffer);
    return result;
}

詳細はもっとC言語のソースに踏み込まないとわからないが、_Py_string_to_number_with_underscoresで上手くドキュメントの記述通りに処理されていることが推測できる。

str.isdigit()の挙動

str.isdigit()ドキュメントから引用する。

文字列中の全ての文字が数字で、かつ 1 文字以上あるなら真を、そうでなければ偽を返します。 ここでの数字とは、十進数字に加えて、互換上付き数字のような特殊操作を必要とする数字を含みます。 また 10 を基数とした表現ができないカローシュティー数字のような体系の文字も含みます。 正式には、数字とは、プロパティ値 Numeric_Type=Digit または Numeric_Type=Decimal を持つ文字です。

この説明から".384"の小数点が小数点と認識されないからFalseとなってしまう、と予想できる。

cpython/Objects/unicodeobject.cを調べると、str.isdigit()は次のような実装である。

/*[clinic input]
str.isdigit as unicode_isdigit
Return True if the string is a digit string, False otherwise.
A string is a digit string if all characters in the string are digits and there
is at least one character in the string.
[clinic start generated code]*/

static PyObject *
unicode_isdigit_impl(PyObject *self)
/*[clinic end generated code: output=10a6985311da6858 input=901116c31deeea4c]*/
{
    Py_ssize_t i, length;
    int kind;
    void *data;

    if (PyUnicode_READY(self) == -1)
        return NULL;
    length = PyUnicode_GET_LENGTH(self);
    kind = PyUnicode_KIND(self);
    data = PyUnicode_DATA(self);

    /* Shortcut for single character strings */
    if (length == 1) {
        const Py_UCS4 ch = PyUnicode_READ(kind, data, 0);
        return PyBool_FromLong(Py_UNICODE_ISDIGIT(ch));
    }

    /* Special case for empty strings */
    if (length == 0)
        return PyBool_FromLong(0);

    for (i = 0; i < length; i++) {
        if (!Py_UNICODE_ISDIGIT(PyUnicode_READ(kind, data, i)))
            return PyBool_FromLong(0);
    }
    return PyBool_FromLong(1);
}

Py_UNICODE_ISDIGITは恐らくヘッダーで定義されたマクロであろう、と推測した。 実際、cpython/Include/unicodeobject.hに定義されていた。

#define Py_UNICODE_ISDIGIT(ch) _PyUnicode_IsDigit(ch)

実体は次のように実装されている。

typedef struct {
    /*
       These are either deltas to the character or offsets in
       _PyUnicode_ExtendedCase.
    */
    const int upper;
    const int lower;
    const int title;
    /* Note if more flag space is needed, decimal and digit could be unified. */
    const unsigned char decimal;
    const unsigned char digit;
    const unsigned short flags;
} _PyUnicode_TypeRecord;

#include "unicodetype_db.h"

static const _PyUnicode_TypeRecord *
gettyperecord(Py_UCS4 code)
{
    int index;

    if (code >= 0x110000)
        index = 0;
    else
    {
        index = index1[(code>>SHIFT)];
        index = index2[(index<<SHIFT)+(code&((1<<SHIFT)-1))];
    }

    return &_PyUnicode_TypeRecords[index];
}

/* Returns the integer digit (0-9) for Unicode characters having
   this property, -1 otherwise. */

int _PyUnicode_ToDigit(Py_UCS4 ch)
{
    const _PyUnicode_TypeRecord *ctype = gettyperecord(ch);

    return (ctype->flags & DIGIT_MASK) ? ctype->digit : -1;
}

int _PyUnicode_IsDigit(Py_UCS4 ch)
{
    if (_PyUnicode_ToDigit(ch) < 0)
        return 0;
    return 1;
}

str.isdigit()の場合、頭から文字を一つずつ調べてstr.isdigit()のドキュメントにある「数字」であるかどうかを調べている。 ピリオド(U+002E)はここの意味で「数字」ではないので、".384".isdigit()Falseを返してしまう。 そのため、"0.384"と頭に0を補っても意味がない。 例えば、

>>> "0.384".isdigit()
False
>>> "0.384".isdecimal()
False
>>> "0.384".isnumeric()
False

指数表記でも同じ運命をたどる。

>>> "384e-3".isdigit()
False
>>> "384e-3".isdecimal()
False
>>> "384e-3".isnumeric()
False
>>> float("384e-3")
0.384

結論

str.isdigit()浮動小数点数を判定するのはダメゼッタイ。

『GE 巨人の復活』を読んだ

中田敦『GE 巨人の復活』を読んだ。

きっかけはTwitterでこの本が面白いというツイートを見かけたことである。 布団に転がりながら読んだら面白くて眠ることなく最後まで一気に読み進めてしまった。

リーマンショックの影響で金融部門の1兆9000億円の特別損出を出したゼネラル・エレクトリック(GE)がジャック・ウェルチコングロマリット戦略からITを駆使した「デジタル製造業」への転換の様子が描かれている。 主な読者対象は日本の電機メーカー(日立、東芝三菱重工三菱電機etc…)に関係する人であるが、読み物としても十分面白いのでITを活用するすべての人に向いていると思う。

経営トップが自ら積極的に行動したこと、シリコンバレーの「規律」(本書ではリーンスタートアップ、デザイン思考、アジャイル開発が挙げられている)を徹底的に学び実践したこと、 適切な人材を雇ったこと、文化面から変革した、など元々IT企業とは思われていない企業がITを真の意味で導入して改善するとはどのようなことなのかを知ることができる。

シリコンバレーの方法論を徹底的に取り入れるのも重要であるが、もう1つの重要な成功要因としてGEには元々学びを重視する文化があったことが挙げられている。

「GEがほかの製造業大手に先んじてデジタル変革に邁進できた要因として、GEには昔から学びを重視する文化があったことが挙げられる。GEはラーニング・カンパニー(学ぶ会社)だ。外部から来た私にとっては、大きな驚きだった」。 ビル・ルーは当時を振り返ってこう証言する。

ちょっと面白かったのは、P.52の記述。引用する。

そこでGEは様々な手を打った。最も効果的だったのは、社外の人材採用会社を使う方針を改め、シリコンバレーで実績のあるリクルーターにGEに入社してもらって、GEが自力で人材を採用できるようにしたことだ。 (中略) もちろん、待遇面の努力もした。たとえばGEでは従来、役職のないエントリーレベルのソフトウエア開発者にはボーナスが支給されなかった。これを改め、あらゆる階層のソフトウエア開発者にボーナスを支給するなど、エンジニアの待遇改善に動いた。 そうすることでようやく、エンジニアやデータサイエンティストの採用が進むようになったという。

思わず「やっぱ世の中金だな!」と叫んでしまった。 給与や待遇は「衛生要因*1」であり、たくさんあっても必ずしも引き付けることはできないが、少ないと決して引き付けることができないのである。

私は現在IT企業にカテゴライズされるであろう企業で仕事をしているが、シリコンバレーの「規律」にはそれほど詳しくない。 将来私は経営者になるのかどうかは全く知らないが、ここで挙げられている「規律」については深く知り、必要に応じて実践する必要がある。 また、「学び続ける姿勢」というのも重視したい。今仕事している企業に変に最適化せず、学び続けて変化し続ける必要がある。