[JavaScript] ÃæÃÖµ­Ë¡¤Î°ì¸µ°ì¼¡ÊýÄø¼°¤ò²ò¤¯ÅÅÂî

[JavaScript] ÃæÃÖµ­Ë¡¤Î°ì¸µ°ì¼¡ÊýÄø¼°¤ò²ò¤¯ÅÅÂî

Á°²ó¤Îµ­»ö¤Ç½ñ¤¤¤¿ÅÅÂî¤òȯŸ¤µ¤»¤Æ¡¢´Êñ¤Ê°ì¸µ°ì¼¡ÊýÄø¼°¤ò²ò¤¯¤³¤È¤Î¤Ç¤­¤ëÅÅÂî¤Ë¤·¤Æ¤ß¤¿¡£x¤ò1¤Ä¤À¤±´Þ¤à°ì¼¡ÊýÄø¼°¤ò²ò¤¯¤³¤È¤¬¤Ç¤­¤ë¡£

Îã

alert(calc('-x+1=2-3'));                   // 2 / 1
alert(calc('3*3/(2-x)=4+(2/3*4)+1.2'));    // 101 / 118
alert(calc('(8-5)/3=1/(x+2)-1.3*0.4'));    // -51 / 38

³ç¸Ì¤Èñ¹à±é»»»Ò¤ò»È¤¦¤³¤È¤¬¤Ç¤­¤Þ¤¹¡£º£¤Î¤È¤³¤í¡¢x¤Ï¿ô¼°Ãæ¤Ë1¤Ä¤À¤±¤·¤«´Þ¤á¤ë¤³¤È¤¬¤Ç¤­¤Þ¤»¤ó¡£

¼Âư¥µ¥ó¥×¥ë¤È¥½¡¼¥¹¥³¡¼¥É¤Ï³¤­¤Ç¡£

ưºî¥µ¥ó¥×¥ë

¿ô¼°

ʬ¿ô²ò
2 / 1
¾®¿ô²ò
2

Á°²ó¤Îµ­»ö¤«¤é¤Î¼ç¤ÊÊѹ¹ÅÀ

¤Þ¤º¡¢UnaryOperation¥¯¥é¥¹¤ÈBinaryOperation¥¯¥é¥¹¤ÈFraction¥¯¥é¥¹¤ÈUnknown¥¯¥é¥¹¤Ë¡¢type¥Õ¥£¡¼¥ë¥É¤ò»ý¤¿¤»¡¢¤½¤ì¤¾¤ì'Unary', 'Binary', 'Atomic', 'Atomic'¤È¤¤¤¦Ãͤò»ý¤¿¤»¤¿¡£¤½¤·¤Æ¡¢¤³¤Îtype¥Õ¥£¡¼¥ë¥É¤ò¸«¤Ê¤¬¤é¡¢²ò¤ò·×»»¤¹¤ësolve()´Ø¿ô¤ò¢­¤Î¤è¤¦¤Ë½ñ¤¤¤¿¡£

var solve = function(left, right) {
    var hasLeftX = left.hasX();
    if (!hasLeftX == !right.hasX()) throw 'Error';    //XXX
    var variable =  hasLeftX ? left : right;
    var constant = (hasLeftX ? right : left).operate();

    switch (variable.type) {
        case 'Atomic':
            return constant;
        case 'Unary':
            return solve(variable.operand, new UnaryOperation(variable.operator, constant));
        case 'Binary':
            var canceler = ({'+': '-', '-': '+', '*': '/', '/': '*'})[variable.operator];
            switch (canceler) {
                case '+': case '*':
                    return solve(variable.operand1, new BinaryOperation(canceler, variable.operand2, constant));
                case '-': case '/':
                    return solve(variable.operand1, new BinaryOperation(canceler, constant, variable.operand2));
            }
    }
};

¤½¤·¤Æ¡¢À¸¤Î¿ô¼°¤ò¼õ¤±¼è¤Ã¤Æ¡¢¥Ñ¡¼¥¹¤·¤¿¸å¤Ësolve()´Ø¿ô¤ËÅϤ¹calc()´Ø¿ô¤ò¢­¤Î¤è¤¦¤ËÄêµÁ¡£

function calc(source) {
    var sides = source.split('=');
    return solve(parse(sides[0]), parse(sides[1]));
}

solve()´Ø¿ô¤Î²òÀâ

