Операції замовлення


13

Вступ

У дитинстві настає момент, коли ти думаєш, що ти освоїв додавання та множення, тоді хтось приходить і повідомляє, що

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

і що це був не такий простий чи лінійний процес, як вас вчили раніше. Ви дізнаєтесь, що існує щось, що називається порядок операцій . Це дуже важливий спосіб збереження певного рівня узгодженості та виразів, не маючи дужок, що перешкоджають усьому.

Родова сюжетна лінія

Одного разу ти прокинешся під звуком паніки на вулицях. Екстремістська група під назвою " 2560 " (скорочення до "Організація проти порядку операцій", з дурним шестигранним поворотом) використовувала свої злі методи, щоб взяти під контроль всю ядерну зброю у світі. Вони тримають всю планету в заручниках, і вони мають простий попит: скасувати прийнятий порядок операцій або стикатися з викоріненням (дужки повинні підтримувати свій пріоритет). Нова система називається PSADME (дужки, віднімання / додавання, ділення / множення, експоненти), а вирази оцінюють справа наліво:

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

Минають дні, і перехід триває. Поки математики та фізики зайняті переписуванням своїх рівнянь, перед вченими-комп’ютерами стоїть завдання змінити моду, в якому математичні вирази інтерпретуються комп'ютерами. Ви належите до таємної повстанської програмуванні групи , яка ставить перед собою за мету заподіяти стільки борошна для нових глобальних повелителів - і, випадково, ви випадково обрана 2560 і поставлена завданням зробити програму розрахунку тесту.

Ваша місія

13+4=17=6.

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

Правила та оцінка

  • Програма повинна приймати введення довжиною до 128 символів - якщо ваша мова / платформа має меншу максимальну довжину введення, це прийнятна привід.
  • Стандартні лазівки заборонені.
  • Виграшний код буде обраний 18 листопада (4 тижні з цієї дати).
  • Не соромтеся розміщувати код, який би не вважався гідним для гольфу. Це про веселощі. Якщо у вас є цікавий спосіб зробити це, але ви не можете його розіграти самостійно (або за характером вашого методу), ви можете все-таки опублікувати його.

Як завжди, виграючий код - це найменша кількість байтів, які мають деякі бонусні цінності:

  • -5 для уникнення будь-якого використання символів у наданому виразі: + , - , ( , ) , ^ , * , /
  • -5 для проведення обчислень потрібно 5 хвилин (але не більше 10 хвилин) для обчислення на стандартному комп'ютері, без того, щоб метод був очевидним (використовуючи годинник або непотрібні петлі); Мета полягає в тому, щоб переконати нових господарів у тому, що ви не намагаєтесь зірвати їх обчислення приреченості.
  • - (5 + N) для прямого образливого повідомлення (довжиною N, не враховуючи провідної / кінцевої пробілів) про членів 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.
Геобіц

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.