Операции заказа


13

Вступление

В детстве наступает момент, когда вы думаете, что освоили сложение и умножение, тогда кто-то приходит и сообщает вам, что:

a * b + c = (a * b) + c! = a * (b + c),

и что это не был простой или линейный процесс, как вас учили ранее. Вы узнаете, что существует нечто, называемое порядком операций . Это очень важный способ сохранения некоторого уровня согласованности и выражений, без использования скобок.

Общая сюжетная линия

Однажды вы просыпаетесь от звука паники на улицах. Экстремистская группировка под названием « 2560 » (сокращение от «Организация против порядка действий», с грубым извращением) использовала свои злые методы, чтобы взять под контроль все ядерное оружие в мире. Они держат в заложниках всю планету, и у них есть простое требование: отменить принятый порядок операций или уничтожить лицо (скобки должны сохранять их приоритет). Новая система называется PSADME (круглые скобки, вычитание / сложение, деление / умножение, экспоненты), а выражения оцениваются справа налево:

a - b - c = a - (b - c) = a + c - b

Проходят дни, и переход идет. В то время как математики и физики все заняты переписыванием своих уравнений, перед специалистами по компьютерам стоит задача изменить способ интерпретации математических выражений компьютерами. Вы принадлежите к секретной группе по программированию мятежников, которая стремится причинить как можно больше мучений новым глобальным повелителям - и, случайно, вас 2560 случайным образом выбирают и поручают создать программу расчета эталонов.

Ваша миссия

Напишите программу (или функцию), которая принимает (числовое) математическое выражение в качестве входных данных, вычисляет выражение с использованием PSADME в качестве порядка операций и выводит результат. Выражения должны оцениваться справа налево, поэтому

1-3+4знак равно1-7знак равно-6.

Для простоты все предоставленные числа будут целыми числами, а вычисления приведут к целочисленным результатам.

Правила и оценки

  • Программа должна принимать ввод длиной до 128 символов - если ваш язык / платформа имеет меньшую максимальную длину ввода, это является приемлемым оправданием.
  • Стандартные лазейки запрещены.
  • Код победителя будет выбран 18 ноября (4 недели с этой даты).
  • Не стесняйтесь размещать код, который не будет считаться достойным игры в гольф. Это о веселье. Если у вас есть интересный способ сделать это, но вы не можете играть в гольф самостоятельно (или по характеру вашего метода), вы можете опубликовать его в любом случае.

Как обычно, выигрышный код - это код с наименьшим количеством байтов, с некоторыми бонусами за развлекательную ценность:

  • -5 для избежания любого использования символов в предоставленном выражении: + , - , ( , ) , ^ , * , /
  • -5 для выполнения расчетов требуется более 5 минут (но не более 10 минут) для расчета на стандартном компьютере, без очевидного метода (с использованием часов или ненужных циклов); Цель состоит в том, чтобы убедить новых повелителей, что вы не пытаетесь нарушить их расчеты.
  • - (5 + N) для прямого оскорбительного сообщения (длиной N, не включая начальные / конечные пробелы) о членах The 2560, которое должно быть написано наглядно в вашем коде, с некоторыми нелепыми объяснениями того, почему это должно быть там. Если он удален, код не должен работать правильно. Да, бесплатные баллы за развлекательную ценность.

Примеры и объяснения

[program] 2 - 2 - 2
2

2 - (2 - 2) = 2

[program] (2 + 2 * 3 + 3) / 3 + 3
4

(4 * 6) / (3 + 3) = 4

[program] 3 + 2 + 1 ^ 3
216

(3 + 2 + 1) ^ 3 = 216

[program] -5^2
25

(-5) ^ 2 = 25

[program] 32 / 8 * 3 - 1
2

32 / (8 * (3 - 1)) = 32/16 = 2


1 - 3 + 4 = 1 - 7? Справа налево это можно предположить, но это добавляет сложение к вычитанию, в отличие от PSADME, нет?
LLlAMnYP

1
@LLlAMnYP Сложение и вычитание находятся в одной и той же «группе», как и в PEMDAS, поэтому они происходят справа налево. То же самое с умножением / делением. Это больше похоже P(SA)(DM)E.
Geobits

