29
\$\begingroup\$

Every phone needs a calculator app. Because of the limited screen real estate, the developer of mine has decided to save some buttons. Instead of having two dedicated bracket buttons - one for open ( and one for close ) - there is just a single bracket button (). It looks something like this:

My phone's calculator app

When the button is pressed, it places a bracket. Based on the input given so far, it predicts whether the bracket should be open ( or close ).

By trial and error, I have found the button to follow these rules:

  • If there are no unmatched brackets, the next bracket will always be open (.
  • Else, if the last character is a numeral 0123456789 or close ) bracket, the next bracket will be close ).
  • Else, if the last character is an operator +-*/ or open ( bracket, the next bracket will be open (.

The challenge

Based on the given input and the rules mentioned above, predict whether the button places an open ( or close ) bracket.

Input

Either a string, an array of characters, or anything equivalent. The input will only contain the following characters*: 0123456789+-*/()

Output

An open ( or close ) bracket or any two consistent values representing them.

Test cases

""                  -> "("
"(1+"               -> "("
"(1+(2/3"           -> ")"
"(1+(2/3)"          -> ")"
"(1+(2/3))-8"       -> "("
"(1+(2/3))-8*("     -> "("
"(1+(2/3))-8*((5"   -> ")"
"(1+(2/3))-8*((5)"  -> ")"
"(1+(2/3))-8*((5))" -> "("

Scoring

This is , so the shortest answer in each language wins.

Note

* The actual calculator also includes the symbols .%, but you don't need to care about those.

\$\endgroup\$
6
  • 1
    \$\begingroup\$ May we assume the input won't contain wonky brackets, e.g. )(1+2? \$\endgroup\$
    – pxeger
    Nov 18, 2021 at 8:31
  • 1
    \$\begingroup\$ @pxeger Yes, you may. You can expect the input to be typed on the calculator, too. \$\endgroup\$
    – Jitse
    Nov 18, 2021 at 8:33
  • 1
    \$\begingroup\$ @Arnauld You can assume that it doesn't contain either. \$\endgroup\$
    – Jitse
    Nov 18, 2021 at 9:25
  • 1
    \$\begingroup\$ These rules aren't correct. For example, the expression ((5+2)(7+3)+4)*2 will not be handled correctly. Instead, your rules will output ((5+2))7+3(+4)*2 \$\endgroup\$ Nov 18, 2021 at 14:36
  • 13
    \$\begingroup\$ @DonThousand That is indeed what the calculator does. If you want to force ((5+2)(7+3)+4)*2, then you have to include a multiplication sign after ((5+2). The rules are fine in my opinion. \$\endgroup\$
    – Jitse
    Nov 18, 2021 at 15:35

31 Answers 31

13
\$\begingroup\$

JavaScript (Node.js v11.6.0), 48 bytes

a=>/k/.test(eval("try{eval(a+')')}catch(e){e}"))

Try it online!

We try to append a character ')' to the end of expression and eval it. Node v11.6.0 (as current version on TIO) may:

  • Calculate the expression if the expression is valid
    • The calculate result may only contain characters /[0-9INafinty]/
  • Report error SyntaxError: Unexpected token ) if the ) should not be there
  • Report error SyntaxError: Unexpected end of input if the ) is valid there

So we just need to check if the result contains letter "k".

\$\endgroup\$
5
  • \$\begingroup\$ 42 \$\endgroup\$
    – l4m2
    Mar 27, 2022 at 18:39
  • \$\begingroup\$ maybe this count as 39 \$\endgroup\$
    – l4m2
    Mar 27, 2022 at 18:40
  • \$\begingroup\$ @l4m2 Why not simply claim that "My function return results via throwing an exception", and only use code a=>eval(a+')m')? \$\endgroup\$
    – tsh
    Mar 28, 2022 at 2:03
  • \$\begingroup\$ because I'm not familiar enough with rules \$\endgroup\$
    – l4m2
    Mar 28, 2022 at 16:16
  • \$\begingroup\$ @l4m2 I'm not sure about the rules. Maybe you can post it as another answer. \$\endgroup\$
    – tsh
    Mar 29, 2022 at 1:55
