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\$
16
  • 2
    \$\begingroup\$ So... it's not strictly integer, since / can yield non-integers. \$\endgroup\$ Commented Aug 28, 2016 at 22:52
  • 2
    \$\begingroup\$ Then you should specify this explicitly. \$\endgroup\$ Commented Aug 28, 2016 at 22:55
  • 6
    \$\begingroup\$ What should -/ return? \$\endgroup\$
    – Dennis
    Commented Aug 28, 2016 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
    Commented Aug 29, 2016 at 3:23
  • 4
    \$\begingroup\$ Please add more test cases. \$\endgroup\$ Commented Aug 29, 2016 at 7:38

42 Answers 42

1
2
1
\$\begingroup\$

Ruby, 37 bytes

#!ruby -n
gsub(/./){eval"$.=$./2*2#$&2"}
p$./2

The shebang counts as 1 byte. This program uses xnor's creative approach. We avoid the need to initialise an accumulator variable by using $.. As it has a value of 1 after the input is read, we have to clear the LSB before applying each operator. This also requires changing &-2 to /2*2 for the correct precedence.

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

Java 7, 73 bytes

int c(char[]i){int r=0;for(int c:i)r=c<43?r*2:c<46?r+44-c:r>>1;return r;}

Shamelessly stolen from @Scepheo's amazing C# answer and implemented in Java 7.

Ungolfed & test cases:

Try it here.

class M{
  static int c(char[] i){
    int r = 0;
    for(int c : i) {
      r = c < 43
           ? r * 2
           : c < 46
              ? r+44 - c
              : r >> 1;
    }
    return r;
  }

  public static void main(String[] a){
    System.out.println(c("*///*-*+-+".toCharArray()));
    System.out.println(c("/*+/*+++/*///*/+-+//*+-+-/----*-*-+++*+**+/*--///+*-/+//*//-+++--++/-**--/+--/*-/+*//*+-*-*/*+*+/+*-".toCharArray()));
    System.out.println(c("+++-+--/-*/---++/-+*-//+/++-*--+*+/*/*/++--++-+//++--*/***-*+++--+-*//-*/+*/+-*++**+--*/*//-*--**-/-*+**-/*-**/*+*-*/--+/+/+//-+*/---///+**////-*//+-+-/+--/**///*+//+++/+*++**++//**+**+-*/+/*/*++-/+**+--+*++++/-*-/*+--/++*/-++/-**++++/-/+/--*/-/+---**//*///-//*+-*----+//--/-/+*/-+++-+*-*+*+-/-//*-//+/*-+//+/+/*-/-/+//+**/-****/-**-//+/+-+/*-+*++*/-/++*/-//*--+*--/-+-+/+/**/-***+/-/++-++*+*-+*+*-+-//+/-++*+/*//*-+/+*/-+/-/*/-/-+*+**/*//*+/+---+*+++*+/+-**/-+-/+*---/-*+/-++*//*/-+-*+--**/-////*/--/*--//-**/*++*+/*+/-+/--**/*-+*+/+-*+*+--*///+-++/+//+*/-+/**--//*/+++/*+*////+-*-//--*+/*/-+**/*//+*+-//+--+*-+/-**-*/+//*+---*+//*/+**/*--/--+/*-*+*++--*+//+*+-++--+-*-*-+--**+/+*-/+*+-/---+-*+-+-/++/+*///*/*-+-*//-+-++/++/*/-++/**--+-////-//+/*//+**/*+-+/+/+///*+*///*-/+/*/-//-*-**//-/-+--+/-*--+-++**++//*--/*++--*-/-///-+/+//--+*//-**-/*-*/+*/-*-*//--++*//-*/++//+/-++-+-*/*-+++**-/-*++++**+-+++-+-***-+//+-/**-+/*+****-*+++*/-*-/***/-/*+/*****++*+/-/-**-+-*-*-++**/*+-/*-+*++-/+/-++*-/*-****-*".toCharArray()));
  }
}

Output:

-1
-17
18773342
\$\endgroup\$
1
\$\begingroup\$

PHP, 146 138, 133, 131 bytes

