Ruby中如何将含复杂表达式的公式替换为实际值并展示?
替换Excel式公式中的变量为实际值以展示计算过程
问题场景
我开发的应用将类Excel风格的公式存储在数据库中,通过eval方法计算最终结果(比如公式"a + b - c"能正常执行得到结果)。现在需要实现一个功能:把公式中的变量、复杂表达式替换为实际取值后展示,让用户清晰看到计算过程。
比如当a=3、b=2、c=1时,将原公式"a + b - c"展示为"3 + 2 - 1"。
涉及的典型公式示例包括:
capital_commitment.committed_amount_cents * ( fund_unit_setting.management_fee * ( ( @end_date - @start_date ).to_i + 1 ) / 365 ) / 100AccountEntry.total_amount(capital_commitment.account_entries, name: 'Cost Of Exited Investments', end_date: @end_date)( capital_commitment.unit_type && capital_commitment.unit_type == 'Series C' ) ? 0 : ( ( @current_portfolio_fmv + @distributions - @remittances ) * 0.2 )
解决方案代码
通过Ruby的parser库解析公式的AST(抽象语法树),提取所有需要求值的表达式,计算后生成映射表,后续即可用该映射表替换原公式中的表达式为实际值。
def parse_statement(binding) statement = formula buffer = Parser::Source::Buffer.new('(string)') buffer.source = statement ast = Parser::CurrentRuby.new.parse(buffer) expressions = [] find_expressions(ast, expressions) expression_map = {} expressions.each do |exp| val = eval(exp, binding) expression_map[exp] = val unless (val.is_a?(ApplicationRecord) || val.is_a?(OpenStruct)) end expression_map end def find_expressions(node, expressions) case node.type when :send expressions << node.loc.expression.source when :lvar, :ivar, :const expressions << node.loc.name.source when :str expressions << node.loc.expression.source end node.children.each do |child| find_expressions(child, expressions) if child.is_a?(Parser::AST::Node) end end
代码说明
- AST解析:使用
Parser::CurrentRuby将公式字符串解析为抽象语法树,精准识别公式中的变量、方法调用、字符串等表达式。 - 表达式提取:递归遍历AST节点,收集所有需要求值的表达式(包括实例变量、局部变量、常量、方法调用、字符串)。
- 求值与映射:通过
eval结合当前上下文的binding计算每个表达式的实际值,生成表达式到值的映射表(排除ActiveRecord模型和OpenStruct实例,避免展示无意义的对象引用)。 - 替换展示:基于生成的
expression_map,即可将原公式中的对应表达式替换为实际值,得到用户可读的计算过程展示。
内容的提问来源于stack exchange,提问作者Mohith Thimmaiah
相关产品推荐
相关产品推荐

