15
\$\begingroup\$

We call a parens group the open paren (, its matching close paren ) and everything inside them.

A parens group or string is called parenthesly balanced if it contains either nothing or only 2 parenthesly balanced parens groups.

For example:

The string   "(()())()"      is parenthesly balanced
              (    )()       Because it contains exactly 2 parenthesly balanced parens groups
               ()()          The left one is parenthesly balanced because it contains 2 parenthesly balanced parens groups (balanced because they are empty). The right one is parenthesly balanced because it contains nothing.

Likewise:

The string   "(()(()))()"    is not parenthesly balanced
              (      )()     Because it contains a parens group that is not parenthesly balanced: the left one
               ()(  )        The left one is not balanced because it contains a parens group that is not balanced: the right one
                  ()         The right one is not balanced because it only contains one balanced group.

So, a parenthesly balanced string or parens group should either:

  • Contain nothing at all.
  • Or contain only and exactly 2 parenthesly balanced parens groups. It should contain nothing else.

Task:

Your task is to write a function or program that checks if a given string is a parenthesly balanced one or not.

Input:

Input will be a string or list of characters or something similar. You can assume that the string will only consist of the characters '(' and ')'. You can also assume that each open paren ( will have its matching close paren ), so don't worry about strings like "(((" or ")(" or "(())("...

Note: As mentioned by @DigitalTrauma in his comment bellow, it's ok to subtitute the () combo by other characters (such as <>, [], ...), if it's causing additional work like escaping in some languages

Output:

Anything to signal whether the string is parenthesly balanced or not (true or false, 1 or 0, ...). Please include in your answer what your function/program is expected to yield.

Examples:

""                                        => True
"()()"                                    => True
"()(()())"                                => True
"(()(()(()())))(()())"                    => True
"(((((((()())())())())())())())()"        => True
"()"                                      => False
"()()()"                                  => False
"(())()"                                  => False
"()(()(())())"                            => False
"(()())(((((()())()))())())"              => False
"()(()()()())"                            => False
"()(()(()())()())"                        => False

The last two examples really made a difference!

Best of luck!

\$\endgroup\$
6
  • \$\begingroup\$ Anything to signal whether the string is parenthesly balanced or not Is consistent output required, i.e. only two values? \$\endgroup\$
    – Luis Mendo
    Aug 1, 2018 at 20:49
  • \$\begingroup\$ @LuisMendo Could be categories. i.e. truthy values to signal truthiness and falsy values to signal otherwise. So there could be more, but it should be consistent nevertheless. \$\endgroup\$ Aug 1, 2018 at 20:54
  • 1
    \$\begingroup\$ Is it okay if I take a binary list as an input? For instance, "(()())()" would be represented as [0, 0, 1, 0, 1, 1, 0, 1]. This would remove the necessity to convert the input to character code and then subtracting. \$\endgroup\$ Aug 1, 2018 at 23:13
  • \$\begingroup\$ Related question: codegolf.stackexchange.com/questions/166457/… \$\endgroup\$ Aug 2, 2018 at 10:05
  • 1
    \$\begingroup\$ @WindmillCookies I don't see how that's related to this one. Totally different things. Even the concept is different. \$\endgroup\$ Aug 2, 2018 at 12:05

22 Answers 22

8
\$\begingroup\$

Japt v2, 20 bytes

V="()"V¥iU1 eViV²1 V

Test it online!

Everyone misunderstood the challenge at first and though that each pair of parentheses had to contain an even number of sub-pairs, when in fact the challenge actually asks for 0 or 2 sub-pairs. So here's my revised answer, using the same technique as before.

We can still solve the challenge with recursive replacement. The thing is, instead of just removing all occurrences of ()(), we need to make sure that there's nothing else in the same wrapper besides the ()() (in other words, no ()()()() or anything like that). We can do this by recursively replacing (()()) with ().

The new problem is that the input itself does not have one pair of outer parentheses (as that would make it not a parenthesly balanced string), forcing us to wrap it in an extra pair to fully reduce it. Finally, the end result for balanced strings is now () instead of the empty string, so we check for equality rather than just taking logical NOT of the output.

\$\endgroup\$
0
7
\$\begingroup\$

sed 4.2.2, 30

:
s/(()())/()/
t
/^()()$\|^$/q1

Try it online.

