291 lines
6.2 KiB
Go
291 lines
6.2 KiB
Go
/*
|
|
calculator.go - 计算器核心逻辑
|
|
实现了基本的四则运算和括号运算功能
|
|
*/
|
|
|
|
package calculator
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Calculator 接口定义了计算器的基本功能
|
|
type Calculator interface {
|
|
Calculate(expression string) (float64, error)
|
|
GetHistory() []HistoryRecord
|
|
ClearHistory()
|
|
}
|
|
|
|
// BasicCalculator 基本计算器实现
|
|
type BasicCalculator struct {
|
|
history []HistoryRecord
|
|
}
|
|
|
|
// NewCalculator 创建新的计算器实例
|
|
func NewCalculator() Calculator {
|
|
return &BasicCalculator{
|
|
history: make([]HistoryRecord, 0),
|
|
}
|
|
}
|
|
|
|
// Calculate 计算数学表达式
|
|
func (c *BasicCalculator) Calculate(expression string) (float64, error) {
|
|
// 清理输入
|
|
expr := strings.ReplaceAll(expression, " ", "")
|
|
if expr == "" {
|
|
return 0, fmt.Errorf("表达式不能为空")
|
|
}
|
|
|
|
// 验证表达式
|
|
if err := c.validateExpression(expr); err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// 解析和计算
|
|
result, err := c.evaluateExpression(expr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// 添加到历史记录
|
|
c.addToHistory(expression, result)
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// GetHistory 获取计算历史
|
|
func (c *BasicCalculator) GetHistory() []HistoryRecord {
|
|
// 返回历史记录的副本
|
|
history := make([]HistoryRecord, len(c.history))
|
|
copy(history, c.history)
|
|
return history
|
|
}
|
|
|
|
// ClearHistory 清空历史记录
|
|
func (c *BasicCalculator) ClearHistory() {
|
|
c.history = make([]HistoryRecord, 0)
|
|
}
|
|
|
|
// validateExpression 验证表达式的有效性
|
|
func (c *BasicCalculator) validateExpression(expr string) error {
|
|
if len(expr) == 0 {
|
|
return fmt.Errorf("表达式不能为空")
|
|
}
|
|
|
|
// 检查括号匹配
|
|
parentheses := 0
|
|
for _, char := range expr {
|
|
switch char {
|
|
case '(':
|
|
parentheses++
|
|
case ')':
|
|
parentheses--
|
|
if parentheses < 0 {
|
|
return fmt.Errorf("括号不匹配")
|
|
}
|
|
}
|
|
}
|
|
|
|
if parentheses != 0 {
|
|
return fmt.Errorf("括号不匹配")
|
|
}
|
|
|
|
// 检查有效字符
|
|
validChars := "0123456789+-*/.() "
|
|
for _, char := range expr {
|
|
if !strings.ContainsRune(validChars, char) {
|
|
return fmt.Errorf("包含无效字符: %c", char)
|
|
}
|
|
}
|
|
|
|
// 检查连续运算符
|
|
operators := "+-*/"
|
|
for i := 0; i < len(expr)-1; i++ {
|
|
if strings.ContainsRune(operators, rune(expr[i])) &&
|
|
strings.ContainsRune(operators, rune(expr[i+1])) {
|
|
return fmt.Errorf("连续的运算符")
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// evaluateExpression 计算表达式的值
|
|
func (c *BasicCalculator) evaluateExpression(expr string) (float64, error) {
|
|
// 处理括号
|
|
for strings.Contains(expr, "(") {
|
|
// 找到最内层的括号
|
|
start := -1
|
|
for i, char := range expr {
|
|
if char == '(' {
|
|
start = i
|
|
} else if char == ')' {
|
|
if start == -1 {
|
|
return 0, fmt.Errorf("括号不匹配")
|
|
}
|
|
|
|
// 计算括号内的表达式
|
|
subExpr := expr[start+1 : i]
|
|
subResult, err := c.evaluateSimpleExpression(subExpr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// 替换括号表达式为结果
|
|
expr = expr[:start] + fmt.Sprintf("%g", subResult) + expr[i+1:]
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
// 计算简单表达式(无括号)
|
|
return c.evaluateSimpleExpression(expr)
|
|
}
|
|
|
|
// evaluateSimpleExpression 计算简单表达式(无括号)
|
|
func (c *BasicCalculator) evaluateSimpleExpression(expr string) (float64, error) {
|
|
if expr == "" {
|
|
return 0, fmt.Errorf("空表达式")
|
|
}
|
|
|
|
// 解析表达式为标记
|
|
tokens, err := c.tokenize(expr)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
if len(tokens) == 0 {
|
|
return 0, fmt.Errorf("空表达式")
|
|
}
|
|
|
|
// 如果只有一个标记,直接返回数值
|
|
if len(tokens) == 1 {
|
|
return strconv.ParseFloat(tokens[0], 64)
|
|
}
|
|
|
|
// 先处理乘法和除法
|
|
for i := 1; i < len(tokens); i += 2 {
|
|
if i >= len(tokens) {
|
|
break
|
|
}
|
|
|
|
operator := tokens[i]
|
|
if operator == "*" || operator == "/" {
|
|
left, err := strconv.ParseFloat(tokens[i-1], 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("无效的数字: %s", tokens[i-1])
|
|
}
|
|
|
|
right, err := strconv.ParseFloat(tokens[i+1], 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("无效的数字: %s", tokens[i+1])
|
|
}
|
|
|
|
var result float64
|
|
if operator == "*" {
|
|
result = left * right
|
|
} else {
|
|
if right == 0 {
|
|
return 0, fmt.Errorf("除零错误")
|
|
}
|
|
result = left / right
|
|
}
|
|
|
|
// 替换三个标记为结果
|
|
newTokens := make([]string, 0, len(tokens)-2)
|
|
newTokens = append(newTokens, tokens[:i-1]...)
|
|
newTokens = append(newTokens, fmt.Sprintf("%g", result))
|
|
newTokens = append(newTokens, tokens[i+2:]...)
|
|
tokens = newTokens
|
|
i -= 2 // 调整索引
|
|
}
|
|
}
|
|
|
|
// 再处理加法和减法
|
|
result, err := strconv.ParseFloat(tokens[0], 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("无效的数字: %s", tokens[0])
|
|
}
|
|
|
|
for i := 1; i < len(tokens); i += 2 {
|
|
if i+1 >= len(tokens) {
|
|
break
|
|
}
|
|
|
|
operator := tokens[i]
|
|
operand, err := strconv.ParseFloat(tokens[i+1], 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("无效的数字: %s", tokens[i+1])
|
|
}
|
|
|
|
switch operator {
|
|
case "+":
|
|
result += operand
|
|
case "-":
|
|
result -= operand
|
|
default:
|
|
return 0, fmt.Errorf("未知的运算符: %s", operator)
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
// tokenize 将表达式分解为标记
|
|
func (c *BasicCalculator) tokenize(expr string) ([]string, error) {
|
|
var tokens []string
|
|
var current strings.Builder
|
|
|
|
for i, char := range expr {
|
|
switch char {
|
|
case '+', '-', '*', '/':
|
|
// 处理负号
|
|
if char == '-' && (i == 0 || expr[i-1] == '(' || strings.ContainsRune("+-*/", rune(expr[i-1]))) {
|
|
current.WriteRune(char)
|
|
continue
|
|
}
|
|
|
|
// 保存当前数字
|
|
if current.Len() > 0 {
|
|
tokens = append(tokens, current.String())
|
|
current.Reset()
|
|
}
|
|
|
|
// 保存运算符
|
|
tokens = append(tokens, string(char))
|
|
|
|
case ' ':
|
|
// 忽略空格
|
|
continue
|
|
|
|
default:
|
|
// 数字或小数点
|
|
current.WriteRune(char)
|
|
}
|
|
}
|
|
|
|
// 保存最后的数字
|
|
if current.Len() > 0 {
|
|
tokens = append(tokens, current.String())
|
|
}
|
|
|
|
return tokens, nil
|
|
}
|
|
|
|
// addToHistory 添加计算记录到历史
|
|
func (c *BasicCalculator) addToHistory(expression string, result float64) {
|
|
record := HistoryRecord{
|
|
Expression: expression,
|
|
Result: result,
|
|
}
|
|
c.history = append(c.history, record)
|
|
|
|
// 限制历史记录数量
|
|
const maxHistory = 100
|
|
if len(c.history) > maxHistory {
|
|
c.history = c.history[len(c.history)-maxHistory:]
|
|
}
|
|
}
|