2
Оператор не предназначен для обработки справа налево - скорее, операции с равным приоритетом оцениваются справа налево. Таким образом, 4/2 = 2, 2-1 = 1, но a / b c = a / (b c), а не обычный (a / b) * c. Я надеюсь, что это проясняет ситуацию.
Джейк

Вероятно, самый простой способ сделать это - написать грамматику flex / bison или lex / yacc.

5
Вы должны изменить акроним на PADME , поскольку членам такой злой организации наверняка понравится новая трилогия «Звездных войн» больше, чем оригиналы. Это также легче запомнить.
mbomb007

Ответы:


9

Haskell, 134 байта

import qualified Prelude as P
infixl 6 ^
(^)=(P.^)
infixr 8 + 
(+)=(P.+)
infixr 8 - 
(-)=(P.-)
infixr 7 /
(/)=P.div
infixr 7 *
(*)=(P.*)

Переопределение математических операторов с новыми фиксированными значениями и приоритетами. Сейчас:

*Main> 32 / 8 * 3 - 1
2

1
Вау. Просто вау. Это возможно даже на любом другом языке? +1
ETHproductions

Я был совершенно уверен, что это возможно в Mathematica или, по крайней мере, в подобном подходе, но быстро понял, что мне не хватает знаний, чтобы это сделать.
LLlAMnYP

1
Я достаточно новичок здесь, чтобы быть неуверенным относительно того, является ли следующее предложение обычно приемлемым на этом форуме. Он полностью основан на вашем коде, но представляет собой скрипт bash, который использует Perl для генерации файла Haskell и передачи его в GHCi. Поступая так, я сохраняю ВЕСЬ БАЙТ. perl -e'$_="import qualified Prelude as Pl 6^r 8+r 8-r 7*r 7/";s/(. \d(.))/\ninfix\1\n(\2)=(P.\2)/g;s~\./~.div~;print'>a.hs;ghci a.hs К сожалению, из-за опечатки в сгенерированном коде не было пробела между цифрой и символом, но все равно он работал нормально. Это означает, что ваш код может потерять 5 байтов и превосходит мое «улучшение».
Джейк,

@JArkinstall Во всяком случае, мой ответ эффективно используется sedдля генерации и оценки кода оболочки. Вероятно, хороший мета вопрос.
Цифровая травма

Это правда, и мне очень нравится ваш подход - однако использование инструмента (perl или sed) для создания файла на языке, который затем читается на другом языке, кажется еще одним шагом вперед. Я не был бы слишком удивлен, если бы существовал способ создания вышеуказанного кода с помощью другого генератора (хотя этот метод не очевиден для меня!), И мы оказались бы в разборе. Если это допустимо, можно даже применить этот подход к вашему коду (и несколько примеров, которые я видел в некоторых из более читаемых ответов на некоторые вопросы на этой доске).
Джейк

2

GNU sed -r с расширением exec, 398