This returns a shell exit code of 1 for True and 0 for False.

:               # label
s/(()())/()/    # replace "(()())" with "()"
t               # jump back to label if above replacement matched
/^()()$\|^$/q1  # exit code 1 if remaining buffer is exactly "()()" or empty
                # otherwise exit with code 0
\$\endgroup\$
0
7
\$\begingroup\$

Perl 5 -lp, 24 22 bytes

$_=/^((<(?1)?>){2})?$/

Try it online! Link includes test cases. Edit: Saved 2 bytes thanks to @JoKing. Explanation: Just a recursive regex. The outer capturing group represents a balanced string as a < followed by an optional balanced string followed by a >, twice. Note that most of the other answers are able to use ()s but this costs an extra two bytes:

$_=/^((\((?1)?\)){2})?$/

Try it online! Link includes test cases.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ Since you can use other pairs of brackets, you can save two bytes by using <> \$\endgroup\$
    – Jo King
    Aug 2, 2018 at 9:39
  • 1
    \$\begingroup\$ @JoKing Almost all of the other answers were able to use ()s so I didn't think it was a fair comparison, however I see @ngn's APL answer also uses <>s so I've updated this one. \$\endgroup\$
    – Neil
    Aug 7, 2018 at 10:29
6
\$\begingroup\$

Brachylog, 28 bytes

Ẹ|~c["(",A,")(",B,")"]∧A;B↰ᵐ

Try it online!

Explanation

                                    --  The string perfectly balanced iff
Ẹ                                   --      the string is empty
 |                                  --  or
  ~c["(",A,")(",B,")"]              --      the string can be written id the format of "($A)($B)"
                      ∧             --          where
                       A;B ᵐ        --          both A and B
                          ↰         --          are perfectly balanced
\$\endgroup\$
5
\$\begingroup\$

V, 21, 20 bytes

é(Á)òÓ(“()()…)òø^()$

Try it online! or Verify all test cases!

é(                      " Insert '(' at the beginning of the line
  Á)                    " Append ')' at the end
    ò         ò         " Recursively...
     Ó                  "   Remove...
      (                 "     '('
       “    …           "     (Limit the part that is removed to this section of the match)
        ()()            "     '()()'
             )          "     ')'
                        " (effectively, this replaces '(()())' with '()', but it's one byte shorter than the straightforward approach
               ø        " Count...
                ^()$    "   Lines containing exactly '()' and nothing more

Hexdump:

00000000: e928 c129 f2d3 2893 2829 2829 8529 f2f8  .(.)..(.()().)..
00000010: 5e28 2924                                ^()$
\$\endgroup\$
2
  • \$\begingroup\$ Can you explain your code so I can (hopefully) find a testcase that doesn't work, like I did with @Adàm's answer. \$\endgroup\$ Aug 2, 2018 at 11:27
  • \$\begingroup\$ @ibrahimmahrir Done. \$\endgroup\$
    – DJMcMayhem
    Aug 2, 2018 at 14:49
5
\$\begingroup\$

6502 machine code routine, 48 bytes

A0 00 84 FD A2 00 B1 FB F0 0E C8 C9 29 18 F0 06 8A 48 E6 FD 90 EE B0 0A E0 01
90 06 E0 02 38 D0 01 18 A5 FD F0 09 C6 FD 68 AA E8 B0 F5 90 D7 60

Expects a pointer to a string in $fb/$fc which is expected to only contain ( and ). Clears C (Carry) flag if the string is "paranthesely balanced", sets it otherwise (which is a typical idiom on the 6502, set carry "on error"). Does nothing sensible on invalid input.

Although the algorithm is recursive, it doesn't call itself (which would need more bytes and make the code position dependent) but instead maintains a recursion depth itself and uses "simple" branching.

Commented disassembly

; function to determine a string is "paranthesly balanced"
;
; input:
;   $fb/$fc: address of the string
; output:
;   C flag set if not balanced
; clobbers:
;   $fd:     recursion depth
;   A,X,Y

 .isparbal:
A0 00       LDY #$00            ; string index
84 FD       STY $FD             ; and recursion depth
 .isparbal_r:
A2 00       LDX #$00            ; set counter for parantheses pairs
 .next:
