Видаліть непотрібні дужки


32

Вам дається рядок, складена з символів 0123456789+*(). Можна припустити, що рядок - це завжди дійсний математичний вираз.

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

Дужки повинні бути видалені лише тоді, коли вони структурно не потрібні :

  • через множення вищий пріоритет: 3+(4*5)=>3+4*5
  • через асоціативності множення чи додавання: 3*(4*5)=>3*4*5
  • коли вони надлишкові навколо виразу: 3*((4+5))=>3*(4+5)

Дужки слід зберігати, коли їх можна спростити через певні значення числа:

  • 1*(2+3) не слід спрощувати 1*2+3
  • 0*(1+0) не слід спрощувати 0*1+0

Приклади:

(4*12)+11         ==>    4*12+11
(1+2)*3           ==>    (1+2)*3
3*(4*5)           ==>    3*4*5
((((523))))       ==>    523
(1+1)             ==>    1+1
1*(2*(3+4)*5)*6   ==>    1*2*(3+4)*5*6
1*(2+3)           ==>    1*(2+3)
0*(1+0)           ==>    0*(1+0)


(((2+92+82)*46*70*(24*62)+(94+25))+6)    ==>    (2+92+82)*46*70*24*62+94+25+6

1
Більше тестів, будь ласка?
Лина монашка

2
1*(2*(3+4)*5)*6має бути цікавим тестом (який зараз не вдається моєму рішенню).
Leaky Nun

8
Чи "непотрібне" визначається структурно або в кожному конкретному випадку? Іншими словами, чи тут дужки не потрібні? (2+2)*1
Луїс Мендо

2
@LuisMendo Я думаю, що справедливо інтерпретувати це будь-яким способом
anatolyg

2
@anatolyg Я не думаю, що це було б справедливо, тому що підходи для двох були б дуже різними. Було б добре, якби ми отримали певні роз’яснення.
Sp3000

Відповіді:


15

Математика, 105 97 91 байт

-6 байт завдяки Роману !

a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&

Замінює +і *з ~~( StringExpression) і **( NonCommutativeMultiply) відповідно, оцінює його, впорядковує і замінює оператори назад.


Що? У Mathematica немає вбудованого?
Ерік Аутгольфер

@EriktheGolfer Це в основному так; Я намагаюся зробити так, щоб не оцінювали операторів.
LegionMammal978

Ось чому Mathematica так рекламована і така дорога ... через вбудовані, я думаю. Але, Mathematica не змінила інших мов, якщо головоломка досить жорстка, але "інші мови" тут взагалі не конкурують.
Ерік Аутгольфер

91 байт , використовуючи StringExpressionзамість цього Dotі видаляючи " "->""пункт:a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&
Роман

@ Роман Дякую! Здається, ви знайшли ще одного хорошого асоційованого некомутативного неоціненого оператора, який не поєднується з числами.
LegionMammal978

7

JavaScript (ES6) 163 178

Редагуйте 15 байт збереженого thx @IsmaelMiguel

a=>eval(`s=[]${_=';for(b=0;a!=b;a=b.replace(/'}\\(([^()]*)\\)(?=(.?))/,(x,y,z,p)=>~y.indexOf('+')?-s.push(b[p-1]=='*'|z=='*'?x:y):y))b=a;${_}-\\d+/,x=>s[~x]))b=a`)

Менше гольфу

a=>{
  for(s=[],b='';
      a!=b;
      a=b.replace(/\(([^()]*)\)(?=(.?))/,(x,y,z,p)=>y.indexOf('+')<0?y:-s.push(b[p-1]=='*'|z=='*'?x:y)))
    b=a;
  for(b=0;
      a!=b;
      a=b.replace(/-\d+/,x=>s[~x]))
    b=a;
  return a
}

Тест

f=a=>eval(`s=[]${_=';for(b=0;a!=b;a=b.replace(/'}\\(([^()]*)\\)(?=(.?))/,(x,y,z,p)=>~y.indexOf('+')
?-s.push(b[p-1]=='*'|z=='*'?x:y)
:y))b=a;${_}-\\d+/,x=>s[~x]))b=a`)

console.log=x=>O.textContent+=x+'\n'

test=`(4*12)+11         ==>    4*12+11
(1+2)*3           ==>    (1+2)*3
3*(4*5)           ==>    3*4*5
((((523))))       ==>    523
(1+1)             ==>    1+1
1*(2*(3+4)*5)*6   ==>    1*2*(3+4)*5*6
1*(2+3)           ==>    1*(2+3)
0*(1+0)           ==>    0*(1+0)
(((2+92+82)*46*70*(24*62)+(94+25))+6)    ==>    (2+92+82)*46*70*24*62+94+25+6`

