Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Oracle Новый топик    Ответить
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
 "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
Понимаю, что сегодня среда, но в пятницу уезжаю в отпуск, а очень хотелось разместить :)
И думаю до пятницы либо кто-нибудь решит либо я опубликую решение.


Итак, есть табличка expressions, содержашая арифметические выражения в постфискной форме:
SQL> with expressions as
  2  (select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' expr from dual union all
  3  select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' expr from dual union all
  4  select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' expr from dual)
  5  select * from expressions
  6  /
 
        ID EXPR
---------- ----------------------------------------------
         1 15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *
         2 11 12 13 + + -2.1 * 25.6 + 1e+2 +
         3 12.4 4 / 10 * 2 + 11 / 4 / 0.25 +
Лексемы в выражении разделены пробелами.
Числа записаны согласно правилам оракла.
Арифметическими операторами являются: "+", "-", "*", "/".

Почитать что такое постфиксная форма и для чего она нужна можно здесь.

Задача:
получить следующую выборку
        ID EXPR                                               RESULT
---------- ---------------------------------------------- ----------
         1 15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *       13,2
         2 11 12 13 + + -2.1 * 25.6 + 1e+2 +                      50
         3 12.4 4 / 10 * 2 + 11 / 4 / 0.25 +                       1
Столбец RESULT - результат вычисления выражения.

Как обычно решения принимаются на чистом SQL (то есть с использованием только SQL engine).
З.Ы. Ошибки деления на ноль и обработку некорректных выражений, так же как и обработку операторов кроме указанных, я не предусматривал т.к. считаю в это контексте данной задачи излишеством.
З.З.Ы. В столбце результатов у меня ',' т.к. это зависит от NLS настроек :)
23 апр 08, 16:45    [5586573]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
Товарищи, слушаю любые замечания! В том числе и в духе "на чистом SQL это решить нельзя потому что...".
23 апр 08, 18:35    [5587448]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Zloxa
Member

Откуда: СССР ☭
Сообщений: 1033
Модель входит в понятие "чистого"?
23 апр 08, 18:48    [5587497]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Elic
Member

Откуда:
Сообщений: 29991
Первое приближение:
select to_number(expr) as expr
  from
  ( select *
      from (select '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' as expr from dual)
      --from (select '11 12 13 + + -2.1 * 25.6 + 1e+2 +' as expr from dual)
      --from (select '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' as expr from dual)
      model
        dimension by (0 i)
        measures (expr, 0 lvl)
        rules iterate(1000) until (regexp_substr(expr[0], '[^ ]+', 1, ITERATION_NUMBER + 2) is null  )
        (
          expr[ITERATION_NUMBER+1] = regexp_substr(expr[0], '[^ ]+', 1, ITERATION_NUMBER + 1),
          expr[lvl[0]
               + case when expr[ITERATION_NUMBER+1] in ('+','-','*','/') then -1 else +1 end
              ] = case expr[ITERATION_NUMBER+1]
                    when '+' then to_char(expr[lvl[0] - 1] + expr[lvl[0]])
                    when '-' then to_char(expr[lvl[0] - 1] - expr[lvl[0]])
                    when '*' then to_char(expr[lvl[0] - 1] * expr[lvl[0]])
                    when '/' then to_char(expr[lvl[0] - 1] / expr[lvl[0]])
                             else expr[ITERATION_NUMBER+1]
                  end,
          lvl[0] = lvl[0] + case when expr[ITERATION_NUMBER+1] in ('+','-','*','/') then -1 else +1 end,
          lvl[1] = lvl[0]
        )
  )
  where i = 1 and lvl = 1
;

         EXPR
-------------
         13.2

Кобанчег
лушаю любые замечания! В том числе и в духе "на чистом SQL это решить нельзя потому что..."
Попахивает провокацией. Сам решение знаешь?!
23 апр 08, 18:50    [5587506]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15487
Кобанчег
Товарищи, слушаю любые замечания! В том числе и в духе "на чистом SQL это решить нельзя потому что...".