var hasLeftX = left.hasX();
if (!hasLeftX == !right.hasX()) throw 'Error';    //XXX
var variable =  hasLeftX ? left : right;
var constant = (hasLeftX ? right : left).operate();

ºÇ½é¤Î4¹Ô¤Ï¡¢º¸ÊÕ¼°¤È±¦ÊÕ¼°¤ò¼õ¤±¼è¤Ã¤Æ¡¢Ì¤Ãοô¤ò´Þ¤àÊդȴޤޤʤ¤ÊդȤËʬ¤±¤Æ¤·¤Þ¤¦¤È¤³¤í¡£!hasLeftX == !right.hasX()¤ÏÇÓ¾ŪÏÀÍýÎØ¤ÎÈÝÄê¤Ç¡¢Î¾ÊդȤâ¤Ë̤Ãοô¤ò´Þ¤ó¤Ç¤¤¤¿¤ê¡¢µÕ¤ËξÊդȤâ¤Ë̤Ãοô¤ò´Þ¤Þ¤Ê¤«¤Ã¤¿¾ì¹ç¤ËÎã³°¤òÅꤲ¤ë¡£Ì¤Ãοô¤ò´Þ¤Þ¤Ê¤¤Êý¤ÎÊդϡ¢operate()¥á¥½¥Ã¥É¤ò¸Æ¤Ó½Ð¤·¤Æ·×»»¤·¤Æ¤·¤Þ¤¦¡£

switchʸ°Ê²¼¤Ï¡¢Ì¤Ãοô¤ò´Þ¤àÊÕ¤Îtype¥Õ¥£¡¼¥ë¥É¤ò¸«¤ÆÆ°ºî¤òÊѤ¨¤ë¡£UnaryOperation¥ª¥Ö¥¸¥§¥¯¥È¤äBinaryOperation¥ª¥Ö¥¸¥§¥¯¥È¤Ç¤¢¤ì¤Ð¡¢Äê¿ôÊÕ¤ËÂǾñ黻¤ò¤·¤Ä¤ÄºÆµ¢Åª¤Ësolve()¤ò¸Æ¤Ó½Ð¤·¡¢ºÇ¸å¤ËUnknown¥ª¥Ö¥¸¥§¥¯¥È¤Ë¤¿¤É¤êÃ夤¤¿¤é¡¢Äê¿ôÊÕ¤òÊýÄø¼°¤Î²ò¤È¤·¤ÆÊÖ¤¹»ÅÁȤߡ£

case 'Atomic':
    return constant;

type¤¬'Atomic'¤Î¾ì¹ç¡¢¤½¤ÎÊÕ¤Ï̤Ãοô¤¬Ã±ÆÈ¤Ç¸ºß¤¹¤ë¤À¤±¤ÎÊդʤΤǡ¢Äê¿ôÊÕ¤ò̤Ãοô¤Î²ò¤È¤·¤ÆÊÖ¤¹¡£

case 'Unary':
    return solve(variable.operand, new UnaryOperation(variable.operator, constant));

type¤¬'Unary'¤Ç¤¢¤ì¤Ð¡¢Äê¿ôÊÕ¤ËÆ±¤¸±é»»¤ò¹Ô¤Ã¤¿¸å¡¢¥ª¥Ú¥é¥ó¥É¤ÈÄê¿ôÊÕ¤òºÆµ¢Åª¤Ësolve()´Ø¿ô¤ËÅϤ¹¡£

case 'Binary':
    var canceler = ({'+': '-', '-': '+', '*': '/', '/': '*'})[variable.operator];
    switch (canceler) {
        case '+': case '*':
            return solve(variable.operand1, new BinaryOperation(canceler, variable.operand2, constant));
        case '-': case '/':
            return solve(variable.operand1, new BinaryOperation(canceler, constant, variable.operand2));
    }

