28
\$\begingroup\$

Implement a simple integer operation scriptable calculator.

Concept

The accumulator starts at 0 and has operations performed on it. At the end of the program output the value of the accumulator.

Operations:

  • + adds 1 to the accumulator
  • - subtracts 1 from the accumulator
  • * multiplies the accumulator by 2
  • / divides the accumulator by 2

Sample script

The input ++**--/ should give the output 3.

Example implementation

def calc(s)
    i = 0
    s.chars.each do |o|
        case o
            when '+'
                i += 1
            when '-'
                i -= 1
            when '*'
                i *= 2
            when '/'
                i /= 2
        end
    end
    return i
end

Rules

  • This is , so lowest answer in bytes wins, but is not selected.
  • Creative implementations are encouraged.
  • Standard loopholes are prohibited.
  • You get the program via stdin or arguments, and you can output the answer via return value or stdout.
  • Have fun.
  • Division truncates down because it is integer division.
  • The program -/ returns -1.

Test cases

*///*-*+-+
-1
/*+/*+++/*///*/+-+//*+-+-/----*-*-+++*+**+/*--///+*-/+//*//-+++--++/-**--/+--/*-/+*//*+-*-*/*+*+/+*-
-17 
+++-+--/-*/---++/-+*-//+/++-*--+*+/*/*/++--++-+//++--*/***-*+++--+-*//-*/+*/+-*++**+--*/*//-*--**-/-*+**-/*-**/*+*-*/--+/+/+//-+*/---///+**////-*//+-+-/+--/**///*+//+++/+*++**++//**+**+-*/+/*/*++-/+**+--+*++++/-*-/*+--/++*/-++/-**++++/-/+/--*/-/+---**//*///-//*+-*----+//--/-/+*/-+++-+*-*+*+-/-//*-//+/*-+//+/+/*-/-/+//+**/-****/-**-//+/+-+/*-+*++*/-/++*/-//*--+*--/-+-+/+/**/-***+/-/++-++*+*-+*+*-+-//+/-++*+/*//*-+/+*/-+/-/*/-/-+*+**/*//*+/+---+*+++*+/+-**/-+-/+*---/-*+/-++*//*/-+-*+--**/-////*/--/*--//-**/*++*+/*+/-+/--**/*-+*+/+-*+*+--*///+-++/+//+*/-+/**--//*/+++/*+*////+-*-//--*+/*/-+**/*//+*+-//+--+*-+/-**-*/+//*+---*+//*/+**/*--/--+/*-*+*++--*+//+*+-++--+-*-*-+--**+/+*-/+*+-/---+-*+-+-/++/+*///*/*-+-*//-+-++/++/*/-++/**--+-////-//+/*//+**/*+-+/+/+///*+*///*-/+/*/-//-*-**//-/-+--+/-*--+-++**++//*--/*++--*-/-///-+/+//--+*//-**-/*-*/+*/-*-*//--++*//-*/++//+/-++-+-*/*-+++**-/-*++++**+-+++-+-***-+//+-/**-+/*+****-*+++*/-*-/***/-/*+/*****++*+/-/-**-+-*-*-++**/*+-/*-+*++-/+/-++*-/*-****-*
18773342
\$\endgroup\$
  • 2
    \$\begingroup\$ So... it's not strictly integer, since / can yield non-integers. \$\endgroup\$ – Conor O'Brien Aug 28 '16 at 22:52
  • 2
    \$\begingroup\$ Then you should specify this explicitly. \$\endgroup\$ – Conor O'Brien Aug 28 '16 at 22:55
  • 5
    \$\begingroup\$ What should -/ return? \$\endgroup\$ – Dennis Aug 28 '16 at 23:32
  • 4
    \$\begingroup\$ I can't help but notice that the snippet of code featured on the home page of rust-lang solves this challenge. \$\endgroup\$ – Zwei Aug 29 '16 at 3:23
  • 4
    \$\begingroup\$ Please add more test cases. \$\endgroup\$ – Martin Ender Aug 29 '16 at 7:38

40 Answers 40

28
\$\begingroup\$

Python 2, 48 bytes

i=0
for c in input():exec"i=i%s2&-2"%c
print i/2

Does +2, -2, *2, or /2. By doing +2 and -2 rather than +1 and -1, we're working in doubled units, so the final output needs to be halved. Except, the floor-division / now needs to round down to a multiple of 2, which is done with &-2.

