15
\$\begingroup\$

Inspiration. Inverse.

Evaluate a given omnifix expression.

Omnifix is like normal mathematics' infix notation, but with additional copies of each symbol surrounding the arguments. The outer symbols take the place of parentheses, and there is therefore no need for additional parentheses.

You must support addition, subtraction, multiplication, division, and positive real numbers (negative ones can be written -0-n-) within a reasonable range for your language.

Plus and minus must be + and -, but you may use * or × for times and / or ÷ for divide. Other reasonable symbols will be allowed upon request.

Brownie points for explanation and additional features (like additional operations, negative numbers, strings, etc.) Even if your answer does not have these features, feel free to show how it could.

Please provide a link to test your solution if at all possible.

Examples

For clarity, the explanations below use high minus (¯) to indicate negative numbers. You may return negative numbers using any reasonable format.

-5-2-3

+2+×3×2×+8 (+2+×3×2×++2+6+8)

-14--3-1--12 (-4--3-1---14-2-12)

+2.1+×3.5×2.2×+9.8 (+2.1+×3.5×2.2×++2.1+7.7+9.8)

×3×÷-0-6-÷2÷×-9 (×3×÷-0-6-÷2÷××3×÷¯6÷2÷××3ׯ3ׯ9)

÷4÷-3-÷1÷2÷-÷1.6 (÷4÷-3-÷1÷2÷-÷÷4÷-3-0.5-÷÷4÷2.5÷1.6)

\$\endgroup\$
  • 1
    \$\begingroup\$ The explanations below use high minus (`¯`) to indicate negative numbers. You definitely love APL. \$\endgroup\$ – Erik the Outgolfer Jul 27 '17 at 13:16
  • \$\begingroup\$ @EriktheOutgolfer You have a better suggestion? Also, TI-BASIC uses high minus. \$\endgroup\$ – Adám Jul 27 '17 at 13:36
  • \$\begingroup\$ Actually not since -s can be confused with -s while ¯s can't be confused with -s. \$\endgroup\$ – Erik the Outgolfer Jul 27 '17 at 13:37
  • \$\begingroup\$ Bah, I just noticed the real number requirement. So much for my 290-byte integer arithmetic Retina solution... \$\endgroup\$ – Neil Jul 27 '17 at 13:43
  • \$\begingroup\$ @Neil Why don't you post it as an answer? \$\endgroup\$ – Adám Jul 31 '17 at 9:15
4
\$\begingroup\$

C# (.NET Core), 198 197 188 bytes

float O(string s){try{return float.Parse(s);}catch{var f=s[0];int i=s.IndexOf(f,1);float a=O(s.Substring(1,i-1)),b=O(s.Substring(i+1,s.Length-i-2));return f<43?a*b:f<44?a+b:f<46?a-b:a/b;}}

Try it online!

Uses * and /.

A recursive function. It first tries to parse the input string as a float. If it fails, it calls itself recursively passing as arguments the first and second operands and then performs the selected operation on the results.

  • 1 byte saved thanks to Mr. Xcoder!
  • 9 bytes saved thanks to TheLethalCoder!
\$\endgroup\$
  • \$\begingroup\$ IndefOf(f, 1) can be IndexOf(f,1) \$\endgroup\$ – Mr. Xcoder Jul 27 '17 at 12:22
  • 1
    \$\begingroup\$ Use floats instead, use the char codes, when you have them can probably shorten them with > and < in a couple of places. \$\endgroup\$ – TheLethalCoder Jul 27 '17 at 12:24
  • \$\begingroup\$ You can golf a byte changing i+1,s.Length-i-2 to ++i,s.Length+~i. \$\endgroup\$ – Kevin Cruijssen Feb 5 '18 at 12:17
4
\$\begingroup\$

Python 3, 159 158 152 144 136 135 132 bytes

def t(i,a=1):
 while'-'<l[i]!='/':i+=1;a=0
 if a:l[i]='(';i=t(t(i+1));l[i-1]=')'
 return-~i
*l,=input()
t(0)
print(eval(''.join(l)))

Try it online!

Doesn't allow negative numbers (though -0-5- works of course) and requires python operators.