type¤¬'Binary'¤Ç¤¢¤ì¤Ð¡¢ÂǾñ黻¤ò¤µ¤ì¤¿Äê¿ôÊդȥª¥Ú¥é¥ó¥É¤È¤òºÆµ¢Åª¤Ësolve()´Ø¿ô¤ËÅϤ¹¡£ÂǾñ黻»Ò¤¬¡¢+, *¤Î¾ì¹ç¤È-, /¤Î¾ì¹ç¤È¤Çʬ¤±¤Æ¤¤¤ë¤Î¤Ï¡¢ÂǾñ黻»Ò¤¬¸ò´¹Ë¡Â§¤òËþ¤¿¤¹¤È¤­¤Ë¡¢constant¤Èvariable.operand2¤È¤òÆþ¤ìÂØ¤¨¤ë¤¿¤á¡£¤³¤Î»ÅÁȤߤò¼ÂÁõ¤·¤Ê¤¤¤È¡¢variable.operand2¤¬Ì¤Ãοô¤ò´Þ¤à¤È¤­¤Ë¡¢ºÆµ¢¤¬»ß¤Þ¤é¤Ê¤¯¤Ê¤ë(variable.operand1¤¬Ì¤Ãοô¤ò´Þ¤à¤³¤È¤ò°ÅÌۤ˲¾Äꤷ¤Æ¤¤¤ë¥¢¥ë¥´¥ê¥º¥à¤Ç¤¢¤ë¤¿¤á)¡£

¥½¡¼¥¹¥³¡¼¥É

¤Þ¤À¤Þ¤ÀºÇŬ²½¤Î;ÃϤϤ¢¤ê¤½¤¦¡Ä

function extend(d, s) { for (var p in s) d[p] = s[p]; }

function calc(source) {
    var sides = source.split('=');
    return solve(parse(sides[0]), parse(sides[1]));
}

/** ¿ô¼°¥Ñ¡¼¥µ */
var parse = function(source) {
    var unary = {'+': 1, '-': 1}, binary = {'+': 1, '-': 1, '*': 2, '/': 2};
    var tokens = source.match(/x|\d+(?:\.\d+)?(?:e\d+)?|[-+*/()]/ig);
    return (function parseGroup(index, end) {
        var stack = [];
        while (index < end) {
            var operand = (function parseUnary(token) {
                if (unary[token]) {
                    return new UnaryOperation(token, parseUnary(tokens[index++]));
                } else if (token == '(') {
                    var depth = 0, start = index;
                    while (token = tokens[index++]) {
                        if (token == '(') depth++;
                        else if ((token == ')') && !depth--)
                            return parseGroup(start, index - 1);
                    }
                } else {
                    return isNaN(token) ? new Unknown(token) : new Fraction(token);
                }
            })(tokens[index++]);

            var operator = tokens[index++], precedence = binary[operator] || 0;
            while (stack.length && precedence <= binary[stack[0]])
                operand = new BinaryOperation(stack.shift(), stack.shift(), operand);
            stack.unshift(operator, operand);
        }
        return stack.pop();
    })(0, tokens.length);
};

/** °ì¸µ°ì¼¡ÊýÄø¼°¤ò²ò¤¯ */
var solve = function(left, right) {
    var hasLeftX = left.hasX();
    if (!hasLeftX == !right.hasX()) throw 'Error';    //XXX
    var variable =  hasLeftX ? left : right;
    var constant = (hasLeftX ? right : left).operate();

    switch (variable.type) {
        case 'Atomic':
            return constant;
        case 'Unary':
            return solve(variable.operand, new UnaryOperation(variable.operator, constant));
        case 'Binary':
            var canceler = ({'+': '-', '-': '+', '*': '/', '/': '*'})[variable.operator];
            switch (canceler) {
                case '+': case '*':
                    return solve(variable.operand1, new BinaryOperation(canceler, variable.operand2, constant));
                case '-': case '/':
                    return solve(variable.operand1, new BinaryOperation(canceler, constant, variable.operand2));
            }
    }
};


/** ʬ¿ô¥¯¥é¥¹ */
var Fraction = function(num, den) {
    var isFraction = num instanceof arguments.callee;
    this.num = Number(isFraction ? num.num : num || 0);
    this.den = Number(isFraction ? num.den : den || 1);
    this.reduce();
};

