经过几周时间的努力,我和我的结对项目伙伴陈同学一起完成这这次结对项目。这次结对项目分为四个模块:ui、算式生成、计算和测试。ui沿用了我个人项目使用的网站的框架,进行一点简单的修改就可以直接使用;由于陈同学正好正在学习python语言,所以我们一起使用python完成了算式生成模块;计算模块则采用go语言来完成,我们之前都没有接触过go语言,所以这也是一个一边学习一边使用的过程,充满了艰辛和挑战,在学习的过程中我们发现了go语言内置了一个很好用的测试模块,于是我们就使用了go test来做测试,设计了测试用例,从XML文件读入对计算模块进行了测试。由于ui模块十分简单而计算模块同时又作为算式生成模块的测试和修正模块,所以只做了计算模块的设计。
ui模块主要代码:
index:
$_stage = $_GET['s'];
switch($_stage)
{
case 0:
include_once 'get.php';
break;
default:
include_once 'show.php';
}
get:
<form action="index.php" method="get">
数字长度: <label>
<input type="number" name="num_length" />
</label><br />
最大项数: <label>
<input type="number" name="max_length" />
</label><br />
算式数量: <label>
<input type="number" name="num" />
</label><br />
<label>
<input name="have" type="checkbox" value=1 />
</label>是否含有括号<br />
<label>
<input style="display:none" name="s" value=1 />
</label>
<input type="submit" value="确认设置" />
</form>
show:
<?php
/**
* Created by PhpStorm.
* User: evi1
* Date: 16-3-9
* Time: 下午5:13
*/
$num_length = $_GET['num_length'];
$max_length = $_GET['max_length'];
$num = $_GET['num'];
$have = $_GET['have'];
if(!is_numeric($max_length)||!is_numeric($num_length)||!is_numeric($num))
{
echo "输入错误";
exit();
}
if($max_length < 2)
{
echo "最大长度不能小于2";
exit();
}
if($num_length < 1 || $num < 1)
{
echo "数据错误";
exit();
}
if(!$have)
{
$have = 0;
}
?>
<script type="text/javascript">
function check_answer(i,answer)
{
var input = document.getElementById("line"+i);
var output = document.getElementById("myDiv"+i);
if(input.value == String(answer))
{
output.innerHTML = "正确";
}
else
{
output.innerHTML = "错误";
}
update_result()
}
function update_result()
{
var result = document.getElementById("result");
var num = <?php echo $num; ?>;
var x = 0;
for(var i = 0; i < num; i++)
{
var output = document.getElementById("myDiv"+i);
if(output.innerHTML == "正确")
{
x ++;
}
}
result.innerHTML = x+"/"+num;
}
</script>
<?php
echo "<table border='0'>";
for($i = 0; $i < $num ; $i++)
{
$a = array();
while(!$a[0])
{
exec('python ./main.py -n ' . $num_length . ' -m ' . $max_length . ' -h '.$have, $a);
}
?>
<tr>
<td><?php echo $a[0]; ?></td>
<td><label for="line<?php echo $i; ?>"></label><input oninput="check_answer(<?php echo $i; ?>, '<?php echo $a[1]; ?>')" type="text" id="line<?php echo $i; ?>" /><a id="myDiv<?php echo $i; ?>"></a></td>
</tr>
<?php
}
echo "</table>";
echo "<p id = 'result'> 0/".$num." </p>"
算式生成:
class generator:
def __init__(self):
self._num_length = 3
self._max_length = 5
self._symbol = ['+', '-', '*', '/']
self._have = 1
self._list = []
self._result = ''
# self._x = 0
self._length = 2
def set(self, num_length, max_length, have):
self._num_length = int(num_length)
self._max_length = int(max_length)
self._have = int(have)
# print(self._num_length, self._max_length, self._have)
def generate(self):
self._length = random.randint(2, self._max_length)
i = 0
max_num = 1
while i < self._num_length:
max_num *= 10
i += 1
max_num -= 1
if self._have:
self.__generate_list()
i = 0
while i < self._length:
for x in self._list:
if x[0] == i:
self._result += '('
t = random.randint(0, max_num)
if t < 0:
t = '(' + str(t) + ')'
else:
t = str(t)
self._result += t
for x in self._list:
if x[1] == i:
self._result += ')'
if i == self._length - 1:
self._result += '='
else:
self._result += random.choice(self._symbol)
i += 1
status, result = commands.getstatusoutput("./trade.o -s "" + self._result + """)
# print(status, result)
if status != 0:
self._symbol = ['+', '-', '*', '/']
self._list = []
self._result = ''
# print(x)
return self.generate()
return self._result, result
def __generate_list(self):
f = random.randint(0, 2)
i = 0
while f and i < 2:
l = random.randint(0, self._length - 2)
r = random.randint(l + 1, self._length - 1)
if l == 0 and r == self._length - 1:
pass
elif not self._list:
self._list.append((l, r))
else:
for x in self._list:
if x[0] >= l and x[1] <= r and (l, r) not in self._list:
self._list.append((l, r))
elif x[0] <= l and x[1] >= r and (l, r) not in self._list:
self._list.append((l, r))
f = random.randint(0, 2)
i += 1
计算:
type MyError struct {
What string
}
func (e MyError) Error() string {
return fmt.Sprintf(" %s", e.What)
}
func isOp(o string) bool{
if o == "+" || o =="-" || o == "*" || o == "/" || o == "=" || o == "(" || o == ")"{
return true
}
return false
}
func vsop(a,b string)string{
if a == "+"{
switch{
case b == "+": return ">"
case b == "-": return ">"
case b == "*": return "<"
case b == "/": return "<"
case b == "=": return ">"
}
}else if a == "-"{
switch{
case b == "+": return ">"
case b == "-": return ">"
case b == "*": return "<"
case b == "/": return "<"
case b == "=": return ">"
}
}else if a == "*"{
switch{
case b == "+": return ">"
case b == "-": return ">"
case b == "*": return ">"
case b == "/": return ">"
case b == "=": return ">"
}
}else if a == "/"{
switch{
case b == "+": return ">"
case b == "-": return ">"
case b == "*": return ">"
case b == "/": return ">"
case b == "=": return ">"
}
}else{
switch{
case b == "+": return "<"
case b == "-": return "<"
case b == "*": return "<"
case b == "/": return "<"
case b == "=": return "="
}
}
return "#"
}
type Trade struct {
nuStack, opStack, bigStack, backStack list.List
i int
str string
}
func (v *Trade) SetStr(s string) {
v.str = s
v.i = 1
v.nuStack.Init()
v.opStack.Init()
v.bigStack.Init()
v.backStack.Init()
}
func (v *Trade) split() error {
if v.str[len(v.str) - 1] != '=' {
v.str += "="
}
for _,s := range v.str{
if s == ' '{
continue
}else if isOp(string(s)){
v.bigStack.PushBack(string(s))
}else{
if s < '0' || s > '9'{
return MyError{"nonnum"}
}
if v.bigStack.Len() - 1 >= 0 && ! isOp(v.bigStack.Back().Value.(string)){
x := v.bigStack.Back()
v.bigStack.Remove(x)
t := x.Value.(string) + string(s)
v.bigStack.PushBack(t)
}else{
v.bigStack.PushBack(string(s))
}
}
}
l := &v.bigStack
for x := l.Front(); x != nil; x = x.Next() {
//fmt.Println(x.Value)
tstr := x.Value.(string)
switch {
case !isOp(tstr):
v.backStack.PushBack(tstr)
//fmt.Println(tstr,"in b")
case tstr == "(":
v.opStack.PushBack(tstr)
case tstr == ")":
if v.opStack.Len() <= 0{
return MyError{"()error1"}
}else{
for v.opStack.Back().Value.(string) != "("{
if v.opStack.Len() <= 0{
return MyError{"()error2"}
}else {
t := v.opStack.Back()
v.backStack.PushBack(t.Value.(string))
v.opStack.Remove(t)
//fmt.Println(t.Value,"in b")
}
}
t := v.opStack.Back()
v.opStack.Remove(t)
}
default:
for v.opStack.Len() != 0 && v.opStack.Back().Value.(string) != "(" && vsop(v.opStack.Back().Value.(string), tstr) == ">"{
t := v.opStack.Back()
v.backStack.PushBack(t.Value.(string))
v.opStack.Remove(t)
//fmt.Println(t.Value,"in b")
}
v.opStack.PushBack(tstr)
}
}
for v.opStack.Len() > 0{
y := v.opStack.Back()
v.opStack.Remove(y)
if y.Value.(string) == "("{
return MyError{"()error3"}
}
v.backStack.PushBack(y.Value.(string))
}
/*ll := &v.opStack
for x := ll.Front(); x != nil; x = x.Next() {
fmt.Println(x.Value)
}
lll := &v.backStack
for x := lll.Front(); x != nil; x = x.Next() {
fmt.Println(x.Value,"b")
}*/
return nil
}
func (v *Trade)DoTrade()(string, error){
err := v.split()
if err != nil{
return " ",err
}
l := &v.backStack
for x := l.Front(); x != nil; x = x.Next() {
tstr := x.Value.(string)
//fmt.Println(x.Value,"b")
switch{
case tstr == "=":
//fmt.Println(v.nuStack.Back().Value)
return v.handelResult(v.nuStack.Back().Value.(int)), nil
case !isOp(tstr):
//fmt.Println("数字入栈")
i, err := strconv.Atoi(tstr)
if err != nil{
return " ",MyError{"non num"}
}
v.nuStack.PushBack(i*v.i)
/*for x := v.nuStack.Front(); x != nil; x = x.Next(){
fmt.Println(x.Value,"n")
}*/
default:
//fmt.Println("op",tstr)
if v.nuStack.Len() < 2{
return " ",MyError{"nustack error"}
}
b := v.nuStack.Back()
v.nuStack.Remove(b)
a := v.nuStack.Back()
v.nuStack.Remove(a)
an, e := v.fuckIt(a.Value.(int), b.Value.(int), tstr)
if e == nil{
v.nuStack.PushBack(an)
}else{
return " ",e
}
}
}
return " ",MyError{"unknow error"}
}
func (v *Trade)fuckIt(a, b int,op string)(int, error){
switch op{
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
default:
//fmt.Println("op f",op)
if b == 0{
return 0, MyError{"divide 0"}
}
t := a
b = b / v.i
v.i *= b
l := v.nuStack
v.nuStack.Init()
for x := l.Front(); x != nil; x = x.Next(){
//fmt.Println(x.Value,"l")
v.nuStack.PushBack(x.Value.(int) * v.i)
}
/*for x := v.nuStack.Front(); x != nil; x = x.Next(){
fmt.Println(x.Value,"n")
}*/
return t, nil
}
return 0, MyError{"unknow"}
}
func (v *Trade)handelResult(answer int)string{
b := v.i
if answer % b == 0{
return strconv.Itoa(answer / b)
}
if b < 0{
b = 0 - b
answer = 0 - answer
}
ta := answer
tb := b
if ta < 0{
ta = 0 - ta
}
if tb < 0{
tb = 0 - tb
}
var x int
if tb > ta{
x = ta
}else{
x = tb
}
for !(ta % x == 0 && tb % x == 0){
x --
}
answer /= x
b /= x
answer = int(answer)
b = int(b)
sa := strconv.Itoa(answer)
sb := strconv.Itoa(b)
//fmt.Println(string(answer),"/",string(b),string(answer) + "/" + string(b))
return sa + "/" + sb
}
var s *string
func init(){
s = flag.String("s", "1+1", " ")
flag.Parse()
}
func main() {
var x Trade
//fmt.Println(*s)
x.SetStr(*s)
ans, er := x.DoTrade()
if er != nil{
fmt.Println(er)
os.Exit(1)
}
fmt.Println(ans)
}
测试:
type Result struct {
Tests []Test `xml:"test"`
}
type Test struct {
Qwe string `xml:"qwe"`
Ans string `xml:"ans"`
}
func TestIndex(t *testing.T) {
content, err := ioutil.ReadFile("./test_trade.xml")
if err != nil {
log.Fatal(err)
}
var result Result
err = xml.Unmarshal(content, &result)
if err != nil {
log.Fatal(err)
}
fmt.Println(result.Tests)
type reSt struct {
s string
out string
}
var tests = []reSt{}
for _,t := range result.Tests{
tests = append(tests,reSt{t.Qwe, t.Ans})
}
for i, test := range tests {
x := Trade{}
x.SetStr(test.s)
re,e := x.DoTrade()
if e != nil && test.out != "error"{
t.Errorf("should not have error %v %q = %q",i,test.s,re)
}else if re != test.out && e == nil {
t.Errorf("%v %q = %q; want %q",
i,test.s, re,test.out)
}
}
}