6
\$\begingroup\$

C (clang) on Linux, 82 79 66 62 bytes

c;f(*x){for(c=0;*x;++x)*x&22?:*x&1?--c:++c;c*=*--x&16|*x==41;}

Try it online!

Expects a nul-terminated ASCII wide string (4 bytes per character). Returns false (zero) for open bracket and true (nonzero) for close bracket.

  • Thank you to @AZTECCO for golfing off 13 bytes!
  • And thank you to @jdt for golfing off 4 more bytes. (This required switching to clang; the original answer was for gcc.)
\$\endgroup\$
6
  • 1
    \$\begingroup\$ @AZTECCO You've been busy! And I had no idea C has an Elvis operator now — that's awesome. \$\endgroup\$ Nov 20, 2021 at 1:07
  • \$\begingroup\$ @jdt Very cool. I will include Clang separately, if that's ok with you, since I had gcc originally. \$\endgroup\$ Nov 20, 2021 at 1:07
  • 1
    \$\begingroup\$ Sorry for noticing now but it fails on higly unbalanced brackets, e.g. "(((((((((((((()", use *= instead of &= to fix it Try it online! btw lol Elvis operator \$\endgroup\$
    – AZTECCO
    Nov 20, 2021 at 5:24
  • \$\begingroup\$ Thanks, I've updated the answer (and merged them). I also swapped the --c and ++c to keep the results nonnegative, for purely aesthetic reasons. \$\endgroup\$ Nov 20, 2021 at 5:50
  • 2
    \$\begingroup\$ @ChrisBouchard about the elvis operator: according to Wikipedia leaving out the second operand in the ternary operator is supported as a GCC extension since 2001, so GCC-style C is probably the origin of the elvis operator :-) \$\endgroup\$
    – cg909
    Nov 21, 2021 at 4:33
5
\$\begingroup\$

05AB1E, 18 16 bytes

„()S¢Ëžh')«Iθå≠~

-2 bytes by outputting 1 for ( and 0 for ).

Try it online or verify all test cases.

Explanation:

„()        # Push string "()"
   S       # Convert it to a character-list: ["(",")"]
    ¢      # Count the "(" and ")" in the (implicit) input-string
     Ë     # Check whether both are the same
žh         # Push builtin "0123456789"
  ')«     '# Append a ")"
     Iθ    # Push the last character of the input-string
       å≠  # Check that it's NOT in the string "0123456789)"
~          # Bitwise-OR to check if at least one is truthy
           # (after which this is output implicitly as result)
\$\endgroup\$
5
\$\begingroup\$

Vyxal, 16 bytes

‛()$vO≈9ʀ\)J?tc∨

Try it Online!

0 for ( and 1 for )

\$\endgroup\$
5
\$\begingroup\$

Python 2, 48 bytes

lambda L:"0">cmp(*map(L.count,"()"))*L[-1:]!=")"

Try it online!

Based on @wasif's Python 3 answer; shortened using cmp which is not available in Python 3.

True for "("

False for ")"

\$\endgroup\$
5
\$\begingroup\$

Perl 5 -nMv5.10, 23 bytes

say!/[\d)]$/||eval&&!$@

Try it online!

Prints true/1 for ( and false/empty string for ).

And for eight extra bytes we can print ( and ) like this: Try it online 31 bytes. The eval&&!$@ checks if the input expression have balancing parentheses by using the Perl compiler to check if it compiles.

\$\endgroup\$
0
4
\$\begingroup\$

GNU AWK, 75 bytes

BEGIN{RS=".|"}RT~/\(/{++n}RT~/\)/{--n}END{print n?RT~/[\)0-9]/?")":"(":"("}

Try it online!

BEGIN{RS=".|"}

Starts reading the input one character at a time, which are stored in the RT variable, by the way.

