四则预算:Node.js实现

前言:这个项目是结对编程的任务。我们基于JavaScript在Node.js环境下实现了基于命令行的四则运算器。

项目详述

在这个项目中,我们需要实现一个命令行程序,以便为小学自动生成四个算术问题。

小组成员:吴堂煌,张国峻

GitHub: https://github.com/m8705/Arithmometer

详细说明

程序使用

生成问题:

node e.js -n 10 -r 100

验证练习:

node e.js -e exercisefile.txt -a answerfile.txt

参数和规定

  1. 使用-n参数控制生成的问题数(1~10000)。
  2. 使用-r参数控制题目中的数值范围(自然数,分数分子和分母)(1~100)。
  3. 生成的问题中的计算过程不会产生负数,也就是说,如果算术表达式中有子表达式,例如e1-e2,那么e1>e2
  4. 如果生成的练习中存在子表达式e1÷e2,则结果应为分数。
  5. 每个问题中不超过3个运算符。
  6. 一次运行的程序产生的问题不能重复,也就是说,任何两个问题都不能通过有限数量的交换+和*算术表达式转换成同一个问题。生成的问题存储在练习中。 txt文件位于执行程序的当前目录下。
  7. 同时,计算所有问题的答案并将其存储在执行程序的当前目录中的Answers.txt文件中。
  8. 该计划应该支持产生一万个问题。
  9. 程序支持给定的问题文件和答案文件,确定正确和错误的答案和统计,统计输出到文件Grade.txt

主要代码

