You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过exprtk的unknown_symbol_resolver创建向量类型变量?

如何通过exprtk的unknown_symbol_resolver创建向量类型变量?

嘿,我刚好碰到过类似的问题,exprtk确实没给symbol_table加直接创建向量的成员函数,但咱们可以在自定义的unknown_symbol_resolver里手动实现这个逻辑,其实不难!

核心思路就是:当解析器遇到未知符号(比如你例子里的foobar)时,我们在process方法里主动创建一个exprtk的向量对象,把它注册到符号表中,再告诉解析器这个符号已经处理完成。

先给你补全并优化一下你的代码,直接就能用:

#include <string>
#include <stdio.h>
#include <vector>
#include "exprtk.hpp"

using symbol_table_t = exprtk::symbol_table<double>;
using parser_t = exprtk::parser<double>;
using expression_t = exprtk::expression<double>;
using vector_t = exprtk::vector<double>;

template<typename T>
struct myusr : public parser_t::unknown_symbol_resolver {

  using usr = typename parser_t::unknown_symbol_resolver;
  symbol_table_t& sym_table_;

  // 必须传入符号表的引用,这样才能把新创建的向量注册进去
  myusr(symbol_table_t& st) : usr(usr::e_usrmode_extended), sym_table_(st) { }

  virtual bool process(const std::string& symbol,
                       T& /* 这个参数对标量有用,向量用不上,所以忽略 */,
                       parser_t::usrmode /* 模式参数,这里我们用默认的扩展模式 */,
                       std::vector<std::string>& /* 额外符号列表,暂时不用 */) override {
    // 这里我默认把所有未知符号都创建成向量,你也可以加判断逻辑
    // 比如只处理以"_vec"结尾的符号:if (symbol.ends_with("_vec")) { ... }
    vector_t* new_vec = new vector_t();
    // 可以先设置向量初始大小,比如0,后续也可以动态调整
    new_vec->resize(0);

    // 把向量注册到符号表,如果注册失败要释放内存
    if (!sym_table_.add_vector(symbol, *new_vec)) {
      delete new_vec;
      return false;
    }

    // 返回true告诉解析器这个符号已经处理好了
    return true;
  }

  // 析构函数里记得清理我们创建的向量,避免内存泄漏
  virtual ~myusr() {
    const auto& vec_list = sym_table_.get_vector_list();
    for (const auto& vec_pair : vec_list) {
      delete vec_pair.second;
    }
  }
};

int main(int argc, char* argv[]) {
  if (argc < 2) {
    printf("用法:%s <表达式>\n", argv[0]);
    return 1;
  }

  std::string expr_str = argv[1];

  symbol_table_t sym_table;
  expression_t expr;
  expr.register_symbol_table(sym_table);

  // 初始化我们的自定义解析器,传入符号表引用
  myusr<double> resolver(sym_table);
  parser_t parser;
  // 告诉解析器启用我们的未知符号解析器
  parser.enable_unknown_symbol_resolver(&resolver);

  // 编译表达式
  if (!parser.compile(expr_str, expr)) {
    printf("编译错误:%s\n", parser.error().c_str());
    return 1;
  }

  // 执行表达式并获取结果向量
  vector_t result;
  expr.value(result);

  printf("结果向量大小:%zu\n", result.size());
  for (std::size_t i = 0; i < result.size(); ++i) {
    printf("result[%zu] = %.2f\n", i, result[i]);
  }

  return 0;
}

这里有几个要注意的地方:

  • 自定义的myusr必须持有符号表的引用,不然没法把新创建的向量注册进去
  • 记得在析构函数里清理动态创建的向量,不然会有内存泄漏
  • 你可以根据自己的需求修改process方法里的逻辑,比如只把特定命名规则的符号创建成向量,而不是所有未知符号

比如你测试的时候运行./a.out "return [foobar];",解析器遇到foobar这个未知符号,就会自动创建一个向量,然后表达式会返回这个向量,程序就能正常输出结果啦。

备注:内容来源于stack exchange,提问作者Eric Johnson

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 08:59:38