\$\endgroup\$
  • \$\begingroup\$ This is brilliant! If you want to post it yourself a CJam port of this would currently be leading the challenge: 0q{2\~-2&}/2/ (2\~ evals the operator with second operand 2, -2& is the bitwise AND, 2/ is the final division by two. q{...}/ is a foreach over the input and 0 is just the initial value.) \$\endgroup\$ – Martin Ender Aug 29 '16 at 10:06
  • \$\begingroup\$ You can post it, I don't know CJam. \$\endgroup\$ – xnor Aug 29 '16 at 10:07
  • \$\begingroup\$ Really clever! Ported to ES6 this would easily outperform my answer \$\endgroup\$ – edc65 Aug 29 '16 at 10:19
  • \$\begingroup\$ Brilliant use of python. Learned something new from this. \$\endgroup\$ – Jacobr365 Aug 29 '16 at 13:25
12
\$\begingroup\$

Haskell, 51 bytes

x#'+'=x+1
x#'-'=x-1
x#'*'=x*2
x#_=div x 2 
foldl(#)0

Usage example: foldl(#)0 $ "++**--/" -> 3.

\$\endgroup\$
12
\$\begingroup\$

Jelly, 18 17 bytes

‘

’

:2
Ḥ
O0;ṛĿ/

Try it online!

How it works

The first six lines define helper links with indices ranging from 1 to 6; they increment, do nothing, decrement, do nothing, halve (flooring), and double.

The main link – O0;ṛĿ/ – converts the input characters to their code points (O), prepends a 0 (initial value) to the array of code points 0;, then reduces the generated array as follows.

The initial value is the first element of the array, i.e., the prepended 0. The quicklink ṛĿ is called for every following element in the array, with the last return value as left argument and the current element as right one. It inspects its right argument () and evaluates the link with that index monadically (Ŀ), thus applying the desired operation.

\$\endgroup\$
  • 10
    \$\begingroup\$ This looks like the jelly answer with the most newlines \$\endgroup\$ – Conor O'Brien Aug 28 '16 at 23:28
10
\$\begingroup\$

Python 2, 54 bytes

i=0
for c in input():exec"i=i"+c+`~ord(c)%5%3`
print i

Input is taken as a string literal. ~ord(c)%5%3 maps the operators to the corresponding right operands.

Previously, I used hash(c)%55%3 which didn't yield consistent results between different versions of Python. This encouraged me to explore other formulas.

\$\endgroup\$
  • \$\begingroup\$ doesn't seem to work... \$\endgroup\$ – Destructible Lemon Aug 29 '16 at 1:18
  • \$\begingroup\$ 55,3 and 65,4 are the two shortest for double mod of hash in python 2 \$\endgroup\$ – Jonathan Allan Aug 29 '16 at 1:18
  • \$\begingroup\$ @DestructibleWatermelon does for me: ideone \$\endgroup\$ – Jonathan Allan Aug 29 '16 at 1:24
  • \$\begingroup\$ I think hash is Python version specific - ideone uses 2.7.10 which gives [1, 1, 2, 2] as the four mappings, whereas locally on 2.7.12 I get [2, 0, 1, 0] \$\endgroup\$ – Sp3000 Aug 29 '16 at 1:29
  • 1
    \$\begingroup\$ it works on ideone, but not on my computers python. Probably version dependent, in which case version should be noted EDIT: ninja'd :/ \$\endgroup\$ – Destructible Lemon Aug 29 '16 at 1:30
10
\$\begingroup\$

S.I.L.O.S, 133 211 bytes

:s
def : lbl G GOTO
readIO
i-46
if i a
i+2
if i b
i+2
if i c
i+1
if i d
G e
:a
G v
:p
a-1
a/2
G o
:v
a+1
if a p
a-1
j=a
j/2
k=j
k*2
k-a
a/2
if k t
G o
:t
a-1
:o
G s
:b
a-1
G s
:c
a+1
G s
:d
a*2
G s
:e
printInt a

Takes the ASCII codes of operators.

Try it online with test cases:
-/
++**--/
*///*-*+-+

\$\endgroup\$
  • \$\begingroup\$ is loadLine golfier? \$\endgroup\$ – Rohan Jhunjhunwala Aug 29 '16 at 1:59
  • \$\begingroup\$ The OP clarified; -/ should return -1, not 0. \$\endgroup\$ – Dennis Aug 29 '16 at 2:53
  • \$\begingroup\$ @Dennis fixed. Added a lot of bytes though :/ \$\endgroup\$ – betseg Aug 29 '16 at 11:10
9
\$\begingroup\$

Turing Machine - 23 states (684 bytes)

Try it here - permalink

0 * * r 0
0 _ . l 1
1 * * l 1
1 _ * l 2
2 * 0 r 3
3 _ * r 3
3 + _ l +
3 - _ l -
3 x _ l x
3 / _ l /
+ _ * l +
+ * * * 4
4 - * l 5
4 _ 1 r 6
4 0 1 l 7
4 1 0 l 4
- _ * l -
- * * * 5
5 - * l 4
5 _ * r 8
5 0 1 l 5
5 1 0 l 7
x * * l x
x 1 0 l 9
x 0 0 l a
9 _ 1 r 6
9 1 1 l 9
9 0 1 l a
a _ _ r 6
a 1 0 l 9
a 0 0 l a
/ _ * l /
/ * * l b
b * * l b
b _ * r c
c 0 0 r d
c 1 0 r e
d * * l 7 
d 0 0 r d
d 1 0 r e
e _ * l 7
e - * l 4
e 0 1 r d
e 1 1 r e
8 * * r 8
8 - _ r 3
8 _ - r 3
7 * * l 7
7 _ * r f
f 0 _ r f
f 1 * r 6
f * _ l g
g * 0 r 6
6 * * r 6
6 _ * r 3
3 . _ l h
h _ * l h
h - _ l i
h * * l halt
i * * l i
i _ - r halt

Input should not contain any '*' since it is a special character in Turing machine code. Use 'x' instead. Outputs the answer in binary.

Unobfuscated Code

init2 * * r init2
init2 _ . l init0
init0 * * l init0
init0 _ * l init1
init1 * 0 r readop
readop _ * r readop
readop + _ l +
readop - _ l -
readop x _ l x
readop / _ l /
+ _ * l +
+ * * * inc
inc - * l dec
inc _ 1 r return
inc 0 1 l zero
inc 1 0 l inc
- _ * l -
- * * * dec
dec - * l inc
dec _ * r neg
dec 0 1 l dec
dec 1 0 l zero
x * * l x
x 1 0 l x1
x 0 0 l x0
x1 _ 1 r return
x1 1 1 l x1
x1 0 1 l x0
x0 _ _ r return
x0 1 0 l x1
x0 0 0 l x0
/ _ * l /
/ * * l //
// * * l //
// _ * r div
div 0 0 r div0
div 1 0 r div1
div0 * * l zero 
div0 0 0 r div0
div0 1 0 r div1
div1 _ * l zero
div1 - * l inc
div1 0 1 r div0
div1 1 1 r div1
neg * * r neg
neg - _ r readop
neg _ - r readop
zero * * l zero
zero _ * r zero1
zero1 0 _ r zero1
zero1 1 * r return
zero1 * _ l zero2
zero2 * 0 r return
return * * r return
return _ * r readop
readop . _ l fin
fin _ * l fin
fin - _ l min
fin * * l halt
min * * l min
min _ - r halt

Explanation of the states:

Initialization:
These states are visited once at the beginning of each run, starting with init2

  • init2: Move all the way to the right and put a '.'. That way the TM knows when to stop. Change to 'init0'.
  • init0: Move all the back to the left until the head reads a space. Then move one cell to the left. Change to 'init1'.
  • init1: Put a zero and move one cell to the right and change to 'readop'.

Reading instructions:
These states will be visited multiple times throughout the program

  • readop: Moves all the way to the right until it reads an operator or the '.'. If it hits an operator, change to the corresponding state (+,-,x,/). If it hits a '.', change to state 'fin'.

  • return: Returns the head to the empty space between the running total and the operators. Then changes to 'readop'.

Operations:
These operations do the actual dirty work

  • +: Move to the left until the head reads any non- whitespace character. If this character is a '-', move left and change to 'dec'. Otherwise, change to 'inc'.

  • -: Similar to '+', except change to 'inc' if there is a '-' and 'dec' otherwise.

  • inc: If the digit under the head is a 0 (or a whitespace), change it to 1 and change to 'zero'. If the digit is a 1, change it to 0, then repeat on the next digit.

  • dec: Similar to inc, except 1 goes to 0, 0 goes to 1, and if the head reads a whitespace, change to 'neg'.

  • x, x0, x1: Bitshift the number one to the left. Change to 'return'.

  • /, //, div, div0, div1: Move all the way to the right of the number, then bitshift one to the right. If there is a '-', change to 'inc'. This simulates rounding down negative numbers. Otherwise, change to 'zero'

  • neg: Place a '-' after the number then change to 'readop'

  • zero, zero1, zero2: Remove leading zeros and change to 'readop'

Cleanup: Makes the output presentable

  • fin, min: Move the '-' in front of the number if necessary. Halt.
\$\endgroup\$
  • 1
    \$\begingroup\$ Thought reading this code was very very cool. So thanks for brightening up my day. \$\endgroup\$ – Jacobr365 Aug 30 '16 at 12:58
8
\$\begingroup\$

Perl 6, 53  52 bytes

{([Ro] %(<+ - * />Z=>*+1,*-1,* *2,*div 2){.comb})(0)}

{[Ro](%(<+ - * />Z=>*+1,*-1,*×2,*div 2){.comb})(0)}

Explanation:

# bare block lambda that has one implicit parameter 「$_」
{
  (
    # reduce the code refs using ring operator 「∘」 in reverse 「R」
    [R[o]]

      # produce a hash from:
      %(

        # list of pairs of "operator" to code ref
        # ( similar to 「'+' => { $^a + 1 }」 )

          # keys
          < + - * / >

        # keys and values joined using infix zip operator 「Z」
        # combined with the infix Pair constructor operator 「=>」
        Z[=>]

          # values (Whatever lambdas)
          * + 1,
          * - 1,
          * × 2, # same as 「* * 2」
          * div 2,

      ){

        # split the block's argument into chars
        # and use them as keys to the hash
        # which will result in a list of code refs
        .comb

      }

  # call composed code ref with 0
  )(0)
}

Usage:

my $input = '++**--/'
my $output = {[Ro](%(<+ - * />Z=>*+1,*-1,*×2,*div 2){.comb})(0)}.( $input );
say $output; # 3
say $output.^name; # Int
\$\endgroup\$
7
\$\begingroup\$

C, 63 62 57 bytes

s,t;c(char*x){for(;*x;s+=t<4?t?2-t:s:-s>>1)t=*x++%6;s=s;}

Wandbox

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

05AB1E, 20 bytes

Thanks to Enigma for fixing the -/-bug!

For 16 bytes if it wasn't integer division: Î"+-*/""><·;"‡.V.

Î…+-*"><·"‡'/"2÷":.V

Explanation:

Î                      # Push 0, which is our starting variable, and input
 …+-*                  # Push the string "+-*"
     "><·"             # Push the string "><·"
          ‡            # Transliterate. The following changes:
                           "+" -> ">"
                           "-" -> "<"
                           "*" -> "·"
           '/"2÷":     # Replace "/" by "2÷"
                  .V   # Evaluate the code as 05AB1E code...
                           '>' is increment by 1
                           '<' is decrement by 1
                           '·' is multiply by 2
                           '2÷' is integer divide by two
                       # Implicitly output the result

Uses the CP-1252 encoding. Try it online!

\$\endgroup\$
  • \$\begingroup\$ The OP clarified; -/ should return -1, not 0. \$\endgroup\$ – Dennis Aug 29 '16 at 3:04
  • \$\begingroup\$ The negative number division issue could be fixed with Î…+-*"><·"‡'/"2÷":.V for the same byte count. \$\endgroup\$ – Emigna Aug 29 '16 at 8:46
  • \$\begingroup\$ @Dennis Fixed the problem. \$\endgroup\$ – Adnan Aug 29 '16 at 9:59
  • \$\begingroup\$ @Emigna Thanks :) \$\endgroup\$ – Adnan Aug 29 '16 at 10:00
5
\$\begingroup\$

JavaScript ES6, 80 68 bytes

k=>[...k].reduce((c,o)=>+{"+":c+1,"-":c-1,"*":c*2,"/":c/2|0}‌​[o],0)

Saved a whopping 12 bytes thanks to Neil!

\$\endgroup\$
  • \$\begingroup\$ Second answer would be more readable if you removed the "c"+ and wrote "c+1 c-1 c*2 c/2|0".split etc. \$\endgroup\$ – Neil Aug 28 '16 at 23:42
  • \$\begingroup\$ For the first answer, why not write o=>c=[c+1,c-1,c*2,c/2|0]["+-*/".indexOf(o)], or I think you can then go on to save a further byte using o=>c={"+":c+1,"-":c-1,"*":c*2,"/":c/2|0}[o]. \$\endgroup\$ – Neil Aug 28 '16 at 23:46
  • \$\begingroup\$ k=>[...k].reduce((c,o)=>+{"+":c+1,"-":c-1,"*":c*2,"/":c/2|0}[o],0) might work out even shorter still, but I've lost count... \$\endgroup\$ – Neil Aug 28 '16 at 23:48
  • \$\begingroup\$ @Neil Ah, yes, I forgot about this \$\endgroup\$ – Conor O'Brien Aug 28 '16 at 23:51
  • 1
    \$\begingroup\$ You somehow got to zero-width characters between } and [o], so this is actually only 66 bytes long. Also, the OP clarified; -/ should return -1, not 0. \$\endgroup\$ – Dennis Aug 29 '16 at 2:59