$a=$_POST['s'];$i=0;while($a){$c=substr($a,0,1);$i=$c=='+'?$i+=1:($c=='-'?$i-1:($c=='*'?$i*2:round($i/2)));$a=substr($a,1);}echo$i;
  1. Takes argument from $_POST['s'].
  2. Uses ternary operator as "if's".
  3. Takes one character at a time, parse that char in operation, then continue reading next char
  4. EDIT 1: Shortened "while(strlen($a))" to "while($a)"
  5. EDIT 2: Removed some useless paranthesis
  6. EDIT 3. Removed extra space after last "echo"
\$\endgroup\$
7
  • \$\begingroup\$ I think with even PHP 5.5 having reached EOL it's safe to say that nobody SHOULD use PHP 4 anymore, so the "5+" is not necessary, imho. \$\endgroup\$
    – YetiCGN
    Commented Aug 29, 2016 at 13:28
  • \$\begingroup\$ That is ok. For some scripts, it is important to show PHP version, maybe here it's not the case. Thanks, however. \$\endgroup\$ Commented Aug 29, 2016 at 13:32
  • \$\begingroup\$ Yes, but if they start including the leaderboard snippet then your entry will be the top entry for the language "PHP 5+" while another one's entry will be leading "PHP 5.6" and maybe somebody else with worse score posts for "PHP 5.6.13 and up" ... You see my point? :-) \$\endgroup\$
    – YetiCGN
    Commented Aug 29, 2016 at 13:34
  • \$\begingroup\$ That is correct. Thanks, again \$\endgroup\$ Commented Aug 29, 2016 at 13:36
  • \$\begingroup\$ You can change $_POST['s'] to $_GET[s], which is bad practice but saves you 2 bytes.You might also try to remove the bracketrs of the while (-2) if you add a newline before the echo (+1) Not 100% sure about that. \$\endgroup\$
    – Martijn
    Commented Aug 29, 2016 at 20:02
1
\$\begingroup\$

ARM Machine Code, 30 bytes

Hex Dump (little endian):

2000 f811 2b01 b14a 2a2a bf08 0040 d0f8 2a2e bf3a 3a2c 1a80 1040 e7f2 4770

This is a function that takes in a string, and returns the accumulated result. In C, the function would be declared int sioc(int dummy, char* string) where the dummy argument is ignored. Tested on the Raspberry Pi 3. No libraries or system calls are used.

Ungolfed assembly:

.syntax unified
.text
.global sioc
.thumb_func
sioc:
    @Input: r0 - dummy argument
    @r1 - string of operations
    @Output: r0 - result of those operations
    movs r0,#0 @Initialize accumulator to 0
    loop:
        ldrb r2,[r1],#1 @r2=*r1++
        cbz r2,end @If (r2==0), goto end
    mul:
        cmp r2,#42
        it eq @if r2==42
        lsleq r0,r0,#1 @r0<<=1
        beq loop @continue
    addsub: @else
        cmp r2,#46
        itte lo @If r2<46, then r2 is either + or -
        sublo r2,r2,#44
        sublo r0,r0,r2 @Add 1 if r2 is +, subtract 1 if r0 is -
        asrhs r0,r0,#1 @else arithmatically shift right by 1
        b loop @while (true)
    end:
        bx lr

Testing script (also assembly):