B1 FB       LDA ($FB),Y         ; load next character
F0 0E       BEQ .done           ; end of string -> to final checks
C8          INY                 ; increment string index
C9 29       CMP #$29            ; compare with ')'
18          CLC                 ; and reset carry
F0 06       BEQ .cont           ; if ')' do checks and unwind stack
8A          TXA                 ; save counter ...
48          PHA                 ; ... on stack
E6 FD       INC $FD             ; increment recursion depth
90 EE       BCC .isparbal_r     ; and recurse
 .cont:
B0 0A       BCS .unwind         ; on previous error, unwind directly
 .done:
E0 01       CPX #$01            ; less than one parantheses pair
90 06       BCC .unwind         ; -> ok and unwind
E0 02       CPX #$02            ; test for 2 parantheses pairs
38          SEC                 ; set error flag
D0 01       BNE .unwind         ; if not 2 -> is error and unwind
18          CLC                 ; clear error flag
 .unwind:
A5 FD       LDA $FD             ; check recursion depth
F0 09       BEQ .exit           ; 0 -> we're done
C6 FD       DEC $FD             ; otherwise decrement
68          PLA                 ; get "pair counter" ...
AA          TAX                 ; ... from stack
E8          INX                 ; and increment
B0 F5       BCS .unwind         ; continue unwinding on error
90 D7       BCC .next           ; otherwise continue reading string
 .exit:
60          RTS

Example C64 assembler program using the routine:

Online demo

screenshot

Code in ca65 syntax:

.import isparbal   ; link with routine above

.segment "BHDR" ; BASIC header
                .word   $0801           ; load address
                .word   $080b           ; pointer next BASIC line
                .word   2018            ; line number
                .byte   $9e             ; BASIC token "SYS"
                .byte   "2061",$0,$0,$0 ; 2061 ($080d) and terminating 0 bytes

.bss
linebuf:        .res    256

.data
prompt:         .byte   "> ", $0
truestr:        .byte   "true", $0
falsestr:       .byte   "false", $0

.code
inputloop:
                lda     #<prompt        ; display prompt
                ldy     #>prompt
                jsr     $ab1e

                lda     #<linebuf       ; read string into buffer
                ldy     #>linebuf
                ldx     #0              ; effectively 256
                jsr     readline

                lda     #<linebuf       ; address of string to $fb/fc
                sta     $fb
                lda     #>linebuf
                sta     $fc
                jsr     isparbal        ; call function

                bcs     isfalse
                lda     #<truestr
                ldy     #>truestr
                bne     printresult
isfalse:        lda     #<falsestr
                ldy     #>falsestr
printresult:    jmp     $ab1e           ; output true/false and exit

; read a line of input from keyboard, terminate it with 0
; expects pointer to input buffer in A/Y, buffer length in X
.proc readline
                dex
                stx     $fb
                sta     $fc
                sty     $fd
                ldy     #$0
                sty     $cc             ; enable cursor blinking
                sty     $fe             ; temporary for loop variable
getkey:         jsr     $f142           ; get character from keyboard
                beq     getkey
                sta     $2              ; save to temporary
                and     #$7f
                cmp     #$20            ; check for control character
                bcs     checkout        ; no -> check buffer size
                cmp     #$d             ; was it enter/return?
                beq     prepout         ; -> normal flow
                cmp     #$14            ; was it backspace/delete?
                bne     getkey          ; if not, get next char
                lda     $fe             ; check current index
                beq     getkey          ; zero -> backspace not possible
                bne     prepout         ; skip checking buffer size for bs
checkout:       lda     $fe             ; buffer index
                cmp     $fb             ; check against buffer size
                beq     getkey          ; if it would overflow, loop again
prepout:        sei                     ; no interrupts
                ldy     $d3             ; get current screen column
                lda     ($d1),y         ; and clear 
                and     #$7f            ;   cursor in
                sta     ($d1),y         ;   current row
output:         lda     $2              ; load character
                jsr     $e716           ;   and output
                ldx     $cf             ; check cursor phase
                beq     store           ; invisible -> to store
                ldy     $d3             ; get current screen column
                lda     ($d1),y         ; and show
                ora     #$80            ;   cursor in
                sta     ($d1),y         ;   current row
                lda     $2              ; load character
store:          cli                     ; enable interrupts
                cmp     #$14            ; was it backspace/delete?
                beq     backspace       ; to backspace handling code
                cmp     #$d             ; was it enter/return?
                beq     done            ; then we're done.
                ldy     $fe             ; load buffer index
                sta     ($fc),y         ; store character in buffer
                iny                     ; advance buffer index
                sty     $fe
                bne     getkey          ; not zero -> ok