\$\endgroup\$
  • \$\begingroup\$ Can you add a TIO link? \$\endgroup\$ – Adám Jul 27 '17 at 12:07
  • 1
    \$\begingroup\$ while~-(l[i]in'+-*/'):i+=1;a=1 and *l,=input() for 152 bytes \$\endgroup\$ – Felipe Nardi Batista Jul 27 '17 at 12:36
  • 1
    \$\begingroup\$ with all the test cases: link \$\endgroup\$ – Felipe Nardi Batista Jul 27 '17 at 12:40
  • 1
    \$\begingroup\$ 144 bytes \$\endgroup\$ – Felipe Nardi Batista Jul 27 '17 at 12:48
  • 1
    \$\begingroup\$ if a:l[i]='(';i=t(t(i+1));l[i-1]=')' with return-~i for 135 bytes :P \$\endgroup\$ – Felipe Nardi Batista Jul 27 '17 at 13:03
3
\$\begingroup\$

Retina, 290 287 286 bytes

\d+
¦$&$*
¯¦
¯
{`\+([¯¦]1*)\+([¯¦]1*)\+
-$1-¯$2-
-(¯|¦)(1*)-([¯¦]+1*\2)-
-¯$3-¯$1$2-
(×|÷)¯(1*\1)([¯¦]1*\1)
$1¦$2¯$3
צ×[¯¦]1*×|¯¯¦?
¦
¯¦|¦¯
¯
+`-((¯|¦)1*)(1*)-\2\3-
$1
-([¯¦]1*)-[¯¦](1*)-
$1$2
צ1(1*)×([¯¦]1*)×
+צ$1×$2×+$2+
}`÷¦(?=1*÷(¯|¦)(1+)÷)(\2)*1*÷\1\2÷
$1$#3$*
((¯)|¦)(1*)
$2$.3

Try it online! Note: Only capable of integer arithmetic, so some of the test cases have been removed. Accepts and returns negative numbers using the ¯ prefix. Edit: Saved 3 4 bytes thanks to @Cowsquack. Explanation:

\d+
¦$&$*

I needed some way of handling zero, so I use ¦ as a positive number prefix. The numbers are then converted to unary.

¯¦
¯

But negative numbers only need a ¯ prefix.

{`\+([¯¦]1*)\+([¯¦]1*)\+
-$1-¯$2-

Quoting +s gets ugly, so I turn additions into subtractions.

-(¯|¦)(1*)-([¯¦]+1*\2)-
-¯$3-¯$1$2-

If the absolute value of the LHS of a subtraction is less than the RHS, switch them around and negate both sides.

(×|÷)¯(1*\1)([¯¦]1*\1)
$1¦$2¯$3

Also if the LHS of a multiplication or division is negative, negate both sides.

צ×[¯¦]1*×|¯¯¦?
¦

Also if the LHS of a multiplication is zero, then the result is zero. Also, two minuses make a plus.

¯¦|¦¯
¯

But a minus and a plus (or vice versa) make a minus.

+`-((¯|¦)1*)(1*)-\2\3-
$1

Subtract two numbers of the same sign. Do this repeatedly until there are no such subtractions left.

-([¯¦]1*)-[¯¦](1*)-
$1$2

If there's still a subtraction, the signs must be different, so add the numbers together. (But only do this once, since this may reveal a subtraction of two numbers of the same sign again.)

צ1(1*)×([¯¦]1*)×
+צ$1×$2×+$2+

Perform multiplication by repeated addition.

}`÷¦(?=1*÷(¯|¦)(1+)÷)(\2)*1*÷\1\2÷
$1$#3$*

Perform integer division. One of the above steps will have simplified the expression, so loop back until there are no operations left.

((¯)|¦)(1*)
$2$.3

Convert back to decimal.

