20
\$\begingroup\$

Your task is to take a list of arguments that are either integers or operators, and parse them like so:

  1. There is a current operator, which starts as +.

  2. Each time an operator is found, the current operator will change to it.

  3. The possible operators are: "+", "-", "*", "/", and "%", which correspond to their meanings in C and most languages.

  4. There is a running solution kept, which starts at 0.

  5. Each time an integer is found, the solution is modified by the number depending on the operator; e.g. if the operator is "/" then the solution is divided by the number.

  6. If an operation would result in a mixed number (i.e. with a decimal) then it must be floored back to an integer (i.e. the decimal must be chopped off).

  7. Output the final solution.

For example:

The arguments 5 8 25 * 9 6 2 - 104 / 4 7 + 6 % 14 would result in:

  5 8  25 * 9   6    2    - 104  / 4    7      + 6 % 14
0 5 13 38   342 2052 4104   4000   1000 142   148    8  -> 8

The inputs will be as command-line or function arguments, or an equivalent for your language.

Shortest code wins!

\$\endgroup\$
6
  • \$\begingroup\$ when you say meanings in C do you mean exactly as they do in C, or is it okay if % rounds towards -inf instead of 0? \$\endgroup\$
    – Maltysen
    Sep 1, 2015 at 3:35
  • \$\begingroup\$ @Maltysen: Whatever your language does. \$\endgroup\$ Sep 1, 2015 at 3:43
  • 3
    \$\begingroup\$ Can the integers from input be negative? \$\endgroup\$
    – Dennis
    Sep 1, 2015 at 5:03
  • \$\begingroup\$ Points 3 and 6 contradict each other: in C and most languages, integer division rounds towards zero rather than flooring. \$\endgroup\$ Sep 1, 2015 at 6:24
  • \$\begingroup\$ It would be interesting to see another challenge similar to this, but including parenthesis precedence... \$\endgroup\$
    – Sarima
    Sep 2, 2015 at 7:54

17 Answers 17

7
\$\begingroup\$

JavaScript (ES6) 53

A function taking an array as input.

Run the snippet in Firefox to test.

f=a=>a.map(t=>t<'0'?o=t:v=eval(v+o+t)|0,v=0,o='+')&&v

// TEST
out=x=>O.innerHTML=x;

input = [5,8,25,"*",9,6,2,"-",104,"/",4,7,"+",6,"%",14];
out(input.join(' ')+' -> '+f(input));

function go() {
  i=I.value.split(/ +/),out(I.value+' -> '+f(i))
}  
<pre id=O></pre>
Your test:<input id=I><button onclick='go()'>GO</button>

\$\endgroup\$
6
\$\begingroup\$

Pyth - 24 23 22 20 bytes

2 bytes saved thanks to @issacg and 1 thanks to @orlp!

Uses reduce with base case of 0 and checks for ' being in repr to detect string vs. int.

u.xsv++GbH&=bHG+\+QZ

Does not work online because I use full eval which is disabled online for security reasons. Takes input from stdin in a list as such: 5, 8, 25, "*", 9, 6, 2, "-", 104, "/", 4, 7, "+", 6.

\$\endgroup\$
1
  • \$\begingroup\$ You can save 2 bytes by switching from ? to .x, because only the else block can throw an exception, and it will do so every time. You can't use K, anymore, though. u.xsv++GbH&=bHG+\+QZ, specifically. \$\endgroup\$
    – isaacg
    Sep 1, 2015 at 7:04
5
\$\begingroup\$

Julia, 85 83 bytes

s->(o=0;p="+";for i=split(s) isdigit(i)?o=eval(parse("ifloor($o$p$i)")):(p=i)end;o)

This creates an unnamed function that accepts a string as input and returns an integer.

Ungolfed:

function f(s::String)
    # Assign the starting output value o and operator p
    o = 0
    p = "+"

    # Split the input string into an array on spaces
    for i = split(s)
        if isdigit(i)
            # Assign o using string interpolation
            o = eval(parse("ifloor($o $p $i)"))
        else
            # Assign p to the new operator
            p = i
        end
    end
end

Fixed issue and saved 2 bytes thanks to Glen O.

\$\endgroup\$
2
  • \$\begingroup\$ Julia complains that o is not defined when you try to run the function freshly. It tries to run the "o=ifloor..." function in Main, rather than inside the function (see here github.com/JuliaLang/julia/issues/2386 ). Might I suggest s->(o=0;p="+";for i=split(s) isdigit(i)?o=eval(parse("ifloor($o$p$i)")):p=i;end;o)? \$\endgroup\$
    – Glen O
    Sep 1, 2015 at 13:15
  • \$\begingroup\$ @GlenO I don't know how I didn't catch that. :/ Thanks, fixed. \$\endgroup\$
    – Alex A.
    Sep 1, 2015 at 16:00
4
\$\begingroup\$

elisp, 101 bytes

