在Qt中评估数学表达式

时间:2016-02-15 18:28:54

标签: c++ qt exprtk

我试图创建一个Qt应用程序,我需要一个数学表达式评估器来评估这样的事情,例如: (4 + 5)* 2-9 / 3。 我将此库(http://www.partow.net/programming/exprtk/)的.hpp文件包含在Qt Creator中的项目中,并尝试启动以下代码示例:

#include <cstdio>
#include <string>
#include "exprtk.hpp"

int main()
{
   typedef exprtk::expression<double> expression_t;
   typedef exprtk::parser<double>         parser_t;

   std::string expression_string = "3 + sqrt(5) + pow(3,2) + log(5)";

   expression_t expression;

   parser_t parser;

   if (parser.compile(expression_string,expression))
   {
     double result = expression.value();

     printf("Result: %19.15\n",result);
   }
   else
     printf("Error in expression\n.");

   return 0;
}

当我尝试编译并运行它时,我得到以下输出:

 debug\main.o:-1: error: too many sections (62303)

可能是什么问题?

3 个答案:

答案 0 :(得分:5)

只使用纯als_model.recommendProductsForUsers(1000) ,您可以执行以下操作:

Qt

您可以做更多事情,请参阅HEREHERE

答案 1 :(得分:1)

实际上,在我的机器上(Qt 5.5,Ubuntu 16.04和g ++ 5.3),上面的代码不起作用。

尽管答案已经很老了,但我提出了我的解决方案以防有人发现它有用。

QScriptEngine使用JavaScript语法。因此,要使上述代码有效,我必须将语法更改为:

QString expression_string("3 + Math.sqrt(5) + Math.pow(3,2) + Math.log(5)");
QScriptEngine expression;
double my_val=expression.evaluate(expression_string).toNumber();

答案 2 :(得分:1)

按照评论中的要求,这里是如何使用 boost::spirit 实现算术解析器。首先,您需要下载 boost tarball,不要尝试从 GitHub 单独克隆 Spirit,因为它依赖于其他 boost 库。

Boost 是巨大的,因此如果您只需要一个足够用于解析器的子集,您可以使用 bcp 提取它。来自 boost 源目录:

cd tools/build/src/engine
./build.sh
cd ../../../bcp
../build/src/engine/b2
cd ../..
dist/bin/bcp fusion/include hana/functional spirit/home/x3 /some/path

bcp 将复制所有依赖项。您只能保留 /some/path/boost 目录,因为我们需要的所有库都只有标题。

最后,这里是解析器的完整代码。

#include <iostream>
#include <numeric>
#include <stdexcept>
#include <string>
#include <vector>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/hana/functional/fix.hpp>
#include <boost/hana/functional/overload.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>

using namespace boost::spirit;
namespace hana = boost::hana;

// Define AST. The root is `ast::expr`, which is the first left-hand side
// operand and a list of all operations on the right-hand side. Each operand is
// a recursive `variant` that has `ast::expr` inside.
namespace ast
{
    struct nil {};
    struct signed_;
    struct expr;

    struct operand : x3::variant<
        nil
      , double
      , x3::forward_ast<signed_>
      , x3::forward_ast<expr>
      >
    {
        using base_type::base_type;
        using base_type::operator=;
    };

    struct signed_
    {
        char    sign;
        operand operand_;
    };

    struct operation
    {
        char    operator_;
        operand operand_;
    };

    struct expr
    {
        operand                first;
        std::vector<operation> rest;
    };
} // namespace ast

// Give the grammar access to the fields of AST.
BOOST_FUSION_ADAPT_STRUCT(ast::signed_, sign, operand_)
BOOST_FUSION_ADAPT_STRUCT(ast::operation, operator_, operand_)
BOOST_FUSION_ADAPT_STRUCT(ast::expr, first, rest)

// Arithmetic expression grammar definition.
namespace ArithExpr
{
    x3::rule<class expression, ast::expr   > const expression("expression");
    x3::rule<class term,       ast::expr   > const term("term");
    x3::rule<class factor,     ast::operand> const factor("factor");

    auto const expression_def =
        term
     >> *(
             (x3::char_('+') >> term)
           | (x3::char_('-') >> term)
         );
    auto const term_def =
        factor
     >> *(
             (x3::char_('*') >> factor)
           | (x3::char_('/') >> factor)
         );
    auto const factor_def =
        x3::double_
     | '(' >> expression >> ')'
     | (x3::char_('-') >> factor)
     | (x3::char_('+') >> factor);

    BOOST_SPIRIT_DEFINE(expression, term, factor);

    auto calc = expression;
} // namespace ArithExpr

template <typename Iterator>
double CalcArithExpr(Iterator const &first, Iterator last) {
    ast::expr expr;
    // Build AST.
    if (!x3::phrase_parse(first, last, ArithExpr::calc, x3::ascii::space, expr)) {
        throw std::runtime_error("Cannot parse arithmetic expression");
    }

    // Parse the AST and calculate the result.
    // hana::fix allows recursive lambda call
    auto astEval = hana::fix([](auto self, auto expr) -> double {
        // hana::overload calls a lambda corresponding to the type in the variant
        return hana::overload(
            [](ast::nil) -> double {
                BOOST_ASSERT(0);
                return 0;
            },
            [](double x) -> double { return x; },
            [&](ast::signed_ const &x) -> double {
                double rhs = boost::apply_visitor(self, x.operand_);
                switch (x.sign) {
                    case '-': return -rhs;
                    case '+': return +rhs;
                }
                BOOST_ASSERT(0);
                return 0;
            },
            [&](ast::expr const &x) -> double {
                return std::accumulate(
                    x.rest.begin(), x.rest.end(),
                    // evaluate recursively left-hand side
                    boost::apply_visitor(self, x.first),
                    [&](double lhs, const ast::operation &op) -> double {
                        // evaluate recursively right-hand side
                        double rhs = boost::apply_visitor(self, op.operand_);
                        switch (op.operator_) {
                            case '+': return lhs + rhs;
                            case '-': return lhs - rhs;
                            case '*': return lhs * rhs;
                            case '/': return lhs / rhs;
                        }
                        BOOST_ASSERT(0);
                        return 0;
                    }
                );
            }
        )(expr);
    });

    return astEval(expr);
}

int main(int argc, char *argv[]) {
    auto expr = std::string{"-(4.5 + 5e-1) * 2.22 - 9.1 / 3.45"};
    std::cout << CalcArithExpr(expr.begin(), expr.end()) << std::endl;
}

计算-(4.5 + 5e-1) * 2.22 - 9.1 / 3.45并输出-13.7377

更新

以下是如何在 Windows 上构建 bcp 和复制选定标头的说明。虽然,没有任何保证。在 Linux 中一切正常,在 Windows 上它总是跳过一些箍,并且跳跃的方向总是不可预测的。

话虽如此,打开 PowerShell 命令行。那里

Import-Module 'C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\Common7\Tools\Microsoft.VisualStudio.DevShell.dll'
Install-Module VSSetup -Scope CurrentUser
Get-VSSetupInstance

用您的 VS 版本替换上面的 2019。您只需为 PowerShell 执行一次。剩下的就是每次需要构建 bcp 时。上面的 Get-VSSetupInstance 将打印有关您计算机上的 Visual Studio 实例的信息。写下您想使用的 InstanceId。现在切换到 PowerShell 中的 boost 目录,然后:

Enter-VsDevShell InstanceId -DevCmdArguments '-arch=x64' -SkipAutomaticLocation

其中 InstanceId 是您从 Get-VSSetupInstance 获得的 ID。然后从相同的命令提示符

cd tools\build\src\engine
& .\build.bat
cd ..\..\..\bcp
..\build\src\engine\b2 address-model=64
cd ..\..
dist\bin\bcp fusion\include hana\functional spirit\home\x3 X:\some\path\boost