NG / SDE · Coding / OOD

Amazon RPN OOD:自定义 Operator 与计算逻辑怎样接入?

用arity与compute注册表驱动栈求值,保持操作数顺序并定义错误语义。

题目重点:用arity与compute注册表驱动栈求值,保持操作数顺序并定义错误语义。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练token为整数文字或已注册operator,注册表只接收受信回调;本文不执行任意用户上传代码。
  • operator提供非负arity,数值文字不得与operator名冲突;未知token、栈下溢和多余结果报错。
  • 除法可选本文truncate-toward-zero helper,正式题舍入合同未公开。

解题路线

  1. Parser只负责token分类,Registry负责Operator(arity,compute),Evaluator负责栈与错误。新增operator不改一串if/else,只加注册项;arity允许0常量、1一元、2二元和更多参数。
  2. 拿栈尾arity个参数时保持从左到右顺序,如tokens=[8,3,-]要compute(8,3),不能逆序弹成3-8。arity=0单独处理,Python[-0:]是整个列表,不应清空已有栈。
  3. 合法表达式每次operator消费arity项并产生1项,最终必须恰一值。少项是下溢,多值表示表达式未完整组合;任何时候都不能默默忽略token。
  4. 计算错误需定义事务边界:示例一次性eval,回调报错让整个调用失败。长寿命stack服务若要继续,须先计算成功再提交栈变化或回滚原栈。
  5. 用户自定义语义不等于允许任意代码执行。受信插件可直接callable;不受信代码要隔离、超时/资源限制、可用类型和副作用权限,不能用eval执行token。
  6. truncate除法用abs整数除再符号修正,避免Python//负数向下取整与int(a/b)大整数浮点精度问题。

手动走一遍

训练注册-为arity2、square为arity1,8 3 - square得25;0元constant插入一个值而不能擦除旧stack;-7 3 /按训练truncate得-2。

Python 参考实现

下面的代码按上文假设实现,适合本地运行并追加自己的测试用例。

代码区可横向滚动;使用键盘时,先聚焦代码区再按方向键查看。

from dataclasses import dataclass
from typing import Callable


@dataclass(frozen=True)
class Operator:
    arity:int
    compute:Callable


def evaluate(tokens,operators):
    stack=[]
    for token in tokens:
        if token in operators:
            operation=operators[token]
            if operation.arity < 0 or len(stack) < operation.arity:
                raise ValueError("invalid arity or stack underflow")
            args=stack[-operation.arity:] if operation.arity else []
            if operation.arity:del stack[-operation.arity:]
            stack.append(operation.compute(*args))
        else:
            try:stack.append(int(token))
            except ValueError:raise ValueError("unknown token") from None
    if len(stack)!=1:raise ValueError("expression must leave exactly one result")
    return stack[0]


def truncate_divide(a,b):
    if b==0:raise ZeroDivisionError("division by zero")
    magnitude=abs(a)//abs(b)
    return -magnitude if (a<0)!=(b<0) else magnitude

复杂度

N token基础遍历O(N+Σarity),空间O(N)最坏;自定义compute成本和大整数运算另算,不能对任意回调承诺O(1)。

易错点与追问

容易失分的边界

  • 二元参数逆序。
  • arity0切片误清stack。
  • 注册表开放eval任意代码。
  • Python//被误当截断到0。

继续深挖

  • 不同返回类型怎样做type checking?
  • compute报错后stack能否恢复?
  • 动态注册与并发eval怎样固定版本?
✓已复制微信号!