从postgresql中的字符串转换方程式

Convert equation from string in postgresql

我正在尝试编写一个接受字符串的查询,其中方程的形式为

x^3 + 0.0046x^2 - 0.159x +1.713

符合预期。该等式用于根据现有值列表计算输出 table 中的新值。因此,我需要将输入的方程式字符串转换为 postgresql 可以处理的方程式,例如

power(data.value,3) + 0.0046 * power(data.value,2) - 0.159 * data.value + 1.713

此任务中的一些令人欣慰的约束是

  1. 方程将始终采用多项式形式,例如总和(A_n * x^n)
  2. 用户将始终使用'x'来表示输入方程中的变量

我一直在将我的查询推送到一个字符串中并在最后执行它,例如

_query TEXT;
SELECT 'select * from ' INTO _query;
SELECT _query || 'product.getlength( ' || min || ',' || max || ')' INTO _query;
RETURN QUERY EXECUTE _query;

因此我知道我只需要以某种方式

  1. 将 'x' 替换为 'data.values'
  2. 找出等式字符串中所有包含数字的地方 紧接在 'x' 之前,并添加一个 '*'
  3. 找到方程字符串中的所有指数运算 (x^n) 和 将它们转换为 power(x,n)

这对很多人来说可能是非常微不足道的事情,不幸的是 postgresql 不是我最好的技能,我已经花费了更多的时间来完成这项工作。非常感谢任何类型的帮助,干杯。

您的上午 9 点到中午的时间范围已经结束,但现在开始了。

多项式的每一项都有4个元素:

  1. Addition/subtraction修饰符
  2. 乘数
  3. 参数,在你的情况下总是x
  4. 功率

问题是这些元素并不总是存在。第一项没有加法元素,尽管它可以有一个减法符号 - 然后通常连接到乘法器。乘数仅在不等于 1 时给出。最后一项中不存在参数,最后两项中也没有幂。

在正则表达式解析中使用可选的捕获组,您可以解决这个问题,而 PostgreSQL 对此有方便的 regexp_matches() function

SELECT * FROM
    regexp_matches('x^3 + 0.0046x^2 - 0.159x +1.713',
                   '\s*([+-]?)\s*([0-9.]*)(x?)\^?([0-9]*)', 'g') AS r (terms);

正则表达式是这样说的:

  • \s*读取0个或多个空格。
  • ([+-]?) 捕获 0 或 1 个加号或减号。
  • \s*读取0个或多个空格。
  • ([0-9.]*) 捕获一个由数字和小数点组成的数字(如果存在)。
  • (x?) 捕获参数x。这是区分最后两个术语所必需的,请参阅下面的查询。
  • \^? 读取电源符号(如果存在)。必须转义,因为 ^ 是约束字符。
  • ([0-9]*) 捕获整数(如果存在)。

g 修饰符对字符串中的每个匹配模式重复此过程。

在你的字符串上,这会以字符串数组的形式产生:

|      terms      |
|-----------------|
| {'','',x,3}     |
| {+,0.0046,x,2}  |
| {-,0.159,x,''}  |
| {+,1.713,'',''} |
| {'','','',''}   |

(我不知道为什么最后一行全是空字符串。也许真正的专家可以解释一下。)

根据这个结果,您可以将查询拼凑起来:

SELECT id, sum(term)
FROM (
  SELECT id, 
         CASE WHEN terms[1] = '-' THEN -1
              WHEN terms[1] = '+' THEN 1
              WHEN terms[3] = 'x' THEN 1  -- If no x then NULL
         END *
         CASE terms[2] WHEN '' THEN 1. ELSE terms[2]::float
         END *
         value ^ CASE WHEN terms[3] = '' THEN 0 -- If no x then 0 (x^0)
                      WHEN terms[4] = '' THEN 1 -- If no power then 1 (x^1)
                      ELSE terms[4]::int
                 END AS term
  FROM data
  JOIN regexp_matches('x^3 + 0.0046x^2 - 0.159x +1.713',
                      '\s*([+-]?)\s*([0-9.]*)(x?)\^?([0-9]*)', 'g') AS r (terms) ON true
  ) sub
GROUP BY id         
ORDER BY id;

SQLFiddle

这假设您有一个要加入的 id 列。如果你只有一个 value 那么你仍然可以这样做,但你应该将上面的查询包装在一个你提供多项式和值的函数中。假定幂是整数,但您可以通过在正则表达式中添加一个点 . 并在 [=31 中添加一个 ::float 而不是 ::int 来轻松地将其转换为实数=] 语句。您还可以通过将另一个捕获组添加到正则表达式和查询中的 case 语句来支持负幂,与乘数项相同;我把这个留给你下周末的 hackfest。

只要保持上述模式,此查询还将处理 "odd" 多项式,例如 -4.3x^3+ 101.2 + 0.0046x^6 - 0.952x^7 +4x