RT~/\(/{++n}
RT~/\)/{--n}

If the character is a (, increments n by one. If it is ), decrements n by one.

END{print 
          n?
            RT~/[\)0-9]/?
                         ")"
                        :"("
           :"("
   }

If n equals zero, i.e., there are as many ( as ), prints (. If n is different from zero, evaluates the last character (registered in RT). If it is a number or ), prints a ), otherwise, prints (.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Nice! Changing the non-BEGIN/END clauses to this n+=(RT=="(")-RT==")"{} seems to work and shaves off 2 chars. \$\endgroup\$
    – cnamejj
    Nov 21, 2021 at 11:20
  • 1
    \$\begingroup\$ @cnamejj That was a nice idea! But I couldn't make it work without parenthesizing that second comparison, like this: n+=(RT=="(")-(RT==")"){}. Try it online! \$\endgroup\$ Nov 21, 2021 at 12:09
4
\$\begingroup\$

K (ngn/k), 30 bytes

{(^"(*+-/"?*|x)>~+/-/x=/:"()"}

Try it online!

Returns 0 for "(" and 1 for ")".

  • ~+/-/x=/:"()" determine whether or not the input contains a balanced number of parenthesis, i.e. that the number of "("s minus the number of ")"s is 0
  • (^"(*+-/"?*|x) determine if the last character is one that should be followed by a "(" (i.e. one of "+-*/(") or not
  • (...)>... use > as material nonimplication (i.e. only return a truthy value if the left side is truthy and the right side isn't)
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6),  52  51 bytes

Expects an array of characters. Returns 0 for (, or 1 for ).

a=>a.map(c=>q+=-(d=c>-1,C=c==')')|c<')',q=0)|q&&d|C

Try it online!

Commented

a =>               // a[] = input array of characters
a.map(c =>         // for each character c in a[]:
  q +=             //   update q
    -(             //   which keeps track of the balance of parentheses
      d = c > -1,  //   d = digit flag
      C = c == ')' //   C = closing parenthesis flag
    )              //   decrement q if C is set
    | c < ')',     //   or increment it if c is '('
  q = 0            //   start with q = 0
) |                // end of map()
q &&               // return 1 if q is not 0 (unbalanced parentheses)
d                  // and the last character was either a digit
  | C              // or a closing parenthesis
\$\endgroup\$
3
\$\begingroup\$

Zsh, 39 bytes

Exits truthy if a ( should be inserted, falsy if a ) should be inserted.

eval ${(s..)1//[^()]/;}||${(M)1%[0-9)]}

Try it online!

The eval handles the matching (getting rid of non-() characters, and spawning nested subshells with spaces and ;s to prevent ()() or (( )) from erroring).

The ${(M)1%[0-9)]} simply tries to run the last character of the input as a program if it is in 0123456789), failing. || brings the whole logic together.

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

TI-Basic, 71 bytes