.syntax unified
.text
.global main
.thumb_func
main:
    ldr r1,[r1,#4] @r0=argv[1]
    bl sioc
    mov r1,r0
    adr r0,msg
    bl printf
    movs r7,#1
    swi #0
.balign 4
msg:
    .asciz "%d\n"
\$\endgroup\$
1
\$\begingroup\$

Java 8, 56 bytes

s->s.chars().reduce(0,(r,c)->c<43?r*2:c<46?r+44-c:r>>1);
\$\endgroup\$
1
\$\begingroup\$

Haskell - 70 bytes

f x=foldl1(flip(.))[[(*2),succ,id,pred,id,(`div`2)]!!(ord n-42)|n<-x]0

64 if I may take the input in RPN:

f x=foldl1(.)[[(*2),succ,id,pred,id,(`div`2)]!!(ord n-42)|n<-x]0


In addition to + - * / my implementation also support , and ., both of which does nothing. It crashes for any other input.

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

Python3, 98 85 74 bytes

C=lambda s,n:s and C(s[1:],{3:n+1,5:n-1,2:n*2,7:n/2}[ord(s[0])%10])or n//1

A recursive solution that loops over the string and takes the (modulo 10) from the ASCII value of the iterated character and treats the carried number accordingly. The dictionary keys are the last digits of the possible ASCII values that may occur (+, -, *, /) and the values are the corresponding results for the symbol.We pass the input string s forward by taking everything onward from the next character with s[1:] (thanks to @Destructible Watermelon!).

Could be shortened even more, by picking the actions from a list and moduloing to keep the index in range, but that feels like plagiarism at this point, so unless I find my own method for that, it won't happen.

Called like: C("+-+**+/",0) ==> 2.0

(Ps. Is it allowed to return -1.0 from input -/?)

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

Racket 116 bytes

(λ(s)(let((l(string->list s))(a 0))(for((i l))(set! a(match i[#\+(+ a 1)][#\-(- a 1)][#\*(* a 2)][#\/(/ a 2)])))a))

Ungolfed version:

(define f
  (lambda(s)
    (let ((l (string->list s))
          (a 0))
      (for ((i l))
        (set! a (match i
                  [#\+ (+ a 1)]
                  [#\- (- a 1)]
                  [#\* (* a 2)]
                  [#\/ (/ a 2)]
                )))
      a)))

Testing:

(f "++**--/")

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

GolfScript - 45 bytes

{1+}:"+";{1-}:"-";{2/}:"/";{2*}:"*";0\1/{`~}/

Explanation

{1+}:"+";  # Creates aliases for operators
{1-}:"-";
{2/}:"/";
{2*}:"*";
0\1/       # Pushes 0 and splits the string in bytes
{`~}/      # Executes each character
\$\endgroup\$
1
  • \$\begingroup\$ you can shave off 2 bytes by using ) and ( instead of 1+ and 1- and you can also lose 2 more by using ]; after declaring the aliases instead of removing them from the stack one at a time \$\endgroup\$
    – Soup Girl
    Commented May 17, 2021 at 17:13
0
\$\begingroup\$

TSQL, 115 bytes

After some golfing I was finally able to replace -IIF(@<0,@,@+1)/2 with (@+ABS(@%2))/-2 saving 2 bytes.

Golfed:

DECLARE @i varchar(max)='*///*-*+-+'
,@ INT=0WHILE @i>''SELECT @+=CHOOSE(CHARINDEX(LEFT(@i,1),'+-*/'),1,-1,@,(@+ABS(@%2))/-2),@i=STUFF(@i,1,1,'')PRINT @

Ungolfed:

DECLARE @i varchar(max)='*///*-*+-+'

,@ INT=0
WHILE @i>''
  SELECT @+=CHOOSE(CHARINDEX(LEFT(@i,1),'+-*/'),1,-1,@,(@+ABS(@%2))/-2),
  @i=STUFF(@i,1,1,'')
PRINT @

Fiddle

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

MMIX, 64 bytes (16 instrs)

00000000: e3010000 f000000b 31ff022f 5aff0003  ẉ¢¡¡ṅ¡¡¿1”£/Z”¡¤
00000010: 3d010101 f0000007 31ff022a 5aff0003  =¢¢¢ṅ¡¡¬1”£*Z”¡¤
00000020: 39010101 f0000003 34ff2c02 200101ff  9¢¢¢ṅ¡¡¤4”,£ ¢¢”
00000030: 81ff0000 e7000001 5b02fff4 f8020000  ¹”¡¡ḃ¡¡¢[£”ṡẏ£¡¡
clc SET  $1,0           // acc = 0
    JMP  1F             // goto load
0H  CMP  $255,$2,'/'
    PBNZ $255,2F        // loop: if(c != '/') goto notdiv
    SR   $1,$1,1        // acc /= 2
    JMP  1F             // goto load
2H  CMP  $255,$2,'*'
    PBNZ $255,2F        // notdiv: if(c != '*') goto notmul
    SL   $1,$1,1        // acc *= 2
    JMP  1F             // goto load
2H  NEG  $255,',',$2
    ADD  $1,$1,$255     // notmul: acc += ',' - c
1H  LDB  $255,$0
    INCL $0,1           // load: c = *s++
    PBNZ $255,0B        // if(c) goto loop
    POP  2,0            // return acc
\$\endgroup\$
0
\$\begingroup\$

Stax, 12 bytes

î┴$'▌>bå╦♦\‼

Run and debug it

14 bytes unpacked.

\$\endgroup\$
1
2

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.