漢数字を数値に変換する(DSLっぽく)

d:id:terazzo:20101212:1292147950のやつ、実は先にJavaで書いてみてた。
メソッドとかは一つずつ定義している。実装の方はProxy使えば少しだけ短くなるかもしれない。

使い方

本当に使うんならちゃんとPOJOクラスにしよう。あと修飾子もちゃんと付けて。

import static sample.janumber.JaNumberTest.*;
...
    long result = 三.億().四().千().九().百().万().二().百().三().longValue();
    assertEquals("三億四千九百万二百三 == 349000203", 349000203L, result); // true!

ソースコードの簡単な説明

  • 見やすくする為にアクセス指定はなるべく消している
    • 見やすさのためなので、パッケージプライベートにしている意図は無い
  • メソッドは数字ひとつずつ定義している。
  • 最初の一文字は定数になっているので、import staticして使えるなど
  • 位によって続けられる文字が違うので、その辺はインタフェースで表現している。
    • 一応「三五」とか「億万」とかはコンパイル時にエラーになる
    • 「百二千」とかは書けてしまう。「二千百」と同じになる。
      • インタフェース細分化すれば型チェックではじけそうではある。
  • Rubyではクロージャっぽく書いたところもJavaではクラスを分けてコンストラクタの引数で渡している
    • Javaでも匿名内部クラスで無理に書けなくはない。

ソースコード

例によって1ファイルで動かしやすくする為にJUnitのテスト内部で作成。

package sample.janumber;

import junit.framework.TestCase;
public class JaNumberTest extends TestCase {

    /**一〜九が続けられる位置 */
    public interface DigitAcceptable {
        DigitResult 一();
        DigitResult 二();
        DigitResult 三();
        DigitResult 四();
        DigitResult 五();
        DigitResult 六();
        DigitResult 七();
        DigitResult 八();
        DigitResult 九();
    }

    /** 十、百、千が続けられる位置 */
    public interface ClassAcceptable {
        ClassResult 十();
        ClassResult 百();
        ClassResult 千();
    }

    /** 万、億、兆、...が続けられる位置 */
    public interface UnitAcceptable {
        UnitResult 万();
        UnitResult 億();
        UnitResult 兆();
    }

    /** 基本的にLongに変換可能 */
    public interface JaNumber {
        long longValue();
    }
    /** 一から九の後は 十、百、千 or 万、億、兆が続く */
    public interface DigitResult extends JaNumber, ClassAcceptable, UnitAcceptable {
    }
    /** 十、百、千の後は 何が来ても良い */
    public interface ClassResult extends JaNumber, DigitAcceptable, ClassAcceptable, UnitAcceptable {
    }
    /** 万、億、兆の後は 一から九 or 十、百、千が続く */
    public interface UnitResult extends JaNumber, DigitAcceptable, ClassAcceptable {
    }

    /** 漢数字の抽象スーパークラス */
    abstract static class Base {
        /** 一つ前の数字を保持してリストを作る */
        private Base previous;

        /** コンストラクタ */
        Base(Base previous) {
            super();
            this.previous = previous;
        }
        Base getPrevious() {
            return previous;
        }

        /** 万、億、兆、...単位の合計値を戻す */
        abstract long unitValue();
        /** 十、百、千単位の合計値を戻す */
        abstract int classValue();
        /** 一〜九単位の合計値を戻す */
        abstract int digitValue();

        /** long値として取り出す */
        public long longValue() {
            return unitValue() + classValue() + digitValue();
        }

        /**
         * 仮にbase(「万」=10000, 「十」10など)が続いた場合のclassValue。
         * 桁の値が二でbaseが10だとすると二十になる。
         * 桁の値が千でbaseが10だとすると千十になる。
         * @param base 呼び出し側の数字の単位(例: 千なら1000)
         */
        abstract int classValueWith(int base);



        /* これ以下は各漢数字用のメソッドの定義 */
        public DigitResult 一() {
            return new DigitResultImpl(1, this);
        }
        public DigitResult 二() {
            return new DigitResultImpl(2, this);
        }
        public DigitResult 三() {
            return new DigitResultImpl(3, this);
        }
        public DigitResult 四() {
            return new DigitResultImpl(4, this);
        }
        public DigitResult 五() {
            return new DigitResultImpl(5, this);
        }
        public DigitResult 六() {
            return new DigitResultImpl(6, this);
        }
        public DigitResult 七() {
            return new DigitResultImpl(7, this);
        }
        public DigitResult 八() {
            return new DigitResultImpl(8, this);
        }
        public DigitResult 九() {
            return new DigitResultImpl(9, this);
        }

        public ClassResult 十() {
            return new ClassResultImpl(10, this);
        }
        public ClassResult 百() {
            return new ClassResultImpl(100, this);
        }
        public ClassResult 千() {
            return new ClassResultImpl(1000, this);
        }

        public UnitResult 万() {
            return new UnitResultImpl(10000, this);
        }
        public UnitResult 億() {
            return new UnitResultImpl(10000 * 10000, this);
        }
        public UnitResult 兆() {
            return new UnitResultImpl(10000 * 10000 * 10000, this);
        }
    }

    /** 一から九用のクラス */
    static class DigitResultImpl extends Base implements DigitResult {
        private int digit;

