This commit is contained in:
2025-08-24 13:01:09 +08:00
parent 61e51ad014
commit f028913eb8
36 changed files with 10420 additions and 70 deletions

View File

@@ -0,0 +1,290 @@
/*
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:]
}
}

View File

@@ -0,0 +1,244 @@
/*
history.go - 历史记录管理
定义了历史记录的数据结构和相关功能
*/
package calculator
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"strings"
"time"
)
// HistoryRecord 表示一条计算历史记录
type HistoryRecord struct {
Expression string `json:"expression"` // 表达式
Result float64 `json:"result"` // 计算结果
Timestamp time.Time `json:"timestamp"` // 计算时间
}
// NewHistoryRecord 创建新的历史记录
func NewHistoryRecord(expression string, result float64) HistoryRecord {
return HistoryRecord{
Expression: expression,
Result: result,
Timestamp: time.Now(),
}
}
// String 返回历史记录的字符串表示
func (h HistoryRecord) String() string {
return fmt.Sprintf("%s = %g (计算时间: %s)",
h.Expression,
h.Result,
h.Timestamp.Format("2006-01-02 15:04:05"))
}
// HistoryManager 历史记录管理器
type HistoryManager struct {
records []HistoryRecord
filePath string
maxSize int
}
// NewHistoryManager 创建新的历史记录管理器
func NewHistoryManager(filePath string, maxSize int) *HistoryManager {
return &HistoryManager{
records: make([]HistoryRecord, 0),
filePath: filePath,
maxSize: maxSize,
}
}
// Add 添加历史记录
func (hm *HistoryManager) Add(expression string, result float64) {
record := NewHistoryRecord(expression, result)
hm.records = append(hm.records, record)
// 限制历史记录数量
if len(hm.records) > hm.maxSize {
hm.records = hm.records[len(hm.records)-hm.maxSize:]
}
}
// GetAll 获取所有历史记录
func (hm *HistoryManager) GetAll() []HistoryRecord {
// 返回副本以防止外部修改
records := make([]HistoryRecord, len(hm.records))
copy(records, hm.records)
return records
}
// GetLast 获取最近的 n 条记录
func (hm *HistoryManager) GetLast(n int) []HistoryRecord {
if n <= 0 {
return []HistoryRecord{}
}
if n >= len(hm.records) {
return hm.GetAll()
}
start := len(hm.records) - n
records := make([]HistoryRecord, n)
copy(records, hm.records[start:])
return records
}
// Clear 清空历史记录
func (hm *HistoryManager) Clear() {
hm.records = make([]HistoryRecord, 0)
}
// Count 获取历史记录数量
func (hm *HistoryManager) Count() int {
return len(hm.records)
}
// SaveToFile 保存历史记录到文件
func (hm *HistoryManager) SaveToFile() error {
if hm.filePath == "" {
return fmt.Errorf("文件路径未设置")
}
data, err := json.MarshalIndent(hm.records, "", " ")
if err != nil {
return fmt.Errorf("序列化历史记录失败: %v", err)
}
err = ioutil.WriteFile(hm.filePath, data, 0644)
if err != nil {
return fmt.Errorf("写入文件失败: %v", err)
}
return nil
}
// LoadFromFile 从文件加载历史记录
func (hm *HistoryManager) LoadFromFile() error {
if hm.filePath == "" {
return fmt.Errorf("文件路径未设置")
}
// 检查文件是否存在
if _, err := os.Stat(hm.filePath); os.IsNotExist(err) {
// 文件不存在,创建空的历史记录
hm.records = make([]HistoryRecord, 0)
return nil
}
data, err := ioutil.ReadFile(hm.filePath)
if err != nil {
return fmt.Errorf("读取文件失败: %v", err)
}
err = json.Unmarshal(data, &hm.records)
if err != nil {
return fmt.Errorf("反序列化历史记录失败: %v", err)
}
// 限制历史记录数量
if len(hm.records) > hm.maxSize {
hm.records = hm.records[len(hm.records)-hm.maxSize:]
}
return nil
}
// Search 搜索包含指定关键词的历史记录
func (hm *HistoryManager) Search(keyword string) []HistoryRecord {
var results []HistoryRecord
for _, record := range hm.records {
if contains(record.Expression, keyword) {
results = append(results, record)
}
}
return results
}
// GetStatistics 获取历史记录统计信息
func (hm *HistoryManager) GetStatistics() map[string]interface{} {
stats := make(map[string]interface{})
stats["total_count"] = len(hm.records)
if len(hm.records) == 0 {
return stats
}
// 统计运算符使用频率
operatorCount := make(map[string]int)
for _, record := range hm.records {
for _, char := range record.Expression {
switch char {
case '+':
operatorCount["addition"]++
case '-':
operatorCount["subtraction"]++
case '*':
operatorCount["multiplication"]++
case '/':
operatorCount["division"]++
}
}
}
stats["operator_usage"] = operatorCount
// 最早和最晚的计算时间
if len(hm.records) > 0 {
earliest := hm.records[0].Timestamp
latest := hm.records[0].Timestamp
for _, record := range hm.records {
if record.Timestamp.Before(earliest) {
earliest = record.Timestamp
}
if record.Timestamp.After(latest) {
latest = record.Timestamp
}
}
stats["earliest_calculation"] = earliest.Format("2006-01-02 15:04:05")
stats["latest_calculation"] = latest.Format("2006-01-02 15:04:05")
}
return stats
}
// ExportToCSV 导出历史记录为 CSV 格式
func (hm *HistoryManager) ExportToCSV(filePath string) error {
var csvContent strings.Builder
// CSV 头部
csvContent.WriteString("Expression,Result,Timestamp\n")
// 数据行
for _, record := range hm.records {
csvContent.WriteString(fmt.Sprintf("\"%s\",%g,\"%s\"\n",
record.Expression,
record.Result,
record.Timestamp.Format("2006-01-02 15:04:05")))
}
err := ioutil.WriteFile(filePath, []byte(csvContent.String()), 0644)
if err != nil {
return fmt.Errorf("导出 CSV 文件失败: %v", err)
}
return nil
}
// contains 检查字符串是否包含子字符串(忽略大小写)
func contains(s, substr string) bool {
return len(s) >= len(substr) &&
(substr == "" ||
len(s) > 0 &&
(s == substr ||
strings.Contains(strings.ToLower(s), strings.ToLower(substr))))
}