5
\$\begingroup\$

Ruby, 48 44 42 + 1 = 43 bytes

+1 byte for -n flag. Takes input on STDIN.

i=0
gsub(/./){i=i.send$&,"+-"[$&]?1:2}
p i

See it on ideone (uses $_ since ideone doesn't take command line flags): http://ideone.com/3udQ3H

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

PHP 76 Bytes

for(;$c=$argv[1][$n++];)eval('$s=floor($s'.$c.(2-ord($c)%11%3).');');echo$s;
\$\endgroup\$
4
\$\begingroup\$

Python 2, 58 56 bytes

-2 bytes thanks to @Lynn

r=0
for c in input():exec'r=r'+c+`2-ord(c)%11%3`
print r

The ordinals of the characters +-*/ are 43,45,42,47 modulo 11 these are 10,1,9,3 modulo 3 those are 1,1,0,0, 2 less those are 1,1,2,2 giving the amounts we need for each operation: r=r+1, r=r-1, r=r*2, and r=r/2


Previous:

r=0
for c in input():exec'r=r'+c+`(ord(c)%5==2)+1`
print r
\$\endgroup\$
  • \$\begingroup\$ How about 2-ord(c)%11%3? \$\endgroup\$ – Lynn Aug 29 '16 at 2:36
  • \$\begingroup\$ @Lynn Well I'll take it if its OK with you? (but really think it's enough of a change you could post it) \$\endgroup\$ – Jonathan Allan Aug 29 '16 at 2:41
  • 2
    \$\begingroup\$ Go ahead :) ---- \$\endgroup\$ – Lynn Aug 29 '16 at 2:42