done:           lda     #$0             ; terminate string in buffer with zero
                ldy     $fe             ; get buffer index
                sta     ($fc),y         ; store terminator in buffer
                sei                     ; no interrupts
                ldy     $d3             ; get current screen column
                lda     ($d1),y         ; and clear 
                and     #$7f            ;   cursor in
                sta     ($d1),y         ;   current row
                inc     $cc             ; disable cursor blinking
                cli                     ; enable interrupts
                rts                     ; return
backspace:      dec     $fe             ; decrement buffer index
                bcs     getkey          ; and get next key
.endproc
\$\endgroup\$
4
\$\begingroup\$

C (gcc), 113 bytes

p(a,r,e,n)char*a;{if(*a-40)return 1;for(r=1,e=0;e<2;r&=e++||*a==40)for(r*=n=p(++a);n+=*a++-40?~0:1;);r=r&&*a-40;}

Try it online!

Explanation

p(a,r,e,n)char*a;{   // function and variable declaration
 if(*a-40)return 1;  // string does not start with '(', thus is empty
 for(r=1,e=0;e<2;    // r: return value, e: group id (look for exactly two groups)
 r&=e++||*a==40)     // after the first group, a second shall follow
  for(r*=n=p(++a);   // check that the group is itself balanced
  n+=*a++-40?~0:1;); // skip group
 r=r&&*a-40;}        // additionally, after those two groups there shall follow none

Try it online!

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

MATL, 26 25 bytes

oo~`tnw52B5LZttnb<]XB10X-

Try it online!

Thanks to @ETHProductions' answer for the "replace (()()) with ()" idea, and @JungHwan Min's question comment for the idea of seeing the brackets as binary digits.

Output is an empty array for truthy, a positive number for falsey - which I think is allowed by OP's comment: "Could be categories. i.e. truthy values to signal truthiness and falsy values to signal otherwise." If it's not, we can add n at the end for +1 byte, to have 0 as truthy output and 1 as falsey output.

With comments:

o         % Convert the parantheses to their ASCII codes
          %  40 for '(', 41 for ')'
o         % Parity - 1 for odd, 0 for even
~         % Not - change 0 to 1 and vice versa, so '(' is now 1 and ')' 0
          % Input char array is now a binary array B
`         % Do-while loop
  tn          % Get the length of the array 
  w           % Bring the array B back on top
  52B         % Push the binary value of 52 on stack
              %  [1 1 0 1 0 0] (equivalent to '(()())')
  5L          % Push the constant [1 0] for '()'
  Zt          % Replace the sequence [1 1 0 1 0 0] in array B
              %  with [1 0]
  tn          % Get the length of the array after replacement 
  b<          % Has it decreased? If so, continue loop
  ]       % end loop
          % Final value for balanced input will be
          %  either [1 0 1 0] for the remaining outer '()()'
          %  or an empty array [] for empty '' input
XB        % Convert the final binary array back to decimal
10X-      % Set difference, remove 10 from that result 
          % Result is [] empty array for balanced input, otherwise 
          %  some decimal number ≠ 10 for unbalanced input
\$\endgroup\$
3
\$\begingroup\$

C# (.NET Core), 78 71 bytes

f=s=>{var r=s.Replace("(()())","()");return r==s?s==""|s=="()()":f(r);}

Try it online!

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

Haskell, 82 59 bytes

