使用 NSPredicate 解析带变量的公式

发布于 2025-01-07 22:52:35 字数 814 浏览 6 评论 0 原文

任务:

我计划解析 NSPredicate 中的公式字符串,并用数值替换字符串中的变量。这些变量是我的数据模型中现有对象实例的属性的名称,例如我有一个类“company”,其实例为“Apple Corp.”。

设置

我的公式如下所示:“Profitability_2011_in% = [Profit 2011] / [Revenue 2011]”

实例“Apple Corp”将具有以下属性:

Revenue 2009 = 10, 2010 年收入 = 20,2011 年收入 = 30, 2009 年利润 = 5,2010 年利润 = 10,2011 年利润 = 20。

因此,该公式将得出 20 / 30 = 67%。

变量通常是二维的,例如由财务报表项目的“利润”和“年份”(例如 2011 年)定义。 变量包含在 [ ] 中,维度由“ ”(空格)分隔。

我会如何做

我的实现将从 NSRegularExpression 的 matchesInString:options:range: 开始,获取公式中所有变量的数组(Profit 2011、 Revenue 2011),然后通过查询我的数据模型,从此数组构造一个 NSDictionary (键 = 变量名)。

你觉得怎么样?

  • 你认为有更好的方法吗?
  • 在公式中,如何用变量的值替换变量?
  • 你会如何解析这个公式?

谢谢你!!

Task:

I am planning to parse a formula string in NSPredicate and to replace variables in the string by their numeric values. The variables are names for properties of existing object instances in my data model, for instance I have a class "company" with an instance "Apple Corp."

Set-up:

My formula would like look like this: "Profitability_2011_in% = [Profit 2011] / [Revenue 2011]"

The instance "Apple Corp" would have the following properties:

Revenue 2009 = 10, Revenue 2010 = 20, Revenue 2011 = 30,
Profit 2009 = 5, Profit 2010 = 10, Profit 2011 = 20.

Hence, the formula would yield 20 / 30 = 67%.

Variables are usually two-dimensional, for instance defined by "profit" as the financial statement item and "year" (for instance 2011).
The variables are enclosed in [ ] and the dimensions are separated by " " (whitespace).

How I would do it

My implementation would begin with NSRegularExpression's matchesInString:options:range: to get an array of all variables in the formula (Profit 2011, Revenue 2011) and then construct an NSDictionary (key = variable name) out of this array by querying my data model.

What do you think?

  • Is there a better way to do it in your view?
  • In the formula, how would you replace the variables by their values?
  • How would you parse the formula?

Thank you!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

酒几许 2025-01-14 22:52:35

是的,你可以这样做。这属于“将 NSPredicate 用于不适合的事情”的类别,但效果很好。

您需要将变量替换为以 $ 开头的单个单词,因为这就是 NSPredicate 表示变量的方式:

NSPredicate *p = [NSPredicate predicateWithFormat:@"foo = $bar"];

但是您想这样做,很好。 NSRegularExpression 是一个很好的方法。

完成此操作后,您将得到如下所示的内容:

@"$profitability2011 = $profit2011 / $revenue2011"

然后您可以通过 +predicateWithFormat: 弹出此内容。您将返回一个 NSComparisonPredicate-leftExpression 的类型为 NSVariableExpressionType,并且 -rightExpression 的类型将为 NSFunctionExpressionType

这就是事情开始变得棘手的地方。如果您要-evarudeWithObject:substitutionVariables:,你只需返回 YESNO 值,因为 谓词只是一个计算结果为 true 或 false 的语句。我还没有探索如何只评估一侧(在本例中为 -rightExpression),但有可能 -[NSExpression expressionValueWithObject:context:] 可能会对您有所帮助。我不知道,因为我不确定“上下文”参数的用途。它看起来不像是一个替换字典,但我可能是错的。

因此,如果这不起作用(我不知道它是否会起作用),您可以使用我的解析器: DDMathParser 。它有一个解析器,类似于 NSPredicate 的解析器,但专门针对解析和评估数学表达式进行了调整。在您的情况下,您会这样做:

#import "DDMathParser.h"

NSString *s = @"$profit2011 / $revenue2011";
NSDictionary *values = ...; // the values of the variables
NSNumber *profitability = [s numberByEvaluatingStringWithSubstitutions:values];

DDMathParser 相当广泛,它可以做很多事情。


编辑 动态变量解析

我刚刚推送了一项更改,允许 DDMathParser 动态解析函数。理解函数与变量的不同非常重要。函数被求值,而变量被简单地替换。但是,此更改仅对函数进行动态解析,而不是对变量进行动态解析。没关系,因为 DDMathParser 有一个叫做无参数函数的巧妙东西。

无参数函数是指后面不跟左括号的函数名称。为了方便起见,它已为您插入。这意味着 @"pi" 被正确解析为 @"pi()" (因为 π 的常量是作为函数实现的)。

在您的情况下,您可以这样做:

无需重新调整字符串来创建变量,只需使用术语名称即可:

@"profit_2011 / revenue_2011";

这将被解析,就像您输入了:

@"divide(profit_2011(), revenue_2011())"

您可以设置您的DDMathEvaluator具有函数解析器的对象。 DDMathParser 存储库中有两个示例:

  1. 此示例展示了如何使用解析器函数在替换字典中查找“缺失”函数(这最像您想要的)
  2. 这个例子向您展示了将任何缺失的函数解释为就像它的计算结果为 42 一样。

一旦实现了解析器函数,您就可以不必将所有变量打包到字典中。

Yes, you can do this. This falls under the category of "Using NSPredicate for things for which it was not intended", but will work just fine.

You'll need to replace your variables with a single word that start with a $, since that's how NSPredicate denotes variables:

NSPredicate *p = [NSPredicate predicateWithFormat:@"foo = $bar"];

However you want to do that, great. NSRegularExpression is a fine way to do that.

Once you do that, you'll have something like this:

@"$profitability2011 = $profit2011 / $revenue2011"

You can then pop this through +predicateWithFormat:. You'll get back an NSComparisonPredicate. The -leftExpression will be of type NSVariableExpressionType, and the -rightExpression will be of type NSFunctionExpressionType.

This is where things start to get hairy. If you were to -evaluteWithObject:substitutionVariables:, you'd simply get back a YES or NO value, since a predicate is simply a statement that evaluates to true or false. I haven't explored how you could just evaluate one side (in this case, the -rightExpression), but it's possible that -[NSExpression expressionValueWithObject:context:] might help you. I don't know, because I'm not sure what that "context" parameter is for. It doesn't seem like it's a substitution dictionary, but I could be wrong.

So if that doesn't work (and I have no idea if it will or not), you could use my parser: DDMathParser. It has a parser, similar to NSPredicate's parser, but is specifically tuned for parsing and evaluating mathematical expressions. In your case, you'd do:

#import "DDMathParser.h"

NSString *s = @"$profit2011 / $revenue2011";
NSDictionary *values = ...; // the values of the variables
NSNumber *profitability = [s numberByEvaluatingStringWithSubstitutions:values];

The documentation for DDMathParser is quite extensive, and it can do quite a bit.


edit Dynamic variable resolution

I just pushed a change that allows DDMathParser to resolve functions dynamically. It's important to understand that a function is different from a variable. A function is evaluated, whereas a variable is simply substituted. However, the change only does dynamic resolution for functions, not variables. That's ok, because DDMathParser has this neat thing called argumentless functions.

An argumentless function is a function name that's not followed by an opening parenthesis. For convenience, it's inserted for you. This means that @"pi" is correctly parsed as @"pi()" (since the constant for π is implemented as a function).

In your case, you can do this:

Instead of regexing your string to make variables, simply use the names of the terms:

@"profit_2011 / revenue_2011";

This will be parsed as if you had entered:

@"divide(profit_2011(), revenue_2011())"

You can the set up your DDMathEvaluator object with a function resolver. There are two examples of this in the DDMathParser repository:

  1. This example shows how to use the resolver function to look up the "missing" function in a substitution dictionary (this would be most like what you want)
  2. This example shows you to interpret any missing function as if it evaluated to 42.

Once you implement a resolver function, you can forego having to package all your variables up into a dictionary.

暗喜 2025-01-14 22:52:35

您认为有更好的方法吗?

- 使用Flex &野牛。

也许您可以使用正则表达式实现您想要的效果 - 但对于许多表达式语法,正则表达式不足以解析语法。此外,像这样的正则表达式会变得很大、不可读且难以处理。

您可以使用 Flex(词法分析器)和 Bison(解析器)为表达式创建语法定义,并生成 C 代码(我相信您知道,它与 Objective-C 完美配合,因为 Objective-C 是 C ) 你可以用它来解析你的表达式。

在公式中,如何用变量的值替换变量?

当您使用 Bison 解析它时,您应该有一个包含变量名称及其当前值的哈希表。生成语法树时,将对变量的引用添加到语法树节点。

您将如何解析该公式?

再次 - Flex &野牛就是专门做这种事情的——而且它们擅长于此。

Is there a better way to do it in your view?

Yes - using Flex & Bison.

Possibly you could achieve what you want with a regex - but for many expression grammars, a regex isn't powerful enough to parse the grammar. Also, regex things like this get large, unreadable, and unyieldy.

You can use Flex (a lexer) and Bison (a parser) to create a grammar definition for your expressions, and generate C code (which, as I'm sure you know, works perfectly with Objective-C since Objective-C is C) which you can use to parse your expressions.

In the formula, how would you replace the variables by their values?

As you parse through it with Bison you should have a hash table with variable names and their current values. When you generate the syntax tree, add references to the variables to your syntax tree nodes.

How would you parse the formula?

Again - Flex & Bison are specifically meant to do this kind of thing - and they excel at it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文