39
\$\begingroup\$

Originally sandboxed by @xnor

Left-or-right is a very simple language @xnor made up. Its expressions are made of arrows < (left), > (right), and parentheses. The goal is to evaluate an expression to either < or >.

An expression A<B picks the left item A, while A>B picks the right one B. Think of < and > as arrows pointing to the item we want, not as comparison operators.

Take, for example, ><>. The operator in the middle is <, and confusingly, the items on each side A and B are also arrows. Since the operator tells us to take the left one A, which is >. So, ><> equals >.

Expressions also nest. We can replace the expression with its value. So, for example, (><>)<< equals ><< equals >. And, >(><>)< equals >>< equals <. For another example, (><>)(<<<)(>><) equals ><< equals >.

In the input, you'll be given a well-formed expression consisting of either a trio of arrows like ><> or the result of repeatedly replacing some arrow by a trio of arrows in parens like ><(><>) . You can assume the input won't already be a lone arrow. You may alternately accept the whole inputs encased in parens like (><>) or (<(><>)>).

The input is given as a flat string consisting of symbols <>(). You may not take it in a pre-parsed form like a tree.

The shortest code in bytes wins.

Test cases

Generated using this script.

Evaluates to <

>><
<<(<><)
(>>>)><
(<(<<>)(<<<))<<
((>(>><)>)(><>)>)><
(<<(>(<>>)<))((><>)(><<)>)(<<<)
((<<<)<>)((>><)<(<><))((>>>)<<)
>(>((><>)<>)(<>>))((>><)((><>)<<)<)
((><<)(><<)(<<>))(<(>><)(>><))(<<(<<>))
(<(><<)(>(>>>)>))((>>>)>>)((<>(>><))<<)

Evaluates to >

<>>
((<<<)>(<<>))(><<)>
((>>>)<>)<((<<<)>>)
>(>>(<<<))(>((<>>)<<)<)
((><>)(<<>)>)(<<(<<<))(<(>>>)<)
(><((><>)><))(>(>>(>>>))(<><))(>>>)
(((>><)<>)(><>)(><>))(<<(<>>))(<<>)
((><>)<(<<(<<>)))((<(<<>)<)(<><)>)(>>>)
(<<(<><))(((<>>)><)(><<)(><>))(<(><>)>)
((>>>)<<)(<(<><)<)((<<(<<<))>(>(><>)<))
\$\endgroup\$
5
  • 1
    \$\begingroup\$ Is a single > valid input? And maybe also (>)(<)(>)? \$\endgroup\$
    – tsh
    Feb 5, 2021 at 1:50
  • 1
    \$\begingroup\$ @tsh No and no. Think of it as a valid ternary tree where the leaves are one of <>, given in a linear form using parens. Also "You can assume the input won't already be a lone arrow." \$\endgroup\$
    – Bubbler
    Feb 5, 2021 at 1:57
  • \$\begingroup\$ Is there an original post or other resource by @xnor introducing left-or-right that you can link to? \$\endgroup\$
    – Oliphaunt
    Feb 7, 2021 at 9:12
  • 1
    \$\begingroup\$ @Oliphaunt It's the linked sandbox post at the top, though it's deleted now (only 10k+ rep users can view). \$\endgroup\$
    – Bubbler
    Feb 7, 2021 at 12:11
  • 2
    \$\begingroup\$ Thanks for bringing this challenge to life! Might you be interested in any of the other Sandboxed challenges I have lying around? \$\endgroup\$
    – xnor
    Feb 11, 2021 at 22:50

31 Answers 31

12
\$\begingroup\$

x86-16 machine code, 31 26 22 bytes

Saved 5 bytes thanks to @Bubbler.

Saved 4 bytes thanks to @640KB.

0000:0000  AC 3C 29 74 03 50 EB 0A-5A 58 3C 3E 58 74 01 92   .<)t.P..ZX<>Xt..
0000:0010  58 52 E2 EC 58 C3                                 XR..X.

Callable function.

Expects SI = address of code, CX = length of code.

Expects the code to be enclosed in (...):

You may alternately accept the whole inputs encased in parens like (><>) or (<(><>)>).

Returns the output at AL.

Disassembly:

         LOOP:                  ; Main loop:
AC            LODSB             ; AX = [SI++]
3C29          CMP     AL, ')'   ; AX == ')'?
7403          JZ      BRACE     ; * If so, jump to 'BRACE'
         PUSHSTH:               ; Otherwise...
50            PUSH    AX        ; * Push AX onto stack
EB0A          JMP     END       ; * Jump to 'END'
         BRACE:                 ; If AX == ')'?