分数转换
function convert(str){//将任何数转成真分数(小数不换)
    
    //整数 2 = 2'1/1
    //真分数 3/8
    //假分数 5/3
    //带分数 1'1/2
    
    //console.log(str)
    
    if( str.indexOf("/") >= 0 ){//真分数或带分数
        
        if( str.indexOf("'") >= 0 ){//带分数
            
            first = str.split("'")[0];
            second = str.split("'")[1];
            
            up = second.split("/")[0];
            down = second.split("/")[1];
            
            if( ( up === down ) || ( down === "1" ) ){//带分数情况下,不可能存在分子分母相同或分母为1的情况
                return "ERROR";
            }
            
            str = ( (+first) * (+down) + (+up) ) + "/" + down;
            
        }
        else{//真分数
            ;
        }
        
    }
    else{//整数
        
        str = str + "/1";
        
    }
    
    return str
    //console.log(str);
}
分数运算
function calculate(num1,num2,operator){//根据数字和符号进行分数运算
    
    var n1 = [];
    var n2 = [];
    
    var result;
    
    n1 = convert(num1).split( "/" ); // [ 0分子,1分母 ]
    n2 = convert(num2).split( "/" ); // [ 0分子,1分母 ]
    
    switch(operator){
        case "+":
            result = (n1[0]*n2[1]+n2[0]*n1[1]) + "/" + (n1[1]*n2[1]);
            break;
        case "-":
            result = (n1[0]*n2[1]-n2[0]*n1[1]) + "/" + (n1[1]*n2[1]);
            break;
        case "*":
            result = (n1[0]*n2[0]) + "/" + (n1[1]*n2[1]);
            break;
        case "/":
            result = (n1[0]*n2[1]) + "/" + (n1[1]*n2[0]);
            break;
    }
    
    //console.log(result);
    return result;
    
}
符号生成
function produceSymbol(){//产生符号

    var symbol = Math.random();
    var symbolNum;
    
    if( symbol <= 1/3 ){//生成一个符号
        symbolNum = 1;
    }
    else if( symbol <= 2/3 ){//生成两个符号
        symbolNum = 2;
    }
    else{//生成三个符号
        symbolNum = 3;
    }
    
    var symbolChoice = [];
    var tmp;
    for(var a = 0; a < symbolNum; a++){//用概率决定符号
        
        tmp = Math.random();
        if( tmp <= 1/4 ){
            symbolChoice.push("+");
        }
        else if( tmp <= 2/4 ){
            symbolChoice.push("-");
        }
        else if( tmp <= 3/4 ){
            symbolChoice.push("*");
        }
        else{
            symbolChoice.push("/");
        }
        
    }
    
    return symbolChoice;
    
}
数字生成
function produceNumber(symbolNum, range){//产生数字
    
    var symbolChoice = produceSymbol();
    
    var numType;
    var numChoice = [];
    var up, down;
    
    for( var b = 0; b < symbolNum + 1; b++ ){//用概率决定数字
        
        numType = Math.random();
        
        if( numType <= 7 / 10 ){//生成整数
            
            numChoice.push( Math.floor(Math.random()*range) + "" );
            
        }
        else{//生成分数或1(避免生成分子或分母为0)
            
            up = Math.ceil( Math.random() * range );//向上取整
            down = Math.ceil( Math.random() * range );//向上取整
            
            if( up === down ){//分子分母相同
                numChoice.push("1");
                continue;
            }
            
            var tmp = Math.random();//是否产生带分数
            if( tmp <= 1/4 ){//产生带分数
                
                while(up <= down || (up%down === 0) ){//重新产生带分数
                    
                    up = Math.ceil( Math.random() * range );//向上取整
                    down = Math.ceil( Math.random() * range );//向上取整
                    
                }
                
                numChoice.push( 
                    (up - up%down)/down + 
                    "'" + 
                    (up%down / gcd(up%down,down)) + 
                    "/" + 
                    down / gcd(up%down,down)
                );
                
            }
            else{//产生分数
            
                numChoice.push( up + "/" + down );
                
            }
            
            
            
        }
        
    }
    return numChoice;
}
生成数组
function produceRightArray(n, range){//产生n组符合规定的数字和符号
    
    var rightArray = [];
    var flag;
    
    for(var a = 0; a < n; a++){//循环n次
        
        flag = "";
        
        symbolChoice = produceSymbol();
        numChoice = produceNumber(symbolChoice.length,range);
        
        for(var b = 0; b < symbolChoice.length; b++ ){//遍历检查每个符号
            
            if( symbolChoice[b] === "*" ||  symbolChoice[b] === "/"  ){
                
                if(numChoice[b] === "0" || numChoice[b+1] === "0"){
                    
                    flag = "err";
                    a--;
                    break;
                    
                }
                
            }
            
        }
        
        //console.log(a + flag);
        
        if(flag !== "err"){
            rightArray.push([
                symbolChoice,numChoice
            ]);
        }
        
        
    }
    
    //console.log(rightArray);
    return rightArray;
    
}
生成题目
function produceExercise(n,range){//产生n个习题(题目+答案)
    
    var expression = [];    
    var tmp = "";//保存用于产生结果的算式
    var tmp1 = "";//保存用于产生题目的算式
    
    var rightArray = produceRightArray(n,range);
    
    for(var a = 0; a < n; a++ ){//遍历每个产生的结果数组,分别验算结果是否非负
        
        tmp = "";
        tmp1 = ""
        tmp += "(" + convert(rightArray[a][1][0]) + ")" ;
        tmp1 += rightArray[a][1][0];
        
        for(var b = 0; b < rightArray[a][0].length; b++ ){//符号+数字   
            tmp += rightArray[a][0][b] + "(" + convert(rightArray[a][1][b+1]) + ")";
            tmp1 += " " + rightArray[a][0][b] + " " + rightArray[a][1][b+1];            
        }
        
        while( eval(tmp) < 0 ){//不允许产生算式最终值小于0的情况
            
            rightArray[a] = produceRightArray(1,range)[0];
            
            
            tmp = "";
            tmp1 = "";
            tmp += convert(rightArray[a][1][0]);
            tmp1 += rightArray[a][1][0];
        
            for(var c = 0; c < rightArray[a][0].length; c++ ){//符号+数字   
                tmp += rightArray[a][0][c] +  "(" +  convert(rightArray[a][1][c+1]) + ")";
                tmp1 += " " + rightArray[a][0][c] + " " + rightArray[a][1][c+1];
            }
            
            
        }
        //console.log(tmp);
        expression.push(tmp1);
    }
    
    //console.log(expression)
    
    //console.log(rightArray);
    
    //遍历符号列表,根据优先级(先乘除,后加减)对数进行运算,并更新运算结果(逐一替换)至数组
    
    var tmpArray = rightArray;
    var operator;
    
    var symIndex;
    var numIndex1, numIndex2;
    
    var answer = [];
    
    for(var d = 0; d < n; d++){
        
        for(var e = 0; e < tmpArray[d][0].length; e++){//先进行乘除运算
            
            operator = tmpArray[d][0][e];
            
            switch(operator){
                
                case "*":
                    //console.log(tmpArray[d][1][e],tmpArray[d][1][e+1]);
                    
                    
                    replaceNumber(tmpArray[d][1],tmpArray[d][1][e],calculate(  convert(tmpArray[d][1][e]),convert(tmpArray[d][1][e+1]),operator  )     );
                    removeOperator(tmpArray[d][0],"*");
                    e--;
                    break;
                
                case "/":
                    //console.log(tmpArray[d][1][e],tmpArray[d][1][e+1]);
                    
                    
                    replaceNumber(tmpArray[d][1],tmpArray[d][1][e],calculate(  convert(tmpArray[d][1][e]),convert(tmpArray[d][1][e+1]),operator  )     );
                    removeOperator(tmpArray[d][0],"/");
                    e--;
                    break;
                
            }
            
            
            
        }
        
        //console.log(tmpArray)
        
        for(var f = 0; f < tmpArray[d][0].length; f++){//后进行加减运算
            
            operator = tmpArray[d][0][f];
            
            switch(operator){
                
                case "+":
                    //console.log(tmpArray[d][1][f],tmpArray[d][1][f+1]);
                    
                    
                    replaceNumber(tmpArray[d][1],tmpArray[d][1][f],calculate(  convert(tmpArray[d][1][f]),convert(tmpArray[d][1][f+1]),operator  )     );
                    removeOperator(tmpArray[d][0],"+");
                    f--;
                    break;
                
                case "-":
                    //console.log(tmpArray[d][1][f],tmpArray[d][1][f+1]);
                    
                    
                    replaceNumber(tmpArray[d][1],tmpArray[d][1][f],calculate(  convert(tmpArray[d][1][f]),convert(tmpArray[d][1][f+1]),operator  )     );
                    removeOperator(tmpArray[d][0],"-");
                    f--;
                    break;
                
            }
            
        }
        
        answer.push( simplify(tmpArray[d][1][0]) );
    }
    
    //console.log(answer);
    
    return [expression,answer];
    
}
生成答案
function produce(n, range){//产生结果
    
    var exercise = produceExercise(n, range);
    
    var expression = exercise[0];
    var answer = exercise[1];
    
    var expressionText = "";
    var answerText = "";
    
    for( var a = 0 ; a < n ; a++ ){
        
        expressionText += (a+1) + ". " + expression[a] + "\r\n";
        answerText += (a+1) + ". " + answer[a] + "\r\n";
        
    }
    
    //console.log(expressionText)
    
    
    var fs = require("fs");
    
    fs.writeFile('Exercises.txt', expressionText,  function(err) {
        if (err) {
            return console.error(err);
        }
        
    });
    
    //console.log(answerText);
    
    fs.writeFile('Answers.txt', answerText,  function(err) {
        if (err) {
            return console.error(err);
        }
        
    });
    
    console.log("题目数据写入成功!请查看 Exercises.txt ");
    console.log("答案数据写入成功!请查看 Answers.txt ");
    console.log("--------我是分割线-------------")
}
PSP2.1预计耗时实际耗时
总体计划12060
预计完成1515
程序开发360300
需求分析6060
设计文档3030
设计复审3030
代码规范1515
具体设计6060
具体编码120120
代码复审120120
程序测试4040
程序报告9090
测试报告6060
计算工作量3030
事后总结3030
总计时间11801180

测试

生成

1485131-20180929133122322-568859229.png

1485131-20180929133128130-1487486211.png

1485131-20180929133131789-1654550075.png

1485131-20180929133231777-1616347146.png

检查

1485131-20180929133423304-650383706.png

1485131-20180929133453534-1012614244.png

1485131-20180929133529082-1220753486.png

1485131-20180929133557160-1892263027.png

总结

这次由于我们日常所使用的语言不同,所以我们在开发过程中采用了先研究算法再编程实现的流程,最后我发现,先规划好核心算法之后再进行编程实现,比直接进行开发会更加便于优化和检查错误。

转载于:https://www.cnblogs.com/fallenstarowo/p/9723291.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值