зя.. :)
exec dbms_session.set_nls('NLS_NUMERIC_CHARACTERS','".,"');
set lines 500
with t as (select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' s from dual 
 union all select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' from dual 
 union all select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' from dual
)
  select id ,more ,decode(more,0,to_number(trim(s))) as res ,s0  from t
  model
  return updated rows
  partition by (id)
  dimension by (1 as rn)
  measures (0   as a
           ,0   as b
           ,cast('' as varchar2(1))     as o
           ,' '||lower(s)||' '          as s
           ,s                           as s0
           ,cast('( )([0-9]|[^ ]{2,})( )([0-9]|[^ ]{2,})( )([+*/-])( )' as varchar2(100)) as mask
           ,cast(null as varchar2(100)) as  ss
           ,cast(null as varchar2(100)) as  rr
           ,1                           as  more
  )
  rules
  iterate(2000)
  until ( more[1] = 0 )
  (
    ss[1] = regexp_substr(s[1],mask[1])
   ,a[1] = regexp_replace(ss[1],mask[1],'\2')
   ,b[1] = regexp_replace(ss[1],mask[1],'\4')
   ,o[1] = regexp_replace(ss[1],mask[1],'\6')
   ,rr[1] = case o[1] when '+' then a[1]+b[1]
                      when '-' then a[1]-b[1]
                      when '*' then a[1]*b[1]
                      when '/' then a[1]/b[1]
            end
   ,s[1] = replace(s[1],ss[1],' '||rr[1]||' ')
   ,more[1] = instr(trim(s[1]),' ')
  );
23 апр 08, 18:58    [5587534]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
Zloxa
Модель входит в понятие "чистого"?

Да, я сам решил моделью.

Elic
Попахивает провокацией. Сам решение знаешь?!

Сам решил, иначе бы не публиковал.

Если кому надо - функция для тестирования.
Подсказка №1: Лексемы я извлекал по такой же схеме как в функции.
CREATE OR REPLACE TYPE TT_VARCHAR2 is table of varchar2(32000);
CREATE OR REPLACE TYPE tt_number IS TABLE OF NUMBER;

CREATE OR REPLACE FUNCTION calc(expr IN VARCHAR2) RETURN NUMBER IS
	i        PLS_INTEGER;
	l_stack  tt_number := tt_number();
	l_tokens tt_varchar2 := tt_varchar2();
	PROCEDURE push(p_stack IN OUT tt_number, p_val NUMBER) IS
	BEGIN
		p_stack.EXTEND;
		p_stack(p_stack.LAST) := p_val;
	END;
	FUNCTION pop(p_stack IN OUT tt_number) RETURN VARCHAR2 IS
		l_val NUMBER;
	BEGIN
		IF p_stack.COUNT >= 1 THEN
			l_val := p_stack(p_stack.LAST);
			p_stack.TRIM;
		END IF;
		RETURN l_val;
	END;
BEGIN
	SELECT token BULK COLLECT
	  INTO l_tokens
	  FROM (SELECT substr(s,
			instr(' ' || s, ' ', 1, LEVEL),
			instr(s || ' ', ' ', 1, LEVEL) -
			instr(' ' || s, ' ', 1, LEVEL)) token
			  FROM (SELECT REPLACE(expr, '.', ',') s FROM dual) t
			CONNECT BY LEVEL <= 1 + length(s) - length(REPLACE(s, ' '))
			 ORDER BY LEVEL);

	FOR i IN l_tokens.FIRST .. l_tokens.LAST
	LOOP
		IF l_tokens(i) = '+' THEN
			push(l_stack, pop(l_stack) + pop(l_stack));
		ELSIF l_tokens(i) = '-' THEN
			push(l_stack, -pop(l_stack) + pop(l_stack));
		ELSIF l_tokens(i) = '/' THEN
			push(l_stack, 1 / pop(l_stack) * pop(l_stack));
		ELSIF l_tokens(i) = '*' THEN
			push(l_stack, pop(l_stack) * pop(l_stack));
		ELSE
			push(l_stack, to_number(l_tokens(i)));
		END IF;
	END LOOP;

	RETURN(l_stack(l_stack.last));
END calc;
23 апр 08, 19:05    [5587554]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
orawish

зя.. :)

Да, поздновато подсказку я написал.
Сам решал без regexp.
Ждем еще решений.
23 апр 08, 19:09    [5587566]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15487
Кобанчег
orawish