4
\$\begingroup\$

Mathematica, 83 73 70 bytes

10 bytes saved due to @MartinEnder.

(#/*##2&@@#/.Thread[{"+","-","*","/"}->{#+1&,#-1&,2#&,⌊#/2⌋&}])@0&

Anonymous function. Takes a list of characters as input and returns a number as output. Golfing suggestions welcome.

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

S.I.L.O.S, 175 164 bytes

loadLine
a=256
o=get a
lbla
a+1
o-42
p=o
p-1
p/p
p-1
r-p
s=o
s-3
s/s
s-1
r+s
m=o
m/m
m-2
m|
r*m
t=r
t%2
d=o
d-5
d/d
d-1
t*d
d-1
d|
r-t
r/d
o=get a
if o a
printInt r

Try it online!

Sane input method. Correct integer division (round towards -infinity).

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

C#, 87 81 bytes

int f(string s){int i=0;foreach(var c in s)i=c<43?i*2:c<46?i+44-c:i>>1;return i;}

Ungolfed:

int f(string s)
{
    int i = 0;

    foreach (var c in s)
        i = c < 43 ? i * 2
          : c < 46 ? i + 44 - c
          : i >> 1;

    return i;
}

Input is assumed to be valid. Division by two is done by shifting right one bit, because regular division always rounds towards zero, and bit shifting always rounds down. Increment and decrement make handy use of the 1 distance between the ASCII codes for + and -.

\$\endgroup\$
  • \$\begingroup\$ Some love for new C#6 syntax and aggregate method of Linq? int f(string s)=>s.Aggregate(0,(i,c)=>c<43?i*2:c<46?i+44-c:i>>1); (65 bytes) \$\endgroup\$ – Cyril Gandon Aug 30 '16 at 14:38
  • \$\begingroup\$ @CyrilGandon as far as I'm aware that would have to include the "using System.Linq;", making it 19 longer and putting it at 84 bytes. Which is why I didn't do it. \$\endgroup\$ – Scepheo Aug 30 '16 at 15:12
4
\$\begingroup\$

Javascript (ES6), 57 bytes (array) / 60 bytes (integer)

Returning an array of all intermediate results:

o=>[...o].map(c=>x=[x>>1,x+1,x*2,x-1][eval(2+c+3)&3],x=0)

For instance, the output for "++**--/" will be [1, 2, 4, 8, 7, 6, 3].

Returning only the final result:

o=>[...o].reduce((x,c)=>[x>>1,x+1,x*2,x-1][eval(2+c+3)&3],0)

How it works

Both solutions are based on the same idea: using the perfect hash function eval(2+c+3)&3 to map the different operator characters c in [0, 3].

 operator | eval(2+c+3)  | eval(2+c+3)&3
----------+--------------+---------------
    +     |  2+3 = 5     |    5 & 3 = 1
    -     |  2-3 = -1    |   -1 & 3 = 3
    *     |  2*3 = 6     |    6 & 3 = 2
    /     |  2/3 ~= 0.67 | 0.67 & 3 = 0
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 57

a=>[...a].map(c=>a=c<'+'?a<<1:c<'-'?-~a:c<'/'?~-a:a>>1)|a

Note: the initial value for accumulator is the program string, using bit operations (~, >>, <<, |) it is converted to 0 at first use.

As a side note, the clever answer of @xnor would score 40 ported to javascript:

a=>[...a].map(c=>a=eval(~~a+c+2))&&a>>1

(if you like this, vote for him)

Test

f=a=>[...a].map(c=>a=c<'+'?a<<1:c<'-'?-~a:c<'/'?~-a:a>>1)|a

function update() {
  O.textContent = f(I.value);
}

update()
<input value='++**--/' id=I oninput='update()'><pre id=O></pre>

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

Java, 77 bytes

int f(String s){return s.chars().reduce(0,(r,c)->c<43?r*2:c<46?r+44-c:r>>1);}

Uses java 8 streams.

\$\endgroup\$
  • 1
    \$\begingroup\$ Nice answer, and welcome to the site! I don't know anything about java, but can you change r >> 1 to r>>1 and save 2 bytes? \$\endgroup\$ – DJMcMayhem Aug 30 '16 at 21:15
  • \$\begingroup\$ You're absolutely correct, thanks @DJMcMayhem \$\endgroup\$ – primodemus Aug 30 '16 at 21:21
  • 1
    \$\begingroup\$ Awesome, glad I could help! One more note, I'm counting 77 bytes. Did you happen to include the newline in your byte count? You can take one more byte off since that isn't necessary. \$\endgroup\$ – DJMcMayhem Aug 30 '16 at 21:22
  • \$\begingroup\$ Correct again @DJMcMayhem , aparently wc counts the null-terminanting byte or something... \$\endgroup\$ – primodemus Aug 30 '16 at 21:35
  • 1
    \$\begingroup\$ as you are using java8, why don't define function by using lambda, s->s.chars().reduce(0,(r,c)->c<43?r*2:c<46?r+44-c:r>>1); that will give you 56 bytes \$\endgroup\$ – user902383 Aug 31 '16 at 13:51
3
\$\begingroup\$

GNU sed, 65 59 57 bytes

Edit: 2 bytes shorter thanks to Toby Speight's comments

s/[+-]/1&/g
s/*/2&/g
s:/:d0>@2&:g
s/.*/dc -e"0[1-]s@&p"/e

Run:

sed -f simple_calculator.sed <<< "*///*-*+-+"

Output:

-1

The sed script prepares the input for the dc shell call at the end, the latter accepting the input in Reverse Polish notation. On division, if the number is negative (d0>), the [1-] decrement command stored in register @ is called. Conversion example: + - * / --> 1+ 1- 2* d0>@2/.

\$\endgroup\$
  • \$\begingroup\$ You don't need the quotes around the argument to dc, if there's no spaces, and no files matching the [1-] pattern... \$\endgroup\$ – Toby Speight Aug 30 '16 at 8:31
  • \$\begingroup\$ @TobySpeight In my mind I switched the meaning of s with S. I forgot that it doesn't replace the registry's stack, it pushes onto it, having the contrary effect of what I wanted (since I used it for every /). The quotes are still needed because you have / symbols in it making the string interpreted as a file path :) I shaved 1 byte more by removing the space after the -e. \$\endgroup\$ – seshoumara Aug 30 '16 at 9:20
  • 1
    \$\begingroup\$ dc won't intepret argument of -e as a filename, so you don't need quotes for the / - try it! I think it's a reasonable for a code-golf to require that the current working directory not contain any files beginning with 01s@ or 0-s@. \$\endgroup\$ – Toby Speight Aug 30 '16 at 14:15
  • \$\begingroup\$ @TobySpeight you were right about -e regarding /, however the quotes are still required as I just saw now. The > is interpreted directly by the shell as a redirect operator I think, since I got this error: cannot create @2/d0: Directory nonexistent \$\endgroup\$ – seshoumara Aug 30 '16 at 15:55
  • \$\begingroup\$ Ah, yes, I didn't consider the >. You do need quotes, after all. Apologies for (attempting to) mislead! And, although adding a backslash looks like one character, it needs to be doubled in a s/// replacement, so no benefit there... \$\endgroup\$ – Toby Speight Aug 30 '16 at 16:17
