2. 自动售货机状态机
任务说明
实现一个自动售货机状态机,接受硬币输入(5角、1元),用户选择商品(A:2.5元,B:3元),系统根据累计金额决定是否可出货,并在出货后计算找零。状态机覆盖:等待投币、可选购、出货中、找零完成四个状态。
核心实现
from enum import Enum, auto
from dataclasses import dataclass
class State(Enum):
IDLE = auto() # 等待投币
READY = auto() # 可选购
DISPENSING = auto() # 出货中
CHANGE = auto() # 找零中
@dataclass
class Product:
name: str
price: float
class VendingMachineFSM:
def __init__(self):
self.state = State.IDLE
self.balance = 0.0
self.products = {
'A': Product('可乐', 2.5),
'B': Product('矿泉水', 3.0)
}
self.coin_values = {'5角': 0.5, '1元': 1.0}
self.change = 0.0
self.selected_product = None
def insert_coin(self, coin: str) -> str:
"""投币,返回当前余额"""
if coin not in self.coin_values:
return f"无效硬币: {coin}"
if self.state == State.DISPENSING or self.state == State.CHANGE:
return "正在处理中,请稍候"
self.balance += self.coin_values[coin]
if self.state == State.IDLE:
self.state = State.READY
return f"当前余额: {self.balance:.1f}元"
def select_product(self, code: str) -> str:
"""选择商品,返回结果"""
if self.state != State.READY:
return "请先投币"
if code not in self.products:
return f"无此商品: {code}"
product = self.products[code]
if self.balance < product.price:
return f"余额不足,还需 {product.price - self.balance:.1f}元"
# 进入出货状态
self.state = State.DISPENSING
self.selected_product = product
self.balance -= product.price
self.change = self.balance
self.balance = 0.0
return f"出货: {product.name}"
def dispense(self) -> str:
"""确认出货完成,进入找零状态"""
if self.state != State.DISPENSING:
return "当前无出货"
self.state = State.CHANGE
return f"出货完成,找零 {self.change:.1f}元"
def take_change(self) -> str:
"""取走找零,回到初始状态"""
if self.state != State.CHANGE:
return "当前无找零"
change = self.change
self.change = 0.0
self.selected_product = None
self.state = State.IDLE
return f"找零 {change:.1f}元已取出"
def cancel(self) -> str:
"""取消购买,退回余额"""
if self.state == State.DISPENSING or self.state == State.CHANGE:
return "无法取消,交易进行中"
refund = self.balance
self.balance = 0.0
self.state = State.IDLE
return f"已取消,退回 {refund:.1f}元"
def get_state(self) -> str:
return self.state.name
运行与验证
# 测试脚本
vm = VendingMachineFSM()
# 场景1:投币2.5元购买A商品
print(vm.insert_coin('1元')) # 余额: 1.0
print(vm.insert_coin('1元')) # 余额: 2.0
print(vm.insert_coin('5角')) # 余额: 2.5
print(vm.select_product('A')) # 出货: 可乐
print(vm.dispense()) # 出货完成,找零 0.0元
print(vm.take_change()) # 找零 0.0元已取出
# 场景2:投币3元购买B商品,找零0.5元
vm2 = VendingMachineFSM()
vm2.insert_coin('1元')
vm2.insert_coin('1元')
vm2.insert_coin('1元')
print(vm2.select_product('B')) # 出货: 矿泉水
print(vm2.dispense()) # 出货完成,找零 0.5元
print(vm2.take_change()) # 找零 0.5元已取出
# 场景3:余额不足
vm3 = VendingMachineFSM()
vm3.insert_coin('1元')
print(vm3.select_product('A')) # 余额不足,还需 1.5元
# 场景4:取消购买
vm4 = VendingMachineFSM()
vm4.insert_coin('5角')
print(vm4.cancel()) # 已取消,退回 0.5元
运行上述测试脚本,观察输出是否符合注释中的预期结果。状态机在每次操作后通过get_state()可查询当前状态,验证状态流转是否符合:IDLE → READY → DISPENSING → CHANGE → IDLE。
验证标准
- 投币后余额正确累加,状态从
IDLE转为READY - 余额充足时选择商品成功,状态进入
DISPENSING,余额扣除商品价格 dispense()后状态进入CHANGE,找零金额等于扣款后剩余余额take_change()后状态回到IDLE,所有余额清零- 余额不足时拒绝出货,提示缺少金额
- 取消操作仅在
IDLE或READY状态有效,退回全部余额