Turn an Array into a Math Problem

35

3

Given a nonempty list of nonnegative integers, consider rewriting it as an arithmetic problem where:

  • A plus sign (+) is inserted between pairs of numbers that increase from left to right (a.k.a. from the start of the list to the end).
  • A minus sign (-) is inserted between pairs of numbers that decrease from left to right.
  • A multiplication sign (*) is inserted between pairs of numbers that are equal.

Said another way: any sublist a,b becomes a+b if a<b, a-b if a>b, and a*b if a==b.

For example, the list

[12, 0, 7, 7, 29, 10, 2, 2, 1]

would become the expression

12 - 0 + 7*7 + 29 - 10 - 2*2 - 1

which evaluates to 75.

Write a program or function that takes in such a list and evaluates it, printing or returning the result.

  • Order of operations matters. Multiplications should be done before any addition or subtraction.
  • If the input list has one number, that should be what it evaluates to. e.g. [64] should give 64.
  • Use of eval or exec or similar constructs is allowed.

Here are some additional examples:

[list]
expression
value

[0]
0
0

[1]
1
1

[78557] 
78557
78557

[0,0]
0*0
0

[1,1]
1*1
1

[2,2]
2*2
4

[0,1]
0+1
1

[1,0]
1-0
1

[1,2]
1+2
3

[2,1]
2-1
1

[15,4,4]
15-4*4
-1

[9,8,1]
9-8-1
0

[4,2,2,4]
4-2*2+4
4

[10,9,9,12]
10-9*9+12
-59

[1,1,2,2,3,3]
1*1+2*2+3*3
14

[5,5,4,4,3,3]
5*5-4*4-3*3
0

[3,1,4,1,5,9,2,6,5,3,5,9]
3-1+4-1+5+9-2+6-5-3+5+9
29

[7637,388,389,388,387,12,0,0,34,35,35,27,27,2]
7637-388+389-388-387-12-0*0+34+35*35-27*27-2
7379

The shortest code in bytes wins. Tiebreaker is earlier answer.

Calvin's Hobbies

Posted 2016-06-12T04:16:57.377

Reputation: 84 000

5Regarding "order of operations matters" it might be good to state explicitly that addition and subtraction are left-associative and have the same precedence. – Martin Ender – 2016-06-12T08:15:04.410

Answers

15

Python 2, 63 bytes

p=s='print-'
for x in input():s+='*+-'[cmp(x,p)]+`x`;p=x
exec s

Constructs and evals the expression string. The arithmetic symbol is chosen by comparing the previous number p to the current one x. The symbol is appended followed by the current number.

The first number is handled with a clever trick from Sp3000. The initial value of p is set to a string, which is bigger than any number and therefore causes a - before the first number. But, s is initialized to print- at the same time that makes the result start with print-- (thanks to xsot for saving 2 bytes by initializing with print.)

xnor

Posted 2016-06-12T04:16:57.377

Reputation: 115 687

I think you can move print into the string and use exec instead of eval. – xsot – 2016-06-12T13:20:32.777

13

Pyth, 31 26 19 17 16 15 bytes

Expressions with * won't evaluate online, but they would theoretically work.

2 bytes thanks to Maltysen.

vsm+@"*-+"._-~k

Test suite (with evaluation).

The other cases (without evaluation).

History

  • 31 bytes: M+G@"*-+"->GH<GHv+sgMC,JsMQtJ\x60e
  • 26 bytes: M+G@"*-+"->GH<GHv+sgVQtQ\x60e
  • 19 bytes: vtssVm@"*-+"->Zd<~Z
  • 17 bytes: vtssVm@"*-+"._-~Z
  • 16 bytes: vssVm@"*-+"._-~k
  • 15 bytes: vsm+@"*-+"._-~k

Leaky Nun

Posted 2016-06-12T04:16:57.377

Reputation: 45 011

Why won't multiplication work online? If you're not sure it works it may be best to test a bit more before answering. – Calvin's Hobbies – 2016-06-12T04:26:17.223

Because security stuff (evaluation only works for + and - online) – Leaky Nun – 2016-06-12T04:26:38.360

