Arithmetic
前言
GitHub地址
合作伙伴:林奇凯
项目简介
- Arithmetic是一个能够自动生成小学四则运算题目的命令行程序
项目需求
- 使用 -n 参数控制生成题目的个数,例如:
Myapp.exe -n 10
- 使用 -r 参数控制题目中数值(自然数、真分数和真分数分母)的范围,例如:
Myapp.exe -r 10
- 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1 − e2的子表达式,那么e1 ≥ e2。
- 生成的题目中如果存在形如e1 ÷ e2的子表达式,那么其结果应是真分数。
- 每道题目中出现的运算符个数不超过3个。
- 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
生成的题目存入执行程序的当前目录下的Exercises.txt文件,格式如下:
1. 四则运算题目1
2. 四则运算题目2
……
其中真分数在输入输出时采用如下格式,真分数五分之三表示为3/5,真分数二又八分之三表示为2’3/8
在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件,格式如下:
1. 答案1
2. 答案2
特别的,真分数的运算如下例所示:1/6 + 1/8 = 7/24。
- 程序应能支持一万道题目的生成。
程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计,输入参数如下:
Myapp.exe -e <exercisefile>.txt -a <answerfile>.txt
统计结果输出到文件Grade.txt,格式如下:
Correct: 5 (1, 3, 5, 7, 9)
Wrong: 5 (2, 4, 6, 8, 10)
其中“:”后面的数字5表示对/错的题目的数量,括号内的是对/错题目的编号。为简单起见,假设输入的题目都是按照顺序编号的符合规范的题目。
开发中遇到的困难
- 运算优先级问题以及算式输出时括号添加问题:
一开始是用char型来存运算符,后来考虑到优先级问题,决定建一个运算符的类——Operator。这个类有两个成员变量,一个就是char型的运算符,另一个就是int型的优先级了,而这个优先级与运算符生成的个数有关,所以在new一个运算符对象时要调用带参构造函数,并传入存有运算符个数的参数。对于括号问题,我是根据优先级来加括号的。
什么样的两个问题算是重复?
一开始我以为如果题目A能通过交换律、结合律变换后得到题目B,那么AB就算重复。如:1+2+3=1+(2+3)=1+(3+2)=(1+3)+2=(3+1)+2=3+(1+2)=3+(2+1)=3+2+1,即1+2+3与3+2+1算是重复。如果是这样的话,就只需要判断两道题的运算数、运算符和运算结果是否都一样就行了。
后来返回看需求时发现1+2+3与3+2+1不算重复。仔细看才发现1+2+3=3+3=6,而3+2+1=5+1=6,虽然最终结果相同,但是计算过程不同。于是,在原基础上,我在Question类添加了一个用来存过程结果的列表results。这样两个问题在运算数、运算符和运算结果都一样的基础上,如果过程结果一一对应,才算是重复。
设计思路
PSP
PSP2.1 |
Personal Software Process Stages |
预估耗时(分钟) |
实际耗时(分钟) |
Planning |
计划 |
60 | 60 |
· Estimate |
· 估计这个任务需要多少时间 |
60 | 60 |
Development |
开发 |
660 | 780 |
· Analysis |
· 需求分析 (包括学习新技术) |
90 | 100 |
· Design Spec |
· 生成设计文档 |
30 | 30 |
· Design Review |
· 设计复审 (和同事审核设计文档) |
30 | 30 |
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
30 | 30 |
· Design |
· 具体设计 |
60 | 60 |
· Coding |
· 具体编码 |
240 | 320 |
· Code Review |
· 代码复审 |
60 | 90 |
· Test |
· 测试(自我测试,修改代码,提交修改) |
120 | 120 |
Reporting |
报告 |
60 | 90 |
· Test Report |
· 测试报告 |
30 | 30 |
· Size Measurement |
· 计算工作量 |
20 | 30 |
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
10 | 30 |
合计 |
780 | 930 |
关键代码
- 分数部分
public class Fraction {int n=0; //整数部分int molecular=0; //分子int denominator=1; //分母/*** 带参构造方法,用于随机生成分数* @param scope 给定分子分母的范围*/public Fraction(int scope) {Random r=new Random();molecular=r.nextInt(scope)+1;denominator=r.nextInt(scope)+1;rationalize();}/*** 带参构造方法,用于赋值* @param n 整数部分* @param molecular 分子* @param denominator 分母*/public Fraction(int n,int molecular,int denominator) {this.molecular=molecular;this.denominator=denominator;this.n=n;rationalize();}/*** 无参构造方法,生成一个数(默认值为0,即0'0/1)*/public Fraction() {}/*** 分数有理化*/public void rationalize() {//假分数化为带分数if(molecular>=denominator){n+=molecular/denominator;molecular%=denominator;}//约分int gcd=Util.gCommonDivisor(molecular,denominator);if(gcd!=1) {molecular/=gcd;denominator/=gcd;}}/*** 通分* @param lcm 最小公倍数*/public void commonReduction(int lcm) {molecular*=lcm/denominator;denominator=lcm;}/*** 带分数化为假分数,用于计算*/public void changeToImproperFraction() {if(n==0) return;molecular+=n*denominator;n=0;}/*** 判断数值是否为0* @return 为0返回true,否则返回false*/public boolean isZero() {if(n==0&&molecular==0) return true;return false;}@Overridepublic String toString() {rationalize();if(molecular%denominator==0) return ""+n;String fraction="";if(n>0) fraction+=n+"\'";fraction+=molecular;if(denominator>1) fraction+="/"+denominator;return fraction;} }
- 运算符部分
ublic class Operator {char operator; //运算符int priority; //优先级(1最高)/*** 带参构造方法,用于随机生成运算符* @param n 运算符个数,用于生成优先级*/public Operator(int n) {Random r=new Random();switch(r.nextInt(4)){case 0:operator='+'; //43break;case 1:operator='-'; //45break;case 2:operator='×'; //215break;default:operator='÷'; //247 }priority=r.nextInt(n)+1;}/*** 带参构造方法,用于指定运算符* @param c 运算符* @param i 优先级*/public Operator(char c,int i) {operator=c;priority=i;}/*** 判断优先级是否重复* 运算符列表的最后一项与前面所有项进行比较,如果出现优先级重复,则删掉最后一项* @param list 运算符列表* @return 重复返回true,否则返回false*/public static boolean isRepeated(List<Operator> list) {for(int i=0;i<list.size()-1;i++) if(list.get(list.size()-1).priority==list.get(i).priority){list.remove(list.size()-1);return true;}return false;}@Overridepublic String toString() {return operator+" : "+priority;} }
- 运算题目方面
public class Question {int n=new Random().nextInt(3)+1; //随机生成题目运算符的个数(1~3)String question=""; //题目List<Fraction> fractions=new ArrayList<Fraction>(); //生成的所有运算数组成的列表(用于计算方法calculate())List<Operator> operators=new ArrayList<Operator>(); //生成的所有运算符组成的列表(用于计算方法calculate())List<Fraction> results=new ArrayList<Fraction>(); //题目每一步运算得出的结果组成的列表Fraction[] fTemp=new Fraction[n+1]; //生成的所有运算数组成的数组(用于生成题目)Operator[] oTemp=new Operator[n]; //生成的所有运算符组成的数组(用于生成题目)Fraction result; //题目得出的最终结果/*** 带参构造方法,用于随机生成题目* @param scope 给定数值的范围*/public Question(int scope) {do{question="";fractions.clear();operators.clear();results.clear();Fraction f;Operator o;f=new Fraction(scope);fractions.add(f);fTemp[0]=f;for(int i=0;i<n;i++){do{o=new Operator(n);operators.add(o);if(i==0) break;}while(Operator.isRepeated(operators)); //用于生成优先级不重复的运算符oTemp[i]=o;f=new Fraction(scope);fractions.add(f);fTemp[i+1]=f;}result=calculate(); //计算}while(result.n==-1); //如果结果为-1,证明题目不合法,需重新设置题目 }/*** 带参构造方法,用于指定题目* @param fs 运算数数组* @param os 运算符数组*/public Question(Fraction[] fs,Operator[] os) {fTemp=fs;oTemp=os;for(Fraction f:fs) fractions.add(f);for(Operator o:os) operators.add(o);result=calculate();}/*** 计算* @return 返回最终结果,如果结果为-1,则证明题目非法* 题目非法的原因有二:* 1.被减数小于减数* 2.除数为0*/public Fraction calculate() {int priority=1;do{for(int i=0;i<operators.size();i++)if(operators.get(i).priority==priority){switch(operators.get(i).operator){case '+':fractions.set(i,Util.add(fractions.get(i),fractions.get(i+1)));break;case '-'://如果减数大于被减数,则返回-1if(!Util.compare(fractions.get(i),fractions.get(i+1))) return new Fraction(-1,0,1);fractions.set(i,Util.minus(fractions.get(i),fractions.get(i+1)));break;case '×':fractions.set(i,Util.multiply(fractions.get(i),fractions.get(i+1)));break;case '÷'://如果除数为0,则返回-1if(fractions.get(i+1).isZero()) return new Fraction(-1,0,1);fractions.set(i,Util.divice(fractions.get(i),fractions.get(i+1)));break;}results.add(fractions.get(i));fractions.remove(i+1);operators.remove(i);break;}priority++;}while(operators.size()>0);return fractions.get(0);}/*** 判断题目是否重复* 题目列表的最后一项与前面所有项进行比较,如果出现题目重复,则删掉最后一项* @param list 题目列表* @return 重复返回true,否则返回false* 判断题目重复的依据有二:* 1.运算数相同,运算符相同* 2.运算顺序相同,即结果列表一一对应*/public static boolean isRepeated(List<Question> list) {for(int i=0;i<list.size()-1;i++){//最终结果不同if(!Util.isEqual(list.get(list.size()-1).result,list.get(i).result)) return false;//结果列表大小不同if(list.get(list.size()-1).results.size()!=list.get(i).results.size()) return false;//结果列表一一对应存在不同元素for(int j=0;j<list.get(list.size()-1).results.size();j++)if(!Util.isEqual(list.get(list.size()-1).results.get(j),list.get(i).results.get(j))) return false;boolean bool=false;//数值列表存在不同元素for(Fraction f1:list.get(list.size()-1).fTemp){for(Fraction f2:list.get(i).fTemp){if(!Util.isEqual(f1,f2)) bool=false;else{bool=true;break;}}if(bool==false) return false;}//符号列表存在不同元素for(Operator o1:list.get(list.size()-1).oTemp){for(Operator o2:list.get(i).oTemp){if(!Util.isEqual(o1,o2)) bool=false;else{bool=true;break;}}if(bool==false) return false;}}list.remove(list.size()-1);return true;}@Overridepublic String toString() {int i1=0,i2=0,i3=0;for(;i1<oTemp.length;i1++)if(oTemp[i1].priority==1){question+=fTemp[i1]+" "+oTemp[i1].operator+" "+fTemp[i1+1];break;}if(oTemp.length>1){for(;i2<oTemp.length;i2++)if(oTemp[i2].priority==2){if(i2==i1-1) question=fTemp[i2]+" "+oTemp[i2].operator+" ( "+question+" )";if(i2==i1+1) question="( "+question+" ) "+oTemp[i2].operator+" "+fTemp[i2+1];if(oTemp.length==3){for(;i3<oTemp.length;i3++)if(oTemp[i3].priority==3){if(i3==0) question=fTemp[i3]+" "+oTemp[i3].operator+" [ "+question+" ]";if(i3==1){if(i1==1) question="( "+fTemp[i1]+" "+oTemp[i1].operator+" ) "+oTemp[i3].operator+" ( "+fTemp[i2]+" "+oTemp[i2].operator+" )";if(i2==1) question="( "+fTemp[i2]+" "+oTemp[i2].operator+" ) "+oTemp[i3].operator+" ( "+fTemp[i1]+" "+oTemp[i1].operator+" )";}if(i3==2) question="[ "+question+" ] "+oTemp[i3].operator+" "+fTemp[i3+1];break;}}break;}}question+=" = ";return question;} }
- Util类基本方法
public class Util {/*** 最大公因数,用于约分* 递归实现辗转相除法* @param a* @param b* @return 返回a、b的最大公因数*/public static int gCommonDivisor(int a,int b) {if(b==0) return a;else return gCommonDivisor(b,a%b);}/*** 最小公倍数,用于通分* [a,b]=a*b/(a,b)* @param a* @param b* @return 返回a、b的最小公倍数*/public static int lCommonMultiple(int a,int b) {return a*b/gCommonDivisor(a,b);}/*** 判断a、b的大小,用于判断两数相减时被减数是否不小于减数* @param a* @param b* @return a>=b返回true,否则返回false*/public static boolean compare(Fraction a,Fraction b) {a.changeToImproperFraction();b.changeToImproperFraction();if(a.denominator!=b.denominator){int lcm=lCommonMultiple(a.denominator,b.denominator);a.commonReduction(lcm);b.commonReduction(lcm);}if(a.molecular>=b.molecular) return true;else return false;}/*** a+b* @param a* @param b* @return 返回a+b的结果*/public static Fraction add(Fraction a,Fraction b) {if(a.denominator!=b.denominator){int lcm=lCommonMultiple(a.denominator,b.denominator);a.commonReduction(lcm);b.commonReduction(lcm);}Fraction c=new Fraction(a.n+b.n,a.molecular+b.molecular,a.denominator);return c;}/*** a-b* @param a* @param b* @return 返回a-b的结果*/public static Fraction minus(Fraction a,Fraction b) {a.changeToImproperFraction();b.changeToImproperFraction();if(a.denominator!=b.denominator){int lcm=lCommonMultiple(a.denominator,b.denominator);a.commonReduction(lcm);b.commonReduction(lcm);}Fraction c=new Fraction(0,a.molecular-b.molecular,a.denominator);return c;}/*** a×b* @param a* @param b* @return 返回a×b的结果*/public static Fraction multiply(Fraction a,Fraction b) {a.changeToImproperFraction();b.changeToImproperFraction();Fraction c=new Fraction(0,a.molecular*b.molecular,a.denominator*b.denominator);return c;}/*** a÷b* @param a* @param b* @return 返回a÷b的结果*/public static Fraction divice(Fraction a,Fraction b) {a.changeToImproperFraction();b.changeToImproperFraction();Fraction c=new Fraction(0,a.molecular*b.denominator,a.denominator*b.molecular);return c;}/*** 判断a、b是否相等,用于判断题目是否重复* @param a* @param b* @return a=b返回true,否则返回false*/public static boolean isEqual(Fraction a,Fraction b) {a.changeToImproperFraction();b.changeToImproperFraction();if(a.denominator!=b.denominator){int lcm=lCommonMultiple(a.denominator,b.denominator);a.commonReduction(lcm);b.commonReduction(lcm);}if(a.n==b.n&&a.molecular==b.molecular&&a.denominator==b.denominator) return true;return false;}/*** 判断运算符a、b是否相同,用于判断题目是否重复* @param a* @param b* @return a、b相同返回true,否则返回false*/public static boolean isEqual(Operator a,Operator b) {if(a.operator==b.operator) return true;return false;} }
- Main主函数
public class Main {public static void main(String[] args) {try {boolean flag=true;for(String s:args){if(s.equals("-n")||s.equals("-r")) break; //生成题目和答案文档else{ //对错题数量统计flag=false;break;}}//生成题目和答案文档if(flag){List<Question> questionBase=new ArrayList<Question>(); //题库int n=1; //题目个数(默认为1)int r=-1; //数值范围for(int i=0;i<args.length;i++){if(args[i].equals("-n")) n=Integer.valueOf(args[i+1]);if(args[i].equals("-r")) r=Integer.valueOf(args[i+1]);}//没有给出数值范围if(r==-1){System.err.println("Warning: The scope of value has not been given!");return;}Question q=new Question(r);questionBase.add(q);while(questionBase.size()<n||Question.isRepeated(questionBase)){q=new Question(r);questionBase.add(q);}//生成题目文件Questions.txtBufferedWriter bw1=new BufferedWriter(new FileWriter("C:\\Users\\asus\\Desktop\\四则运算\\Questions.txt"));for(int i=0;i<questionBase.size();i++){String s=i+1+". "+questionBase.get(i)+"\r\n";bw1.write(s);}bw1.flush();bw1.close();//生成答案文件Answers.txtBufferedWriter bw2=new BufferedWriter(new FileWriter("C:\\Users\\asus\\Desktop\\四则运算\\Answers.txt"));for(int i=0;i<questionBase.size();i++){String s=i+1+". "+questionBase.get(i).result+"\r\n";bw2.write(s);}bw2.flush();bw2.close();//生成答题文件Exercises.txtBufferedWriter bw3=new BufferedWriter(new FileWriter("C:\\Users\\asus\\Desktop\\四则运算\\Exercises.txt"));for(int i=0;i<questionBase.size();i++){String s=i+1+". \r\n";bw3.write(s);}bw3.flush();bw3.close();return;}//对错题数量统计String e="",a="";for(int i=0;i<args.length;i++){if(args[i].equals("-e")) e=args[i+1];if(args[i].equals("-a")) a=args[i+1];}//没有给出答题文件路径if(e.equals("")){System.err.println("404: The exercises file is not found!");return;}//没有给出答案文件路径if(a.equals("")){System.err.println("404: The answers file is not found!");return;}List<String> exercises=new ArrayList<String>();List<String> answers=new ArrayList<String>();String str="";//读取答题文件BufferedReader br1=new BufferedReader(new FileReader(e));for(int i=1;null!=(str=br1.readLine());i++)exercises.add(str.replace(i+". ",""));br1.close();//读取答案文件BufferedReader br2=new BufferedReader(new FileReader(a));for(int i=1;null!=(str=br2.readLine());i++)answers.add(str.replace(i+". ",""));br2.close();String correct="",wrong="";List<Integer> corrects=new ArrayList<Integer>();List<Integer> wrongs=new ArrayList<Integer>();for(int i=0;i<answers.size();i++){if(exercises.get(i).equals(answers.get(i))) corrects.add(i+1);else wrongs.add(i+1);}for(int i=0;i<corrects.size();i++) correct+=corrects.get(i)+",";for(int i=0;i<wrongs.size();i++) wrong+=wrongs.get(i)+",";if(corrects.size()>0) correct=correct.substring(0,correct.length()-1);if(wrongs.size()>0) wrong=wrong.substring(0,wrong.length()-1);//生成分数文件Grade.txtBufferedWriter bw=new BufferedWriter(new FileWriter("C:\\Users\\asus\\Desktop\\四则运算\\Grade.txt"));bw.write("Correct:"+corrects.size()+"("+correct+")\r\nWrong:"+wrongs.size()+"("+wrong+")");bw.flush();bw.close();} catch (Exception e) {//-a C:\Users\asus\Desktop\四则运算\Answers.txt -e C:\Users\asus\Desktop\四则运算\Exercises.txt e.printStackTrace();}} }
单元测试
- 测试题目重复的问题,例:3+(2+1)和1+2+3这两个题目是重复的,1+2+3和3+2+1是不重复的。
public class test {public static void main(String[] args) {/** 测试题目重复*/Fraction[] r1 = {new Fraction(1,0,1),new Fraction(2,0,1),new Fraction(3,0,1)};Operator[] n1 = {new Operator('+',1),new Operator('+',2)};//1+2+3Fraction[] r2 = {new Fraction(3,0,1),new Fraction(2,0,1),new Fraction(1,0,1)};Operator[] n2 = {new Operator('+',2),new Operator('+',1)};//3+(2+1)Fraction[] r3 = {new Fraction(3,0,1),new Fraction(2,0,1),new Fraction(1,0,1)};Operator[] n3 = {new Operator('+',1),new Operator('+',2)};//3+2+1Fraction[] r4 = {new Fraction(1,0,1),new Fraction(2,0,1),new Fraction(3,0,1)};Operator[] n4 = {new Operator('+',1),new Operator('+',2)};//1+2+3 Question q1=new Question(r1,n1);Question q2=new Question(r2,n2);Question q3=new Question(r3,n3);Question q4=new Question(r4,n4);List<Question> questionBase1=new ArrayList<Question>();questionBase1.add(q1);questionBase1.add(q2);List<Question> questionBase2=new ArrayList<Question>();questionBase2.add(q3);questionBase2.add(q4);for (Question question : questionBase1) {System.out.println(question);} boolean flag1 = Question.isRepeated(questionBase1); System.out.println(flag1);for (Question question : questionBase2) {System.out.println(question);} boolean flag2 = Question.isRepeated(questionBase2); System.out.println(flag2);}}
- 运行结果
测试结果
正常测试:
- 首先四则运算的文件夹是空的
- 输入-n 10 -r 10
- 文件夹内生成了如下三个TXT文件
这是题目的答案
这是练习时的答题卡
这里是生成的10道题目
- 填入以下答案(3和9故意填错的),保存
- 输入 -e C:\Users\asus\Desktop\四则运算\Exercises.txt -a C:\Users\asus\Desktop\四则运算\Answers.txt
这里生成了一个Grade文件
- 对结果的判断如下:
转载于:https://www.cnblogs.com/ac666/p/9695396.html
Arithmetic相关推荐
- LeetCode 1027. Longest Arithmetic Sequence--笔试题--C++解法
LeetCode 1027. Longest Arithmetic Sequence–笔试题–C++解法 LeetCode题解专栏:LeetCode题解 我做的所有的LeetCode的题目都放在这个专 ...
- 构造 HDOJ 5400 Arithmetic Sequence
题目传送门 题意:问有多少个区间,其中存在j使得ai + d1 == ai+1(i<j) && ai + d2 == ai+1 (i>j) 构造:用c1[i], c2[i] ...
- usaco Arithmetic Progressions
题意:给出n,m,其中n表示等差数列的长度,每项由a^2+b^2表示,其中a和b不超过m,要求输出满足条件的首项及等差 思路:先根据m,可以得到由[0,m]构成的a^2+b^2,最大的等差,然后根据首 ...
- LeetCode - 413. Arithmetic Slices - 含中文题意解释 - O(n) - ( C++ ) - 解题报告
1.题目大意 A sequence of number is called arithmetic if it consists of at least three elements and if th ...
- PAT甲级1088 Rational Arithmetic:[C++题解]分数的加减乘除
文章目录 题目分析 题目链接 题目分析 来源:acwing 加减乘除: ab+cd=ad+bcbd\frac{a}{b}+\frac{c}{d}=\frac{ad+bc}{bd}ba+dc=bda ...
- Modular Arithmetic 模算术
Modular Arithmetic 模算术 我们都见过时钟,在时钟上有12个刻度.假设某天晚上时针指向6的位置,那么它表示的是晚上六点钟,可是到了第二天早上,当时针再次指向6时,它表示的又是早上六点 ...
- boost::mpl模块实现arithmetic相关的测试程序
boost::mpl模块实现arithmetic相关的测试程序 实现功能 C++实现代码 实现功能 boost::mpl模块实现arithmetic相关的测试程序 C++实现代码 #include & ...
- Arithmetic Slices
这两天一直复习动态规划,就想到leetcode上刷刷题,easy难度的很少,大部分都是medium和hard.本题是第一道DP类型medium难度的题目,但是用其他的方法比如暴力法也可以求解.首先来看 ...
- 2020牛客国庆集训派对day4 Arithmetic Progressions
Arithmetic Progressions 链接:https://ac.nowcoder.com/acm/contest/7831/B 来源:牛客网 题目描述 An arithmetic prog ...
最新文章
- 图灵出品的人气O'Reilly动物书,你更钟意哪本?
- IBM借QISKit打造基于云平台的量子计算
- mysql查询数据教程_MySQL 查询数据
- mysql基础(二)—— 简单sql
- 机器人学习--Mobile robot国内外优秀实验室
- 单片机c语言 openssl,Linux下C语言使用openssl库进行加密
- sqlplus配置连接mysql_如何配置和使用iSQL*Plus
- 【通俗易懂】RPA是什么?
- 如何评价一个推荐系统的好坏?
- NoSQL为什么需要模式自由的ETL工具:不知道的大概都没用过!
- html怎么直接给服务器发消息,HTML5教程之服务器发送事件
- java 从一个容器获取对象,Java 如何实现从spring容器中获取注入的bean对象
- 《报错与问题解决方案》总结v1.0版本
- SqlMapConfig.xml 的配置
- express+mongodb制作简单的文章发布系统(一)之环境的搭建
- 加密的PDF文件如何解密?教你使用手机就能解密的方法
- Cauchy-Schwarz不等式
- 基于51,人体红外感应和RC522的门禁系统
- 复利java_复利计算1.0,2.0,3.0(java)
- 微信小程序带图片弹窗简单实现