5A            POP     DX        ; * pop TOS to DX
                                ;   * (So DX is now the rightmost angle brace)
58            POP     AX        ; * pop TOS to AX
                                ;   * (So AX is now the middle angle brace)
3C3E          CMP     AL,'>'    ; * Compare current AL with '>' (result stored in PSW)
58            POP     AX        ; * AX = left argument
7401          JZ      RAB       ;   * If prev. value of AL == '>', jump to 'RAB'
         LAB:                   ; * Handle prev. value of AL == '<':
92            XCHG    DX,AX     ;   * DX = left argument instead
         RAB:                   ; Now, DX is the chosen value.

58            POP     AX        ; * Discard TOS (which is an excess '(')
52            PUSH    DX        ; * Push DX onto stack
         END:                   ; Finally,
E2EC          LOOP    LOOP      ; * Loop the above CX times
58            POP     AX        ; Pop TOS to AX
C3            RET               ; Return to caller
\$\endgroup\$
4
  • 3
    \$\begingroup\$ Good old stack-based expression evaluation, in one loop. Btw, can't you change ADD SP,2 to, say, POP BX? \$\endgroup\$
    – Bubbler
    Feb 5, 2021 at 7:59
  • \$\begingroup\$ @Bubbler You're right! Silly me. :) \$\endgroup\$
    – user99151
    Feb 5, 2021 at 8:00
  • 2
    \$\begingroup\$ Hmm now I see what you mean about output at top of the stack. Unfortunately I don't think this is okay by the rules since you're now leaving things on the stack and SP not preserved - that RET will not actually return to caller. Specifically ESP/RSP must be call-preserved and x87 st0 is reasonable, but returning in st3 with garbage in other x87 register isn't. The caller would have to clean up the x87 stack. If it's too costly to unwind the stack in the code, maybe save SP in the beginning and restore SP at the end? \$\endgroup\$
    – 640KB
    Feb 5, 2021 at 17:47
  • \$\begingroup\$ @640KB Hopefully fixed. \$\endgroup\$
    – user99151
    Feb 6, 2021 at 2:43
11
\$\begingroup\$

JavaScript (Node.js), 50 bytes

f=a=>a[1]?f(a.replace(/\((.>)?(.)(<.)?\)/,'$2')):a

Try it online!

Repeat apply regex replace until only 1 char left. The regex /\((.>)?(.)(<.)?\)/ get the calculate result as 2nd capturing group.


Save 1 byte, thanks to Arnauld

\$\endgroup\$
0
10
\$\begingroup\$

><>, 121 bytes