        /** コンストラクタ。 */
        DigitResultImpl(int digit, Base previous) {
            super(previous);
            this.digit = digit;
        }
        long unitValue() {
            return getPrevious().unitValue();
        }
        int classValue() {
            return getPrevious().classValue();
        }
        int digitValue() {
            return digit;
        }
        int classValueWith(int base) {
            return getPrevious().classValue() + base * digitValue();
        }
    }

    /** 十、百、千用のクラス */
    static class ClassResultImpl extends Base implements ClassResult {
        private int base;

        ClassResultImpl(int base, Base previous) {
            super(previous);
            this.base = base;
        }
        long unitValue() {
            return getPrevious().unitValue();
        }
        int classValue() {
            return getPrevious().classValueWith(base);
        }
        int digitValue() {
            return 0;
        }

        int classValueWith(int base) {
            return classValue() + base;
        }
    }
    static class UnitResultImpl extends Base implements UnitResult {
        private long base;

        UnitResultImpl(long base, Base previous) {
            super(previous);
            this.base = base;
        }

        long unitValue() {
            return getPrevious().unitValue()
                + base * (getPrevious().classValue() + getPrevious().digitValue());
        }
        int classValue() {
            return 0;
        }
        int digitValue() {
            return 0;
        }

        int classValueWith(int base) {
            return base;
        }
    }

    /** 最初の一文字用のオブジェクト。 */
    private static final UnitResult BASE = new UnitResultImpl(0, null) {
        long unitValue() {
            return 0;
        }
    };
    /** 最初の一文字は定数で用意しておく */
    public static final DigitResult 一 = BASE.一();
    public static final DigitResult 二 = BASE.二();
    public static final DigitResult 三 = BASE.三();
    public static final DigitResult 四 = BASE.四();
    public static final DigitResult 五 = BASE.五();
    public static final DigitResult 六 = BASE.六();
    public static final DigitResult 七 = BASE.七();
    public static final DigitResult 八 = BASE.八();
    public static final DigitResult 九 = BASE.九();
    public static final ClassResult 十 = BASE.十();
    public static final ClassResult 百 = BASE.百();
    public static final ClassResult 千 = BASE.千();

テスト

上の続きで

    public void test3() {
        long result = 三.longValue();
        assertEquals("三==3", 3, result);
    }
    public void test10() {
        long result = 十.longValue();
        assertEquals("十==10", 10, result);
    }
    public void test30() {
        long result = 三.十().longValue();
        assertEquals("三十==30", 30, result);
    }
    public void test13() {
        long result = 十.三().longValue();
        assertEquals("十三==13", 13, result);
    }
    public void test33() {
        long result = 三.十().三().longValue();
        assertEquals("三十三==33", 33, result);
    }
    public void test100() {
        long result = 百.longValue();
        assertEquals("百==100", 100, result);
    }
    public void test103() {
        long result = 百.三().longValue();
        assertEquals("百三==103", 103, result);
    }
    public void test113() {
        long result = 百.十().三().longValue();
        assertEquals("百十三==113", 113, result);
    }
    public void test130() {
        long result = 百.三().十().longValue();
        assertEquals("百三十==130", 130, result);
    }
    public void test133() {
        long result = 百.三().十().三().longValue();
        assertEquals("百三十三==133", 133, result);
    }
    public void test1000() {
        long result = 千.longValue();
        assertEquals("千==1000", 1000, result);
    }
    public void test1003() {
        long result = 千.三().longValue();
        assertEquals("千三==1003", 1003, result);
    }
    public void test1013() {
        long result = 千.十().三().longValue();
        assertEquals("千十三==1013", 1013, result);
    }
    public void test1033() {
        long result = 千.三().十().三().longValue();
        assertEquals("千三十三==1033", 1033, result);
    }
    public void test3013() {
        long result = 三.千().十().三().longValue();
        assertEquals("三千十三==3013", 3013, result);
    }
    public void test3033() {
        long result = 三.千().三().十().三().longValue();
        assertEquals("三千三十三==3033", 3033, result);
    }
    public void test3303() {
        long result = 三.千().三().百().三().longValue();
        assertEquals("三千三百三==3303", 3303, result);
    }
    public void test3300() {
        long result = 三.千().三().百().longValue();
        assertEquals("三千三百==3300", 3300, result);
    }
    public void test3333() {
        long result = 三.千().三().百().三().十().三().longValue();
        assertEquals("三千三百三十三==3333", 3333, result);
    }
    public void test10000() {
        long result = 三.万().longValue();
        assertEquals("三万==30000", 30000, result);
    }
    public void test10003() {
        long result = 三.万().三().longValue();
        assertEquals("三万三==30003", 30003, result);
    }
    public void test10013() {
        long result = 三.万().十().三().longValue();
        assertEquals("三万十三==300013", 30013, result);
    }
    public void test10030() {
        long result = 三.万().三().十().longValue();
        assertEquals("三万三十==30030", 30030, result);
    }
    public void test10033() {
        long result = 三.万().三().十().三().longValue();
        assertEquals("三万三十三==30033", 30033, result);
    }
    public void test349000203() {
        long result = 三.億().四().千().九().百().万().二().百().三().longValue();
        assertEquals("三億四千九百万二百三==349000203", 349000203L, result);
    }
}