Удалить ненужные скобки


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должен быть интересный тестовый сценарий (для которого мое решение в настоящее время не удается).
Утренняя монахиня

8
Определяется ли «ненужное» структурно или индивидуально? Другими словами, здесь скобки не нужны? (2+2)*1
Луис Мендо

2
@LuisMendo Я думаю, что это справедливо в любом случае
Анатолий

2
@anatolyg Я не думаю, что это будет справедливо, потому что подходы к ним будут очень разными. Было бы хорошо, если бы мы получили некоторые разъяснения.
Sp3000

Ответы:


15

Mathematica, 105 97 91 байт

-6 байт благодаря Роману !

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

Заменяет +и *на ~~( StringExpression) и **( NonCommutativeMultiply) соответственно, оценивает, структурирует и заменяет операторы обратно.


Какая? Mathematica не имеет встроенного?
Эрик Outgolfer

@EriktheGolfer Это в основном так; Я пытаюсь заставить его не оценивать операторов.
LegionMammal978

Вот почему Mathematica так рекламируется и стоит так дорого… из-за встроенных модулей, я думаю. Но у Mathematica нет изменений по сравнению с другими языками, если головоломка достаточно сложная, но «другие языки» здесь вообще не конкурируют.
Эрик Outgolfer

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

@Romman Спасибо! Кажется, вы нашли еще один хороший ассоциативный некоммутативный неоцененный оператор, который не объединяется с числами.
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, это действительно умно, спасибо!
Извлеченный

Я рад, что вам понравилось мое простое решение по сокращению кода. Я хотел бы что-то сделать for(b=, =='*'и другие повторяющиеся биты. Кроме того, не так ~y.indexOf('+')<0же, как ~y.indexOf('+')? Так как только значение , которое indexOf()возвращается , которое оценивает falsy значения является -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 В Ruby expr while condэквивалентно while cond; expr; end. Здесь я хочу выполнять только condнесколько раз, и у меня нет тела цикла. Обычно можно написать это как while cond; endили, возможно, loop{ break unless cond }но 0 while condменьше байтов. 0Ничего не делает; это просто потому, что для короткой формы цикла while требуется тело.
Езраст

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 Спасибо, исправлено.
Дрянная Монахиня

Сбой для(1*(2+3)+4)*5
Sp3000

@ Sp3000 Спасибо, исправлено.
Утренняя монахиня

2

Python 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)

Test Harness

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), потому что OP сказал не упрощать для особых случаев чисел. Хороший ответ, хотя; это мой голос.
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

Пролог (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который удаляет скобки из строкового значения первого аргумента и выводит строку через второй аргумент. Если бы ввод мог быть в терминах Пролога, это было бы только 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.