За последние 24 часа нас посетили 18779 программистов и 1705 роботов. Сейчас ищут 694 программиста ...

калькулятор выражения

Тема в разделе "Прочие вопросы по PHP", создана пользователем Negaty, 18 апр 2013.

  1. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    ребята, помогите пожалуйста, надо написать калькулятор выражений типа: 3+4-8/2
    т.е. в input вводиться это выражение, нажимаю на submit и оно считается.
    только есть одно но: не использовать eval
    я новичок, только пытаюсь освоить php и этот калькулятор нужен в учебных целях =)
    спасибо всем, кто ответит. )
     
  2. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    ну раз в учебных целях, то читайте один из методов разбора

    а можете и сами проанализировать задачу и написать простейший парсер выражений.
    вспоминайте про приоритет операций, скобки... и вперед. сложного ничего нет
     
  3. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    а как прописать приоритет операций? и учесть скобки? пока получилась так:
    Код (Text):
    1. $exp=$_POST['expression'];
    2. function convert($exp){  //преобразую из инфиксной нотации в ОПН
    3.     $expArr=preg_split('#([+\-*/\^()]|\d+)#', $exp, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //разбить строку на символы
    4.     $stack0=array(); //стэк
    5.     $exit=array(); // выходная строка
    6.     foreach($expArr as $exp0){
    7.         if( is_numeric($exp0)){
    8.             $exit[]=$exp0;
    9.         } elseif (in_array($exp0, array('*','/','+','-'))) {
    10.             $stack0[]=$exp0;
    11.         }
    12.     }
    13.     $exprArray=array_merge($exit, $stack0);
    14.     $expr=implode(" ", $exprArray);
    15.     return $expr;
    16. }
    17. echo 'RESULT='.convert($exp);
    Добавлено спустя 1 минуту 5 секунд:
    это только перевод из инфиксной записи в польскую обратную нотацию =)
     
  4. smitt

    smitt Старожил

    С нами с:
    3 янв 2012
    Сообщения:
    3.166
    Симпатии:
    65
    Хочешь научиться программировать или сайты на php делать?
    Если программировать бери другой язык, если сайты пиши на php сайт.
     
  5. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    да просто надо сделать этот долбанный калькулятор =( !
     
  6. smitt

    smitt Старожил

    С нами с:
    3 янв 2012
    Сообщения:
    3.166
    Симпатии:
    65
    Препод мучает?

    А обязательно в такой записи 3+4-8/2?

    Добавлено спустя 19 секунд:
    Нельзя допустим задать какой то стандарт, допустим между числами должен быть пробел на пр.
    3 + 4 - 8 / 2 + (2 - 3) думаю так парсить проще будет... эх что то фантазия сегодня у меня разыгралась:)
     
  7. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    так уже сделала =) осталось приоритеты и скобки =(
    Код (Text):
    1. <?php
    2. $exp=$_POST['expression'];
    3. function convert($exp){  //преобразую из инфиксной нотации в ОПН
    4.     $expArr=preg_split('#([+\-*/\^()]|\d+)#', $exp, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //разбить строку на символы
    5.     $stack0=array(); //стэк
    6.     $exit=array(); // выходная строка
    7.     foreach($expArr as $exp0){
    8.         if( is_numeric($exp0)){
    9.             $exit[]=$exp0;
    10.         } elseif (in_array($exp0, array('*','/','+','-','('))) {
    11.             $stack0[]=$exp0;
    12.         }
    13.         if (in_array($exp0, ')')){   // непонятное действие со скобками
    14.             while (array_pop($exp0)!='('){
    15.                 $exit[]=array_pop($exp0);
    16.             }
    17.         }
    18.     }
    19.     $exprArray=array_merge($exit, $stack0);
    20.     $expr=implode(" ", $exprArray);
    21.     return $expr;
    22. }
    23. echo 'RESULT='.convert($exp);
    24.  
    25.  
    26. // switch ($stack0) {   // пытаюсь расставить приоритеты
    27. //     case '+' and '-': return 1; break;
    28. //     case '*' and '/': return 2; break;  
    29. //     case '^': return 3; break;  
    30. // }
    31.  
    32. function calc($str){ //считаю само выражение в ОПН
    33.     $stack = array();
    34.     $token = strtok($str, ' ');
    35.     while ($token !== false){
    36.         if (in_array($token, array('*', '/', '+', '-'))){
    37.             $b = array_pop($stack);
    38.             $a = array_pop($stack);
    39.             switch ($token){
    40.                 case '*': $res = $a*$b; break;
    41.                 case '/': $res = $a/$b; break;
    42.                 case '+': $res = $a+$b; break;
    43.                 case '-': $res = $a-$b; break;
    44.             }
    45.             array_push($stack, $res);
    46.         } elseif (is_numeric($token)){
    47.             array_push($stack, $token);
    48.         }
    49.         $token = strtok(' ');
    50.     }
    51.     return array_pop($stack);
    52. }
    53.    $str=convert($exp);
    54.    echo calc($str);
    55.  ?>
    Добавлено спустя 2 минуты 25 секунд:
    вот в первой функции переводим из нормальной записи в польскую обратную, во второй функции считаем =)
    про счет у меня почти все правильно, осталось только "^" добавить...
    а про перевод ступарюсь с приоритетами и скобками... =( не знаю даже как алгоритм с приоритетами сделать да и чем =(
     
  8. smitt

    smitt Старожил

    С нами с:
    3 янв 2012
    Сообщения:
    3.166
    Симпатии:
    65
    А он точно у тебя работает?
    Я вот запустил и вижу
    Warning: in_array() expects parameter 2 to be array, string given
     
  9. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    не работает из-за того, что начала скобки растовлять....
    если их убрать, то будет работать, только не правильно(
    он из выражения 2+2*3 будет писать 223+*, а не 223*+
    т.е. нет приоритетов у операторов =(
     
  10. smitt

    smitt Старожил

    С нами с:
    3 янв 2012
    Сообщения:
    3.166
    Симпатии:
    65
    Я вернулся :)
    О а ты оказывается девушка, какой у тебя оригинальный ник... думал что парень:)

    Добавлено спустя 33 секунды:
    Мда, лень как то разбирать код, может пойти другим путем если парсить проблема.
    Не совсем эстетично но думаю проблема решиться, зачет поставят
    Попробуй регом

    Добавлено спустя 10 секунд:
    1) вытащить то что в скобках, и посчитать, результат в массив
    2) preg_replace замени скобки на пр на ?
    3) когда найдешь в строке ? по порядку вытаскиваем из массива данные ну и т.д.

    Добавлено спустя 39 секунд:
    может когда время будет предложу что то лучше
     
  11. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    хмм... да просто неахото делать что-то другое, если здесь почти все сделано =)

    Добавлено спустя 2 минуты 16 секунд:
    девушка =) и пока не шарю в php ничего, как оказалось =(
     
  12. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    а какая разница, обратная или не обратная запись-то?
     
  13. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    igordata
    в обратной записи считать машина, под названием компьютер, может, а в нормальной (инфиксной) только evel можно применять... но у меня задача именно без него делать(

    кстати, вот что получилось при расставлении приоритетов... вроде работает, но что-то неправильно. переобозначение prev мне не нравиться=(
    и траблы в возведению в степень и скобками(
    Код (Text):
    1. $exp=$_POST['expression'];
    2. function convert($exp){  //преобразую из инфиксной нотации в ОПН
    3.     $expArr=preg_split('#([+\-*/\^()]|\d+)#', $exp, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); //разбить строку на символы
    4.     $stack0=array(); //стэк
    5.     $exit=array(); // выходная строка
    6.     $prev='';
    7.     foreach($expArr as $exp0){
    8.     //for ($i=0; $i<=count($expArr); $i++)
    9.         if( is_numeric($exp0)){
    10.             $exit[]=$exp0;
    11.         } elseif (in_array($exp0, array('*','/','+','-','('))) {
    12.             if (prior($exp0)==1){
    13.                 $stack0[]=$exp0;
    14.             } elseif (prior($exp0)==2) {
    15.                 if (prior($prev)<2){
    16.                 $stack0[]=$exp0;
    17.                 } else {
    18.                 $exit[]=$prev;
    19.                 array_pop($stack0);
    20.  
    21.                 $stack0[]=$exp0;
    22.                 }              
    23.             } elseif (prior($exp0)==3) {
    24.                 if (prior($prev)<3){
    25.                 $stack0[]=$exp0;
    26.                 } else {
    27.                 $exit[]=$prev;
    28.                 array_pop($stack0);
    29.  
    30.                 $stack0[]=$exp0;
    31.                 }
    32.                         $prev=$exp0;
    33.             }  
    34.         }
    35.         // if (in_array($exp0, ')')){   // непонятное действие со скобками
    36.         //     while (array_pop($exp0)!='('){
    37.         //         $exit[]=array_pop($exp0);
    38.         //     }
    39.         // }
    40.     }
    41.  
    42.     $exprArray=array_merge($exit, array_reverse($stack0));
    43.     $expr=implode(" ", $exprArray);
    44.     return $expr;
    45. }
    46. echo 'RESULT='.convert($exp);
    47.  
    48. function prior($stack0){
    49.     switch ($stack0) {   // пытаюсь расставить приоритеты
    50.         case '(': return 1; break;
    51.         case '+' and '-': return 2; break;
    52.         case '*' and '/': return 3; break;
    53.         case '^': return 4; break;
    54.         // case '^': return 4; break;  
    55.         default: return 0;
    56.     }
    57. }
     
  14. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    Я всетки не понимаю разницы между 2*(3+7) и (7+3)*2
     
  15. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    ну и корень тоже в траблы

    Добавлено спустя 1 минуту 34 секунды:
    igordata
    в этом разницы нет, я про польскую обратную запись) я по ней все делаю... а в твоем примере: от перемены мест слагаемых сумма не меняеться =)
     
  16. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    там не совсем так.
    смысл такой чтобы перевести обычное выражение в выражение когда идет поток данных и операций между ними в порядке приоритета, в котором нужно вычислить это выражение. тогда его можно просто вычислить программно

    Добавлено спустя 2 минуты 32 секунды:
    Negaty

    Код (PHP):
    1. function convertToPolishNotation($s) {
    2.     static $prior = array('^'=>3, '*'=>3, '/'=>3, '+'=>2, '-'=>2, '('=>1);
    3.     $stack = $out = $items = array();
    4.     $pair = array();
    5.     // get operators & operands
    6.     $d=null;
    7.     for($i=0,$l=strlen($s); $i<$l; ++$i) { // scan expression
    8.         if (is_numeric($s{$i}) || $s{$i}=='.')
    9.             $d.=$s{$i};
    10.         else {
    11.             if ($d!=null) {
    12.                 $out[]=$d; // add operand(numeric)
    13.                 $pair[] = $d;
    14.                 $d=null;
    15.             }
    16.             // add operator
    17.             if (sizeof($stack) == 0 || $s{$i} == '(') {
    18.                 $stack[] = $s{$i};
    19.             } elseif ($s{$i} == ')') {
    20.                 for ($j=sizeof($stack)-1; $j>=0; --$j) {
    21.                     if ($stack[$j] != '(')
    22.                         $out[] = array_pop($stack);
    23.                     else{
    24.                         array_pop($stack);
    25.                         break;
    26.                     }
    27.                 }
    28.             } else { // + - * /
    29.                 for ($j = sizeof($stack)-1; $j>=0; --$j) {
    30.                     if ($prior[$stack[$j]] < $prior[$s{$i}])
    31.                         break;
    32.                     $out[] = $stack[$j];
    33.                     unset($stack[$j]);
    34.                 }
    35.                 $stack = array_values($stack);
    36.                 $stack[] = $s{$i};
    37.             }
    38.         }// else
    39.     }
    40.     if ($d!=null)
    41.         $out[]=$d;
    42.     if (sizeof($stack))
    43.         $out = array_merge($out, array_reverse($stack));
    44.     return $out;
    45. } 
     
  17. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    а, понял. со школы этого выражения не слышал.
    но всё равно вопрос, почему обратная? =)
     
  18. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    потому что она противоположна "прямой польской нотации", где операторы идут слева от операндов.
    польская нотация позволяет избавится от скобок в выражении, среди всего прочего.
    а обратная польская запись - позволяет вычислить результат выражения используя более просто алгоритм и вообще ресурсы компа. тоесть проще делать ее разбор программно
     
  19. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    а не всё ли равно с какой стороны они?
     
  20. runcore

    runcore Старожил

    С нами с:
    12 окт 2012
    Сообщения:
    3.625
    Симпатии:
    158
    видимо не все равно.
     
  21. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.819
    Симпатии:
    1.333
    Адрес:
    Лень
  22. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    т.е. ответа на мой вопрос мне тут не найти? =)
     
  23. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    да вы издеваетесь?
    igordata а в чем вопрос-то?
     
  24. igordata

    igordata Суперстар
    Команда форума Модератор

    С нами с:
    18 мар 2010
    Сообщения:
    32.408
    Симпатии:
    1.768
    почему обратная польская, а не прямая польская?
     
  25. Negaty

    Negaty Новичок

    С нами с:
    18 апр 2013
    Сообщения:
    10
    Симпатии:
    0
    Польская нотация (запись), также известна как префиксная нотация (запись), это форма записи логических, арифметических и алгебраических выражений. Характерная черта такой записи — оператор располагается слева от операндов. +34

    Обра́тная по́льская нота́ция (ОПН) — форма записи математических выражений, в которой операнды расположены перед знаками операций. 34+