\$\endgroup\$
  • \$\begingroup\$ Wow, that is – epic. Largest Retina post on PPCG? However, usually QuadR and Retina solutions resemble each other closely. Can I maybe inspire? \$\endgroup\$ – Adám Jul 31 '17 at 10:15
  • \$\begingroup\$ In this line +`-(([¯¦])1*)(1*)-\2\3-, [¯¦] can become ¯|¦ \$\endgroup\$ – Cows quack Jul 31 '17 at 10:18
  • \$\begingroup\$ @Cowsquack Happens three times in fact, thanks! \$\endgroup\$ – Neil Aug 2 '17 at 14:24
  • \$\begingroup\$ There's one you missed ([×÷]) ;) \$\endgroup\$ – Cows quack Aug 2 '17 at 14:30
  • 1
    \$\begingroup\$ @Cowsquack You'd better not find another one, otherwise I'll have to cross out 4... \$\endgroup\$ – Neil Aug 2 '17 at 15:09
2
\$\begingroup\$

QuadR, 33 32 27 bytes

-1 thanks to Cows Quack. -5 thanks to Erik the Outgolfer.

((.)[\d¯\.]+){2}\2
⍎¯1↓1↓⍵M

with the argument/flag

Try it online!

This is equivalent to the 40-byte Dyalog APL solution:

'((.)[\d¯\.]+){2}\2'⎕R{⍕⍎1↓¯1↓⍵.Match}⍣≡

Try it online!

Explanation

(parenthesised text refers to Dyalog APL instead of QuadR)

(){2}\2 the following pattern twice, and the whole match twice too:
  (.) any character
  []+ followed by one or more of the following set of characters:
   \ddigits,
   ¯ high minus (negative sign)
   \. period

(⎕R is Replaced with:)

({} the result of the following anonymous function applied to the namespace ⍵:)

⍵M (⍵.Match) the text of the Match
¯1↓ drop the last character (the symbol + - × or ÷)
1↓ drop the first character (symbol)
 execute as APL code
 ( stringify)

 (⍣≡) repeat the replacement until no more changes happen

\$\endgroup\$
  • \$\begingroup\$ I think you can drop \$\endgroup\$ – Cows quack Jul 31 '17 at 10:23
  • \$\begingroup\$ @Cowsquack You are right about QuadR. ⎕R cannot operate on numeric data. Thanks. \$\endgroup\$ – Adám Jul 31 '17 at 10:24
  • \$\begingroup\$ Shorter regex; -5 bytes. \$\endgroup\$ – Erik the Outgolfer Feb 5 '18 at 13:41
1
\$\begingroup\$

Haskell, 132 chars

(134 bytes, because × and ÷ take two bytes in UTF-8)

f y|(n@(_:_),r)<-span(`elem`['.'..'9'])y=(read n,r)
f(c:d)|(l,_:s)<-f d,(m,_:r)<-f s=(o[c]l m,r)
o"+"=(+)
o"-"=(-)
o"×"=(*)
o"÷"=(/)

Try it online!

f parses as much of the input as it can and yields the result as well as the remaining string (which is empty in the test cases). If that's not rule-conformant, strip the unparseable remainder string with

Haskell, 139 chars

...
g=fst.f
\$\endgroup\$
1
\$\begingroup\$

PHP, 116 114 109 bytes

-5 thanks to Martin Ender

for($s=$argv[$o=1];$o!=$s;)$s=preg_replace('#([*+/-])(([\d.]+|(?R))\1(?3))\1#','($2)',$o=$s);eval("echo$s;");

Uses * for multiplication and / for division. Negative numbers happen to work despite me making no specific attempts for that to be the case.

Try it online!

Ungolfed and Explained

for($s=$argv[$o=1];   # Initialize with $s = input and $o = 1;
    $o!=$s;)          # While $o != $s
    # Set $o to $s and set $s to be $s after this regex replacement:
    $s=preg_replace('#([*+/-])(([\d.]+|(?R))\1(?3))\1#','($2)',$o=$s);
    # i.e., continue to do this replacement until the result is the same on two consecutive
    # steps (no replacement was made)
# Once $o == $s (i.e. no more replacement can be made), eval the result and print
eval("echo$s;"); 

I'll also explain the regex because it's a bit magical:

([*+/-])(([\d.]+|(?R))\1(?3))\1


([*+/-])

First, we want to match any of the four operators: *+/-

([\d.]+|(?R))

Then, we need to match either a number [\d.]+ or another valid omnifix expression (?R).

\1

Then, we match the same operator that was at the beginning.

(?3)

Then we do the same thing we did in group 3: match a number or an omnifix expression.

\1

Finally, match the initial operator again.

Whatever matches this is replaced with ($2). This takes the part inside the surrounding operators and puts it inside brackets, so it looks like normal infix notation.

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

Java 8, 205 bytes

float O(String s){try{return new Float(s);}catch(Exception e){char f=s.charAt(0);int i=s.indexOf(f,1);float a=O(s.substring(1,i)),b=O(s.substring(i+1,s.length()-1));return f<43?a*b:f<44?a+b:f<46?a-b:a/b;}}

Port of @Charlie's C# answer.

Try it online.

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

Perl, 64 53 bytes

Include +1 for -p

perl -pe 's%([*-/])(([\d.]+|(?0))\1(?3))\1%($2)%&&redo;$_=eval' <<< "/4/-3-/1/2/-/"

Accidentally also implements , (throws away the first argument) and sometimes . (append the arguments together). . doesn't work very reliable though since it interferes with decimal point both at the parsing level and at the evaluation level

\$\endgroup\$

Your Answer

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

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