@HelkaHomba I didn't have a chance to try it offline yet, but it should work. The online interpreter uses the --safe switch, which replaces eval with ast.literal_eval. – Dennis – 2016-06-12T04:31:13.793

Ok, fair enough. – Calvin's Hobbies – 2016-06-12T04:42:06.840

Confirmed, this works with the offline interpreter. – Dennis – 2016-06-12T05:16:31.317

I tried the latest revision. – Dennis – 2016-06-12T05:18:15.553

nice one, have an upvote – Maltysen – 2016-06-12T06:05:48.723

12

Jelly, 18 16 15 14 bytes

I0;ð1g×⁹⁸œṗP€S

Uses no built-in eval. Try it online! or verify all test cases.

How it works

I0;ð1g×⁹⁸œṗP€S  Main link. Input: A (list)

I               Increments; compute the deltas of all adjacent items of A.
 0;             Prepend a 0.
   ð            Begin a new, dyadic chain.
                Left argument: D (0 plus deltas). Right argument: A
    1g          Compute the GCD of 1 and each item in D.
                This yields 1 for non-negative items, -1 for negative ones.
      ×⁹        Multiply each 1 or -1 with the corresponding item of A.
                This negates every item in A that follows a - sign.
        ⁸œṗ     Partition the result by D. This splits at occurrences of non-zero
                values of D, grouping items with identical absolute value.
           P€   Take the product of each group.
             S  Sum; add the results.

Dennis

Posted 2016-06-12T04:16:57.377

Reputation: 196 637

I out-golfed you! – Leaky Nun – 2016-06-12T06:04:35.150

1Nicely done. I should add Python's eval as an atom... – Dennis – 2016-06-12T06:07:52.220

9I out-out-golfed you. :P – Dennis – 2016-06-12T06:25:54.400

Nicely done, your turn! – Leaky Nun – 2016-06-12T06:29:45.313

9

MATL, 12 bytes

Y'^l6MdZSh*s

This uses @aditsu's very nice idea of run-length encoding.

Try it online!

Explanation

       % Take input vector implicitly
Y'     % RLE. Produces two vectors: values and lengths
^      % Rise each value to the number of consecutive times it appears. This
       % realizes the product of consecutive equal values
l      % Push 1
6M     % Push vector of values again
d      % Consecutive differences
ZS     % Sign function. Gives 1 or -1 (no 0 in this case)
h      % Concatenate horizontally with previous 1
*      % Multiply. This gives plus or minus depending on increasing character
s      % Sum of vector. This realizes the additions or subtractions
       % Display implicitly

Luis Mendo

Posted 2016-06-12T04:16:57.377

Reputation: 87 464

Haha had just written up something similar. RLE works out well for this – Suever – 2016-06-12T13:42:49.133

@Suever I see :-D – Luis Mendo – 2016-06-12T14:59:37.027

7

CJam, 20

q~e`{~_W-g\:W@#*}%:+

Try it online

Explanation:

q~       read and evaluate the input (array of numbers)
e`       RLE encode, obtaining [count number] pairs
{…}%     map each pair
  ~_     dump the count and number on the stack, and duplicate the number
  W-     subtract the previous number (W is initially -1 by default)
  g      get the sign of the result
  \      swap with the other copy of the number
  :W     store it in W (for next iteration)
  @#     bring the count to the top, and raise the number to that power
  *      multiply with the sign
:+       add all the results together

aditsu quit because SE is EVIL

Posted 2016-06-12T04:16:57.377

Reputation: 22 326

7

JavaScript (ES6), 54

p=>eval(0+p.map(v=>x+='*-+'[(p>v)+2*(p<v)]+(p=v),x=1))

eval receives a comma separated list of expressions and returns the value of the last one.

Test

f=p=>eval(0+p.map(v=>x+='*-+'[(p>v)+2*(p<v)]+(p=v),x=1))

t=p=>(0+p.map(v=>x+='*-+'[(p>v)+2*(p<v)]+(p=v),x=1))