3
\$\begingroup\$

PHP, 75 bytes

This uses a modified version of Jörg Hülsermann's answer.

eval(preg_replace('~.~','$s=($s\0(2-ord("\0")%11%3))|0;',$argv[1]));echo$s;

It heavily relies on string substitution, using a simple regular expression (~.~).

The variable $s is re-assigned with the new value for each character. At the end, it outputs the result.


Note: This is meant to be executed using the -r flag.


Try it here:

if('\0' == "\0")
{
	$argv = Array($s = 0, prompt());
	function preg_replace($pattern, $replacement, $subject)
	{
		$regexp = new RegExp($pattern.replace(new RegExp('~', 'g'), ''), 'g');
		return $subject.replace($regexp, $replacement.split('\0').join('$&'));
	}
	
	function printf($string)
	{
		console.log($string);
	}
	
	function ord($chr)
	{
		return $chr.charCodeAt(0);
	}
}
else
{
    if(!isset($argv))
    {
        $argv = array('', '++*+');
    }
}

eval(preg_replace('~.~','$s=($s\0(2-ord("\0")%11%3))|0;',$argv[1]));printf($s);

Or try on: http://sandbox.onlinephpfunctions.com/code/7d2adc2a500268c011222d8d953d9b837f2312aa

Differences:

  • Instead of echo$s, I'm using sprintf($s). Both perform the same action on numbers. Since this is just for testing, it is fine.
  • In case there's no passed argument, it will run as if you passed ++*+ as the first argument, which should show 5.