s@ *\^ *@ ** @
:
s@\((-?[0-9]+)\)@\1@
t
s@(-?[0-9]+ - )+(-?[0-9]+ - -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [-+] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ / )+(-?[0-9]+ / -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ [*/] -?[0-9]+)(.*)@echo '\1'$((\3))'\4'@e
t
s@(-?[0-9]+ \*\* )+(-?[0-9]+ \*\* -?[0-9]+)@\1(\2)@
t
s@(.*(^| |\())(-?[0-9]+ \*\* -?[0-9]+)(.*)@bash -c 'echo \1$[\3]\4'@e
t

Не особенно коротко, но выполняет свою работу.

Sed подходит для анализа приоритета, но не выполняет арифметику. Поэтому мы используем расширение GNU sed exec дляs команды, чтобы передать необходимую арифметику оболочке.

На данный момент предполагаются все операторы, за исключением ^ имеют ровно один пробел впереди и сзади.

Тестовый вывод:

$ cat psadme.txt 
2 - 2 - 2
(2 + 2 * 3 + 3) / 3 + 3
3 + 2 + 1 ^ 3
-5^2
32 / 8 * 3 - 1
$ sed -rf psadme.sed psadme.txt 
2
4
216
25
2
$ 

Хорошая аватарка. xD
Оливер Ни

1

JavaScript (ES6) 287 300

редактироватьИсправлена ​​ошибка (только опечатка, 6 должно было быть 4) - Добавлено полное объяснение в конце фрагмента

Редактировать 2 Обнаружено некоторое улучшение работы над другой проблемой

Еще одно портирование того же парсера с минимальной разницей. (сравните с этим )

f=(x,W=[],Q=['('],z=1,h=p=>'+-*/^^))('.indexOf(p)>>1,C=n=>{for(;h(q=Q.pop())<h(n);W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))a=W.pop(b=W.pop());z&&Q.push(q,n)})=>((x+')').replace(/\d+|\S/g,t=>t>'('?t>'('?~h(t)?z&&t=='-'?z=-z:C(t,z=1):(W.push(z*t),z=0):C(t,z=0):(Q.push(t),z=1)),W.pop())

// More readable
U=(x,W=[],Q=['('],z=1,
  h=p=>'+-*/^^))('.indexOf(p)>>1,
  C=n=>{
    for(;h(q=Q.pop())<h(n);
        W.push(q=='^'?Math.pow(a,b):eval(`a${q}b`)))
      a=W.pop(b=W.pop());
    z&&Q.push(q,n)
  }
)=>(
  (x+')')
  .replace(/\d+|\S/g,t=> 
       t>'('
       ?t>'('
       ?~h(t)
       ?z&&t=='-'?z=-z:C(t,z=1)
       :(W.push(z*t),z=0)
       :C(t,z=0)
       :(Q.push(t),z=1)
  ),
  W.pop()
)

// TEST
console.log=(...x)=>O.innerHTML+=x.join` `+'\n'

console.log(f('1 - 3 + 4')) // -6
console.log(f('2-2-2')) // 2
console.log(f('(2 + 2 * 3 + 3) / 3 + 3')) // 4
console.log(f('3 + 2 + 1 ^ 3')) // 216
console.log(f('-5^2')) // 25
console.log(f('32 / 8 * 3 - 1')) // 2

// Explained
X=(x,W=[],Q=['('],z=1,
  h=p=> // operator priority '+-:1, */:3, ^:5, ):7, (:9. If an operand then -1
     '+-*/^^))('.indexOf(p)>>1,
  C=n=>{ // Evaluate operators present on stack if they have upper priority, then push new operator on stack
    //console.log('Operand '+n)
    while( h(q = Q.pop()) < h(n) ) // pop operator from op stack and compare priority with current
    {
      // Pop operands from stack and push result
      b = W.pop();
      a = W.pop();
      r = q=='^' ? Math.pow(a,b) : eval('a'+q+'b')
      // console.log('Evaluate '+a+q+b+'='+r)
      W.push(r);
    }
    // if z == 0 do nothing, because the current operands are '(' and ')' that must be discarded
    // else Push again the last operator popped and the current one
    z && Q.push(q, n) // 
  }
)=>(
  (x+')')
  .replace(/[\d.]+|\S/g,t=> {
    //console.log('Q:'+Q,'W:'+W,'T:'+t,'U:'+h(t),'Z:'+z), // display status
    if (t == '(') 
    { // open parenthesis
      z = 1
      Q.push(t) // push a operator, its the highest priority
    }
    else if (t == ')')
    { //close parenthesis
      z = 0
      C(t) 
    }
    else if (h(t) < 0)
    { // operand
      W.push(z*t) // push operand with right sign
      z = 0 // set z to 0 to mark that we just pushed an operand, so next '-' (if present) is a binary operator 
    }
    else
    { // operator
      if (z && t=='-') // the minus is an unary operator (the only unary operator allowed is '-', '+' will not work)
        z =-z // change the sign
      else
        z = 1, // no unary minus
        C(t)
    }    
  }),
  W.pop() // return the value at top of operand stack
)
<pre id=O></pre>

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.