函数调用处理机制
核心思路
- 实参与形参分离:通过
assignment哈希表实现参数映射,实现形参(函数定义时的参数名)与相对实参(调用时传入的具体因子)的对应关系。 - 递归替换:通过
assign方法逐层展开函数调用 - 终止条件:当递归到初始定义表达式时完成解析
关键实现步骤
数据结构
为函数调用因子引入通过HashMap<String, Factor>实现的新成员变量assignment,并在为语素实现的assign方法中作为传入参数。
对于FuncCall因子,引入Integer index,FuncDefine,ArrayList<Factor>,用于实现对Expr中的FuncCall因子的完全解析。
public class FuncCall extends VarFactor {
private final ConstantFactor index;
private final FuncDefine define;
private final HashMap<BaseVar, Factor> assignment; // 实际参数列表
}
处理流程
通过expr = expr.assign(null)中调用funcCall.define.get(index).assign(assignment).assign(null),因除FuncCall以外的其他语素有其assign方法,所以将不断递归下降直到funcCall.define.get(1)和funcCall.define.get(0)。此时再递归向下将不再出现FuncCall类,递归结束并逐级返回。从而实现的Expr的去FuncCall流程,以便进行toPoly和simplify。
初始化调用
expr = expr.assign(null); // 通过expr.assign(null)启动替换链条,null参数表示要解析的最上层表达式中的参数为绝对实参参数替换机制
// FuncCall.java ExprFactor assign(HashMap<String, Factor> assignment) { return this.define.get(this.index) .assign(this.assignment) // 先应用this.assignment当前层的参数绑定 .assign(assignment); // 再应用外层传入的assignment参数,实现参数作用域的嵌套覆盖 }递归展开过程
// 高层调用 → 中间层展开 → 基础层解析 funcCall.assign(null) → funcCall.define.get(index).assign(assignment).assign(null) → ... → funcCall'.define.get(index').assign(assignment').assign(assignment).assign(null) → ... → funcCall''.define.get(index'').assign(assignment'').assign(assignment').assign(assignment).assign(null) → ... → funcCall.define.get(0 | 1).assign... // 初始定义表达式
类方法实现
经assign方法,BaseVar.assign(assignment)返回Factor,PowerFunc.assign(assignment)返回ExprFactor,FuncCall递归调用(Expr)define.get(this.index).assign(this.assignment).assign(assignment)返回ExprFactor,其他语素的assign方法返回同类对象。最终递归返回不含FuncCall的纯Expr,即可施之toPoly方法。
| 类名 | assign方法行为 | 返回类型 |
|---|---|---|
| BaseVar | 直接返回对应的含相对实参的Factor | Factor |
| PowerFunc | 返回参数赋值的ExprFactor | ExprFactor |
| FuncCall | 递归调用下层定义进行参数替换 | ExprFactor |
| 其他因子 | 返回由自身组成语素.assign()构造的同类对象 | 同类对象 |
处理终点
当递归到define.get(0 | 1)时:
- 不再包含FuncCall对象,函数调用结构完全消解
- 所有形参已被实际参数替换
- 表达式完全展开为基本元素
- 可安全调用
toPoly()进行多项式转换 - 可进行最终化简
simplify()