LoginSignup
9
3

More than 1 year has passed since last update.

漢数字クラスで演算処理

Last updated at Posted at 2017-07-07

@xuyun19840618 さんの「漢数字とアラビア数字の相互変換」を拝見し、私なりの実装をコメントに書かせていただきました。
どうせなら、漢数字のまま計算できたら面白いかな? と思ってクラスにしてみました。
intクラスのサブクラスにして演算できるようにしています。
intのインスタンスはimmutable(不変)なので、値は __new__メソッドで与えなければいけません。
演算結果は再びKanjiIntのインスタンスにしないと漢字表示にならないので、演算メソッドをオーバーライドしてKanjiIntにしています。いちいちメソッド定義するのは大変なので、forループで単項演算子と二項演算子のメソッドをオーバーライドしてます。

class KanjiInt(int):

    def __new__(cls, value=0):
        arabic = kanji_to_int(value)
        if isinstance(arabic, int):
            value = arabic
        return int.__new__(cls, value)

    def __str__(self):
        return int_to_kanji(self)

    def __repr__(self):
        return 'KanjiInt(%s)' % repr(str(self))


_unary = ('abs', 'inv', 'invert', 'neg', 'pos')
_binary = ('add', 'sub', 'mul', 'floordiv', 'mod', 'pow',
           'and', 'or', 'xor', 'lshift', 'rshift')

for method in _unary:
    exec(f'KanjiInt.__{method}__ = lambda self: KanjiInt(int.__{method}__(self))')

for method in _binary:
    exec(f'KanjiInt.__{method}__ = lambda self, value: KanjiInt(int.__{method}__(self, value))')


unitlong = '恒河沙', '阿僧祇', '那由他', '不可思議', '無量大数'
unit4 = ('',) + tuple('万億兆京垓秭穣溝澗正載極') + unitlong
unit1 = ('',) + tuple('十百千')
digits = tuple('〇一二三四五六七八九')
arabic4 = {unit[:1]: 10000**i for i, unit in enumerate(unit4)}
arabic1 = {unit: 10**i for i, unit in enumerate(unit1)}
arabics = {digit: i for i, digit in enumerate(digits)}


def int_to_kanji(arabic):
    arabic = int(arabic)
    if arabic == 0:
        return digits[0]
    sign = '-' if arabic < 0 else ''
    arabic = abs(arabic)
    def convert():
        for column, digit in enumerate(map(int, str(arabic)[::-1])):
            if column % 4 == 0 and (arabic // (10 ** column)) % 10000 != 0:
                yield unit4[column // 4]
            if digit != 0:
                yield unit1[column % 4]
                if digit != 1 or column % 4 == 0:  # 一千,一百,一十の 一 は除く
                    yield digits[digit]
    return sign + ''.join(list(convert())[::-1])


def kanji_to_int(kanji):
    if not isinstance(kanji, str):
        return 'error'
    for longname in unitlong:
        kanji = kanji.replace(longname, longname[0])
    a0 = a1 = a4 = 0
    for j in kanji:
        if j in arabics:
            a0 = a0 * 10 + arabics[j]
        elif j in arabic1:
            a1 += (a0 or 1) * arabic1[j]
            a0 = 0
        elif j in arabic4:
            a4 += (a0 + a1 or 1) * arabic4[j]
            a0 = a1 = 0
        else:
            return 'error'
    return a0 + a1 + a4


if __name__ == '__main__':
    value = KanjiInt('五十六')
    print("value =", value)
    print('repr(value) =', repr(value))
    print('str(value) =', str(value))
    print('int(value) =', int(value))
    print('abs(value) =', abs(value))
    print('+value =', +value)
    print('-value =', -value)
    print('~value =', ~value)
    print('value + 3 =', value + 3)
    print('3 + value =', 3 + value)
    print('value - 3 =', value - 3)
    print('value * 3 =', value * 3)
    print('value / 3 =', value / 3)
    print('value // 3 =', value // 3)
    print('value % 3 =', value % 3)
    print('value ** 3 =', value ** 3)
    print('value & 3 =', value & 3)
    print('value | 3 =', value | 3)
    print('value ^ 3 =', value ^ 3)
    print('value << 3 =', value << 3)
    print('value >> 3 =', value >> 3)
実行結果
value = 五十六
repr(value) = KanjiInt('五十六')
str(value) = 五十六
int(value) = 56
abs(value) = 五十六
+value = 五十六
-value = -五十六
~value = -五十七
value + 3 = 五十九
3 + value = 59
value - 3 = 五十三
value * 3 = 百六十八
value / 3 = 18.666666666666668
value // 3 = 十八
value % 3 = 二
value ** 3 = 十七万五千六百十六
value & 3 = 〇
value | 3 = 五十九
value ^ 3 = 五十九
value << 3 = 四百四十八
value >> 3 = 七
9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3