\$\endgroup\$
  • \$\begingroup\$ Yay! The e modifier is back! :D \$\endgroup\$ – Titus Sep 28 '16 at 8:28
  • \$\begingroup\$ @Titus I don't get it. Can you elaborate a little? \$\endgroup\$ – Ismael Miguel Sep 28 '16 at 8:58
  • \$\begingroup\$ PHP before version 7 had a pattern modifier e, which was replaced by preg_replace_callback and could be abused to ... but this isn´t quite that. \$\endgroup\$ – Titus Sep 28 '16 at 13:06
  • \$\begingroup\$ @Titus That patern modifier was used to tell that the output would be actual PHP code, and to try to keep the syntax correct. This here, doesn't use it, but replace every single character with a piece of code to execute, regardless of it's syntax. Bad inputs will cause severe security issues. \$\endgroup\$ – Ismael Miguel Sep 28 '16 at 14:20
  • \$\begingroup\$ I know. But it resembles. \$\endgroup\$ – Titus Sep 28 '16 at 15:19
2
\$\begingroup\$

Batch, 61 bytes

@set n=
@for %%a in (%*)do @set/an=n%%a2^&-2
@cmd/cset/an/2

Translation of @xnor's xcellent Python answer.

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

Pyke, 24 22 bytes

\*\}:\/\e:\+\h:\-\t:0E