With the arguments passed as a quoted list: e.g. (c '(5 5 * 10))

    (defun c(a)(let((f 0)(o '+))(dolist(x a)(if(not(integerp x))(setf o x)(setq f (eval(list o f x)))))f))

Version with new lines:

    (defun c (a)
      (let ((f 0)
            (o '+))
        (dolist (x a)
          (if (not (integerp x))
              (setf o x) 
            (setq f (eval (list o f x)))))
        f))
\$\endgroup\$
4
\$\begingroup\$

CJam, 24 bytes

0'+ea+{_A,s&O{:O;}?S}%s~

This is a full program that reads the input as command-line arguments.

To try the code online in the CJam interpreter (which doesn't support command-line arguments), replace ea with lS/ to read from simulated STDIN.

How it works

0'+                       Push a 0 and the character '+'.
   ea                     Push the array of command-line arguments.
     +                    Prepend the character to the array.
      {             }%    For each element:
       _                    Push a copy.
        A,s                 Push "0123456789".
           &                Intersect the copy with the string of digits.
             {   }?         If the intersection is non-empty:
            O                 The element is a number. Push O.
              :O;             The element is an operator. Save it in O.
                   S        Push a space.
                      s~  Flatten the array of strings and evaluate it.
\$\endgroup\$
3
\$\begingroup\$

JavaScript, 85 bytes

r=0;o="+";prompt().split(" ").forEach(t=>+t+1?r=parseInt(eval(r+o+ +t)):o=t);alert(r)
\$\endgroup\$
3
  • \$\begingroup\$ why o+ +t? you are building a string anyway, no need to convert to number. Moreover, .forEach has no place in Code Golf: use .map \$\endgroup\$
    – edc65
    Sep 1, 2015 at 9:06
  • \$\begingroup\$ ... and ~~ instead of parseInt (codegolf.stackexchange.com/a/2788/21348) \$\endgroup\$
    – edc65
    Sep 1, 2015 at 9:06
  • \$\begingroup\$ prompt(o="+",r=0).split(" ").forEach(t=>+t+1?r=+eval(r+o+ +t):o=t);alert(r) --> 75 bytes. \$\endgroup\$ Sep 1, 2015 at 16:57
3
\$\begingroup\$

Lua, 142 bytes

function f(s)o="+"r=0 for c in s:gmatch"%S+" do if tonumber(c)~=nil then loadstring("r=r"..o..c)() else o=c end r=math.floor(r)end print(r)end

Ungolfed:

function f(s)
    o="+" --original operator
    r=0 --return value
    for c in s:gmatch"%S+" do --split by spaces
        if tonumber(c)~=nil then --check if the current character is a number
            loadstring("r=r"..o..c)() --appends the current operator and current character ex "r=r+5" and then evaluates as another Lua script 
        else 
            o=c --if the character is not a number, it is the new operator
        end
        r=math.floor(r) --floor after each operation
    end 
    print(r) --print the result
end
\$\endgroup\$
3
\$\begingroup\$

Powershell, 57 bytes

$o="+"
$args|%{$r=iex "$r$o$_"
if(!$?){$o=$_}$r-=$r%1}
$r

ungolfed;

$operator="+"
$args | ForEach-Object
{
    $result = Invoke-Expression "$result $operator $_"
    if(!$?)
    {
        $operator=$_
    }
    $result -= $result % 1
}
$result

If the implicit variable in the for-each is an operator rather than number, Invoke-Expression (POSH's eval()) will fail and the execution status $? will be false.

Floor in POSH is unwieldy - $foo=[math]::floor($foo) and $foo-=$foo%1 was the golfiest alternative I could think of.

\$\endgroup\$
1
  • \$\begingroup\$ Nice. I read it a little more literally by assuming a string input and parsing it on spaces, then ifing on digits, but essentially the same. 89 Bytes $o="+";$r=0;$args-split'\s+'|%{if($_-match'^\d+$'){$r=iex $r$o$_;$r-=$r%1}Else{$o=$_}};$r \$\endgroup\$ Sep 1, 2015 at 18:21
3
\$\begingroup\$

GNU Sed (with the eval extension, + dc), 102

(Score includes +1 for the -r option to sed.)

s/.*/0 + &p/
s/([-+/*%]) ([0-9]+)/\2 \1/g
:
s/([-+/*%] )([0-9]+ )([0-9]+)/\1\2\1\3/
t
s/.*/dc<<<'&'/e

Transforms the input expression to reverse polish notation, and then uses dc to evaluate it.

Test output:

$ sed -rf calclist.sed <<< '5 8 25 * 9 6 2 - 104 / 4 7 + 6 % 14'
8
$ 
\$\endgroup\$
2
\$\begingroup\$

CJam, 34 bytes

'+0lS/{"+-*/%"1$#){@;\}{i2$~}?}/\;

Try it online

I thought this was going to be fairly reasonable. But I wasn't fast enough posting it to be the shortest CJam answer at least for a moment. :(

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

Python 3 - 131 bytes 129 bytes 121 bytes 116 Bytes

Thanks to Maltysen for shaving off two bytes, Beta Decay for shaving off 8, and Steven Rumbalski for shaving off 5.

def f(x):
    a,b="+",0
    for i in x:
        if i in"+-*/%":a=i
        else:b=int(eval(str(b)+a+i))
    return b

I'm trying to figure out a way to reduce the length of the if statement, but for now this seems about as golfed as I can get it. Takes input as a list.

\$\endgroup\$
14
  • \$\begingroup\$ you can save some bytes on indentation and replacing int with //1 \$\endgroup\$
    – Maltysen
    Sep 1, 2015 at 3:46
  • \$\begingroup\$ also, why the parens in the `if? \$\endgroup\$
    – Maltysen
    Sep 1, 2015 at 3:47
  • \$\begingroup\$ @ Maltysen whoops, I forgot I didn't need the parentheses in the if statement. Thanks. I don't think that using //1 will be allowed, though I didn't think to use it, as it seems to leave a trailing 0 (e.g. 10.0) which I don't think is allowed. \$\endgroup\$
    – cole
    Sep 1, 2015 at 3:53
  • \$\begingroup\$ i don't think you need that space between in and the quote. \$\endgroup\$
    – Maltysen
    Sep 1, 2015 at 3:56
  • \$\begingroup\$ You could save some bytes by assuming that list is passed in the function arguments and getting rid of .split(). \$\endgroup\$
    – Beta Decay
    Sep 1, 2015 at 7:59
2
\$\begingroup\$

Bash, 69

set -f
for t in $*
do
((1${t}1>2))&&((r${o-+}=$t))||o=$t
done
echo $r

This only works with non-negative integers - its not clear in the question if this is ok or not.

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

Groovy, 79 bytes

def f(x,a=0,b='+'){x.each{z->a=z=~/\d/?Eval.me(a+b+z)as int:a;b=z=~/\d/?b:z};a}

Demo:

groovy> f([5,8,25,'*',9,6,2,'-',104,'/',4,7,'+',6,'%', 14])
Result: 8

Ungolfed:

def f(x, a=0, b='+') {                                   
    x.each {z->
        a = z =~ /\d/ ? Eval.me(a+b+z) as int : a
        b = z =~ /\d/ ? b : z
    }
    a
}
\$\endgroup\$
1
\$\begingroup\$

Perl 5.10+, 52 bytes

perl -E '$o="+";/\D/?$o=$_:eval"\$x=int\$x$o$_"for@ARGV;say$x'

Demo:

$ perl -E '$o="+";/\D/?$o=$_:eval"\x=int\$x$o$_"for@ARGV;say$x' 5 8 25 \* 9 6 2 - 104 / 4 7 + 6 % 14
8

(Note that * has to be escaped in my shell so it's not interpreted as a glob pattern.)

Ungolfed:

$o="+";                      # Start with addition
/\D/ ? $o=$_                 # If not a number, update the current operator
     : eval"\$x=int\$x$o$_"  # Otherwise, make a string like '$x=int$x+1' and eval it
for@ARGV;                    # Repeat for each item in the argument list
say$x                        # Print the result
\$\endgroup\$
1
\$\begingroup\$

C#, 132 165 168 bytes

This function assumes the input is valid. This is tough for C# given there's no eval equivalent.

Thanks edc65 for saving 33 bytes!

Indented for clarity.

int C(string[]a){
    int o=1,r=0,n;
    foreach(var b in a)
        n=int.TryParse(b,out n)
            ?r=o<0?r%n
              :o<1?r*n
              :o<2?r+n
              :o<4?r-n
                  :r/n
            :o=b[0]-42;
    return r;
}
\$\endgroup\$
3
  • \$\begingroup\$ You can take out most of the newlines. \$\endgroup\$ Sep 1, 2015 at 23:47
  • \$\begingroup\$ I haven't counted any newlines or insignificant whitespace. \$\endgroup\$ Sep 2, 2015 at 3:40
  • 1
    \$\begingroup\$ 132 using ?: -->int C(string[]a){int o=1,r=0,n;foreach(var b in a)n=int.TryParse(b,out n)?r=o<0?r%n:o<1?r*n:o<3?r+n:o<5?r-n:r/n:o=b[0]-42;return r;} \$\endgroup\$
    – edc65
    Sep 4, 2015 at 13:13
1
\$\begingroup\$

Ruby, 59 bytes

a=0
o=?+
gets.split.map{|s|s=~/\d/?a=eval([a,s]*o):o=s}
p a

Test run:

$ ruby calc.rb <<< "5 8 25 * 9 6 2 - 104 / 4 7 + 6 % 14"
8
\$\endgroup\$
1
\$\begingroup\$

C 122

Thanks to @ceilingcat for finding a much better (and shorter) version

#define A atoi(*a):s
o,s;main(z,a)char**a;{for(;*++a;)**a<48?o=**a-43:(1?s=o+6?o-4?~o?o-2?o?:s+A-A*A/A%A);printf("%d",s);}

Try it online!

But if you are compiling it with mingw32 you need to turn off globbing (see https://www.cygwin.com/ml/cygwin/1999-11/msg00052.html) by compiling like this:

gcc x.c C:\Applications\mingw32\i686-w64-mingw32\lib\CRT_noglob.o
\$\endgroup\$
0

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.