test.split`\n`.forEach(r=>{
  var t,k,x
  [t,,k]=r.match(/\S+/g)
  x=f(t)
  console.log((x==k?'OK ':'KO ')+t+' -> '+x+(x==k?'':' expected '+k))
})
<pre id=O></pre>


Чому ви писали y.indexOf('+')замість y.indexOf`+`[...]? ([...] додано, щоб уникнути відключення форматування).
Ісмаїл Мігель

1
Ось, 170 байт:a=>eval(`for(b=s=[]${_=';a!=b;a=b.replace(/'}\\(([^()]*)\\)(?=(.?))/,(x,y,z,p)=>~y.indexOf('+')<0?-s.push(b[p-1]=='*'|z=='*'?x:y):y))b=a;for(b=0${_}-\\d+/,x=>s[~x]))b=a`)
Ісмаель Мігель

@IsmaelMiguel це дуже розумно, дякую! Урок засвоєний: переходячи до eval, переосмислити все це знову
edc65

Я радий, що вам сподобалось моє просте рішення зменшити ваш код. Я хотів би зробити що - то for(b=, =='*'і інші повторювані біти. Крім того, не ~y.indexOf('+')<0те саме, що ~y.indexOf('+')? Оскільки єдине значення, яке indexOf()повертає, що оцінює фальшиве значення -1, <0здається зайвим. Або, якщо я помилився, ви могли б зробитиy.indexOf('+')>1
Ісмаель Мігель

@IsmaelMiguel 1: так, <0це дерьмо, що залишилося від версії, що не має волі, і його слід видалити. 2: мислення знову, forможна переглядати, щоб бути включеною до повторної частини. Ще раз
дякую

5

Реалізація Python3 + PEG в Python , 271 байт

import peg
e=lambda o,m=0:o.choice and str(o)or(m and o[1][1]and"("+e(o[1])+")"or e(o[1]))if hasattr(o,"choice")else o[1]and e(o[0],1)+"".join(str(x[0])+e(x[1],1)for x in o[1])or e(o[0])
print(e(peg.compile_grammar('e=f("+"f)*f=p("*"p)*p="("e")"/[0-9]+').parse(input())))

Якось назад я зробив реалізацію PEG в Python . Я думаю, я можу це використати тут.

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


4

Perl, 132 байти

129 байт джерело + 3 для -pпрапора:

#!perl -p
0while s!\(([^\(\)]+)\)!$f{++$i}=$1,"_$i"!e;s!_$i!($v=$f{$i})=~/[+]/&&($l.$r)=~/[*]/?"($v)":$v!e
while($l,$i,$r)=/(.?)_(\d+)(.?)/

Використання:

echo "1*(2*(3+4)*5)*6" | perl script.pl

4

Рубін, 140 130 байт

127 байт джерело + 3 для -pпрапора:

t={}
r=?%
0while$_.gsub!(/\(([^()]+)\)/){t[r+=r]=$1;r}
0while$_.gsub!(/%+/){|m|(s=t[m])[?+]&&($'[0]==?*||$`[/\*$/])??(+s+?):s}

І неозорені:

tokens = Hash.new
key = '%'

# convert tokens to token keys in the original string, innermost first
0 while $_.gsub!(/\(([^()]+)\)/) { # find the innermost parenthetical
  key += key # make a unique key for this token
  tokens[key] = $1
  key # replace the parenthetical with the token key in the original string
}

# uncomment to see what's going on here
# require 'pp'
# pp $_
# pp tokens

# convert token keys back to tokens, outermost first
0 while $_.gsub!(/%+/) {|key|
  str = tokens[key]
  if str['+'] and ($'[0]=='*' or $`[/\*$/]) # test if token needs parens
    '(' + str + ')'
  else
    str
  end
}
# -p flag implicity prints $_

дуже приємна відповідь. що відбувається з 0 whileсинтаксисом?
Йона

1
@Jonah In Ruby, expr while condеквівалентно while cond; expr; end. Тут я хочу лише виконувати condкілька разів і насправді не мати корпусу циклу. Зазвичай можна було б написати це як while cond; endможливо, loop{ break unless cond }але 0 while condце менше байтів. 0Нічого не робить; це просто там, тому що коротка форма циклу while вимагає тіла.
ezrast

2

Сітківка, 155 байт

{`\(((\d+|\((((\()|(?<-5>\))|[^()])*(?(5)^))\))(\*(\d+|\((((\()|(?<-10>\))|[^()])*(?(10)^))\)))*)\)
$1
(?<!\*)\((((\()|(?<-3>\))|[^()])*(?(3)^))\)(?!\*)
$1

Спробуйте в Інтернеті!

Перевірте всі тести одразу.

Пояснення

Головне, це код:

(((\()|(?<-3>\))|[^()])*(?(3)^)

Цей регулярний вираз може відповідати будь-якому рядку, в якому дужки врівноважені, наприклад, 1+(2+(3))+4або 2+3.

Для простоти пояснення нехай це буде регулярний вираз B.

Крім того , давайте використовувати <і >замість того, щоб за дужки, а також pі mдля \+і \*.

Код стає:

{`<((\d+|<B>)(m(\d+|<B>))*)>
$1
(?<!m)<B>(?!m)
$1

Перші два рядки відповідають дужкам, які складаються лише з множення, наприклад, (1*2*3)парного (1*(2+3)*4). Вони замінюються своїм вмістом всередині.

Останні два рядки відповідають дужкам, яким не передує і за якими не слідує множення. Вони замінюються своїм вмістом всередині.

Початковий {`засіб "замінити до ідентичного", тобто заміни виконуються до тих пір, поки вони більше не збігаються, або вони не заміняться собою.

У цьому випадку заміни проводяться до тих пір, поки вони більше не збігаються.


Невдачі для 1*(2*(3+4)*5)*6.
orlp

@orlp Спасибі, виправлено.
Leaky Nun

Не (1*(2+3)+4)*5
вдалося

@ Sp3000 Спасибі, виправлено.
Leaky Nun

2

Пітон 3, 274 269 359 337 336 байт

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

from re import *
def f(x):
    *n,=sub('\D','',x);x=sub('\d','9',x);v,i,r,l=eval(x),0,lambda d,a,s:d.replace(s,"?",a).replace(s,"",1).replace("?",s),lambda:len(findall('\(',x))
    while i<l():
        j=0
        while j<l():
            h=r(r(x,i,"("),j,")")
            try:
                if eval(h)==v:i=j=-1;x=h;break
            except:0
            j+=1
        i+=1
    return sub('9','%s',x)%tuple(n)

Тестовий джгут

print(f("(4*12)+11")=="4*12+11")
print(f("(1+2)*3") =="(1+2)*3")
print(f("3*(4*5)")=="3*4*5")
print(f("((((523))))")=="523")
print(f("(1+1)")=="1+1")
print(f("1*(2*(3+4)*5)*6")=="1*2*(3+4)*5*6")
print(f("(((2+92+82)*46*70*(24*62)+(94+25))+6)")=="(2+92+82)*46*70*24*62+94+25+6")
print(f("1*(2+3)")=="1*(2+3)")
print(f("0*(1+0)")=="0*(1+0)")

Оновлення

  • -1 [16-10-04] Видалено додатковий простір
  • -22 [16-05-07] Використовується relib
  • +90 [16-05-07] Оновлено для обробки нових тестових випадків
  • -5 [16-05-07] Видалений параметр із lлямбда довжини ( )

1
Це не вдається для тестового випадку 1*(2+3), оскільки ОП заявило, що не спрощується для спеціальних випадків кількості. Хоча гарна відповідь; це моє підсумки.
HyperNeutrino

1
@AlexL. Дякуємо, що це зробили! Я не оновлював свої тестові справи D: Але це виправлено зараз.
Нелінійний

1

PHP, 358 байт

function a($a){static$c=[];$d=count($c);while($g=strpos($a,')',$g)){$f=$a;$e=0;for($j=$g;$j;--$j){switch($a[$j]){case')':++$e;break;case'(':--$e;if(!$e)break 2;}}$f[$g++]=$f[$j]=' ';if(eval("return $f;")==eval("return $a;"))$c[str_replace(' ', '', $f)]=1;}if(count($c)>$d){foreach($c as$k=>$v){a($k);}}return$c;}$t=a($argv[1]);krsort($t);echo key($t);

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

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

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


1

Prolog (SWI) , 122 118 байт

T+S:-term_string(T,S).
I/O:-X+I,X-Y,Y+O.
E-O:-E=A+(B+C),A+B+C-O;E=A*(B*C),A*B*C-O;E=..[P,A,B],A-X,B-Y,O=..[P,X,Y];E=O.

Спробуйте в Інтернеті!

Визначає предикат, //2який видаляє дужки зі значення першого рядка першого аргументу та виводить рядок через його другий аргумент. Якби введення могло б бути з точки зору Prolog, це буде лише 81 77 байт, що визначає, +/2не маючи справу з багатослівним term_string/2, але багато непотрібних дужок просто не існувало б для початку саме так, це було б досить близько до обману, оскільки все, що +/2робить, - це впоратися з асоціативністю.

Я намагався використовувати =../2для цього все, але це вийшло набагато довше, тому що трибайтовий оператор, який працює зі списками, не зовсім короткий:

Пролог (SWI) , 124 байти

T+S:-term_string(T,S).
I/O:-X+I,X-Y,Y+O.
X-Y:-X=..[O,A,B],(B=..[O,C,D],E=..[O,A,C],F=..[O,E,D],F-Y;A-C,B-D,Y=..[O,C,D]);X=Y.

Спробуйте в Інтернеті!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.