Try it here!

Or 12 bytes (noncompetitive)

~:"ht}e".:0E

Try it here!

Add translate node - basically multiple find and replace.

~:           -   "+-*/"
        .:   -  input.translate(^, V)
  "ht}e"     -   "ht}e"
          0E - eval(^, stack=0)
\$\endgroup\$
2
\$\begingroup\$

PHP, 104 102 82 bytes

First version with eval:

$i=0;while($c<9999)eval('$i'.['+'=>'++','-'=>'--','*'=>'*=2','/'=>'>>=1'][$argv[1]{$c++}].';');echo$i;

Second version with ternary operators:

while($o=ord($argv[1]{$c++}))$i=$o<43?$i*2:($o<44?$i+1:($o<46?$i-1:$i>>1));echo$i;

Takes the input string as first argument from the command line.

This "only" works for input strings shorter than 10,000 characters - which should be plenty. Tested with all the test cases, unfortunately can't save on the initialization in the beginning. Second version works with strings of any length and without initialization. :-)

The main element is the eval function which manipulates $i based on a map of arithmetic operations, which are pretty straightforward except for the division. PHP returns a float when using / and intdiv is too many bytes, so we do a right-shift.

Updates

  1. Saved 2 bytes by shortening $i=$i>>1 to $i>>=1 for integer division.
  2. Threw out eval in favor of ternary operators.
\$\endgroup\$
2
\$\begingroup\$

Python 3, 98 66 60 bytes

Thanks Tukkax!

Not as golfy as the other answer, but I can't compete with them without plagiarism.