function Test() {
  var a=I.value.match(/\d+/g).map(x=>+x) // convert input to a numeric array
  
  var x=f(a),y=t(a)
  O.textContent='Value '+x+'\n(no eval '+y+')'
}  

Test()
#I { width:80%}
<input value='12, 0, 7, 7, 29, 10, 2, 2, 1' id=I>
<button onclick='Test()'>Test</button>
<pre id=O></pre>

edc65

Posted 2016-06-12T04:16:57.377

Reputation: 31 086

4That's the worst abuse of the comma operator that I can remember seeing on this site... – Neil – 2016-06-12T10:26:36.873

5

Julia, 76 57 bytes

!x=[[" ""-*+"[2+sign(diff(x))]...] x]'|>join|>parse|>eval

My first time golfing Julia, so maybe there are obvious improvements. Try it online!

Dennis saved a ton of bytes.

Lynn

Posted 2016-06-12T04:16:57.377

Reputation: 55 648

Nice job. I didn't realize you can define a custom function for !. – Rɪᴋᴇʀ – 2016-06-12T19:43:25.643

@Eᴀsᴛᴇʀʟʏ Iʀᴋ http://codegolf.stackexchange.com/a/81028/12012

– Dennis – 2016-06-12T19:51:24.543

4

Pyth - 23 22 20 bytes

As with Kenny's, multiplication doesn't work online.

vs.i+\+@L"*+-"._M-Vt

Test Suite without doing eval.

Maltysen

Posted 2016-06-12T04:16:57.377

Reputation: 25 023

Whoo is Kevin ? – Leaky Nun – 2016-06-12T04:43:02.933

@LeakyNun I forgot your name after a while >_> – Maltysen – 2016-06-12T04:43:42.133

@Maltysen Haha, you're thinking of kevin-not-kenny Lau

– James – 2016-06-12T04:57:59.437

Sorry, I was eating, so I could not golf my solution. Your turn. – Leaky Nun – 2016-06-12T05:14:33.203

@LeakyNun almost there – Maltysen – 2016-06-12T06:00:06.580

Oh, thanks for the ._, didn't know of that – Leaky Nun – 2016-06-12T06:02:10.607

3

Brachylog, 34 32 bytes

~b≜ḅ⟨h⟨h≡^⟩l⟩ᵐs₂ᶠ{zh>₁&ttṅ|tt}ᵐ+

Try it online!

Kroppeb

Posted 2016-06-12T04:16:57.377

Reputation: 1 558

3

R, 92 bytes

There's likely still some good golfing that can be done here.

eval(parse(t=paste(i<-scan(),c(ifelse(d<-diff(i),ifelse(d>0,"+","-"),"*"),""),collapse="")))

Ungolfed:

i = scan()                # Read list from stdin
d = diff(i)               # Calculate difference between each element of list
s = ifelse(d,             # If d!=0
             ifelse(d>0,  # And if d>1
                    "+",  # Return plus
                    "-"), # Else return minus
             "*")         # Else (i.e. d==0) return multiply.
s = c(s,"")               # Pad the list s with an empty string, so it's the same
                          # length as i
p = paste(i,s,collapse="")# Paste the elements of i and s into one long string.
eval(parse(t=p))          # Evaluate the string as a language object.

rturnbull

Posted 2016-06-12T04:16:57.377

Reputation: 3 689

I managed to save just one byte using an indexing approach

– JayCe – 2018-07-19T19:04:39.767

2

TI-BASIC, 146 bytes

I'll format it nicely when not on mobile. Sleep escapes me, so you get this. Enjoy.

