19
\$\begingroup\$

A "triplet bracket" (that I made up for this challenge) is one of the following:

(...+...)
[...:...]
{...|...}
<...-...>

A balanced triplet bracket string (BTBS for short) is either an empty string, two BTBSes concatenated, or one of the above triplet brackets with each ... replaced with a BTBS.

Your task is to write a program or function that checks whether a string that consists of only (+)[:]{|}<-> is balanced. Shortest code wins.

Examples

Your program should return truthy for the following strings:

(+)(+)(+)
[[[:]:]:(+(+))]{<->|<(+)->[:]}(+)
<<<<<<<<<<<<->->->->->->->->->->->->
{|(+[:<-{|(+[:<->])}>])}
[[[:]:[:]]:[[:]:[:]]]
{[:](+)|<->{|}}[(+)<->:{|}(+)]

Your program should return falsy for the following strings:

:[
<|>
(+(+)
[:][:](+[[:]):]
{|{|{|(+{|{|{|}}}}}+)}[:]{|}
{{||}}
<<->-<->-<->>
[(+):((+)+)+(+(+))]
<<<<<->->->->->->
\$\endgroup\$
  • \$\begingroup\$ Is (|) valid. I don't think so, but I'm not sure \$\endgroup\$ – Roman Gräf Oct 22 '16 at 12:37
  • \$\begingroup\$ @RomanGräf No, as <|> in the falsy examples. \$\endgroup\$ – jimmy23013 Oct 23 '16 at 7:14
1
\$\begingroup\$

Japt, 25 bytes

!Ue"%(%+%)|%[:]|\{%|}|<->

Test it online!

e on strings is a recursive-replace function. The second parameter defaults to the empty string, which means this recursively removes matches of the Japt regex "%(%+%)|%[:]|\{%|}|<->" (in regular terms, /\(\+\)|\[:]|{\|}|<->/). This returns an empty string for balanced triplet strings and a non-empty string for non-balanced, so the correct return value is the logical NOT of this.

\$\endgroup\$
14
\$\begingroup\$

JavaScript (ES6), 77 58 57 56 bytes

f=s=>s==(s=s.replace(/\(\+\)|\[:]|{\|}|<->/,''))?!s:f(s)

Test cases

f=s=>s==(s=s.replace(/\(\+\)|\[:]|{\|}|<->/,''))?!s:f(s)

console.log("Testing truthy values");
console.log(f("(+)(+)(+)"));
console.log(f("[[[:]:]:(+(+))]{<->|<(+)->[:]}(+)"));
console.log(f("<<<<<<<<<<<<->->->->->->->->->->->->"));
console.log(f("{|(+[:<-{|(+[:<->])}>])}"));
console.log(f("[[[:]:[:]]:[[:]:[:]]]"));
console.log(f("{[:](+)|<->{|}}[(+)<->:{|}(+)]"));

console.log("Testing falsy values");
console.log(f(":["));
console.log(f("<|>"));
console.log(f("(+(+)"));
console.log(f("[:][:](+[[:]):]"));
console.log(f("{|{|{|(+{|{|{|}}}}}+)}[:]{|}"));
console.log(f("{{||}}"));
console.log(f("<<->-<->-<->>"));
console.log(f("[(+):((+)+)+(+(+))]"));
console.log(f("<<<<<->->->->->->"));

\$\endgroup\$
  • 2
    \$\begingroup\$ The same idea in Retina is 26 bytes (the first line just makes it a test suite): retina.tryitonline.net/… \$\endgroup\$ – Martin Ender Oct 22 '16 at 11:17
  • 1
    \$\begingroup\$ And 39 with perl : perl -lpe 's/<->|\(\+\)|{\|}|\[:]//&&redo;$_=!$_'. \$\endgroup\$ – Dada Oct 22 '16 at 11:33
  • \$\begingroup\$ @Dada That will consider 0 a BTBS. Use this 38 instead: perl -pe 's/<->|\(\+\)|{\|}|\[:]//&&redo;$_=/^$/' \$\endgroup\$ – Ton Hospel Oct 22 '16 at 11:45
  • \$\begingroup\$ @TonHospel Didn't thought of that, indeed.. thanks for pointing it out! (feel free to post it if you want by the way, or at least don't hold back because of me) \$\endgroup\$ – Dada Oct 22 '16 at 11:48
  • \$\begingroup\$ That S/s mix was slightly confusing, perhaps f=s=>s==(s=s.replace(...))?!s:f(s)? \$\endgroup\$ – Neil Oct 23 '16 at 10:25
5
\$\begingroup\$

sed, 28 27 bytes

:
s#(+)\|\[:]\|{|}\|<->##
t

sed doesn't have a concept of truthy/falsy, so I'm considering an empty string truthy and a non-empty string falsy. This checks out if we consider the conditional /^$/.

Thanks to @Neil for golfing off 1 byte!

Try it online!

\$\endgroup\$
  • 1
    \$\begingroup\$ For once, BRE is actually an advantage. \$\endgroup\$ – Dennis Oct 22 '16 at 15:23
  • \$\begingroup\$ Do you need the \] or does ] suffice? \$\endgroup\$ – Neil Oct 23 '16 at 10:26
  • \$\begingroup\$ I don't. Thanks! \$\endgroup\$ – Dennis Oct 23 '16 at 12:45