Prompt Str1
For(I,1,length(Str1
sub(Str1,I,1→Str2
O+(Ans="(→O
C+(Str2=")→C
End
0
If O≠C
not(inString("+-*/(",sub(Str1,length(Str1),1
Ans

Output is stored in Ans and displayed. Outputs 0 for ( and 1 for ).

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

Jelly, 15 14 bytes

ċⱮØ(Iɓ”)ØD;faƒ

Try it online!

-1 because I actually looked at other solutions and remembered ċ exists

Returns falsy (empty list) for open or truthy (nonempty list) for close. Took a hell of a lot of permuting to get here from =€Ø(ISaṪe”)ṭØD¤Ɗ.

        ØD;       Concatenate "0123456789" and
      ”)          a closing parenthesis.
           f      Keep only its elements which are also elements of
     ɓ      aƒ    the input reduced LtR by logical AND starting with:
ċⱮØ(              Count the opening and closing parentheses in the input,
    I             and subtract the opens from the closes.
\$\endgroup\$
3
\$\begingroup\$

Haskell, 64 bytes

o i=elem(last i)"+-*/("||let[o,c]=[[1|c<-i,c==p]|p<-"()"]in o==c

Try it online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I think this works as another 64-byter, in case there's some opportunity to golf more from there \$\endgroup\$ Nov 22, 2021 at 22:39
3
\$\begingroup\$

C# (8.0), 76 68 64 bytes

-3 bytes thanks to ceilingcat
-1 byte thanks to Aaroneous Miller

bool p(char[]i)=>i.Sum(x=>x<42?x*2-81:0)==0||i[^1]<48&i[^1]!=41;

Try it online!
Takes not-null (but it might be empty) array of characters and outputs true for ( and false for ).

Ungolfed:

bool p(char[] input){
   int sum = input.Where(x=>x<42)  // take only brackets
       .Select(x=>x*2-81)          // replace '(' with -1 and ')' with 1
       .Sum();                     // sum them 

   if(sum == 0)       // if sum is 0, brackets are balanced
       return true;
   // when left condition in || operator is true the right one isn't checked
   // this is beneficial for us because it prevents from taking last element
   // of empty array - empty input will always have balanced brackets

   char lastChar = i[^1];  // takes last character (this notation requires C# 8.0)
   
   return            // return true (opening bracket) IF:
      lastChar < 48  // last char is '/','*','-','+','(' or ')' 
      &&             // AND
      lastChar != 41;// last char isn't ')'
}
\$\endgroup\$
1
  • 2
    \$\begingroup\$ You can remove the space in (char[] i) \$\endgroup\$ Nov 26, 2021 at 20:10
3
\$\begingroup\$

php 4 (83 chars)

Edited because the initial 67 chars answer was not a function, it was a single imperative line :

Using a traditional function allowing to call f($input), so the answer is printed to stdout :

function f($a){$s=count_chars($a);echo$s[41]-$s[40]&&$a!=chop($a,')0..9')?')':'(';}

php 7.4 (73 chars)

Using an arrow function allowing to call $g($input), the answer is returned by $g :

$g=fn($a)=>$a!=chop($a,')0..9')&&($a=count_chars($a))[41]-$a[40]?')':'(';
\$\endgroup\$
2
  • \$\begingroup\$ Welcome to Code Golf! You might want to move the comment to text in the answer body, to avoid confusion about what is and isn't part of the actual code. \$\endgroup\$ Mar 30, 2022 at 4:12
  • \$\begingroup\$ More fundamentally, this answer takes input as a pre-defined variable, which is not an allowed input form \$\endgroup\$ Mar 30, 2022 at 4:19
2
\$\begingroup\$

Charcoal, 18 bytes

∨⁼№θ(№θ)№(*+-/§θ±¹

Try it online! Link is to verbose version of code. Outputs - for (, nothing for ). Pretty output can be obtained by prefixing §)( (equivalent to AtIndex(")(", ...). Explanation: Port of @wasif's Python answer.

  №                 Count of
    (               Literal `(`
   θ                In input string
 ⁼                  Is equal to
     №              Count of
       )            Literal `)`
      θ             In input string
∨                   Logical Or
        №           Count of
              §θ±¹  Last character of input string
         (*+-/      In literal `(*+-/`
                    Implicitly print
\$\endgroup\$
2
  • \$\begingroup\$ @KevinCruijssen Yeah originally I wrote it with the §)( and then forgot which character was at which index... \$\endgroup\$
    – Neil
    Nov 18, 2021 at 9:51
  • 1
    \$\begingroup\$ @KevinCruijssen Yeah I noticed your comment on wasif's post, I just wanted the two fixes to be separate edits. \$\endgroup\$
    – Neil
    Nov 18, 2021 at 10:00
2
\$\begingroup\$

Retina 0.8.2, 39 bytes

(?>(\()|(?<-1>\))|.)*$(?<-1>(?<=[\d)]))

Try it online! Outputs 0 for ( or 1 for ), but link is to test suite that maps the results for you. Explanation:

(?>(\()|(?<-1>\))|.)*$

Atomically count the (s and )s. (It needs to be atomic otherwise the . messes up the count of )s, but it's still golfier than [^()].)

(?<-1>(?<=[\d)]))

Test whether there is an unclosed ( and the last character is a digit or a ).

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

Flex, 86 85 bytes

%{
p;l;
%}
%%
[(] l=!++p;
[)] p-=l=1;
[0-9] l=1;
[-+*/] l=0;
\n putchar(!!p*l+40);
%%   

To run this code, copy the above into a file named calc.l and run the following (on linux/bash):

flex calc.l
gcc lex.yy.c -o calc -lfl
for t in "" "(1+" "(1+(2/3" "(1+(2/3)" "(1+(2/3))-8" "(1+(2/3))-8*(" "(1+(2/3))-8*((5" "(1+(2/3))-8*((5)" "(1+(2/3))-8*((5))"; do  
    echo -n "\"$t\" -> "
    echo $t | ./calc
    echo
done  

and it will output either ( or ) to stdout for each test string.

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

Vyxal, 23 bytes

k(↔k(øo[tkd\(+$ck($i|\(

Try it Online!

k(↔                     # When you remove all but `()` from the input
   k(øo                 # Then remove that until there is no change
       [                # Is there nothing left?
                     \( # If so, opening bracket
                    |   # Else...
        t               # Is the last character of the input...
              $c        # Contained in...
         kd\(+          # Digits + `(`?
                k($i    # Index that into `()`
\$\endgroup\$
5
  • \$\begingroup\$ noob here, what does "index that into )(" mean? specifically unsure what "indexing" means in this context, or how you can "index" something into a string or list \$\endgroup\$ Nov 18, 2021 at 14:11
  • 1
    \$\begingroup\$ @thejonymyster in this context, "indexing X into Y" essentially means "get the X'th element of Y" \$\endgroup\$
    – pxeger
    Nov 18, 2021 at 17:07
  • \$\begingroup\$ @Jitse Fixed... \$\endgroup\$
    – emanresu A
    Nov 18, 2021 at 18:39
  • \$\begingroup\$ This can probably lose 4 or so bytes with decision-problem output defaults. \$\endgroup\$ Nov 21, 2021 at 10:08
  • \$\begingroup\$ @UnrelatedString Yeah, but I can't be boothered fixing it. \$\endgroup\$
    – emanresu A
    Nov 21, 2021 at 18:47
2
\$\begingroup\$

Ruby, 47 43 bytes

->e{!!e[/[\d)]$/]&&e.count(?()>e.count(?))}

Try it online!

Outputs True for ), False for (.

  • Thanks to @Dingus for the insights and 4 Bytes saved

To choose ) two conditions must hold:

!!e[/[\d)]$/] # match($ at the end) the set of characters [numbers, ')'].
And double negate it to transform nil(no match) to false else true

e.count(?()>e.count(?)) # open brackets can be equal if balanced, else more than closed ones in which case the second condition is met.

\$\endgroup\$
3
  • \$\begingroup\$ e.count(?()^e.count(?))? \$\endgroup\$
    – emanresu A
    Nov 18, 2021 at 18:41
  • \$\begingroup\$ @emanresu A I'm not sure it's acceptable, it return false or 0 for ( and n>0 for ) but false and 0 are not the same like in many other languages, I may also use =~ instead of === to check last char , it would return nil or 0 for (. Thanks anyways \$\endgroup\$
    – AZTECCO
    Nov 18, 2021 at 19:55
  • \$\begingroup\$ @Dingus no worries, many thanks! Indexing into a regex is insane \$\endgroup\$
    – AZTECCO
    Nov 18, 2021 at 23:42
2
\$\begingroup\$

PHP (8.0), 110 bytes

<?php
$a=$argv[1];
$b=ord($a[-1]??'');
die($b>=47||$b==41&&substr_count($a,'(')>substr_count($a,')')?')':'(');

Try it online!

Ungolfed:

<?php
$a = $argv[1];
// Get the binary value of the first byte of the last character
$lastCharAsciiCode = ord($a[-1] ?? '');
// If the last character in the string is either 0-9 (47-57) OR Closing parenthesis (41)
if ($lastCharAsciiCode >= 47 || $lastCharAsciiCode == 41) {
    // If we have more opening parenthesis than closing, return closing.
    if (substr_count($a,'(') > substr_count($a,')')) {
        die(')');
    }
}
// Return opening by default
die('(');
\$\endgroup\$
6
  • \$\begingroup\$ You can golf off 2 bytes by removing the newlines after the semicolons. 108 bytes \$\endgroup\$ Nov 20, 2021 at 1:35
  • 1
    \$\begingroup\$ Two more suggestions: I don't think you need ?? '' since ord(null) == 0; and you could replace two calls to substr_count with one call to count_chars, which returns an array indexed by character code. 95 bytes \$\endgroup\$ Nov 20, 2021 at 1:46
  • 1
    \$\begingroup\$ To clarify, ord(null) == ord('') == 0. \$\endgroup\$ Nov 20, 2021 at 1:54
  • \$\begingroup\$ @ChrisBouchard The null coalesce is to prevent $a[-1] from throwing an exception in the event that $argv[1] is either an empty string or not passed. I'll have a look at your suggestion for count_chars. Thanks! \$\endgroup\$ Nov 22, 2021 at 17:52
  • \$\begingroup\$ I believe accessing an undefined array entry should only be a notice, which is nonfatal. It's up to you, of course, but I don't think advisory messages would be held against you as long as the code produces the correct result. \$\endgroup\$ Nov 22, 2021 at 19:50
2
\$\begingroup\$

R, 57 bytes

function(x)sum((x=="(")-(x==")"))&&grepl("[)0-9]",rev(x))

Try it online!

Input is an array of characters; outputs TRUE if next parenthesis is close ()), FALSE otherwise.

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

Rust, 107 104 bytes

|s:&str|(|(n,x)|n==0||x)(s.chars().fold((0,true),|v,c|(v.0+match c{'('=>1,')'=>-1,_=>0},c<'0'&&c!=')')))

Try it online!

true for ( and false for )

Ungolfed

|s: &str| {
    // Iterate over all chars, folding from left
    let (paren_counter, ends_with_op_or_open) = s.chars().fold(
        (0, true), // Initial value for fold
        |(n,_), character| (
            // Increment for opening braces and decrement for closing ones
            n + match character { '(' => 1, ')' => -1, _ => 0 },
            // true for +-*/( but not for 0123456789)
            character < '0' && character != ')'
        )
    );
    // Return true if parentheses are balanced or the string ends
    // with an operator or open parenthesis, false otherwise
    paren_counter == 0 || ends_with_op_or_open
}
\$\endgroup\$
2
\$\begingroup\$

Javascript, 90 chars

s=>s.replace(/.*?([\d)])?$/,(m,c)=>")("[s.replace(/[()]/g,m=>t+=m=='('||-1,t=0),+!(t&&c)])

Test:

f=s=>s.replace(/.*?([\d)])?$/,(m,c)=>")("[s.replace(/[()]/g,m=>t+=m=='('||-1,t=0),+!(t&&c)])

console.log(`
""                  -> "("
"(1+"               -> "("
"(1+(2/3"           -> ")"
"(1+(2/3)"          -> ")"
"(1+(2/3))-8"       -> "("
"(1+(2/3))-8*("     -> "("
"(1+(2/3))-8*((5"   -> ")"
"(1+(2/3))-8*((5)"  -> ")"
"(1+(2/3))-8*((5))" -> "("
`.split`
`.filter(x=>x).map(x=>x.match(/"(.*)".*"(.)"/)).map(([,x,r])=>f(x)==r).every(x=>x))

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

brainfuck, 73 bytes

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

Try it online!

Outputs truthy for ) and falsy for (. Note that this will fail if there are 256 unmatched (.

Version which outputs '(' or ')' (not particularly golfed)

Explanation:

,[                                           ,]                               loop until end of input
  +>-->[+]+[->+[--->]<<<]                                                       paren depth calculation: (brute forced black magic)
                                                                                  '(' => 1
                                                                                  ')' => 255
                                                                                  '0123456789+-*/' => 0 
                         <[->>+<<]                                              accumulate paren depth counter
                                  >>>[->>-<<]                                   preserve a data cell uniquely identifying last character
                                               <[,>>]                         if depth counter is nonzero move to cell identifying last character
                                                     ->[+>[>]--<<<++<+]>.     character to final output calculation: (brute forced black magic)
                                                                                '+-*/(' or \0 => 0
                                                                                '0123456789)' => nonzero
\$\endgroup\$
2
\$\begingroup\$

Pip, 17 bytes

$>^pNa&`[\d)]$`Na

Takes the partial expression as a command-line argument. Outputs 0 for open parenthesis, 1 for close parenthesis. Attempt This Online!

Verify all test cases!

Explanation

$>^pNa&`[\d)]$`Na
                   a is command-line arg; p is "()" (implicit)
  ^p               Split p into a list of two parenthesis characters
    Na             Get the count of each parenthesis in the argument
$>                 Fold on > (1 if there are more open than close parens, 0 otherwise)
                   (Because we "can expect the input to be typed on the calculator,"
                   there will never be more close than open parens)
      &            Logical AND
       `      `    The following regex:
        [\d)]       Either a digit or a close paren
             $      followed by end-of-string
               N   Count matches in
                a  The argument

Thus:

  • If the parentheses are balanced, $> gives 0, which short-circuits the AND and returns 0 (open paren)
  • Otherwise:
    • If the argument ends with a digit or ), return 1 (close paren)
    • If it doesn't, return 0 (open paren)

To get ( and ) as output, wrap the whole thing in (p ... ) for +3 bytes.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ Every time I see pip on Code Golf SE, I wonder for a moment how someone used the Python package manager to solve the challenge. \$\endgroup\$ Nov 20, 2021 at 1:59
  • 2
    \$\begingroup\$ @ChrisBouchard And I always wonder how it's possible that the Peripheral Interchange Program is Turing-complete. \$\endgroup\$
    – Neil
    Nov 21, 2021 at 9:26
2
\$\begingroup\$

JavaScript (Node.js), 15 bytes

a=>eval(a+')m')

Try it online!

tsh claim that "My function return results via throwing an exception"

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

MATLAB, 65 bytes

@(x)all([sum(x(x<42)*2-81),feval(@(a,b)a(end)~=b,[0,x],'/*-+(')])

Try it online! Anonymous function. Outputs logical 1 for ) and logical 0 for (.
Monstrosity with eval is required to handle empty input. Without that case it reduces to:

@(x)all([sum(x(x<42)*2-81),x(end)~='/*-+('])

which is much easier to comprehend. Ungolfed:

@(x)all([                                    % all of the conditions must be met
         sum(x(x<42)*2-81)                   % brackets are unbalanced
                          ,                  % AND
                           x(end)~='/        % last character isn't / AND
                                     *-+('   % isn't * AND ... (so on)
                                          ])

Checking of bracket unbalance works by taking only brackets in input: x(x<42) and then with multiplication and subtraction transforming ( into -1 and ) into 1. Then sum of these is converted to logical during execution of all - 0 stays logical 0 anything else becomes logical 1.

Omitted in ungolfed version eval additionaly puts 0 before whole input so last character will always exist.

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

Core Maude, 206 bytes

mod B is pr LIST{Int}. ops b p : Nat ~> Nat . var A : Nat . var X Y Z :[Nat]. eq
b(nil)= 0 . eq b(X A)= 0 ^((~ A & 16)*(A xor 41)+ 0 ^ p(X A)). eq p(nil)=
0 . eq p(A X)= 0 ^(A & 22)* -1 ^(A & 1)+ p(X). endm

The result is obtained by reducing the b function with the input string given as a list of ASCII code points. The output will be 0 for open bracket and 1 for close bracket.

Example Session

Maude> --- "" -> "("
> red b(nil) .
result Zero: 0
Maude> --- "(1+" -> "("
> red b(40 49 43) .
result Zero: 0
Maude> --- "(1+(2/3" -> ")"
> red b(40 49 43 40 50 47 51) .
result NzNat: 1
Maude> --- "(1+(2/3)" -> ")"
> red b(40 49 43 40 50 47 51 41) .
result NzNat: 1
Maude> --- "(1+(2/3))-8" -> "("
> red b(40 49 43 40 50 47 51 41 41 45 56) .
result Zero: 0
Maude> --- "(1+(2/3))-8*(" -> "("
> red b(40 49 43 40 50 47 51 41 41 45 56 42 40) .
result Zero: 0
Maude> --- "(1+(2/3))-8*((5" -> ")"
> red b(40 49 43 40 50 47 51 41 41 45 56 42 40 40 53) .
result NzNat: 1
Maude> --- "(1+(2/3))-8*((5)" -> ")"
> red b(40 49 43 40 50 47 51 41 41 45 56 42 40 40 53 41) .
result NzNat: 1
Maude> --- "(1+(2/3))-8*((5))" -> "("
> red b(40 49 43 40 50 47 51 41 41 45 56 42 40 40 53 41 41) .
result Zero: 0

Ungolfed

mod B is
    pr LIST{Int} .

    ops b p : Nat ~> Nat .

    var A : Nat .
    var X Y Z : [Nat] .

    eq b(nil) = 0 .
    eq b(X A) = 0 ^ ((~ A & 16) * (A xor 41) + 0 ^ p(X A)) .

    eq p(nil) = 0 .
    eq p(A X) = 0 ^ (A & 22) * -1 ^ (A & 1) + p(X) .
endm

I hope it's not too cheesy to accept input as ASCII code points. Maude has a string module, but it's pretty weak and kind of verbose.

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

Ly, 50 bytes

is<l>0sp["("=l+f")"=fp-sp]<")"=!f'0Lfp*l!+!!")"f-o

Try it online!

The algorithm for this one is:

  • read in the input, store the last character in a separate stack
  • run through all the chars, track ( vs ) balance in a saved cell
  • test, last="(" and last<"0"
  • combine those three tests (paren balance is the 3rd one) to pick output

There's a caveat though. I don't think Ly can handle null character (as opposed to numeric) input. So it doesn't handle the first test case, which is a null string.

Here's the sausage making...

First part, get input and setup for run

i        - read the input onto the stack as codepoints 
 s       - save the last char in the backup cell
  <l>    - switch stacks, load backup cell, switch back to original stack
     0sp - set the backup cell to "0", then delete 0 from stack

Count the ( vs ) chars, keeping a summary count of the balance

[               p] - process one char per iteration, until stack is empty
 "("=              - compare top of stack to "("
     l+            - load balance summary, increments on match
       f           - flip the current character to the top
        ")"=       - compare top of stack to ")"
            fp     - flip current char to the top, delete it
              -    - decrement balance summary if char was ")"
               s   - save balance summary to backup cell

Third, test the last character of the input.

<            - switch to the stack where we stashed the last char
 ")"=        - compare to ")"
     !       - negate result, we want "not equal"
      f      - pull the character to the top of the stack
       '0L   - test if less than character codepoint for "0"
          fp - pull the char to the top, then delete it

Fourth, combine the tests to get an output choice

*            - compute "and" of the last two tests
 l!          - load balance summary, negate
   +         - compute "or" of that and the previous test combo
    !!       - convert anything ">0" to "1" via double negate
      ")"f   - add ")" to stack, pull tests results to top
          -  - subtract to change char to "(" if called for
           o - output top of stack as a character
\$\endgroup\$

Your Answer

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

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