如何写一个脚本自动售货机逻辑

wen 实用脚本 2

从零到一:手把手教你构建自动售货机脚本的核心逻辑(附伪代码与避坑指南)


目录导读

  1. 自动售货机脚本的“灵魂”:状态机思维
  2. 核心模块拆解:库存、货币、交易与找零
  3. 关键算法:如何优雅地处理“并发购买”与“异常取消”?
  4. 实战伪代码:一段可直接改写的Python骨架
  5. 高频问答:新手最容易踩的5个逻辑陷阱

自动售货机看似简单,但它的逻辑本质是一个有限状态机(Finite State Machine),如果你直接写“if-else连环套”,脚本很快就会变成一锅粥,本文将结合主流嵌入式与后端设计模式,带你梳理一套可扩展、可测试的脚本方案。

如何写一个脚本自动售货机逻辑

状态机:先画流程图,再写代码
不要急着写if判断,先把机器状态枚举出来:待机(IDLE)接收货币(COIN_INSERTED)选择商品(SELECTING)出货(DISPENSING)找零(CHANGE_RETURN),每个状态只负责“当前动作”和“触发下一个状态的条件”,比如在COIN_INSERTED状态,唯一事件是“插入更多钱”或“选择商品”,其他输入(如反复按退货键)会被标记为无效请求,这种模式让你后期增加“扫码支付”时,只需新增一个QR_PAYING状态,不动核心代码。

库存与货币:用“字典”而不是“列表”
库存建议用{商品ID: {'价格': int, '数量': int}}结构,价格用最小货币单位(如分/美分)存储,避免浮点数精度问题(例如0.1+0.2≠0.3),货币模块要区分“已投入金额”和“临时暂存金额”——后者在确认出货前才扣减,找零算法采用贪心策略:优先使用大面额硬币(如25分→10分→5分→1分),但必须检查库存余额,否则会造成“找零不足”的尴尬。

并发与取消:引入“超时锁”与“事务回滚”
如果是物联网设备,用户可能在投币后突然拔电,脚本需要每10秒记录一次“当前状态快照”到非易失性存储,当检测到异常重启时,自动回滚到最近一个一致状态,对于“出货失败”(比如卡货),应当触发“退款指令”,且该指令优先于普通交易——这需要一个优先级队列,而不是简单的单线程顺序执行。

实战伪代码(Python风格)

class VendingMachine:
    def __init__(self, stock):
        self.state = "IDLE"
        self.balance = 0  # 单位: 分
        self.stock = stock  # {id: {'price': 250, 'qty': 5}}
    def insert_coin(self, value):
        if self.state == "IDLE":
            self.balance += value
            self.state = "COIN_INSERTED"
        elif self.state == "COIN_INSERTED":
            self.balance += value
        # 其他状态忽略
    def select_item(self, item_id):
        if self.state != "COIN_INSERTED":
            return "请先投币"
        item = self.stock.get(item_id)
        if not item or item['qty'] <= 0:
            return "缺货"
        if self.balance < item['price']:
            return "余额不足"
        # 执行出货
        item['qty'] -= 1
        change = self.balance - item['price']
        self.balance = 0
        self.state = "IDLE"
        return f"出货成功,找零{change}分"

注意:真实环境需加入try/except包裹出货硬件调用,且状态转移前置校验必须放在函数第一行。

高频问答:逻辑排雷指南

  • 问:为什么我的找零逻辑容易“死循环”?
    答:贪心算法会尝试用max(coin_stock)找零,但若某面额硬币库存为零,必须跳过该面额,建议循环内增加while remaining >= coin_value and coin_available双重判断。

  • 问:如何处理用户投币后5分钟不选商品?
    答:设计一个time.time()戳在每次状态变更时重置,后台守护线程检测到超时则强制退款并回到IDLE,注意退款函数要独立于交易主流程,避免被阻塞。

  • 问:是否需要用数据库存交易记录?
    答:嵌入式设备用SQLite,云端可用MySQL,但脚本核心逻辑不要直接操作数据库,而是通过事件钩子(如on_dispense(id))异步记录,防止IO卡顿。

  • 问:脚本如何测试?
    答:将“出货”、“退币”等物理动作抽象为接口,测试时替换为mock_actuator(),并用pytest跑状态转移矩阵,重点测试:缺货退款、余额不足、零钱不足、重复投币取消。

  • 问:扫码支付怎么融合?
    答:把“支付成功”当作一个外部回调事件,在主循环中,通过消息队列接收该事件,并强制将状态从WAIT_SCAN切换到SELECTING,而不是轮询网络状态。


自动售货机脚本的精髓在于“约束状态转移”“硬件解耦”,不要试图在一个函数里干完所有事——把“决策”和“执行”分开,你的代码会像售货机内部的传送带一样,清晰而可靠,当遇到复杂需求时,先回到状态机图里画一笔,再让代码落地。

抱歉,评论功能暂时关闭!