Loại bỏ dấu ngoặc đơn không cần thiết


32

Bạn được cung cấp một chuỗi bao gồm các ký tự 0123456789+*(). Bạn có thể giả sử rằng chuỗi luôn là một biểu thức toán học hợp lệ.

Nhiệm vụ của bạn là loại bỏ các dấu ngoặc đơn không cần thiết, giả sử phép nhân có mức độ ưu tiên cao hơn phép cộng.

Các dấu ngoặc chỉ nên được loại bỏ khi chúng không cần thiết về mặt cấu trúc :

  • vì nhân lên ưu tiên cao hơn: 3+(4*5)=>3+4*5
  • bởi vì phép nhân hoặc phép cộng: 3*(4*5)=>3*4*5
  • khi chúng dư thừa xung quanh một biểu thức: 3*((4+5))=>3*(4+5)

Các dấu ngoặc đơn nên được giữ khi chúng có thể được đơn giản hóa vì các giá trị số cụ thể:

  • 1*(2+3) không nên đơn giản hóa để 1*2+3
  • 0*(1+0) không nên đơn giản hóa để 0*1+0

Ví dụ:

(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
Vui lòng kiểm tra thêm?
Leaky Nun

2
1*(2*(3+4)*5)*6nên là một thử nghiệm thú vị (mà giải pháp của tôi hiện không thành công).
Nữ tu bị rò rỉ

8
Là "không cần thiết" được định nghĩa theo cấu trúc hoặc trên cơ sở từng trường hợp? Nói cách khác, các dấu ngoặc đơn không cần thiết ở đây? (2+2)*1
Luis Mendo

2
@LuisMendo Tôi nghĩ thật công bằng khi diễn giải nó theo bất kỳ cách nào
anatolyg

2
@anatolyg Tôi không nghĩ điều đó là công bằng, bởi vì cách tiếp cận của hai người sẽ rất khác nhau. Sẽ tốt hơn nếu chúng ta có một số làm rõ.
Sp3000

Câu trả lời:


15

Toán học, 105 97 91 byte

-6 byte nhờ vào Roman !

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

Thay thế +*bằng ~~( StringExpression) và **( NonCommutativeMultiply) tương ứng, đánh giá nó, xâu chuỗi nó và thay thế các toán tử trở lại.


Gì? Mathematica không có tích hợp?
Erik the Outgolfer 6/10/2016

@EriktheGolfer Về cơ bản nó có; Tôi đang cố gắng để làm cho nó không đánh giá các nhà khai thác.
LegionMammal978

Đó là lý do tại sao Mathematica được quảng cáo rầm rộ và đắt đỏ như vậy ... bởi vì tôi nghĩ nó được tích hợp sẵn. Nhưng, Mathematica đã không có sự thay đổi so với các ngôn ngữ khác nếu câu đố đủ khó, nhưng "các ngôn ngữ khác" không cạnh tranh ở đây.
Erik the Outgolfer 6/10/2016

91 byte bằng cách sử dụng StringExpressionthay vì Dotvà xóa " "->""mệnh đề:a=StringReplace;ToString@ToExpression@a[#,{"*"->"**","+"->"~~"}]~a~{" ** "->"*","~~"->"+"}&
Roman

@Roman Cảm ơn! Có vẻ như bạn đã tìm thấy một toán tử không đánh giá kết hợp không giao hoán tốt khác mà không kết hợp với các số.
LegionMammal978

7

JavaScript (ES6) 163 178

Chỉnh sửa 15 byte đã lưu 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`)

Ít chơi gôn

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
}

Kiểm tra

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>


Tại sao bạn viết y.indexOf('+')thay vì y.indexOf`+`[...]? ([...] được thêm vào để tránh bị vấp định dạng) Có phải nó đã bị lỗi như vậy không?
Ismael Miguel

1
Ở đây bạn đi, 170 byte: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`)
Ismael Miguel

@IsmaelMiguel thật sự rất thông minh, cảm ơn! Bài học rút ra: khi chuyển sang eval, hãy suy nghĩ lại tất cả một lần nữa
edc65

Tôi rất vui vì bạn thích giải pháp đơn giản của tôi để giảm mã của bạn. Tôi ước tôi có thể làm một cái gì đó về for(b=, =='*'và các bit lặp đi lặp lại khác. Ngoài ra, không ~y.indexOf('+')<0giống như ~y.indexOf('+')? Vì giá trị duy nhất indexOf()trả về đánh giá thành giá trị giả là -1, nên <0có vẻ dư thừa. Hoặc, nếu tôi hiểu sai, bạn có thể làmy.indexOf('+')>1
Ismael Miguel

@IsmaelMiguel 1: vâng, cái <0tào lao còn lại từ phiên bản không có bản quyền và cần được loại bỏ. 2: suy nghĩ lại, forcó thể được sửa đổi để được bao gồm trong phần lặp lại. Cảm ơn một lần nữa
edc65

5

Triển khai Python3 + PEG trong Python , 271 byte

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

Một thời gian trước, tôi đã thực hiện PEG bằng Python . Tôi đoán tôi có thể sử dụng nó ở đây.

Phân tích biểu thức thành một cây và chỉ giữ dấu ngoặc đơn nếu con là phép cộng và cha mẹ là phép nhân.


4

Perl, 132 byte

Nguồn 129 byte + 3 cho -pcờ:

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

Sử dụng:

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

4

Ruby, 140 130 byte

Nguồn 127 byte + 3 cho -pcờ:

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

Và vô lương tâm:

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 $_

câu trả lời rất hay Điều gì đang xảy ra với 0 whilecú pháp?
Giô-na

1
@Jonah Trong Ruby, expr while condtương đương với while cond; expr; end. Ở đây, tôi chỉ muốn thực hiện condnhiều lần và không thực sự có một vòng lặp. Thông thường người ta sẽ viết cái này như while cond; endhoặc có lẽ loop{ break unless cond }nhưng 0 while condít byte hơn. Các 0không làm gì cả; nó chỉ ở đó bởi vì hình thức ngắn của vòng lặp while yêu cầu một cơ thể.
ezrast

2

Võng mạc, 155 byte

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

Hãy thử trực tuyến!

Xác nhận tất cả các testcase cùng một lúc.

Giải trình

Điều chính là mã này:

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

Regex này có thể khớp với bất kỳ chuỗi nào trong đó dấu ngoặc được cân bằng, ví dụ 1+(2+(3))+4hoặc 2+3.

Để dễ giải thích, hãy để regex này được B.

Ngoài ra, hãy để chúng tôi sử dụng <>thay vào đó cho dấu ngoặc, cũng như pmcho \+\*.

Mã trở thành:

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

Hai dòng đầu tiên khớp với dấu ngoặc chỉ bao gồm phép nhân, ví dụ (1*2*3)hoặc chẵn (1*(2+3)*4). Chúng được thay thế bởi nội dung của chúng bên trong.

Hai dòng cuối cùng khớp với dấu ngoặc không được đặt trước và không được theo sau bởi phép nhân. Chúng được thay thế bởi nội dung của chúng bên trong.

Nghĩa ban đầu {`có nghĩa là "thay thế cho đến khi không hoạt động", nghĩa là việc thay thế được thực hiện cho đến khi chúng không còn phù hợp hoặc chúng được thay thế bằng chính chúng.

Trong trường hợp này, việc thay thế được thực hiện cho đến khi chúng không còn phù hợp.


Thất bại cho 1*(2*(3+4)*5)*6.
orlp

@orlp Cảm ơn, đã sửa.
Rò rỉ Nun

(1*(2+3)+4)*5
Thất

@ Sp3000 Cảm ơn, đã sửa.
Leaky Nun

2

Python 3, 274 269 359 337 336 byte

Phương pháp này về cơ bản loại bỏ mọi cặp dấu ngoặc đơn có thể và kiểm tra xem liệu nó có còn đánh giá như nhau không.

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)

Khai thác thử nghiệm

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

Cập nhật

  • -1 [16-10-04] Đã xóa thêm dung lượng
  • -22 [16-05-07] Sử dụng relib
  • +90 [16-05-07] Đã cập nhật để xử lý các trường hợp thử nghiệm mới
  • -5 [16-05-07] Tham số đã xóa khỏi chiều dài ( l) lambda

1
Điều này không thành công trong trường hợp thử nghiệm 1*(2+3), vì OP cho biết không đơn giản hóa cho các trường hợp số đặc biệt. Câu trả lời tốt mặc dù; cái này có upvote của tôi
HyperNeutrino

1
@AlexL. Cảm ơn đã bắt nó! Tôi đã không cập nhật các trường hợp thử nghiệm của mình D: Nhưng hiện tại nó đã được sửa.
Phi tuyến

1

PHP, 358 byte

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

Không phải là một độ dài ấn tượng, đó là những gì tôi nhận được khi thực hiện một cách tiếp cận ít hơn tối ưu (và sử dụng một ngôn ngữ ít hơn tối ưu).

Loại bỏ một cặp dấu ngoặc ra, sau đó tránh biểu thức kết quả. Nếu kết quả giống với bản gốc, nó sẽ thêm nó vào bản đồ các biểu thức hợp lệ và đệ quy cho đến khi không tìm thấy biểu thức mới nào. Sau đó in biểu thức hợp lệ ngắn nhất.

Phá vỡ khi kết quả của biểu thức trở nên lớn và chuyển thành ký hiệu kép / số mũ xuất hiện.


1

Prolog (SWI) , 122 118 byte

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.

Hãy thử trực tuyến!

Xác định một vị từ //2loại bỏ dấu ngoặc từ giá trị chuỗi của đối số thứ nhất và xuất ra một chuỗi thông qua đối số thứ hai. Nếu đầu vào có thể theo thuật ngữ Prolog, thì đây chỉ là 81 77 byte xác định +/2mà không phải xử lý dài dòng term_string/2, nhưng rất nhiều dấu ngoặc đơn không cần thiết sẽ không tồn tại để bắt đầu theo cách đó vì vậy nó sẽ khá gần với gian lận, vì tất cả những gì +/2làm là xử lý sự kết hợp.

Tôi đã cố gắng sử dụng =../2cho tất cả, nhưng nó đã xuất hiện lâu hơn, bởi vì một toán tử ba byte hoạt động với các danh sách không chính xác lắm:

Prolog (SWI) , 124 byte

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.

Hãy thử trực tuyến!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.