当前位置:首页 > 问答 > 正文

用Redis来搞个计算器,边学边写redis计算功能挺有意思的

知乎用户“程序员阿明”的分享《用Redis实现一个简单计算器,边玩边学》)

好,咱们直接开干!用Redis做个计算器,听起来有点怪对吧?它明明是个存东西的数据库,咋能算数呢?别急,关键不在于让它真的去算1+1,而是用它来“计算的过程和结果,模拟出计算器的核心功能,咱们一步步来,边写边学。

第一步:先想清楚计算器要干嘛

一个最简单的计算器,起码得能做四件事:加、减、乘、除,用户按下一串按钮,3 + 5 * 2”,计算器得知道先算乘除后算加减,最后给出结果13。

但Redis自己不会算这个式子,我们的思路是:把用户输入的一长串计算表达式(3+5*2”),拆解成一个一个的小步骤,把这些步骤和中间结果都存到Redis里,然后我们写个小程序(比如用Python)去Redis里读出这些步骤,按顺序算出来。

第二步:设计怎么在Redis里“存”计算

Redis有好几种存数据的方式,这里我们用它的“列表”(List)最合适,你可以把列表想象成一个排队的队伍,数据一个接一个放进去,先放的在前面,后放的在后面,我们就把计算表达式里的数字和运算符,按顺序塞进这个列表里。

对于“3 + 5 2”: 我们就在Redis里创建一个列表,里面按顺序存上:`[“3”, “+”, “5”, “”, “2”]`。

怎么存呢?用Redis的命令,假设我们给这个计算器起个名字,它的列表叫my_calculator

  • 存数字和符号:用 LPUSH(从左边塞进去)或 RPUSH(从右边塞进去),为了让顺序和我们写的顺序一样,我们用 RPUSH
    • 在Redis命令行里(或者在你的程序里执行Redis命令),就这样操作:
      RPUSH my_calculator "3"
      RPUSH my_calculator "+"
      RPUSH my_calculator "5"
      RPUSH my_calculator "*"
      RPUSH my_calculator "2"
  • 看看存对了没:用 LRANGE 命令查看列表里所有元素。
    LRANGE my_calculator 0 -1

    应该会返回:1) "3" 2) "+" 3) "5" 4) "*" 5) "2",完美!

第三步:写个程序来“算”

现在数据在Redis里排好队了,我们需要个“大脑”来执行计算,这里我用Python写个简单的例子,因为它读起来像伪代码,好理解。

这个“大脑”要做的事:

  1. 从Redis的my_calculator列表里,把所有元素都读出来。
  2. 因为要考虑“先乘除后加减”,我们不能简单地从左到右算,一个常见的办法是先把表达式转换成“后缀表达式”(也叫逆波兰表达式),简单说,就是把运算符放到数字后面,像“3 + 5 2”的后缀表达式就是“3 5 2 +”,这种表达式没有括号,计算顺序是唯一的,特别适合计算机处理。
  3. 计算后缀表达式。

我们来写代码(来源:基于常见算法思路的简化实现):

import redis
# 连接到本地的Redis服务器
r = redis.Redis(host='localhost', port=6379, db=0)
# 1. 从Redis列表里读取计算表达式
expression_list = r.lrange('my_calculator', 0, -1)
# 读出来是字节串,我们转换成字符串
expression_parts = [item.decode('utf-8') for item in expression_list]
print("从Redis读取的表达式部分:", expression_parts)
# 2. 定义一个函数,把中缀表达式(我们平常写的)转换成后缀表达式
def infix_to_postfix(infix_tokens):
    # 定义运算符的优先级
    precedence = {'+': 1, '-': 1, '*': 2, '/': 2}
    output = []
    operator_stack = []
    for token in infix_tokens:
        if token.isdigit():  # 如果是数字,直接输出
            output.append(token)
        elif token in precedence:  # 如果是运算符
            # 当栈顶运算符优先级不低于当前运算符,就弹出栈顶到输出
            while (operator_stack and
                   precedence.get(operator_stack[-1], 0) >= precedence[token]):
                output.append(operator_stack.pop())
            operator_stack.append(token)
    # 把栈里剩下的运算符全部弹出到输出
    while operator_stack:
        output.append(operator_stack.pop())
    return output
# 转换我们的表达式
postfix_expression = infix_to_postfix(expression_parts)
print("转换后的后缀表达式:", postfix_expression)
# 3. 计算后缀表达式
def calculate_postfix(postfix_tokens):
    stack = []
    for token in postfix_tokens:
        if token.isdigit():
            stack.append(int(token))
        else:
            # 弹出栈顶两个数字
            b = stack.pop()
            a = stack.pop()
            if token == '+':
                result = a + b
            elif token == '-':
                result = a - b
            elif token == '*':
                result = a * b
            elif token == '/':
                result = a / b
            stack.append(result) # 把计算结果压回栈中
    return stack[0] # 最后栈里剩下的就是最终结果
# 开始计算!
final_result = calculate_postfix(postfix_expression)
print("最终计算结果:", final_result)
# 4. (可选)把结果存回Redis,方便以后看
r.set('calculator_result', final_result)
print("结果已存回Redis,键为 'calculator_result'")

第四步:运行和扩展

你运行这个Python程序,它就会从Redis读出[“3”, “+”, “5”, “*”, “2”],转换成后缀表达式[“3”, “5”, “2”, “*”, “+”],然后计算:

  • 先遇到数字3,5,2,依次压栈。
  • 遇到,弹出2和5,计算5*2=10,把10压栈,现在栈里是[3, 10]。
  • 遇到,弹出10和3,计算3+10=13,得到最终结果13。

看,我们就用Redis“了算式,用Python完成了计算,你可以通过修改Redis里my_calculator(用RPUSHLPOP等命令),轻松计算不同的式子。

这有意思在哪?

  1. 学了Redis列表:我们用了RPUSH存数据,LRANGE取数据,这是Redis最基础也最常用的数据结构之一。
  2. 理解了计算原理:为了让它能处理优先级,我们不得不去了解后缀表达式这个知识点,这是编译原理和计算器设计的核心基础之一。
  3. 看到了协作:Redis负责高效、持久地存储“状态”(你输入的计算式),应用程序(Python)负责复杂的逻辑运算,这是一种典型的架构思想。

你可以继续扩展这个玩具:比如加个简单的Web界面,让用户输入表达式,你把它拆解后存Redis,然后触发Python计算,最后把结果显示在网页上,这样一个完整的、有“记忆”功能的计算器就诞生了!是不是比单纯写个1+1=2的程序有意思多了?

用Redis来搞个计算器,边学边写redis计算功能挺有意思的