extend(Fraction.prototype, {
    valueOf: function() { return this.num / this.den; },
    toString: function() { return this.num + ' / ' + this.den; },
    /** Ìóʬ */
    reduce: function() {
        var num = Math.abs(this.num), den = Math.abs(this.den);
        if (!isFinite(num) || !isFinite(den) || den === 0) throw 'Error';    //XXX
        if (num) {
            var sign = this.num / num * this.den / den;
            while ((num % 1) || (den % 1)) { num *= 10; den *= 10; }    //À°¿ô²½
            var r, m = Math.max(num, den), n = Math.min(num, den);    //¸ß½üË¡
            while (r = m % n) { m = n; n = r; }
            this.num = sign * num / n;
            this.den = den / n;
        } else {
            this.num = 0;
            this.den = 1;
        }
        return this;
    },
    /** ²Ã»» */
    add: function(n) {
        n = new Fraction(n);
    
        this.num = this.num * n.den + n.num * this.den;
        this.den *= n.den;
    
        return this.reduce();
    },
    /** ¸º»» */
    subtract: function(n) {
        n = new Fraction(n);
    
        this.num = this.num * n.den - n.num * this.den;
        this.den *= n.den;
    
        return this.reduce();
    },
    /** ¾è»» */
    multiply: function(n) {
        n = new Fraction(n);
    
        this.num *= n.num;
        this.den *= n.den;
    
        return this.reduce();
    },
    /** ½ü»» */
    divide: function(n) {
        n = new Fraction(n);
    
        this.num *= n.den;
        this.den *= n.num;
    
        return this.reduce();
    }
});


/** ñ¹à±é»»¥¯¥é¥¹ */
var UnaryOperation = function(operator, operand) {
    this.operator = operator;
    this.operand = operand;
};

extend(UnaryOperation.prototype, {
    type: 'Unary',
    hasX: function() { return this.operand.hasX(); },
    operate: function() {
        switch (this.operator) {
            case '-':
                return this.operand.operate()['*'](-1);
            case '+':
            default:
                return this.operand.operate();
        }
    }
});


/** Æó¹à±é»»¥¯¥é¥¹ */
var BinaryOperation = function(operator, operand1, operand2) {
    this.operator = operator;
    this.operand1 = operand1;
    this.operand2 = operand2;
};

extend(BinaryOperation.prototype, {
    type: 'Binary',
    hasX: function() { return this.operand1.hasX() || this.operand2.hasX(); },
    operate: function() { return this.operand1.operate()[this.operator](this.operand2.operate()); }
});


/** ̤Ãοô¥¯¥é¥¹ */
var Unknown = function(name) { this.name = name; };
extend(Unknown.prototype, {
    type: 'Atomic',
    hasX: function() { return true; },
    operate: function() { throw 'This is an Unknown Value.'; }
});


/** ʬ¿ô¥¯¥é¥¹¤ò³ÈÄ¥ */
extend(Fraction.prototype, {
    '+': Fraction.prototype.add,
    '-': Fraction.prototype.subtract,
    '*': Fraction.prototype.multiply,
    '/': Fraction.prototype.divide,
    type: 'Atomic',
    hasX: function() { return false; },
    operate: function() { return this; }
});

¥È¥é¥Ã¥¯¥Ð¥Ã¥¯URL

http://liosk.blog103.fc2.com/tb.php/92-746c4092

1 ·ï¤Î¥È¥é¥Ã¥¯¥Ð¥Ã¥¯

°ì¼¡ÊýÄø¼°¤ò²ò¤±¤ëÅÅÂ¥¤¥È
[JavaScript] ÃæÃÖµ­Ë¡¤Î°ì¸µ°ì¼¡ÊýÄø¼°¤ò²ò¤¯ÅÅÂî http://liosk.blog103.fc2.com/blog-entry-92.html Ãæ³Ø1ǯ¤¢¤¿¤ê¤Î³§¤µ¤ó¡¢¤³¤ì¤ÏÌòΩ¤Ä¤È»×¤¤¤Þ¤¹¤è¡£ ¤Þ¤¢¡¢ÊýÄø¼°¤¬²ò¤±¤ëÅÅÂî¤È¤¤¤Ã¤¿¤é¡¢ ÀìÌçŹ¤Ê¤É¤ÇÇ㤨¤ë¤Î¤Ç¤¹¤¬¡¢ º£²ó¾Ò²ð¤¹¤ë¤Î¤Ï¡¢¡Ö̵ÎÁ¡×¤...
  • 2009-09-23
  • ȯ¿®¸µ: ¿ʬËèÆü¹¹¿·¡ªSUGAZINE¡ª(¥·¥å¥¬¥¸¥ó)

0 ·ï¤Î¥³¥á¥ó¥È

¥³¥á¥ó¥È¤ÎÅê¹Æ

¤ªÌ¾Á°
¥³¥á¥ó¥È
ÊÔ½¸¥­¡¼