使用NCalc获取表达式中的参数

本文关键字:参数 表达式 NCalc 获取 使用 | 更新日期: 2023-09-27 17:58:04

我有一个表达式,我想解析它以获得所有使用参数的列表。

例如:"X+5/(Y-1)"应该会给出以下结果:X,Y

我已经在我的项目中使用了NCalc;那么,可以使用NCalc来获得表达式中使用的参数吗?

根据这个讨论条目(https://ncalc.codeplex.com/discussions/361959)是的,但我不太明白答案。

使用NCalc获取表达式中的参数

根据此处的讨论/答案:http://ncalc.codeplex.com/discussions/360990

我测试过的一个实现(对于您提供的示例表达式)是实现LogicalExpressionVisitor,并让它记录找到的参数:

class ParameterExtractionVisitor : LogicalExpressionVisitor
{
    public HashSet<string> Parameters = new HashSet<string>();
    public override void Visit(NCalc.Domain.Identifier function)
    {
        //Parameter - add to list
        Parameters.Add(function.Name);
    }
    public override void Visit(NCalc.Domain.UnaryExpression expression)
    {
        expression.Expression.Accept(this);
    }
    public override void Visit(NCalc.Domain.BinaryExpression expression)
    {
        //Visit left and right
        expression.LeftExpression.Accept(this);
        expression.RightExpression.Accept(this);
    }
    public override void Visit(NCalc.Domain.TernaryExpression expression)
    {
        //Visit left, right and middle
        expression.LeftExpression.Accept(this);
        expression.RightExpression.Accept(this);
        expression.MiddleExpression.Accept(this);
    }
    public override void Visit(Function function)
    {
        foreach (var expression in function.Expressions)
        {
            expression.Accept(this);
        }
    }
    public override void Visit(LogicalExpression expression)
    {
    }
    public override void Visit(ValueExpression expression)
    {
    }
}

然后你会用它作为:

var expression = NCalc.Expression.Compile("2 * [x] ^ 2 + 5 * [y]", false);
ParameterExtractionVisitor visitor = new ParameterExtractionVisitor();
expression.Accept(visitor);
var extractedParameters = visitor.Parameters;
foreach (var param in extractedParameters)
    Console.WriteLine(param);

这为我输出"x"answers"y"。

注意在ParameterExtractionVisitor中使用HashSet。这是因为,如果表达式多次包含同一变量(例如:"[x] + [x]"),它将被添加两次。如果每次使用相同的变量时都要存储一个条目,请将HashSet替换为List


也就是说,我对NCalc的经验很少,所以我对LogicalExpressionVisitor的重写方法的实现是猜测。当我用expression.Accept(this)覆盖void Visit(ValueExpression expression)方法时,它产生了StackOverflowException。因此,我只是将实现留空,它似乎可以工作。所以我建议你在这里用一粒非常大的盐来回答我的问题。你的里程数可能会有所不同,我不能说这是否适用于所有类型的表达式。

这对我有用。您的里程可能会有所不同。

   public List<string> GetParameters(string expression) {
       List<string> parameters = new List<string>();
       Random random = new Random();
       NCalc.Expression e = new NCalc.Expression(expression);
       e.EvaluateFunction += delegate(string name, NCalc.FunctionArgs args) {
           args.EvaluateParameters();
           args.Result = random.Next(0, 100);
       };
       e.EvaluateParameter += delegate(string name, NCalc.ParameterArgs args) {
           parameters.Add(name);
           args.Result = random.Next(0, 100);
       };
       try {
           e.Evaluate();
           }
       catch {
            }
       return parameters;
    }

参考:https://ncalc.codeplex.com/discussions/79258#editor

这里是我使用的另一种方法:

我构建了一个NCalc扩展方法,允许实时处理参数和函数。

internal static class NCalcExtensions
{
    public static object Evaluate(this Expression exp, EvaluateParameterHandler evaluateParameters = null, EvaluateFunctionHandler evaluateFunctions = null)
    {
        try
        {
            if (evaluateParameters != null)
                exp.EvaluateParameter += evaluateParameters;
            if (evaluateFunctions != null)
                exp.EvaluateFunction += evaluateFunctions;
            return exp.Evaluate();
        }
        finally
        {
            exp.EvaluateParameter -= evaluateParameters;
            exp.EvaluateFunction -= evaluateFunctions;
        }
    }
}

除此之外,我还可以使用它来运行虚拟求值,以获取参数和函数名称。

var paramNames = new List<string>();
var functionNames = new List<string>();
expression.Evaluate(
    new EvaluateParameterHandler((s, a) =>
    {
        paramNames.Add(s);
        a.Result = 1; // dummy value
    }),
    new EvaluateFunctionHandler((s, a) =>
    {
        functionNames.Add(s);
        a.Result = 1; // dummy value
    }));

基于Chris Sinclairs的回答,现在是非常流行的NCalcAsync nuget包。在这种情况下,你可以有这样的东西:

class ParameterExtractionVisitor : LogicalExpressionVisitor
{
    public HashSet<string> Parameters = new();
    public override Task VisitAsync(Identifier function)
    {
        //Parameter - add to list
        Parameters.Add(function.Name);
        return Task.CompletedTask;
    }
    public override async Task VisitAsync(UnaryExpression expression)
    {
        await expression.Expression.AcceptAsync(this);
    }
    public override async Task VisitAsync(BinaryExpression expression)
    {
        //Visit left and right
        await expression.LeftExpression.AcceptAsync(this);
        await expression.RightExpression.AcceptAsync(this);
    }
    public override async Task VisitAsync(TernaryExpression expression)
    {
        //Visit left, right and middle
        await expression.LeftExpression.AcceptAsync(this);
        await expression.RightExpression.AcceptAsync(this);
        await expression.MiddleExpression.AcceptAsync(this);
    }
    public override async Task VisitAsync(Function function)
    {
        foreach (var expression in function.Expressions)
        {
            await expression.AcceptAsync(this);
        }
    }
    public override Task VisitAsync(LogicalExpression expression)
    {
        return Task.CompletedTask;
    }
    public override Task VisitAsync(ValueExpression expression)
    {
        return Task.CompletedTask;
    }
}