純粋な Python を使用して関数をより効率的にするのは難しいでしょう。str.replace
すでにかなり効率的ですが、文字列を何度もスキャンし、少なくとも場合によってはいくつかの新しい文字列を作成する必要があります。の複数の呼び出しをreplace
、文字列を 1 回だけスキャンするよりスマートなアルゴリズムに置き換えると、純粋な Python でより多くの作業を行うことになり、str.replace
.
あなたのケースで C 拡張モジュールを作成できる場合は、そうすることをお勧めします。で測定するとtimeit
、次の関数は元の関数よりも約 17 倍優れています (Python バージョンの 3.28 usec と比較して 0.184 usec) "foobaaar"
。
PyObject *
indian_soundex_encode(PyObject *ignore, PyObject *args)
{
PyObject *py_s, *py_ret;
bool replaced = false;
if (!PyArg_ParseTuple(args, "S", &py_s))
return NULL;
const char *s = PyString_AS_STRING(py_s);
Py_ssize_t len = PyString_GET_SIZE(py_s);
char *ret = malloc(len + 1), *retptr = ret;
if (!ret)
return PyErr_NoMemory();
while (len > 0) {
#define REPLACE(first, second, replacement) \
if (*s == first && *(s + 1) == second) { \
s += 2; \
len -= 2; \
*retptr++ = replacement; \
replaced = true; \
continue; \
}
REPLACE('a', 'a', 'a');
REPLACE('e', 'e', 'i');
REPLACE('z', 'h', 'l');
REPLACE('o', 'o', 'u');
REPLACE('b', 'u', 'b');
REPLACE('d', 'h', 'd');
REPLACE('g', 'h', 'g');
REPLACE('j', 'h', 'j');
REPLACE('k', 'h', 'k');
REPLACE('s', 'h', 's');
REPLACE('t', 'h', 't');
REPLACE('c', 'k', 'k');
REPLACE('k', 'k', 'k');
REPLACE('n', 'n', 'n');
#undef REPLACE
*retptr++ = *s++;
--len;
}
if (!replaced) {
py_ret = py_s;
Py_INCREF(py_ret);
}
else
py_ret = PyString_FromStringAndSize(ret, retptr - ret);
free(ret);
return py_ret;
}
switch
上記の関数は、C でコーディングされたステートメントまたはより効率的なルックアップ テーブルを使用してさらに高速化できる可能性がありますが、それは読者の課題として残されています。
Cython でこの関数のバージョンをコーディングして、そのパフォーマンスを上記の手書きの C 拡張機能と比較することは、別の興味深い演習になるでしょう。
更新:上記の C 関数は、質問の元の Python コードに対応しています。編集者の Jostは、彼の editの書式設定の変更に加えて主要なコードの変更を忍び込みました。