0&v
>o ;
^? <=1l~<;o~$?=">"$
<<v<<<&0 ~&~$?=">"$<
~]v&0[0~ < >
>>>i:0(?^   :>>>v
<< v<<<<<^? ="("<
+&^>>:")"=?^&:&2= ?^&1

Try it online!

This code takes advantage of the ><> functionality where a new stack/register can be created, code can operate on that new stack, then once that stack is closed, the results are merged back into the lower level stack.

It reads one character (codepoint really) at a time creating new stacks whenever it hits a ( and closing them when a ) is encountered.

The other logic use is a simple state machine, based on the contents of the register associated with the current stack.

When the value of the register is 0 the char just read is the left hand side of the statement, a value of 1 means it's the operator (< or >), and a value of 2 means we have the right hand side.

Anytime the state is 2, the code can resolve the 3 character on the stack (RHS, OP, LHS) to get an answer for that operation.

When the input is exhausted, we'll be back at the base stack with either 1 or 3 entries on the stack. If there are 3 entries on the stack, the code does one final operation to resolve the stack, then the sole entry on the stack is printed as a character.

The code is broken up to save characters, but I'm going to explain it using the original code. The only difference is that direction shifts, extraneous spaces and directional indicators have been removed to make it easier to see what's happening. Well, unless I goofed when formatting it...

# initialization
0&v  # Set the register (state) to 0, call input loop
 [0]

# [0] loop to read input and process each char
       [1]    [2]    [3]    [4]
>>>i:0(?^:"("=?^:")"=?^&:&2=?^&1+&
.. i                               # Read next char/codepoint
    :0(                            # Check it codepoint < 0
       ?^                          # If true, all input done call [1]
         :"("=                     # Compare char to "("
              ?^                   # If true, call [2] to open new stack
                :")"=              # Compare char to ")"
                     ?^            # If true, call [3] to close stack
                       &:&         # Load and resave the register
                          2=       # Compare state to 2 (RHS,OP,LHS)
                            ?^     # If true, call [4] to pick answer
                              &1+& # Increment the register (state)

# [1] code to handle EOF
             [5]
        >~1l=?^$"<"=?$~o;
.. ~                 # Delete top of stack, clean-up
    1l=              # Compare stack size to 1
       ?^            # If true, call [5] to print/exit
         $           # Flip top two stack entrys, pulls OP to top
           ">"=      # Compare OP to ">"
               ?$    # If true, flip top two stack entries
                 ~   # Delete top of stack (the choice we don't want)
                  o; # Print top of stack as char, then exit 

# [5] called on EOF if the stack has only 1 entry

              >o;
.. o;           # Print top of stack as char, then exit

# [2] code to handle "(", stack a new stack
               >~0[0&
.. ~            # Delete top of stack, clean-up...
    0[          # Open a new stack with 0 entries from the old one
      0&        # Set the register (state) to 0

# [3] code to handle ")", close the stack
                       >~]
.. ~]           # Delete top of stack, close stack to merge down

# [4] code to resolve a stack with RHS,OP,LHS
                             >$">"=?$~&~0&
.. $            # Flip top two stack entrys, pulls OP to top
    ">"=        # Compare OP to ">"
        ?$      # If true, flip top two stack entries
          ~     # Delete top of stack (the choice we don't want)
           &~&0 # Set register (state) to 0
\$\endgroup\$
9
\$\begingroup\$

APL (Dyalog Extended), 30 23 bytes

(⍎'(⊣⊢)'['(<>'⍳⍞])/'<>'
         '(<>'⍳⍞          take the input ⍞ and get the indices of '(<>'
                          this fills with the last index + 1 if not found, saving 1 char
  '(⊣⊢)'[ stuff ]         index into '(⊣⊢)', replacing <> with ⊣⊢, the left and right tack functions
 ⍎                        evaluate this as a train
                          APL trains of the form (f g h) when applied to left and right arguments x and y produce (x f y) g (x h y)
                          This translates directly to this language
                          Replacing <> with ⊣ and ⊢ which do the same thing, the input becomes a valid function in APL
(    train       )/'<>'   Reduce over '<>', which uses '<' as a left argument and '>' as a right one

Try it online!

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

Retina 0.8.2, 22 bytes

{`\((.>)?(.)(<.)?\)
$2

Try it online!

Basically the port of my JavaScript answer in Retina. I just learnt how to write loop in past 10 minutes and got this.

{` loops the replace until nothing changed. The replacement calculate one step each time.

\$\endgroup\$
2
  • \$\begingroup\$ For a single-stage loop you should prefer + rather than {. \$\endgroup\$
    – Neil
    Feb 5, 2021 at 10:56
  • \$\begingroup\$ Thanks for pointing out the + loop. I just find the { trick by searching "retina loop" on ppcg. I didn't ever know the + way. Since switching to + won't help me save a single byte, I would leave it as is... \$\endgroup\$
    – tsh
    Feb 7, 2021 at 1:34
7
\$\begingroup\$

J, 30 bytes

')/''<>'''".@,~'(',rplc&'<[>]'

Try it online!

Very similar to rak1507's APL answer, though I had the idea independently -- it's natural for J, though loses some elegance because of the quote escaping.

  • rplc&'<[>]' replace <> with [], which in J are the "left" and "right" identity operators, meaning they return the left or right argument.
  • ( Prepend a left paren to that result.
  • ')/''<>'''...,~ Append )/'<>' to the previous result.
  • Now we have ( ~~input with [] instead of <>~~ )/'<>'.
  • Which is the same as: '<' ( ~~input with [] instead of <>~~ ) '>'
  • ".@ Evaluate that string. From here, J trains and the definition of [ and ] do exactly what we want.
\$\endgroup\$
4
\$\begingroup\$

Python 3, 98 74 bytes

Saved a whopping 24 bytes thanks to ovs!!!

import re
f=lambda p:p[1:]and f(re.sub('\(((.)<.|.>(.))\)',r'\2\3',p))or p

Try it online!

Takes an input string with enclosing in parentheses and returns whether it's left (<) or right (>).

\$\endgroup\$
2
  • \$\begingroup\$ 74 bytes \$\endgroup\$
    – ovs
    Feb 5, 2021 at 9:27
  • \$\begingroup\$ @ovs Nice ones - thanks! :D \$\endgroup\$
    – Noodle9
    Feb 5, 2021 at 11:12
4
\$\begingroup\$

Perl 5 -p, 32 bytes

s/\((.>(.)|(.)<.)\)/$2$3/g&&redo

Try it online!

Takes advantage of the spec allowing outside parentheses to be required.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Drop the g to save a byte, then that allows you to use . instead of \( to save another byte. \$\endgroup\$
    – Neil
    Feb 6, 2021 at 1:08
4
\$\begingroup\$

Red, 136 120 86 bytes

This version is courtesy @hiimboris (https://gitter.im/red/parse)!

func[s][r:["<"|">"]while[parse s[to change["("x: r["<"r |">"x: r]")"](x/1)to end]][]s]

Try it online!

\$\endgroup\$
6
  • \$\begingroup\$ Beautiful, someone is using Red! What do you think about suggesting it for LotM? \$\endgroup\$
    – Wezl
    Feb 5, 2021 at 14:28
  • \$\begingroup\$ @Wezl Thank you! I'm affraid I'm not good at it, although I've been trying to solve some of the challenges here. About the LoTM - I don't know - I think I've only seen a single Red solution in CGCC from person other than me. \$\endgroup\$ Feb 5, 2021 at 14:33
  • 1
    \$\begingroup\$ Then there are at least two people who use it, which is enough, and a lot more who can be introduced. \$\endgroup\$
    – Wezl
    Feb 5, 2021 at 14:35
  • 1
    \$\begingroup\$ ok it's posted, but feel free to edit it because I don't have much experience with Red, and probably missed lots. \$\endgroup\$
    – Wezl
    Feb 5, 2021 at 15:17
  • 1
    \$\begingroup\$ I've been using Rebol/Red on and off for a few months now, good to see interest in it! \$\endgroup\$
    – Razetime
    Feb 5, 2021 at 17:24
4
\$\begingroup\$

C (gcc), 117 \$\cdots\$ 101 98 bytes

Saved 10 bytes thanks to an idea from Davide!!!
Saved 3 bytes (and got below 100!) thanks to ceilingcat!!!

i;f(char*p){for(i=0;*p++-40||p[3]-41;++i);p[-1]=p[p[1]-62?0:2];bcopy(p+4,p,strlen(p));i&&f(p+~i);}

Try it online!

Takes an input string with enclosing parentheses and reduces it to either a left (<) or a right (>).

Explanation (before some golfs)

i;f(char*p){                       // function taking a string parameter p  
  for(i=0;p[i++]-40||p[i+3]-41;);  // loop until we find chars '(' and ')'   
                                   // separated by 3 characters.   
                                   // since our input is enclosed in 
                                   // parentheses we will always find one.  
  p[i-1]=                          // i has gone forward one but set the 
                                   // first of these characters  - the '('  
                                   // to...     
         p[i+1]-62?                // ...depending on whether the middle   
                                   // char is a '<' or a '>'...   
                   p[i]:           // ...the one before the middle if '<'
                        p[i+2];    // ...the one after the middle if '>'  
  memcpy(p+i,p+i+4,strlen(p));     // move the back end of the string forward  
                                   // over the other 4 chars.    
                                   // now a "(A<B)" is reduced to "A"     
                                   // and a "(A>B)" is reduced to "B" 
  ~-i&&                            // if we didn't just reduce the start of p     
       f(p);                       // then recursively call f.   
                                   // otherwise p is reduced to the answer 
                                   // and we're done.     
 }  
\$\endgroup\$
7
  • \$\begingroup\$ You can save 9 bytes by incrementing the pointer in the loop. The link to the TIO doesn't fit in a comment so I am going to put it as an edit. Also check the second code that is even shorter but doesn't work and I don't know why: I just moved i++ to the end of the loop and swapped ~-i with i \$\endgroup\$
    – anotherOne
    Feb 5, 2021 at 18:05
  • \$\begingroup\$ @Davide The first one core dumps and the second doesn't work either. It's a golf why the i++ is where it is. \$\endgroup\$
    – Noodle9
    Feb 5, 2021 at 18:21
  • \$\begingroup\$ @Davide That 3rd suggestion doesn't work either. \$\endgroup\$
    – Noodle9
    Feb 5, 2021 at 18:23
  • \$\begingroup\$ For me it works, I don't know maybe there was something wrong with the link, just copy/paste this code i;f(char*p){for(i=0;i++,*p++-40||p[3]-41;);p[-1]=p[1]-62?*p:p[2];memcpy(p,p+4,strlen(p));~-i&&f(p-i);} \$\endgroup\$
    – anotherOne
    Feb 5, 2021 at 18:30
  • 1
    \$\begingroup\$ @Davide Yeah, i lags behind the pointer bumps by one. I just re-wrote it with your idea of bumping the pointer and having i keep track of where the string starts. \$\endgroup\$
    – Noodle9
    Feb 5, 2021 at 18:48
4
\$\begingroup\$

Jelly, 15 bytes

O;1ị“/ṛ  ɗḷ”vØ<

A monadic Link accepting a list of characters*, which yields a character.

* Uses the encased in parens option.

Try it online!

How?

Converts the input to Jelly code which will reduce the pair ['<', '>'] to produce the answer. Jelly ignores spaces and has a 1-indexed and modular index-into function, , so the Link starts by converting the input characters to their ordinal values and uses to form the code:

input character   ordinal   indexed into "/ṛ  ɗḷ"   meaning
           (        40        (space)                 ignored
           <        60        ḷ                       yield left argument
           >        62        ṛ                       yield right argument
           )        41        ɗ                       last three links as a dyadic function
(or reduce command)  1        /                       reduce by

So:

O;1ị“/ṛ  ɗḷ”vØ< - Link: list of characters   e.g. (<>>)
O               - ordinals                        [40,60,62,62,41]
 ;1             - concatenate a one               [40,60,62,62,41,1]
    “/ṛ  ɗḷ”    - "/ṛ  ɗḷ"                        "/ṛ  ɗḷ"
   ị            - index into                      " ḷṛṛɗ/"
             Ø< - "<>"                            "<>"
            v   - evaluate with input             '>'

The example above evaluated:

ḷṛṛɗ/ - monadic chain (with left argument ['<', '>'])
    / - reduce (['<', '>']) by - i.e. f('<', '>'):
   ɗ  -   last three links as a dyad - i.e. g('<', '>'):
ḷ     -     yield left = '<'
  ṛ   -     yield right = '>'
 ṛ    -     (ḷ) yield right (ṛ) = '>'
\$\endgroup\$
4
\$\begingroup\$

><>, 36 31 30 bytes

>i:6%0$.

v

v~0[
v0(?o$6%?$~]

Try it online! Verification of test cases

Takes input surrounded by parenthesis, and outputs a single character before terminating with an error.

Explanation:

>i:           Take input and make a copy
   6%         Map the possible codepoints through modulo: EOF,<,>,(,) => 5,0,2,4,5
     0$.      Jump to that line

>             Line 0 ('<'): This just jumps back to the first line again
v             Line 2 ('>'): Does nothing and goes back to the first line again

v~0[          Line 4 ('('): Pop the extra copy of input and create a new stack

 0(?          Line 5 (')'): If the input is EOF (-1)
    o                       Output the top of stack (and error later)
     $6%                    Otherwise, check what the middle character is
        ?$                  If it is a >, swap the other two
v         ~]                Discard the right character and leave the other other one on top of the next stack

o~            Line 1 (EOF): If there is no more input, pop the copy and output the remaining character
\$\endgroup\$
3
\$\begingroup\$

Jelly, 17 bytes

“)ɗ<ḷ>ṛ( ”y;”/vØ<

Try it online!

Same essential approach as rak1507's APL answer. Requires the input fully parenthesized.

“        ”y          Replace
 )                   ) with
  ɗ                  last-three-links-as-dyad,
   <                 < with
    ḷ                return-left-argument-and-ignore-right,
     >               > with
      ṛ              return-right-argument-and-ignore-left,
       (             and ( with a space which is essentially ignored.
           ;”/       Append the reduce quick,
              v      and evaluate the result as a monad with argument
               Ø<    "<>".
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thought someone would do this, as Jelly has the right built-ins for everything. (I was wondering why there's no > at the end, until I realized Ø< is a built-in for the two chars <>.) \$\endgroup\$
    – Bubbler
    Feb 5, 2021 at 3:28
3
\$\begingroup\$

05AB1E, 20 bytes

Δ„<>3々(ÿ)¬¦¨ºĆ'>ª:

Takes the input wrapped in parenthesis in both programs, as is allowed.

Try it online or verify all test cases.

05AB1E (legacy), 20 bytes

'<∞©3ãε…(ÿ)}¬®Ã∞®«S:

Should have been 19 bytes by also using €…(ÿ), but apparently there is a bug in the legacy version, so we'll have to use ε…(ÿ)} (or '(ì')«) instead.

Try it online or verify all test cases.

Explanation:

Δ                     # Continue until it no longer changes, using the (implicit) input:
 „<>                  #  Push string "<>"
    3ã                #  Create all possible triplets of these two characters:
                      #   ["<<<","<<>","<><","<>>","><<","><>",">><",">>>"]
      €               #  Map over each string in the list
       …(ÿ)           #   And wrap it in parenthesis
           ¬          #  Get the first item (without popping the list): "(<<<)"
            ¦¨        #  Remove its first/last characters: "<<<"
              º       #  Mirror it: "<<<>>>"
               Ć      #  Enclose; appending its own head: "<<<>>><"
                '>ª  '#  Convert it to a list of characters, and append ">":
                      #   ["<","<","<",">",">",">","<",">"]
                   :  #  Replace all ["(<<<)",...,"(>>>)"] with ["<",...,">"]
                      # (after the loop, the result is output implicitly)

'<∞                  '# Push "<" and mirror it to "<>"
   ©                  # Store this string in variable `®` (without popping)
    3ãε…(ÿ)}¬         # Same as above
             ®Ã       # Only keep the characters that are also in `®`, removing the "()"
               ∞      # Mirror it: "<<<>>>"
                ®«    # Append `®`: "<<<>>><>"
                  S   # Convert it to a list of characters:
                      #  ["<","<","<",">",">",">","<",">"]
                   :  # Keep replacing all ["(<<<)",...,"(>>>)"] with ["<",...,">"]
                      # until there is nothing more to replace
                      # (after which the result is output implicitly)
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6),  69  67 bytes

Saved 2 bytes thanks to @Neil

f=s=>s<(s=s.replace(/.?([<>]{3}).?/,(_,s)=>s[s[1]>'<'?2:0]))?f(s):s

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ "You may alternately accept the whole inputs encased in parens", which should simplify the handling of the last step. \$\endgroup\$
    – Bubbler
    Feb 5, 2021 at 2:03
  • 1
    \$\begingroup\$ Save 2 bytes by using . instead of \( and \). \$\endgroup\$
    – Neil
    Feb 5, 2021 at 17:15
3
\$\begingroup\$

PCRE, 52 bytes

(((<|>|\((?3){3}\))(?=(?>(?4))?>)(?3))?(\((?1))?)(.)

Try it online! Link includes Perl 5 test harness. Does not take outer parentheses (admittedly a version that takes outer parentheses would probably be shorter.) The desired result is captured in group 5. Explanation: Group 3 matches an operator. Group 2 matches two operators, but only if the second operator evaluates to >; a recursive atomic match is used to determine whether that is true. Group 4 then recurses if the current operator is an expression.

(               Group 1 is:
 (              Group 2, which is:
  (             Group 3, which is either:
   <|>|         ... a literal `<` or `>` or...
   \((?3){3}\)  ... three recursive matches of group 3 inside parentheses;
  )
  (?=           Followed by a lookahead to:
   (?>(?4))?    An optional atomic recursive match of group 4...
   >            ... which must be followed by a `>`;
  )
  (?3)          Followed by another recursive match of group 3.
 )?             Group 2 is optional.
 (              Group 4, which is...
  \(            ... a literal `(`...
  (?1)          ... followed by a recursive match of group 1.
 )?             Group 4 is optional.
)               End of group 1.
(.)             Group 5 then captures the desired result.
\$\endgroup\$
1
  • \$\begingroup\$ I've just noticed that the inputs I shamelessly stole from some other answer include the enclosing ()s, but fortunately the regex itself doesn't validate the expression and so it assumes extra <s exist where necessary, so the output is still correct. \$\endgroup\$
    – Neil
    Feb 7, 2021 at 19:01
3
\$\begingroup\$

C (gcc) (no builtins), 147 143 bytes

4 bytes saved thanks to ceilingcat!

i,m,c,q,w;f(char*s){for(i=m=w=0;c=s[i]%9;i++)if(c-1?c-4?w+=w-~c%3,++m==4:(q=i,m=w=0):0)for(s[q]=w-2&&w<9?62:60;q++<i;)s[q]=1;s=*s%3-1?*s:f(s);}

Try it online!

Function that takes a null-terminated char array as input, and returns a char (int).

Here's a visualization of how it works:

(((>>>)<<)(<(<><)<)((<<(<<<))>(>(><>)<)))
((>....<<)(<<....<)((<<<....)>(>>....<)))
(>........<........(<........><........))
(>........<........<....................)
>........................................

(In the code, it uses 0x01 instead of ., but due to the modulus, they're equivalent even in data representation.)

Essentially it scans the code looking for instances of (data), where data consists of substring of 3 characters, except '.' and '('. (4, in the code, since it tracks the ) as the 4th character.) The program keeps track of the start of this string (q) and a binary representation of the visited characters (w). If we consider '>' to be equivalent to the binary 1, and '<' the binary 0, we simply look at a table of what the results of the binary strings should be. As it turns out, for q=1,4,5,6, the answer is '<', and '>' otherwise. Thus, w-1&&w<5 is a sufficient determiner. (In fact, since we keep track of ) as part of this binary string, we have to consider that everything is doubled, and for no byte cost, we can modify this formula to obtain the w-2&&w<9.)

After we determine what the result should be, we replace the initial ( with the result, and all subsequent characters with .. Then, so long as the initial character of the string is (, we repeat our function. This allows us to skip moving the string around, which, in my head, should save bytes, but I haven't tested the alternative.

Started working on this before I saw the existing (much shorter) C answer. But I enjoyed this nonetheless.

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

Ruby, 51 49 44 bytes

-1 byte thanks to Neil! (match any character . instead of the opening bracket \()
-4 bytes thanks to Dingus! (String#sub! returns nil if there was no match)

Outputs by modifying the input. This would be 43 bytes with tsh's regex.

->s{s.sub!(/.((.)<.|.>(.))\)/,'\2\3')&&redo}

Try it online!


Ruby -p, 40 39 38 bytes

-1 byte thanks to Sisyphus!

sub /.((.)<.|.>(.))\)/,'\2\3'while/../

Try it online!

\$\endgroup\$
5
  • 1
    \$\begingroup\$ Because you're using sub and not gsub you can save a byte by using . instead of \( since the first ) must have an opening ( and your regex is fixed length. \$\endgroup\$
    – Neil
    Feb 5, 2021 at 11:23
  • \$\begingroup\$ Is the lambda version legit? The lambda itself returns nil. \$\endgroup\$
    – Dingus
    Feb 6, 2021 at 21:03
  • \$\begingroup\$ @Dingus codegolf.meta.stackexchange.com/a/4942/64121 \$\endgroup\$
    – ovs
    Feb 6, 2021 at 21:18
  • 1
    \$\begingroup\$ Thanks - I must have read that before but can't recall seeing it used, so there's another trick to add to my golfing. In that case you can save 4 bytes (only on the lambda one) by replacing while s[1] with &&redo. \$\endgroup\$
    – Dingus
    Feb 6, 2021 at 21:26
  • 1
    \$\begingroup\$ For the second one you can replace $_[1] with /../ for a byte saved. \$\endgroup\$
    – Sisyphus
    Feb 7, 2021 at 10:14
3
\$\begingroup\$

><>, 22 20 bytes

-2 bytes thanks to Jo King

i:2%?\
?$~$~\0(?o$6%

Try it online! or verify all test cases

Explanation

i:2%?\              main loop:
i                     Read a character from the input and push it onto the stack
 :2%?                 If it's not a multiple of two (")" or EOF),
     \                  Swap to the branch
                      IP wraps around, repeat

?$~$~\0(?o$6%       branch:
     \                Entrance
      0(?             If the character is less than 0 (EOF),
         o$             Output the result and error
                      Otherwise,
          $             Check the middle value
?          6%           If it's not a multiple of 6 (">"), [the IP wraps around]
 $         $              Reverse the other two values
  ~                     Pop the right value
   $~                   Remove the "(" from the stack
     \                  Return to the main loop 

\$\endgroup\$
1
  • \$\begingroup\$ Oh right, i guess you don't need stack stacks, lol \$\endgroup\$
    – Jo King
    Aug 31, 2021 at 23:58
2
\$\begingroup\$

Stax, 23 bytes

ü▼2δ`▀ÆH╞íq`º╪ ≈♀ô☺ßX→ú

Run and debug it

Uses the same regex as tsh's Javascript solution, and replaces till a fixed point is reached.

Explanation

"\((.>)?(.)(<.)?\)".$2RgiH
                       g   generator:
                        i  apply the following till invariant:
"\((.>)?(.)(<.)?\)".$2     regex replace with second capture group
                         H take last generated value
\$\endgroup\$
2
\$\begingroup\$

Wolfram Language (Mathematica), 54 bytes

#//._[a___,"(",b_,c_,d_,")",e___]->a.If[c=="<",b,d].e&

Try it online!

Input a list of characters.

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

Python 3.8 (pre-release), 73 bytes

f=lambda s:~(q:=s.rfind('('))and f(s[:q]+s[q+ord(s[q+2])-59]+s[q+5:])or s

Try it online!

Python 3, 77 bytes

def f(s):q=s.rfind('(');return~q and f(s[:q]+s[q+ord(s[q+2])-59]+s[q+5:])or s

Try it online!

Solution without regex. Requires the whole input be parenthesized.

-1 byte thanks to ovs

\$\endgroup\$
2
  • \$\begingroup\$ s[q+ord(s[q-2])-63] saves a byte \$\endgroup\$
    – ovs
    Aug 31, 2021 at 10:08
  • \$\begingroup\$ @ovs I was looking for something like that, thanks! \$\endgroup\$
    – Jitse
    Aug 31, 2021 at 10:09
1
\$\begingroup\$

Charcoal, 27 bytes

F⁻S(≡ι)«≔⊟υθ≡⊟υ>§≔υ±¹θ»⊞υιυ

Try it online! Link is to verbose version of code. Takes input wrapped in ()s. Explanation: Port of @2x-1's answer.

F⁻S(

Remove (s from the input and loop over the remaining characters.

≡ι)«

If the current character is a ) then...

≔⊟υθ

... pop the last character from the stack; and ...

≡⊟υ>

... if the (second) last character was a > then...

§≔υ±¹θ

... overwrite the (third) last character with the originally popped character.

»⊞υι

Otherwise push the current character to the stack.

υ

Print the final remaining character.

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

Java (JDK), 76 bytes

s->{while(s.length()>1)s=s.replaceAll("\\((.>)?(.)(<.)?\\)","$2");return s;}

Try it online!

Credits

\$\endgroup\$
2
  • 1
    \$\begingroup\$ 76 bytes by porting the regex of tsh's JavaScript answer. And maybe you're missed it, but the rules allow the input to be wrapped in parenthesis: "You may alternately accept the whole inputs encased in parens" \$\endgroup\$ Feb 5, 2021 at 13:19
  • \$\begingroup\$ Yes, I missed it. Totally. I was wondering how their regex succeeded (because I tried porting that one regex but without wrapping in parentheses), now I know :p \$\endgroup\$ Feb 5, 2021 at 14:25
1
\$\begingroup\$

Julia, 61 bytes

using tsh's regex

f(x,y=replace(x,r"\((.>)?(.)(<.)?\)"=>s"\2"))=x==y ? x : f(y)

Try it online!

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

APL (Dyalog Unicode), 27 bytes

'\((.>)?(.)(<.)?\)'⎕R'\2'⍣≡

Try it online!

This is longer than rak1507's answer, but I just wanted to use a different approach. Uses the same regex as everyone else.

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

Pip, 24 bytes

L#aaR:`\((...)\)`B@A@>Ba

Try it online!

Explanation

This one is a regex replacement solution:

L#aaR:`\((...)\)`B@A@>Ba
                          a is command-line argument
L#a                       Do the following len(a) times:
   aR:                     In a, replace (and assign back to a)
      `         `          this regex:
       \(                   Literal open paren
         (...)              Three characters (captured in group 1)
              \)            Literal close paren
                           with the result of the following callback function:
                    @>      All but the first character of
                      B     capture group 1
                   A        ASCII code of (the first character of) that string
                 B@         Use that value to index cyclically into group 1
                       a  After the loop, output the value of a

The replacement works because the charcode of < is 60 and the charcode of > is 62. Using those values as modular 0-based indices into a three-character string gives the first character and the last character, respectively--exactly what we need.

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

Pyth, 25 bytes

u:G."(\‹Kž%<—åkí2""\\2

Try it online!


Uses the regex from tsh's Javascript answer. Replaces each match with its second matching group until a fixed point is found and printed.

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

sed -r 4.2.2, 27

Using the same regex as everyone else:

:
s/\((.>)?(.)(<.)?\)/\2/
t

Try it online!

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

Pip, 27 bytes

VaR^"<>"''._R'("({bQ'<?ac}"

Takes input with outer parentheses. Verify all test cases: Try it online!

Explanation

Translates the input into a Pip expression and evaluates it. In Pip, function call syntax is (f a b c), so all we have to do is quote the angle brackets and insert an appropriate function inside each opening parenthesis:

 a                           Command-line input
  R                          Replace
   ^"<>"                      either of < and > with
        ''._                  itself with a single quote prepended
            R                Replace
             '(               open parenthesis with
               "(         "   open parenthesis followed by a string representation of
                 {       }    this function:
                  bQ'<?        If the second argument is equal to <
                       a        then return the first argument
                        c       else return the third argument
V                            Evaluate as Pip code
\$\endgroup\$

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.