зя.. :)

Да, поздновато подсказку я написал.
Сам решал без regexp.Ждем еще решений.

А меня - как раз больше всего зацепил (в спортивном смысле :) поиск маски для того регуляруса.
23 апр 08, 19:15    [5587585]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Zloxa
Member

Откуда: СССР ☭
Сообщений: 1033
За вами не успеешь :((
SQL> alter session set NLS_NUMERIC_CHARACTERS = '. ';
 
Session altered
SQL> with expressions as
  2     (select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' expr from dual
  3     union all
  4     select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' expr from dual
  5     union all
  6     select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' expr from dual
  7  )
  8  select id,expr,stack result from (
  9    select * from expressions
 10    model
 11      partition by (id)
 12      dimension by (1 x)
 13      measures (expr,cast(null as varchar2(200)) sexpr, 0 stack, 0 sp)
 14      rules
 15      iterate (1e6) until  (sexpr[1] is null)
 16      (
 17        sexpr[1] = substr(expr[1],instr(' '||expr[1],' ',1,iteration_number+1),instr(expr[1]||' ',' ',1,iteration_number+1) - instr(' '||expr[1],' ',1,iteration_number+1))
 18        ,sp[1] = case when sexpr[1] in ('+','-','*','/') then sp[1]-1 else sp[1]+1 end
 19        ,stack[sp[1]] = case sexpr[1]
 20                        when '+' then stack[sp[1]+1]+stack[sp[1]]
 21                        when '-' then stack[sp[1]]-stack[sp[1]+1]
 22                        when '*' then stack[sp[1]+1]*stack[sp[1]]
 23                        when '/' then stack[sp[1]]/stack[sp[1]+1]
 24                        else to_number(sexpr[1])
 25                       end
 26      )
 27   )
 28  where x = 1;
 
        ID EXPR                                               RESULT
---------- ---------------------------------------------- ----------
         1 15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *       13.2
         2 11 12 13 + + -2.1 * 25.6 + 1e+2 +                      50
         3 12.4 4 / 10 * 2 + 11 / 4 / 0.25 +                       1
 
23 апр 08, 19:54    [5587690]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Elic
Member

Откуда:
Сообщений: 29991
Zloxa
За вами не успеешь
   union all select 4 id, '1 2' expr from dual
   union all select 5 id, '/ 3 4' expr from dual
23 апр 08, 20:28    [5587779]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Zloxa
Member

Откуда: СССР ☭
Сообщений: 1033
Elic
   union all select 4 id, '1 2' expr from dual
   union all select 5 id, '/ 3 4' expr from dual

угу
SQL> alter session set NLS_NUMERIC_CHARACTERS = '. ';
 
Session altered
SQL> with expressions as
  2     (select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' expr from dual
  3     union all
  4     select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' expr from dual
  5     union all
  6     select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' expr from dual
  7     union all select 4 id, '1 2' expr from dual
  8     union all select 5 id, '/ 3 4' expr from dual
  9  )
 10  select id,expr,decode(sp,2,stack,null) result
 11  from (
 12    select * from expressions
 13    model
 14      partition by (id)
 15      dimension by (1 x)
 16      measures (expr,cast(null as varchar2(200)) sexpr, 0 stack, 0 sp)
 17      rules
 18      iterate (100) until  (sexpr[1] is null or sp[1]<=0)
 19      (
 20        sexpr[1] = substr(expr[1],instr(' '||expr[1],' ',1,iteration_number+1),instr(expr[1]||' ',' ',1,iteration_number+1) - instr(' '||expr[1],' ',1,iteration_number+1))
 21        ,sp[1] = case when sexpr[1] in ('+','-','*','/') then sp[1]-1 else sp[1]+1 end
 22        ,stack[sp[1]] = case sexpr[1]
 23                        when '+' then stack[sp[1]+1]+stack[sp[1]]
 24                        when '-' then stack[sp[1]]-stack[sp[1]+1]
 25                        when '*' then stack[sp[1]+1]*stack[sp[1]]
 26                        when '/' then stack[sp[1]]/decode(stack[sp[1]+1],0,null,stack[sp[1]+1])
 27                        else to_number(sexpr[1])
 28                       end
 29      )
 30   )
 31  where x=1;
 
        ID EXPR                                               RESULT
---------- ---------------------------------------------- ----------
         1 15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *       13.2
         2 11 12 13 + + -2.1 * 25.6 + 1e+2 +                      50
         4 1 2                                            
         5 / 3 4                                          
         3 12.4 4 / 10 * 2 + 11 / 4 / 0.25 +                       1
 

хотя...
Кобанчег

З.Ы. Ошибки деления на ноль и обработку некорректных выражений, так же как и обработку операторов кроме указанных, я не предусматривал т.к. считаю в это контексте данной задачи излишеством.
23 апр 08, 22:04    [5588028]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Тифа
Guest
а вот без модели можно интересно как нибудь?
уперся в то что нужна рекурсия

и кстати вопрос первая формула как получается 13,2?
У меня по нормальному вышло такое выражение что дает 14.22, просто ошибку у себя хочется найти
(((15+16)-4)/9)+(((9+((10-3)*6))/3)/50)*33
24 апр 08, 11:50    [5589865]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Elic
Member

Откуда:
Сообщений: 29991
Тифа
(((15+16)-4)/9)+(((9+((10-3)*6))/3)/50)*33
+--------------------------------------------+
|+---------------------------------------+ |
||+----------------------------------+ | |
||| +----------------+| | |
|||+-------------+ |+------------+ || | |
||||+---------+ | || +--------+| || | |
|||||+-----+ | | || |+----+ || || | |

((((((15+16)-4)/9)+((9+((10-3)*6))/3))/50)*33)
24 апр 08, 12:04    [5590007]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15487
Тифа
а вот без модели можно интересно как нибудь?
уперся в то что нужна рекурсия

и кстати вопрос первая формула как получается 13,2?
У меня по нормальному вышло такое выражение что дает 14.22, просто ошибку у себя хочется найти
(((15+16)-4)/9)+(((9+((10-3)*6))/3)/50)*33


(((((15+16)-4)/9)+(9+(10-3)*6)/3)/50)*33
24 апр 08, 12:09    [5590072]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
Сразу замечу, что мой алгоритм разбиения на лексемы использует баг/фичу десятки по поводу connect_by_root, хотя можно было сделать и по схеме типа такой (вместе с substr, instr).

Изначально я решал как все "белые люди" и Zloxa можно сказать опубликовал решение очень похожее на мое, только более усовершенствованое.
with expressions as
(select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' expr from dual union all
select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' expr from dual union all
select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' expr from dual),
tokens AS
(SELECT ID,
       LEVEL lev,
       substr(expr,
              instr(' ' || expr, ' ', 1, LEVEL),
              instr(expr || ' ', ' ', 1, LEVEL) -
              instr(' ' || expr, ' ', 1, LEVEL)) token
  FROM expressions
  CONNECT BY LEVEL <= 1 + length(expr) - length( REPLACE(expr, ' ')) AND ID = connect_by_root ID),
calculate AS
  (SELECT ID, lev, token, res
   FROM 
   (SELECT * FROM tokens
    MODEL
      PARTITION by (ID)
      DIMENSION by (lev)
      MEASURES (token, 0 res, 0 ind)
      RULES ITERATE (2000) UNTIL (token[iteration_number + 2] is NULL)
      (
        ind[1] = CASE
                   WHEN token[iteration_number + 1] in ('+','-','*','/')
                   THEN ind[1]-1 ELSE ind[1]+1
                 END,
        res[ind[1]] = CASE token[iteration_number + 1]
                        WHEN '+' THEN res[ind[1]]+res[ind[1]+1]
                        WHEN '-' THEN res[ind[1]]-res[ind[1]+1]
                        WHEN '*' THEN res[ind[1]]*res[ind[1]+1]
                        WHEN '/' THEN res[ind[1]]/res[ind[1]+1]
                        ELSE to_number(token[iteration_number + 1])
                       END
      )
    )
   ),
results AS
(SELECT ID, MAX(res) KEEP (DENSE_RANK FIRST ORDER BY lev) RESULT FROM calculate GROUP BY ID)
SELECT e.ID, e.expr, r.RESULT 
FROM expressions e, results r
WHERE e.ID = r.ID
Потом ради спортивного интереса я пробовал решить без использования ITERATE и приплел аналитику, древовидные запросы и даже reference model look-ups, но сделать мне этого так и не удалось.
Получился такой монстр:
with expressions as
(select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' expr from dual union all
select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' expr from dual union all
select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' expr from dual),
tokens AS
(SELECT ID, row_number() over (ORDER BY ID, lev) id_token, token FROM
(SELECT ID,
       LEVEL lev,
       substr(expr,
              instr(' ' || expr, ' ', 1, LEVEL),
              instr(expr || ' ', ' ', 1, LEVEL) -
              instr(' ' || expr, ' ', 1, LEVEL)) token
  FROM expressions
  CONNECT BY LEVEL <= 1 + length(expr) - length(REPLACE(expr, ' ')) AND ID = connect_by_root ID)),
calculate AS
(SELECT ID, id_token, token, res
FROM tokens
MODEL
  REFERENCE ids ON
(SELECT gr - 1 id_token, MAX(id_token) - 1 id_token2
  FROM (SELECT gr,
               id_token,
               SUM(sign) over(PARTITION BY gr ORDER BY lev) s
          FROM (SELECT id_token + LEVEL gr,
                       id_token,
                       decode(token, '+', -1, '-', -1, '*', -1, '/', -1, 1) sign,
                       LEVEL lev
                  FROM tokens
                 START WITH id_token IN
                            (SELECT id_token
                               FROM tokens
                              WHERE token IN ('+', '-', '*', '/'))
                CONNECT BY id_token = PRIOR id_token - 1 AND ID = ID)
         WHERE lev <> 1)
 WHERE s = 1
 GROUP BY gr)
  DIMENSION BY (id_token)
  MEASURES (id_token2)
  MAIN calc
  DIMENSION BY (id_token)
  MEASURES (ID, token, 0 res)
  RULES ITERATE(2000) UNTIL token[iteration_number+2] IS NULL
    (res[iteration_number+1] = CASE
     WHEN token[cv()] = '+' THEN res[ids.id_token2[iteration_number+1]] + res[cv()-1]
     WHEN token[cv()] = '-' THEN res[ids.id_token2[iteration_number+1]] - res[cv()-1]
     WHEN token[cv()] = '*' THEN res[ids.id_token2[iteration_number+1]] * res[cv()-1]
     WHEN token[cv()] = '/' THEN res[ids.id_token2[iteration_number+1]] / res[cv()-1]
     ELSE to_number(token[cv()]) END
     )),
results AS
(SELECT ID, MAX(res) KEEP (DENSE_RANK LAST ORDER BY id_token) RESULT FROM calculate GROUP BY ID)
SELECT e.ID, e.expr, r.RESULT 
FROM expressions e, results r
WHERE e.ID = r.ID
Хотя может Reference Model здесь была лишней и без нее получилось бы...
24 апр 08, 12:40    [5590364]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15487
ну, чё..
некст степ - аналогично налабать прямой (не-по-польски:) калькулятор:

+ - * / ( )
24 апр 08, 12:49    [5590444]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
Тифа
а вот без модели можно интересно как нибудь?

Можно для каждого оператора получить айдишники операндов в массиве лексем.
with expressions as
(select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' expr from dual union all
select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' expr from dual union all
select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' expr from dual),
tokens AS
(SELECT ID, row_number() over (ORDER BY ID, lev) id_token, token FROM
(SELECT ID,
       LEVEL lev,
       substr(expr,
              instr(' ' || expr, ' ', 1, LEVEL),
              instr(expr || ' ', ' ', 1, LEVEL) -
              instr(' ' || expr, ' ', 1, LEVEL)) token
  FROM expressions
  CONNECT BY LEVEL <= 1 + length(expr) - length(REPLACE(expr, ' ')) AND ID = connect_by_root ID)),
ids AS
(SELECT gr - 1 id_token, MAX(id_token) - 1 id_token1, gr - 2 id_token2 
  FROM (SELECT gr,
               id_token,
               SUM(sign) over(PARTITION BY gr ORDER BY lev) s
          FROM (SELECT id_token + LEVEL gr,
                       id_token,
                       decode(token, '+', -1, '-', -1, '*', -1, '/', -1, 1) sign,
                       LEVEL lev
                  FROM tokens
                 START WITH id_token IN
                            (SELECT id_token
                               FROM tokens
                              WHERE token IN ('+', '-', '*', '/'))
                CONNECT BY id_token = PRIOR id_token - 1 AND ID = ID)
         WHERE lev <> 1)
 WHERE s = 1
 GROUP BY gr)
SELECT t.ID, t.id_token, t.token, i.id_token1, i.id_token2
FROM ids i, tokens t
WHERE t.ID_token = i.id_token(+)
ORDER BY ID, id_token
Однако поскольку при вычислениях используется результат, вычисленный на предыдущем этапе, то если без модели, если и можно то очень изврещенно, имхо.
24 апр 08, 12:49    [5590448]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
orawish
ну, чё..
некст степ - аналогично налабать прямой (не-по-польски:) калькулятор:

+ - * / ( )


Ну это уже для истинных гурманов
Есть у меня еще пару идеек для пятничных задач. Вернусь из отпуска - сам их дорешаю, оформлю и буду публиковать. :)
24 апр 08, 12:55    [5590525]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Elic
Member

Откуда:
Сообщений: 29991
Кобанчег
Однако поскольку при вычислениях используется результат, вычисленный на предыдущем этапе, то если без модели, если и можно то очень изврещенно, имхо.
Если из этого получить оскобоченное выражение, то его можно "нечестно" динамически вычислить.
24 апр 08, 13:37    [5590850]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Кобанчег
Member

Откуда: Рахів
Сообщений: 841
Кобанчег
пробовал решить без использования ITERATE

таки можно, если без излишеств :)
SQL> with expressions as
  2  (select 1 id, '15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *' expr from dual union all
  3  select 2 id, '11 12 13 + + -2.1 * 25.6 + 1e+2 +' expr from dual union all
  4  select 3 id, '12.4 4 / 10 * 2 + 11 / 4 / 0.25 +' expr from dual),
  5  tokens AS
  6  (SELECT ID, row_number() over (ORDER BY ID, lev) id_token, token FROM
  7  (SELECT ID,
  8         LEVEL lev,
  9         substr(expr,
 10                instr(' ' || expr, ' ', 1, LEVEL),
 11                instr(expr || ' ', ' ', 1, LEVEL) -
 12                instr(' ' || expr, ' ', 1, LEVEL)) token
 13    FROM expressions
 14    CONNECT BY LEVEL <= 1 + length(expr) - length(REPLACE(expr, ' ')) AND ID = connect_by_root ID)),
 15  ids AS
 16  (SELECT gr - 1 id_token, MAX(id_token) - 1 id_token1, gr - 2 id_token2
 17    FROM (SELECT gr,
 18                 id_token,
 19                 SUM(sign) over(PARTITION BY gr ORDER BY lev) s
 20            FROM (SELECT id_token + LEVEL gr,
 21                         id_token,
 22                         decode(token, '+', -1, '-', -1, '*', -1, '/', -1, 1) sign,
 23                         LEVEL lev
 24                    FROM tokens
 25                   START WITH id_token IN
 26                              (SELECT id_token
 27                                 FROM tokens
 28                                WHERE token IN ('+', '-', '*', '/'))
 29                  CONNECT BY id_token = PRIOR id_token - 1 AND ID = ID)
 30           WHERE lev <> 1)
 31   WHERE s = 1
 32   GROUP BY gr),
 33  arr AS
 34  (SELECT t.ID, t.id_token, t.token, i.id_token1, i.id_token2
 35  FROM ids i, tokens t
 36  WHERE t.ID_token = i.id_token(+)
 37  ORDER BY ID, id_token),
 38  calculate AS
 39  (SELECT ID, id_token, res
 40  FROM arr
 41    MODEL
 42    DIMENSION BY (id_token)
 43    MEASURES (id, token, id_token1, 0 res)
 44    RULES
 45      (res[id_token] = CASE
 46       WHEN token[cv()] = '+' THEN res[id_token1[to_number(cv())]] + res[cv()-1]
 47       WHEN token[cv()] = '-' THEN res[id_token1[to_number(cv())]] - res[cv()-1]
 48       WHEN token[cv()] = '*' THEN res[id_token1[to_number(cv())]] * res[cv()-1]
 49       WHEN token[cv()] = '/' THEN res[id_token1[to_number(cv())]] / res[cv()-1]
 50       ELSE to_number(token[cv()]) END
 51       )
 52  )
 53  SELECT e.id, e.expr, MAX(res) KEEP (DENSE_RANK LAST ORDER BY id_token) RESULT
 54  FROM calculate c, expressions e
 55  WHERE c.ID = e.ID
 56  GROUP BY e.ID, e.expr;
 
        ID EXPR                                               RESULT
---------- ---------------------------------------------- ----------
         1 15 16 + 4 - 9 / 9 10 3 - 6 * + 3 / + 50 / 33 *       13,2
         2 11 12 13 + + -2.1 * 25.6 + 1e+2 +                      50
         3 12.4 4 / 10 * 2 + 11 / 4 / 0.25 +                       1
24 апр 08, 15:19    [5591669]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15487
orawish
ну, чё..
некст степ - аналогично налабать прямой (не-по-польски:) калькулятор:

+ - * / ( )


во:

exec dbms_session.set_nls('NLS_NUMERIC_CHARACTERS','".,"');
set lines 500
with t as (
select 1 id, '(((((15+16)-4)/9)+(9+(10-3)*6)/3)/50)*33' s from dual 
 union all select 2 id, '-10*((1+2)*(8+9)/5-5/(4+46)+-.1)' from dual 
 union all select 3 id, '10**2 - 2**2 - 1e2 + 4e-3' s from dual
)
  select id ,more ,decode(more,0,to_number(trim(s))) as res ,s0  
   from t
  model
  partition by (id)
  dimension by (1 as rn)
  measures (0   as a
           ,0   as b
           ,cast('' as varchar2(100))         as o
           ,'('||replace(replace(lower(s),' '),'e','*10**')||')'  as s
           ,s                                 as s0
           ,'([+-]?[0-9.]+)'                  as d
           ,cast(null as varchar2(100))       as s2
           ,cast(null as varchar2(100))       as s3
           ,cast(null as varchar2(100))       as rr
           ,1                                 as more
           ,0 as i
  )
  rules
  iterate(10)
  until ( more[1] = 0 )
  (
    s2[1] = coalesce(regexp_substr(s[1],d[1]||'([*]{2})'||d[1])
                    ,regexp_substr(s[1],'\('||d[1]||'([*/+-])'||d[1]||'\)')
                    ,regexp_substr(s[1],d[1]||'([*/])'||d[1])
                    ,regexp_substr(s[1],d[1]||'([+-])'||d[1])
            )
   ,s3[1] = rtrim(ltrim(s2[1],'('),')')
   ,a[1] = regexp_replace(s3[1],d[1]||'([*/+-]|[*]{2})'||d[1],'\1')
   ,o[1] = regexp_replace(s3[1],d[1]||'([*/+-]|[*]{2})'||d[1],'\2')
   ,b[1] = regexp_replace(s3[1],d[1]||'([*/+-]|[*]{2})'||d[1],'\3')
   ,rr[1] = case o[1] when '+'  then a[1]+b[1]
                      when '-'  then a[1]-b[1]
                      when '*'  then a[1]*b[1]
                      when '/'  then a[1]/b[1]
                      when '**' then power(a[1],b[1])
            end
   ,i[1] = instr(s[1],s2[1])
   ,s[1] = substr(s[1],1,i[1]-1)||to_char(rr[1],'stm9')||substr(s[1],i[1]+length(s2[1]))
   ,more[1] = instr(s[1],'(')
  );

        ID       MORE        RES S0
---------- ---------- ---------- ----------------------------------------
         1          0       13.2 (((((15+16)-4)/9)+(9+(10-3)*6)/3)/50)*33
         3          0      4.004 10**2 - 2**2 - 1e2 + 4e-3
         2          0       -100 -10*((1+2)*(8+9)/5-5/(4+46)+-.1)
25 апр 08, 14:15    [5596892]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15487
чёта энтузиазма не наблюдается..
люди! пятница жеж!

добавил обработку избыточных и/или с унарной операцией внутри скобок
exec dbms_session.set_nls('NLS_NUMERIC_CHARACTERS','".,"');
set lines 500

with t as (select 1 id, '(((((15+16)-4)/9)+(9+(10-3)*6)/3)/50)*33' s from dual
 union all select 2 id, '-10*((1+2)*(8+9)/5-5/(4+46)+-.1)' from dual
 union all select 3 id, '10**2 - 2**2 - (-(--1)) + (((+2*2))+ ((1)) ) - 1e2 + 5e-3' from dual
)
  select id ,more ,decode(more,0,to_number(trim(s))) as res ,s0
    from t
  model
  partition by (id)
  dimension by (1 as rn)
  measures (0  a
           ,0  b
           ,0  i
           ,cast('' as varchar2(100))         as o
           ,'('||replace(replace(lower(s),' '),'e','*10**')||')'  as s
           ,s                                 as s0
           ,'([+-]?[0-9.]+)'                  as d
           ,cast(null as varchar2(100))       as s2
           ,cast(null as varchar2(100))       as s3
           ,cast(null as varchar2(100))       as rr
           ,1                                 as more
  )
  rules
  iterate(1000)
  until ( more[1] = 0 )
  ( s[1] = regexp_replace(
           regexp_replace(s[1],'([+][-])|([-][+])','-')
                              ,'([+][+])|([-][-])','+')
   ,s2[1] = coalesce(regexp_substr(s[1],'\('||d[1]||'\)')
                    ,regexp_substr(s[1],d[1]||'([*]{2})'||d[1])
                    ,regexp_substr(s[1],'\('||d[1]||'([*/+-])'||d[1]||'\)')
                    ,regexp_substr(s[1],d[1]||'([*/])'||d[1])
                    ,regexp_substr(s[1],d[1]||'([+-])'||d[1])
            )
   ,s3[1] = rtrim(ltrim(s2[1],'('),')')
   ,a[1]  = regexp_replace(s3[1],d[1]||'([*/+-]|[*]{2})'||d[1],'\1')
   ,o[1]  = regexp_replace(s3[1],d[1]||'([*/+-]|[*]{2})'||d[1],'\2')
   ,b[1]  = regexp_replace(s3[1],d[1]||'([*/+-]|[*]{2})'||d[1],'\3')
   ,rr[1] = case o[1]
              when '+'  then a[1]+b[1]
              when '-'  then a[1]-b[1]
              when '*'  then a[1]*b[1]
              when '/'  then a[1]/b[1]
              when '**' then power(a[1],b[1])
              else a[1] -- == when a[1]||null then a[1]
            end
   ,i[1] = instr(s[1],s2[1])
   ,s[1] = substr(s[1],1,i[1]-1)||to_char(rr[1],'stm9')||substr(s[1],i[1]+length(s2[1]))
   ,more[1] = instr(s[1],'(')
  )
  order by id;
25 апр 08, 16:16    [5597971]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
test_2008
Member

Откуда: Москва
Сообщений: 1209
Какой смысл решать подобное с моделью ....
С моделью это уже не sql.

Проще уж тогда на PL/SQL написать .....
25 апр 08, 16:21    [5598006]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
Rihard
Member

Откуда: Киев
Сообщений: 366
test_2008
Какой смысл решать подобное с моделью ....
С моделью это уже не sql.

Проще уж тогда на PL/SQL написать .....

Почему не SQL? =)
Всего лишь не ANSI SQL!
25 апр 08, 16:29    [5598055]     Ответить | Цитировать Сообщить модератору
 Re: "Пятничная" задачка - вычисление постфиксного выражения  [new]
orawish
Member

Откуда: Гадюкино-2 (City)
Сообщений: 15487
test_2008
Какой смысл решать подобное с моделью ....
С моделью это уже не sql.

Проще уж тогда на PL/SQL написать .....

1) по пятницам - смысл вторичен ;))))
2) про моделью это уже не sql есть ровно два (противоположных) мнения
3) а что писать-то на pl/sql ? execute immediate ? дык он написан давно..
;)
25 апр 08, 16:30    [5598066]     Ответить | Цитировать Сообщить модератору
Топик располагается на нескольких страницах: [1] 2   вперед  Ctrl      все
Все форумы / Oracle Ответить