i=0
for c in input():i+=[1,-i//2,-1,i][ord(c)%23%4]
print(i)

Also, I have a recursive lambda solution as well

73 67 bytes (improved!)

s=lambda x,z=0:s(x[1:],z+[1,-z//2,-1,z][ord(x[0])%23%4])if x else z
\$\endgroup\$
  • \$\begingroup\$ By applying part of your recursive solution to the procedural version: 60 bytes: i=0 for c in input():i+=[1,-i//2,-1,i][ord(c)%23%4] print(i). (not formatted properly of course). Also I think you should mention that you're using Python3. In Python2, input() would evaluate to int(raw_input()). \$\endgroup\$ – Yytsi Aug 29 '16 at 7:11
  • \$\begingroup\$ @TuukkaX doesn't work for z=0 (+- does 1) \$\endgroup\$ – Destructible Lemon Aug 29 '16 at 9:23
  • \$\begingroup\$ oh yes, my mistake. \$\endgroup\$ – Yytsi Aug 29 '16 at 10:21
  • 1
    \$\begingroup\$ Add the title Python3 please. \$\endgroup\$ – Yytsi Aug 29 '16 at 13:48
2
\$\begingroup\$

R, 201 bytes

Golfed

p=.Primitive;"-"="+"=function(x)p("+")(x,1);body(`-`)[[1]]=p("-");"*"="/"=function(x)p("*")(x,2);body(`/`)[[1]]=p("%/%");Reduce(function(f, ...)f(...),rev(mget(strsplit(scan(stdin(),""),"")[[1]])),0,T)

Commented

p = .Primitive                       # Redefine
"-" = "+" = function(x)p("+")(x,1)   # Define - and +
body(`-`)[[1]] = p("-")              # Change the body, what we do to save a byte
"*" = "/" = function(x)p("*")(x,2)   # Same as above
body(`/`)[[1]] = p("%/%")            # Same as above
Reduce(function(f, ...)f(...),       # Function wrapper to evaluate list of func.  
  rev(mget(strsplit(scan(stdin(),""),"")[[1]])), # Strsplit input into list of functions
  init = 0,                                      # Starting Arg = 1
  right = T)                                     # Right to left = True 

Strategy is to refine the +, -, % operators. Split the string then parse the string into a long list of functions, to be fed into Reduce()'s accumulator.

Couldn't golf it anymore. If someone can get b=body<- to work, there could be a few bytes of savings (refine every function with b after "-"="+"="/"="*"). Initially tried to substitute and parse eval, but the order of operations and parentheses were terrifying.

\$\endgroup\$
  • \$\begingroup\$ This is a year later, but I managed to get it down 10 bytes by swapping your approach a bit -- you can drop 8 bytes by removing the space between f, ... in the definition of the Reduce function and getting rid of stdin() in scan but I just tried a naive approach that dropped two more bytes by defining the functions a little differently. tio.run/##XcvLCsMgEAXQrwnO6Gge29B/… \$\endgroup\$ – Giuseppe Jun 7 '17 at 14:39
1
\$\begingroup\$

Lex + C, 78, 74, 73 bytes

The first character is a space.

 c;F(){yylex(c=0);return c;}
%%
\+ c++;
- c--;
\* c*=2;
\/ c=floor(c/2.);

Reads from stdin, returns result.

Compile with lex golfed.l && cc lex.yy.c main.c -lm -lfl, test main:

int main() { printf("%d\n", F()); }
\$\endgroup\$
1
\$\begingroup\$

Javascript (ES5), 127 bytes

function(b){for(a=c=0;a<b.length;++a)switch(b[a]){case"+":++c;break;case"-":--c;break;case"*":c*=2;break;case"/":c/=2}return c}

Ungolfed:

function c(a){
  c=0;
  for(var i=0;i<a.length;++i){
    switch(a[i]){
      case "+":++c;break;
      case "-":--c;break;
      case "*":c*=2;break;
      case "/":c/=2;break;
    }
  }
  return c;
}
\$\endgroup\$
1
\$\begingroup\$

Pyth, 23 bytes

FNQ=Z.v%".&%sZ2_2"N;/Z2

A full program that takes input as a string and prints the result.

This is a port of @xnor's Python answer.

Try it online

How it works

FNQ=Z.v%".&%sZ2_2"N;/Z2   Program. Input: Q. Z initialised as 0
FNQ                       For. For N in Q:
        ".&%sZ2_2"         String. Literal string ".&%sZ2_2"
       %          N        String format. Replace %s with the current operator N
           %sZ2            Operator. Yield Z*2, Z//2, Z+2, Z-2 as appropriate
         .&    _2          Bitwise and. Result of above & -2
     .v                    Evaluate. Yield the result of the expression
   =Z                      Assignment. Assign result of above to Z
                   ;      End. End for loop
                    /Z2   Integer division. Yield Z//2
                          Print. Print the above implicitly 
\$\endgroup\$
  • 1
    \$\begingroup\$ Converting Python to Pyth is mostly a bad idea. u@[yGhG0tG0/G2)CHQ0 19 bytes \$\endgroup\$ – Jakube Aug 29 '16 at 15:34
  • \$\begingroup\$ @Jakube Thanks - I am very new to Pyth, so any advice is appreciated. Feel free to post that as a separate answer, since it is a different approach. \$\endgroup\$ – TheBikingViking Aug 29 '16 at 17:37
1
\$\begingroup\$

PHP, 79 bytes

<?$i=0;switch($_POST['a']){case"+":$i+1;case"-":$i-1;case"/":$i/2;case"*":$i*2}
\$\endgroup\$
  • 2
    \$\begingroup\$ Include the bytecount in your header, remove uneccesary spaces and use 1-letter variable names. \$\endgroup\$ – TuxCrafting Aug 29 '16 at 9:42
  • \$\begingroup\$ Is this even golfed?! :-D \$\endgroup\$ – YetiCGN Aug 29 '16 at 12:50
  • \$\begingroup\$ @TùxCräftîñg I did it. \$\endgroup\$ – Winnie The Pooh Aug 29 '16 at 17:11
  • \$\begingroup\$ You divide and multiply by 1; you need to divide and multiply by 2 \$\endgroup\$ – TuxCrafting Aug 29 '16 at 17:58
  • \$\begingroup\$ @TùxCräftîñg I did it. \$\endgroup\$ – Winnie The Pooh Aug 29 '16 at 17:59

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.