all(`elem`[0,2]).foldl(#)[0]
b#'('=0:b
(x:y:z)#_=y+1:z++[x]

Try it online!

I assume it can be golfed much further since it's my first time golfing in haskell, so any tricks or comments are more than welcome.

EDIT - Thanks @nimi for saving 23 bytes (more than 28% of the original submission :)

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Some tips: no need for the () around y+1. As unnamed functions are allowed, you can drop the f=, r[0] is a proper function. Put the base case r b[] at the end and switch to a infix function (say #), then you can use b#_=. You can also change your algorithm slightly by building the list to check for 0s and 2s step by step instead of carrying it around the calls of r in an accumulator r(x:y:z) ... = x : r (...) a with base case r b [] = b. Do the check after the initial call r[0]. All in all 73 bytes. \$\endgroup\$
    – nimi
    Aug 2, 2018 at 14:37
  • \$\begingroup\$ ... Try it online!. \$\endgroup\$
    – nimi
    Aug 2, 2018 at 14:38
  • 1
    \$\begingroup\$ ... or even better: stay with the accumulator and switch to foldl (59 bytes): Try it online!. \$\endgroup\$
    – nimi
    Aug 2, 2018 at 14:46
  • \$\begingroup\$ @nimi Thank you very much, exactly the kind of tips I was looking for :) \$\endgroup\$
    – Vincent
    Aug 2, 2018 at 15:14
3
\$\begingroup\$

JavaScript (ES6), 63 bytes

Takes input as an array of characters. Returns false for parenthesly balanced, true for not parenthesly balanced.

a=>[...a,k=0].some(c=>c<')'?!(a[k]=-~a[k++]):a[k]=~5>>a[k--]&1)

Try it online!

Commented

a =>                     // a[] = input array of characters; we are going to reuse it to
  [                      // store the number of parenthesis groups at each depth
    ...a,                // append all characters
    k = 0                // initialize k = current depth to 0 and append a value that will
  ]                      // be treated as a final closing parenthesis for the root level
  .some(c =>             // for each character c in this array:
    c < ')' ?            //   if c is an opening parenthesis:
      !(                 //     increment the number of groups at the current depth
        a[k] = -~a[k++]  //     increment the depth
      )                  //     yield false
    :                    //   else:
      a[k] = ~5          //     make sure that the current depth contains either 0 or 2
             >> a[k--]   //     groups, by shifting the 1-complement of 5 (101 in binary)
             & 1         //     and testing the least significant bit
                         //     it resets the number of groups to 0 if the bit is not set
                         //     otherwise, it forces some() to return true
                         //     decrement the depth
  )                      // end of some()

Recursive, 54 bytes

Using recursive replacements (like in ETHproductions' Japt answer) is however significantly shorter.

Takes input as a string. Returns 1 for parenthesly balanced, 0 for not parenthesly balanced.

f=s=>s==(s=s.split`(()())`.join`()`)?!s|s=='()()':f(s)

Try it online!


Recursive, 46 bytes

This one throws a recursion error for not parenthesly balanced:

f=s=>!s|s=='()()'||f(s.split`(()())`.join`()`)

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ I'm not that good in JavaScript but could x[k]=-~x[k++] be replaced with x[k]++;k++ or even ++x[k++] ? \$\endgroup\$ Aug 2, 2018 at 10:17
  • 2
    \$\begingroup\$ @АндрейЛомакин No, because x[k] is initially undefined and x[k]++ would give NaN, whereas -~undefined gives 1. \$\endgroup\$
    – Arnauld
    Aug 2, 2018 at 10:20
  • \$\begingroup\$ @АндрейЛомакин I'm now re-using the input array, so a[k] initially contains a character. But the same logic applies to strings: applying the ++ operator on them yields NaN, but bitwise operators (such as ~) force them to be coerced to 0 beforehand. \$\endgroup\$
    – Arnauld
    Aug 2, 2018 at 11:33
  • \$\begingroup\$ Takes javascript to a whole new level. :D \$\endgroup\$ Aug 2, 2018 at 11:49
3
\$\begingroup\$

Perl 6,  43 41  37 bytes

{my rule f{\([<&f>**2]?\)};?/^<&f>**2$|^$/}

Test it

{(my$f)=/\([<$f>**2]?\)/;?/^[<$f>**2]?$/}

Test it

{$!=/\([<$!>**2]?\)/;?/^[<$!>**2]?$/}

Test it

Expanded:

{  # bare block lambda with implicit parameter $_

  $! = # store regex into $! (no need to declare it)
  /
    \(

      [
        <$!> ** 2 # recurse into regex twice
      ]?          # optionally

    \)
  /;


  ?      # boolify (causes it to be run against $_)

    /
      ^         # beginning of string

      <$!> ** 2 # match using regex twice

      $         # end of string

    |           # or

      ^ $       # empty string
    /
}
\$\endgroup\$
3
\$\begingroup\$

R, 71 bytes

f=function(s,r=sub('(()())','()',s,f=T))'if'(r==s,s==''|s=='()()',f(r))

Try it online!

  • porting of recursive Japt solution of @ETHproductions
  • -2 bytes thanks to @JayCe

Another - longer - solution but interesting for the different approach

R, 85 bytes

g=gsub;!sum(eval(parse(t=g('\\)\\(',')-(',g('\\)','-1)',g('\\(','(2+',scan(,'')))))))

Try it online!

Explanation :

Take the input string and replaces :

'('  with '(2+'
')'  with '-1)'
')(' with ')-('

then evaluates the resulting expression. If it's equal to zero is balanced, otherwise is not. The use of sum is only necessary to handle the empty string case, because its evaluation returns NULL.

e.g.

()(()()) => (2+-1)-(2+(2+-1)-(2+-1)-1) = 0
(()())   => (2+(2+-1)-(2+-1)-1)        = 1
\$\endgroup\$
4
  • \$\begingroup\$ Save two bytes: f=function(s,r=sub('(()())','()',s,f=T))'if'(r==s,s==''|s=='()()',f(r)) \$\endgroup\$
    – JayCe
    Aug 8, 2018 at 2:53
  • \$\begingroup\$ You should put the shorter solution first \$\endgroup\$
    – ASCII-only
    Aug 8, 2018 at 6:09
  • \$\begingroup\$ @ASCII-only: you're right, but since it's basically a porting of another solution it seemed like "stealing" :P \$\endgroup\$
    – digEmAll
    Aug 8, 2018 at 6:36
  • 3
    \$\begingroup\$ @digEmAll Well, in a lot of challenges here most of the challenges do just port another solution \$\endgroup\$
    – ASCII-only
    Aug 8, 2018 at 6:49
2
\$\begingroup\$

Wolfram Language (Mathematica), 65 bytes

#=={}||{1,1}==(#//.{a___,"(",i:1...,")",b___}/;1!=+i<3:>{a,1,b})&

Try it online!

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

05AB1E, 18 16 13 bytes

…(ÿ)…(()∞„()©:®Q

Port of @ETHproductions's Japt answer to fix the test case ()(()()(()())(()())).
-2 bytes thanks to @Adnan.

Based on this comment of OP I now use () as truthy value, and anything else as falsey. If both values need to be consistent instead of just one, it would be the old 16-bytes answer instead (…(ÿ)…(()∞„()©:®Q), returning 0 for truthy and 1 for falsey test cases.

Try it online or verify all test cases.

Explanation

…(ÿ)             # Take the input (implicitly) and surround it with "(" and ")"
            :    # Infinite replacement of:
    …(()∞        #  "(()())"    ("(()" mirrored)
         „()     #  with "()"
                 # After the infinite replacement: return the result
                 # ("()" for truthy; falsey otherwise)

(Old 18-bytes answer which failed for test case ()(()()(()())(()()))..):

ΔD„()∞©6∍å_i®õ.:]Ā

Try it online or verify all test cases.

\$\endgroup\$
5
  • \$\begingroup\$ I think you can use the infinite replace method: „()∞õ:g_. \$\endgroup\$
    – Adnan
    Aug 2, 2018 at 8:35
  • \$\begingroup\$ no wait I misunderstood the challenge \$\endgroup\$
    – Adnan
    Aug 2, 2018 at 8:36
  • \$\begingroup\$ @Adnan I thought so at first as well, but it fails for test cases containing (()()()()) which should return falsey. Every parenthesis group should contain exactly 0 or 2 inner groups. \$\endgroup\$ Aug 2, 2018 at 8:36
  • 1
    \$\begingroup\$ You can replace '(®')J with …(ÿ). \$\endgroup\$
    – Adnan
    Aug 2, 2018 at 11:58
  • \$\begingroup\$ @Adnan Thanks! I knew ÿ existed, but never used it before, so completely forgot about it. \$\endgroup\$ Aug 2, 2018 at 12:02
2
\$\begingroup\$

APL (Dyalog Classic), 27 bytes

3>≢'<<><>>'⎕r'<>'⍣≡1⌽'><',⎕

Try it online!

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

Prolog, 46 bytes

a-->p,p.
a-->[].
p-->[l],a,[r].
f(X):-a(X,[]).

Try it online! or Verify all test cases!

Uses lists of l and r as input, e.g. "()()" is tested as f([l,r,l,r])..

The first three lines are the grammar of valid strings in Prolog's Definite Clause Grammar syntax. a(A,B). returns true when A is a list which follows the grammar and B is empty. Thus the main function f takes some X and checks whether a(X,[]) holds.

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

Python 2, 73 71 bytes

f=lambda s,P='(()())':P in s and f(s.replace(P,'()'))or s in['()()','']

Try it online!

\$\endgroup\$
1
\$\begingroup\$

brainfuck, 50 bytes

,[<+>[-[->>]<[-[--[>->,]]>>]<]<[>>],]<[--[>->]]<+.

Formatted:

,
[
  <+>
  [
    -[->>]
    <
    [
      -
      [
        --
        [
          >->,
        ]
      ]
      >>
    ]
    <
  ]
  <[>>]
  ,
]
<
[
  --
  [
    >->
  ]
]
<+.

Expects a string of ( and ) without a trailing newline, and outputs \x01 for true and \x00 for false. (For legibility, you can e.g. add 48 +s before the final . to make it print 1 and 0 instead.)

Try it online

This maintains a stack with the number of groups at each depth, distinguishing characters by parity and checking whether the number of groups is in {0, 2} after each close parenthesis; if the condition is not met, consumes the rest of the input and sets a flag; then checks the condition again at the end of the program.

If we are allowed to terminate the input stream with an odd character, we can omit the final check <[--[>->]] to save 10 bytes. (If \n weren't inconveniently even, I might have proposed this variant as the main answer.)

(We could also save some bytes by changing the output format to \x00 for true and non-\x00 for false, which seems to be allowed (maybe accidentally) by the problem statement as written, but anyway it wouldn't be very interesting, and I prefer not to make that change.)

\$\endgroup\$
1
\$\begingroup\$

Python2, 95 94 bytes

f=lambda i:g(eval("(%s)"%i.replace(")","),")))
g=lambda i:len(i)in(0,2)and all(g(j)for j in i)

Try it online!

f() transforms the string into a nested tuple, which it passes to g().

g() recursively navigates the tuple and returns False if any element doesn't have exactly 0 or 2 children.

Saved one byte by using string formatting.

\$\endgroup\$
1
\$\begingroup\$

Stax, 13 11 bytes

₧aaS▐îî»Å·╢

Run and debug it

I saved two bytes when I realized the inputs can coincidentally be implicitly evalled as array literals. By removing the double quotes, the input is simplified.

The general idea is to eval the input as an array literal, and recursively map the elements to check parethesly balance. If the final assertion ever fails, then there will be a subsequent pop on an empty stack. In stax, popping with empty stacks immediately terminates the program.

Unpacked, ungolfed, and commented, it looks like this.

        input is implicitly treated as array literals
L       wrap entire input stack in an array
G       jump to the trailing '}', and come back when done
}       terminate the program, the rest is a recursive call target
{Gm     map array on top of the stack by using the recursive call target
%       get the length of the mapped array
02\#    is the length one of [0, 2]?
|c      assert value is truthy, pop if not

Run this one

\$\endgroup\$
1
\$\begingroup\$

Java 10, 99 96 95 83 bytes

s->{s="("+s+")";for(var p="";!p.equals(s);s=s.replace("(()())","()"))p=s;return s;}

Port of my 05AB1E answer (so also returns () as truthy and anything else as falsey).

Try it online.

Explanation:

s->{                 // Method with String as both parameter and return-type
  s="("+s+")";       //  Surround the input-String between "(" and ")"
  for(var p="";      //  Previous-String, starting empty
      !p.equals(s)   //  Loop as long as the previous and current Strings differ
      ;              //    After every iteration:
       s=s.replace("(()())","()"))
                     //     Replace all "(()())" with "()"
    p=s;             //   Set the previous String with the current
  return s;}         //  Return the modified input-String
                     //  (if it's now "()" it's truthy; falsey otherwise)

return s; can be return"()".equals(s); if an actual boolean result was required.

\$\endgroup\$
2
  • \$\begingroup\$ You can save one byte if you just check !s.contains("()()(") \$\endgroup\$
    – Charlie
    Aug 2, 2018 at 11:39
  • \$\begingroup\$ @Charlie Thanks, but the code contained a bug anyway, so had to change it. It's now fixed (for the last added falsey test case), and golfed by 4 bytes at the same time. \$\endgroup\$ Aug 2, 2018 at 11:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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