4
\$\begingroup\$

Python, 77 bytes

lambda s:eval("s"+".replace('%s','')"*4%('(+)','[:]','{|}','<->')*len(s))==''

Uses Arnauld's replacement idea. Generates and evaluates a long string like

s.replace('(+)','').replace('[:]','').replace('{|}','').replace('<->','').replace('(+)','').replace('[:]','').replace('{|}','').replace('<->','')

to cycle between replacing all the bracket types. Then, checks if the result is the empty string.

\$\endgroup\$
3
\$\begingroup\$

Mathematica, 55 bytes

StringDelete["(+)"|"[:]"|"{|}"|"<->"]~FixedPoint~#==""&

Anonymous function. Takes a string as input and returns True or False as output. Uses the standard method for doing this.

\$\endgroup\$
2
\$\begingroup\$

Grime, 39 bytes

e`\(_\+_\)|\[_\:_\]|\{_\|_\}|\<_\-_\>v*

Try it online! Sadly, the TIO version runs out of memory on most of the test cases.

Explanation

Nothing too fancy here. _ is shorthand for the entire pattern, and v* is the same as *, but with lower precedence.

e`                                       Match entire input against this pattern:
  \(_\+_\)                               '(', match of _, '+', match of _, ')'
          |                              or
           \[_\:_\]|\{_\|_\}|\<_\-_\>    same for the other bracket types
                                     v*  repeated 0-∞ times
\$\endgroup\$
2
\$\begingroup\$

J, 48 bytes

a:=([:delstring&.>/'(+)';'[:]';'{|}';'<->';])^:_

Similar to the others, this is also based on Arnauld's method.

Usage

   f =: a:=([:delstring&.>/'(+)';'[:]';'{|}';'<->';])^:_
   f '(+)(+)(+)'
1
   f '[[[:]:]:(+(+))]{<->|<(+)->[:]}(+)'
1
   f '<<<<<<<<<<<<->->->->->->->->->->->->'
1
   f '<|>'
0
   f '[:][:](+[[:]):]'
0
   f '{|{|{|(+{|{|{|}}}}}+)}[:]{|}'
0

Explanation

a:=([:delstring&.>/'(+)';'[:]';'{|}';'<->';])^:_  Input: string S
   (                                        )^:_  Repeat until convergence
                                         ]          Get S
                 '(+)';'[:]';'{|}';'<->';           Append to ['(+)', '[:]', '{|}', '<->']
    [:delstring&.>/                                 Reduce right-to-left by deleting
                                                    occurrences of each string on the
                                                    left from the right
                                                    Return the final string as the new S
a:=                                               Test if the final value of S is empty
\$\endgroup\$
0
\$\begingroup\$

Scala, 96 bytes

def&(s:String):Any={val t=s.replaceAll(raw"\(\+\)|\[:]|\{\|}|<->","")
if(t==s)t.size<1 else&(t)}

This is basically the same idea as the other answers, but with some scala boilerplate.

without stealing ideas from others (188 bytes):

import scala.util.parsing.combinator._
val a = new RegexParsers{def r:Parser[_]="("~r~"+"~r~")"|"["~r~":"~r~"]"|"{"~r~"|"~r~"}"|"<"~r~"-"~r~">"|""
def!(s:String)= !parseAll(r,s).isEmpty}!_
\$\endgroup\$
0
\$\begingroup\$

Pip, 26 bytes

L#aaR:"(+)[:]{|}<->"<>3x!a

Try it online!

Loops len(a) times, replacing all occurrences of empty triplet brackets ("(+)[:]{|}<->"<>3, where <> is the "group" operator, => ["(+)"; "[:]"; "{|}"; "<->"]) with empty string (x). This many iterations is overkill, but it will always be more than enough to completely reduce all properly formed triplet brackets to nothing. After the loop completes, outputs !a: 0 if a is truthy (still has some characters), 1 if a is falsey (empty).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.