Prompt L₁
"(→Str1
For(A,1,dim(L₁
{0,1→L₂
{0,L₁(A→L₃
LinReg(ax+b) L₁,L₃,Y₁
Equ►String(Y₁,Str2
sub(Str2,1,-1+inString(Str2,"X→Str2
If A>1
Then
L₁(A-1
2+(Ans>L₁(A))-(Ans<L₁(A
Str1+sub("+*-",Ans,1→Str1
End
Str1+Str2→Str2
End
expr(Str1

Conor O'Brien

Posted 2016-06-12T04:16:57.377

Reputation: 36 228

2

Javascript ES6, 64 62 chars

a=>eval(a.map((x,i)=>x+('*+-'[x<a[++i]|(x>a[i])*2])).join``+1)

Qwertiy

Posted 2016-06-12T04:16:57.377

Reputation: 2 697

3Should not this be a function and a a parameter? – edc65 – 2016-06-12T13:26:25.907

This is invalid as-is. – Rɪᴋᴇʀ – 2016-06-12T15:19:01.660

@edc65, yes, it should. But in fact it was counted (61 specified, but real code length was 59), I just badly copied a new code (the edit should be a[i+1]...a[i+1] => a[++i]...a[i] - 2 chars shorter, but I mistakenly replaced the whole code dropping a=>). – Qwertiy – 2016-06-12T19:39:46.060

@EᴀsᴛᴇʀʟʏIʀᴋ, it's just a wrong paste. See comment above and edit history for more detailes. – Qwertiy – 2016-06-12T19:40:27.083

@Qwertiy okay cool. Nice answer btw.. – Rɪᴋᴇʀ – 2016-06-12T19:42:09.750

I count 62 chars - can we get a recount? – Bevo – 2016-08-15T22:31:49.490

@Bevo, fixed... – Qwertiy – 2016-08-15T23:01:01.640

1

R, 120 44 bytes

r=rle(scan());c(1,sign(diff(r$v)))%*%r$v^r$l

Try it online!

The algorithm is similar to this answer's, but I only realized it after coding my answer. Much better than my original answer that was using eval(parse).

Fully leverages R's vectorized operations - does the * operation first using rle(x)$values ^ rle(x)$lenghts and dot-products this vector with sign( diff( rle(x)$values ) ) (predended with 1).

JayCe

Posted 2016-06-12T04:16:57.377

Reputation: 2 655

1

05AB1E (legacy), 17 16 15 bytes

ü.S…*-+sè‚ζJJ.E

-2 bytes thanks to @Emigna.

Try it online or verify all test cases.

Explanation:

ü                  # Pair-wise loop over the (implicit) input-list
                   #  i.e. [12,0,7,7] → [[12,0],[0,7],[7,7]]
 .S                # Calculate the signum of the pair (-1 for a<b; 0 for a==b; 1 for a>b)
                   #  i.e. [[12,0],[0,7],[7,7]] → [1,-1,0]
   …*-+sè          # Index over "*-+" (with wrap-around)
                   #  i.e. [1,-1,0] → ['-','+','*']
         ‚ζ        # Zip the two lists together (appending the operands to the numbers)
                   #  i.e. [12,0,7,7] and ['-','+','*','+']
                   #   → [[12,'-'],[0,'+'],[7,'*'],[7,' ']]
           JJ      # Join them together
                   #  [[12,'-'],[0,'+'],[7,'*'],[7,' ']] → '12-0+7*7 '
             .E    # Evaluate as Python code
                   #  i.e. '12-0+7*7' → 61

Kevin Cruijssen

Posted 2016-06-12T04:16:57.377

Reputation: 67 575

1Due to modular indexing, you can remove > by moving + to the end of the string. – Emigna – 2018-08-23T07:10:46.837

@Emigna Not sure how I missed that.. Thanks! – Kevin Cruijssen – 2018-08-23T07:13:36.787

1You can save another byte by removing Ć and ¨, if you use ‚ζ instead of ø – Emigna – 2018-08-23T07:18:54.960

@Emigna Oh, now that is smart! Thanks. I knew the enclose was a bit of a weird work-around, but didn't knew how to fix it. ‚ζ is a perfect alternative work-around, since the space is ignore in the eval. Thanks again. :) – Kevin Cruijssen – 2018-08-23T07:37:55.603

1

Java, 384 bytes

int s(int[]l){int n=l[0],m;for(int i=0;i<l.length-1;i++)if(l[i]<l[i+1])if(i<l.length-2&&l[i+1]!=l[i+2])n+=l[i+1];else{m=l[i+1];while(i<l.length-2&&l[i+1]==l[i+2])m*=l[(i++)+1];n+=m;}else if(l[i]>l[i+1])if(i<l.length-2&&l[i+1]!=l[i+2])n-=l[i+1];else{m=l[i+1];while(i<l.length-2&&l[i+1]==l[i+2])m*=l[(i++)+1];n-=m;}else{m=l[i];while(i<l.length-1&&l[i]==l[i+1])m*=l[i++];n+=m;}return n;}

Ungolfed try online

int s(int[] l)
{
    int n=l[0], m;

    for(int i=0; i<l.length-1; i++)
    {
        if(l[i] < l[i+1])
        {
            if (i<l.length-2 && l[i+1]!=l[i+2])
            {
                n += l[i+1];
            }
            else
            {
                m = l[i+1];
                while(i<l.length-2 && l[i+1]==l[i+2]) m *= l[(i++)+1];
                n += m;
            }
        }
        else if(l[i] > l[i+1])
        {
            if (i<l.length-2 && l[i+1]!=l[i+2])
            {
                n -= l[i+1];
            }
            else
            {
                m = l[i+1];
                while(i<l.length-2 && l[i+1]==l[i+2]) m *= l[(i++)+1];
                n -= m;
            }
        }
        else
        {
            m = l[i];
            while(i<l.length-1 && l[i]==l[i+1]) m *= l[i++];
            n += m;
        }
    }

    return n;
}

Khaled.K

Posted 2016-06-12T04:16:57.377

Reputation: 1 435

1Some quick golfs: int a=l.length, && => &, put the int i=0 on the same "line" as int n=l[0],m. – Leaky Nun – 2016-06-12T10:10:03.927

In if(i<l.length-2&&l[i+1]!=l[i+2])n+=l[i+1];else{m=l[i+1];while(i<l.length-2&&l[i+1]==l[i+2])m*=l[(i++)+1];n+=m;, you can just replace this with the content inside the else block. – Leaky Nun – 2016-06-12T10:11:55.610

1

Perl, 49 bytes

48 bytes code + 1 for -p

s/\d+ (?=(\d+))/$&.qw(* - +)[$&<=>$1]/ge;$_=eval

Usage

perl -pe 's/\d+ (?=(\d+))/$&.qw(* - +)[$&<=>$1]/ge;$_=eval' <<< '12 0 7 7 29 10 2 2 1'
75

Notes

I learnt here that you can capture a lookahead in PCRE, although it's a little unintuitive ((?=(\d+)) instead of ((?=\d+))). It does make sense after reading though as you would be capturing a zero-length match (the lookahead) with the latter, and instead capture the match with the former).

Thanks to @ninjalj for saving 8 bytes!

Dom Hastings

Posted 2016-06-12T04:16:57.377

Reputation: 16 415

@LeakyNun I never know exactly what to count for that, I can't find the relevant meta post, I'm happy to bump the count, but I thought that since you can run with -e for free, adding a p making it -pe was +1? Will update for now, but if you could find a source I could quote/link to going forward, that'd be awesome! – Dom Hastings – 2016-06-12T12:37:01.360

3

@DomHastings 1 is correct, per the reason you say + this meta post

– Sp3000 – 2016-06-12T12:39:39.223

Thanks @Sp3000! I couldn't find that post for the life of me! @LeakyNun meta post for +1 as per comment from Sp3000

– Dom Hastings – 2016-06-12T12:47:37.190

Instead of using chained conditional operators, you can use the spaceship operator to select from a list: $&.qw(* - +)[$&<=>$1] in the replacement part of the s/// operator. – ninjalj – 2016-06-13T17:44:20.830

@ninjalj Of course! amazing, thanks! -8 with that! – Dom Hastings – 2016-06-13T18:12:04.407

1

Javascript ES6, 79 chars

a=>eval(`${a}`.replace(/(\d+),(?=(\d+))/g,(m,a,b)=>a+('*+-'[+a<+b|(+a>+b)*2])))

Qwertiy

Posted 2016-06-12T04:16:57.377

Reputation: 2 697

1

Actually, 30 bytes

;2@VpXdX`i-su"+*-"E`M' @o♀+εj≡

Unfortunately, because the eval () command only evaluates literals on TIO, this program does not work on TIO.

Explanation:

;2@VpXdX`i-su"+*-"E`M' @o♀+εj≡
;                               duplicate input
 2@V                            overlapping sublists of length <= 2
    pXdX                        discard first and last element (length-1 sublists)
        `i-su"+*-"E`M           map: for each pair of elements
         i-su                     flatten, subtract, sign, increment (results in a 0 if b < a, 1 if b == a, and 2 if b > a)
             "+*-"E               select the correct operation
                     ' @o       put a space at the beginning of the list to pad it properly
                         ♀+     pairwise addition (since addition is not defined for strings and integers, this just zips the lists and flattens the result into a single flat list)
                           εj   join with empty string
                             ≡  eval

Mego

Posted 2016-06-12T04:16:57.377

Reputation: 32 998

0

Japt, 25 bytes

Would like to cut it shorter, but I couldn't make an eval-less version work.

S+Uä!- ®"*+-"gZÎì)íU
OxU

Try it online!

Nit

Posted 2016-06-12T04:16:57.377

Reputation: 2 667

Got it down to 22 bytes but still think it can be improved on.

– Shaggy – 2018-08-24T14:32:01.290

0

Japt -x, 21 19 bytes

änJ f mÎí*Uò¦ ®ÎpZÊ

Try it


Explanation

                        :Implicit input of array U
  J                     :Prepend -1
än                      :Get deltas
    f                   :Filter (remove 0s)
      m                 :Map
       Î                : Signs
        í               :Interleave
          U             :  Original input
           ò            :  Partition on
            ¦           :   Inequality
              ®         :  Map each sub-array Z
               Î        :    Get first element
                p       :    Raise to the power of
                 ZÊ     :     Length of Z
         *              :Reduce each pair of elements by multiplcation
                        :Implicitly reduce by addition and output

Shaggy

Posted 2016-06-12T04:16:57.377

Reputation: 24 623

0

PHP, 103 bytes

Neat challenge. This got longer than expected. I think using array_map or similar won't improve the byte count, as anonymous functions are still expensive in PHP.

foreach(fgetcsv(STDIN)as$v)(0<=$p?$o.=$p.('*-+'[($p>$v)+2*($p<$v)]):_)&&$p=$v;echo eval("return$o$v;");

Runs from command line, will prompt for a comma separated list, like:

php array_to_math.php
12, 0, 7, 7, 29, 10, 2, 2, 1

insertusernamehere

Posted 2016-06-12T04:16:57.377

Reputation: 4 551

0

PowerShell v2+, 62 bytes

-join($args|%{"$o"+'*+-'[($o-lt$_)+2*($o-gt$_)];$o=$_})+$o|iex

Takes input as space-separated command-line arguments, which gets converted into automatic array $args. We iterate through each element, using helper variable $o each iteration to remember what our previous entry was. We use a indexed-string to pull out the appropriate operator, done by performing math on the implicitly-converted Boolean values (e.g., if the previous entry is smaller, the [] evaluates to 1+2*0 so '*+-'[1] means the + is selected).

The concatenated strings are left on the pipeline. We collect all of those snippets together (e.g., 3-, 1+, 4-, etc.) with a -join operation, concatenate on the final number (implicitly converted to string), and pipe it to iex (alias for Invoke-Expression and similar to eval).

AdmBorkBork

Posted 2016-06-12T04:16:57.377

Reputation: 41 581

A concern is that if the caller has already given $o a value (say $o = 999) then the expression in this entry will not compute the correct value. An initialization of $o needs to be added to this solution. – Bevo – 2016-08-12T21:39:54.520

@Bevo This is intended to be a full script, executed via the command line, and not a function or via the interactive shell. The vast majority of my submissions are as such, since in such a scenario, there is no predefined variables to worry about and thus the code can be a bit shorter. – AdmBorkBork – 2016-08-14T03:52:20.577