Precedenza dell'operatore: quanto posso sbagliarmi?


65

Di 'che ho un'espressione:

9 * 8 + 1 - 4

Questa espressione può essere interpretata in sei modi diversi, a seconda della precedenza dell'operatore:

(((9 * 8) + 1) - 4) = 69 (* + -)
((9 * 8) + (1 - 4)) = 69 (* - +)
((9 * (8 + 1)) - 4) = 77 (+ * -)
(9 * ((8 + 1) - 4)) = 45 (+ - *)
((9 * 8) + (1 - 4)) = 69 (- * +)
(9 * (8 + (1 - 4))) = 45 (- + *)

Supponiamo che io sia uno sviluppatore e che non abbia voglia di memorizzare tabelle di precedenza, ecc., Quindi ho intenzione di indovinare.

In questo caso, il margine di errore più grande sarebbe 45-77, che è una differenza di 32. Ciò significa che la mia ipotesi sarà disattivata solo per un massimo di 32.

La sfida

Data un'espressione costituiti da numeri e +, -, *, /(divisione intera) e %, in uscita la differenza assoluta del massimo e minimo valore possibile per tale espressione, basato sulla precedenza degli operatori.

specificazioni

  • L'espressione di input non conterrà parentesi e ogni operatore è associativo a sinistra.
  • L'espressione di input conterrà solo numeri interi non negativi. Tuttavia, le sottoespressioni possono essere valutate come negative (ad es 1 - 4.).
  • Puoi prendere l'espressione in qualsiasi formato ragionevole. Per esempio:
    • "9 * 8 + 1 - 4"
    • "9*8+1-4"
    • [9, "*", 8, "+", 1, "-", 4]
    • [9, 8, 1, 4], ["*", "+", "-"]
  • L'input conterrà almeno 1 e al massimo 10 operatori.
  • Qualsiasi espressione che contiene una divisione o un modulo per 0 deve essere ignorata.
  • Si può presumere che al modulo non verranno dati operandi negativi.

Casi test

9 * 8 + 1 - 4             32
1 + 3 * 4                  3
1 + 1                      0
8 - 6 + 1 * 0              8
60 / 8 % 8 * 6 % 4 * 5    63

1
@AndersKaseorg Sembra che tu stia trattando %come due precedenti diverse nel tuo secondo esempio.
Esolanging Fruit,

1
Tre dei "sei" sono identici, così come altri due. Ciò lascia tre casi reali, non sei.
user207421

3
come %funziona l'operatore su numeri negativi? Come C o Python o qualcos'altro?
TSH

8
Sto solo dicendo che non devi aggiungere la parte "e sono pigro" alla tua descrizione. Basta dire che sei uno sviluppatore è abbastanza. :)
Gryphon - Ripristina Monica il

1
@tsh Qualsiasi comportamento. Fai quello che vuoi. Puoi far volare i demoni dal mio naso .
Esolanging Fruit,

Risposte:


27

Python 2 , 171 156 byte

lambda a:max(e(a))-min(e(a))
u=')%s('
def e(a,t=u):
 try:b=[eval(a)]
 except:b=[]
 return sum([e('(%s)'%a.replace(o,t%o),u%t)for o in"+-*/%"if' '+o in a],b)

Provalo online!

Come funziona

Circondiamo ogni operatore con un diverso numero di coppie di parentesi rivolte verso l'esterno per simulare diverse precedenza (in tutti i modi possibili) e avvolgere abbastanza coppie di parentesi rivolte verso l'interno attorno all'intera stringa, per ottenere un'espressione che possiamo eval. Ad esempio, con

+)+(
*))*((
-)))-(((

noi abbiamo

9 * 8 + 1 - 4(((9 ))*(( 8 )+( 1 )))-((( 4)))= 77.


Puoi salvare 2 byte spostando l' oresterno sumper rimuovere uno strato di parentesi quadre: sum([...],[])or[eval(a)]invece disum([...]or[[eval(a)]],[])
Strigoides

@Strigoides Avevo pensato che non era equivalente, perché sumpotrebbe essere vuoto senza che la sua argomentazione sia vuota, tuttavia, in realtà va bene perché evalin quel caso deve fallire. Grazie.
Anders Kaseorg,

8

Gelatina , 126 byte

"Precedenza dell'operatore? Parentesi? Pah, chi ne ha bisogno?" - sfide dell'utilizzo di Jelly per una sfida di precedenza per l'operatore.

⁾[]i$€Ḥæ%3+\¬œp¹Ḋ€
ǵḟØDO%9µÐṀṪɓœṣ⁹,ṚÑj@¥/
ǵVṾµ1ĿFḟØDḟ”-Lµ?ÐL
5Ḷx@€“]“[”ż⁸j/€,@y³Fɓ³i@€Ṁ’x@“[“]”jÇ
“+_×:%”Œ!Ç€µṾL_L’ỊµÐfV€ṢIS

Provalo online!

L'input viene considerato come una stringa, ad es. "1 + 2_3 × 4: 5% 6". Nota la moltiplicazione usa "×" invece di "*", la divisione usa ":" invece di "/" e la sottrazione usa "_" invece di "-".

Come funziona Il programma è diviso in tre parti: generazione di tutte le espressioni con diversa precedenza dell'operatore, valutazione delle stesse e restituzione della differenza tra il massimo e il minimo.

Tutte le espressioni sono generate con il codice:

5Ḷx@€“]“[”ż⁸j/€,@y³Fɓ³i@€Ṁ’x@“[“]”jÇ (4) helper link: returns all outputs given a permutation. Input e.g. "_+:×%"
5Ḷx@€“]“[”           - repeat outer brackets to get ["",""],["]","["],["]]","[["],["]]]","[[["],["]]]]","[[[["]
          ż⁸j/€      - insert the operations in to get "_","]+[","]]:[[","]]]×[[[","]]]]%[[[["
               ,@    - turn this into a mapping equivalent to "_"↦"_","+"↦"]+[",":"↦"]]:[[","×"↦"]]]×[[[","%"↦"]]]]%[[[["
                 y³F - use this mapping to get the right number of outward brackets on each operation. e.g. "1]+[3]]]×[[[4"
ɓ³i@€Ṁ’x@“[“]”j      - add the right number of brackets to the end to get e.g."[[[1]+[3]]]×[[[4]]]"
               Ç     - this calls the link which evaluates the expression
“+_×:%”Œ!Ç€                          (5a) main link. Input e.g. "1+3×4"
“+_×:%”                                 - the string "+_×:%"
       Œ!                               - all permutations
         ǀ                             - apply link (4) to each permutation

I collegamenti vengono valutati con questo (probabilmente potrei migliorare con una struttura diversa):

⁾[]i$€Ḥæ%3+\¬œp¹Ḋ€      (1) Helper link: Outputs a list of expressions within brackets, e.g. "[[[1]+[3]]]×[[[4]]]"↦"[[1]+[3]]","[[4]]"
⁾[]i$€Ḥæ%3                 - map "[" to 2, "]" to -2, and any other character to 0.
          +\¬              - cumulative sum negated: 1s at characters not in brackets (includes opening brackets), 0s otherwise (includes closing brackets)
             œp¹           - partition the input, not including borders, based on the sum to get "[[[1]+[3]]","[[[4]]"
                Ḋ€         - remove opening brackets
ǵḟØDO%9µÐṀṪɓœṣ⁹,ṚÑj@¥/ (2) Return the input to this link with one of the expressions from (1) evaluated
ǵVṾµ1ĿFḟØDḟ”-Lµ?ÐL     (3) link called from part 1: Evaluates expressions
 µ  µ          µ?          - if:
     1ĿFḟØDḟ”-L            - the input contains no operators within brackets:         
  VṾ                         - evaluate this one expression with normal Jelly calculation and return to string
                           - otherwise:
Ç                            - evaluate one subexpression using link (2)
                  ÐL       - repeat this until a single output is determined

La differenza tra il massimo e il minimo è calcolata con il codice nel collegamento (5):

µṾL_L’ỊµÐfV€ṢIS (5b) determine difference between minimum and maximum
µ      µÐf        - filter out outputs involving division or modulo by 0. Determined with:
 ṾL_L’Ị           - actual numbers have their unevaled form Ṿ no more than one byte longer than the non-unevaled form.
          V€      - evaluate each of these valid numbers to get integers from strings
            Ṣ     - sort
             IS   - return the sum of all difference between consecutive elements.

4
Probabilmente la risposta Jelly più lunga (senza dati incorporati) che abbia mai visto. Molto bene!
Keyu Gan,

@KeyuGan Se vuoi risposte Jelly più lunghe, guarda questa risposta . Non riesco a pensare ad altre lunghe risposte di Jelly senza compressione.
fireflame241

6

Python 2 , 235 234 233 226 byte

-1 byte (e una correzione) grazie ad Anders Kaseorg !

-7 byte grazie a Step Hen !

from itertools import*
def f(e,a=()):
 for o in permutations("+-*/%"):
	l=e[:]
	for c in o:
	 for i in range(len(l),0,-1):
		if l[i-1]==c:l[i-2:i+1]=["("+l[i-2]+l[i-1]+l[i]+")"]
	try:a+=eval(*l),
	except:0
 print max(a)-min(a)

Provalo online!


1
L'invio di funzioni deve essere riutilizzabile . Puoi risolvere quel problema lasciando che asia una tupla invece di un elenco, e puoi anche salvare 1 byte in questo modo ( a=(), a+=eval(*l),).
Anders Kaseorg,

Eh, TIL. Grazie per il consiglio!
Notjagan,

1
Dato che sei in Python 2, puoi salvare alcuni byte alternando spazi e schede per il rientro (in questo caso, 2 spazi -> scheda, tre spazi -> scheda + spazio, quattro spazi -> due schede) Provalo online!
Stephen,

4

Haskell 582 byte

Questo non è andato quasi bene come speravo che ...

import Data.List
f x=case x of '+'->(+);'-'->(-);'*'->(*);'/'->div;_->rem
e _ s[]=s
e 1 s(')':x:a)|0<-(read$e 0""a),(x=='%'||x=='/')=""|""<-(e 0""s)=""|""<-(e 0""a)=""|0<3=show$(f x)(read$e 0""s)$read$e 0""a
e 1 s")"=e 0""s
e n s(')':a)=e(n-1)(s++")")a
e 0 s('(':a)=e 1 s a
e n s('(':a)=e(n+1)(s++"(")a
e n s(x:a)=e n(s++[x])a
n?s=take n$cycle s
a!b=e 0""(9?"("++(concat$zipWith(++)a(b++[[]]))++9?")")
c#b|l<-[read x|x<-map(c!)(a b),x/=""]=maximum l-minimum l
a c=transpose$map(\x->map((\(Just q)->q).lookup x)$map(\a->zipWith(\x y->(y,x?")"++y:x?"("))[1..5]a)$permutations"+-*%/")c

Provalo online!

Cercare di giocare a golf con un programma lungo mi fa scrivere un codice errato :(

Ho provato ad usare l'algoritmo di Anders in Haskell, ma è uscito dal mio controllo

La funzione e è come un caso specifico di valutazione. (#) prende un elenco di stringhe che rappresentano numeri interi e una stringa di operatori e restituisce la differenza tra i valori massimo e minimo possibili. per esempio

(#) ["9","8","1","4"] "*+-" => 32

1
Se è stato rinominato #a ##, si potrebbe rinominare ea (#), in questo modo:(n#s)(x:a)=...
Esolanging frutta

Se si alias le seguenti tre funzioni comunemente utilizzate, è possibile salvare altri 6 byte. r=read;j=zipWith;o=mape quindi sostituire quelle funzioni con gli alias delle lettere.
maple_shaft

Inoltre sto contando 594 byte, non 582.
maple_shaft

3

Pyth, 45 byte

KS.nm.x.vj\ u.nm+*H/kHckHGd]s.iFQY.p_{eQ-eKhK

Sono sicuro che si può fare molta più ottimizzazione, ma finora mi piace.

Riceve l'input in questo modo: [9, 8, 1, 4], ["*", "+", "-"].

Provalo online!


2
Puoi aggiungere una spiegazione?
Jim

2

Mathematica, 186 164 159 byte

eMax@#-Min@#&[Fold[#//.{m___,x_,#2[[0]],y_,n___}:>{m,x~Last@#2~y,n}&,e,#]&/@Permutations@{"+"@Plus,"-"[#-#2&],"*"@Times,"/"@Quotient,"%"@Mod}/. 0/0|1/0->{}]

\[Function] richiede 3 byte.

Alcune alternative (continua a seguire lo stesso)

#2-#&@MinMax[...] Rimpiazzare Max@#-Min@#&[...]

Head@#2 Rimpiazzare #2[[0]]

Provalo online su http://sandbox.open.wolframcloud.com : entra ( .... )[{60, "/", 8, "%", 8, "*", 6, "%", 4, "*", 5}]con ....sostituito dal codice sopra per caso di test 60 / 8 % 8 * 6 % 4 * 5. Premere Shift + enterper valutare.


2

Javascript, 280 byte

Nota : la divisione dei numeri interi viene arrotondata utilizzando la funzione floor, il che significa che i numeri negativi vengono arrotondati da zero.

Questa soluzione si basa su questa risposta .

b=>(Math.max(...(f=(a,h="(",i=")",r=[...a[d="replace"](/[^-+*/%]|(.)(?=.*\1)/g,"")])=>(r[0]?(r.map((c,j)=>s=s.concat(f(h+a[d](RegExp("\\"+(n=r.concat()).splice(j,1),"g"),i+c+h)+i,h+"(",i+")",n)),s=[]),s):(a=eval(`(${a})`[d](/\(/g,"Math.floor(")))==a&&1/a?a:r))(b))-Math.min(...f(b)))

Esempio di frammento di codice:

g=

b=>(Math.max(...(f=(a,h="(",i=")",r=[...a[d="replace"](/[^-+*/%]|(.)(?=.*\1)/g,"")])=>(r[0]?(r.map((c,j)=>s=s.concat(f(h+a[d](RegExp("\\"+(n=r.concat()).splice(j,1),"g"),i+c+h)+i,h+"(",i+")",n)),s=[]),s):(a=eval(`(${a})`[d](/\(/g,"Math.floor(")))==a&&1/a?a:r))(b))-Math.min(...f(b)))

for(k=0;k<5;k++)
  v=["9*8+1-4","1+3*4","1+1","8-6+1*0","60/8%8*6%4*5"][k],
  console.log(`g(${v}) = ${g(v)}`)


Quanto sarebbe difficile renderlo conforme sostituendo il caso a / b con a / b | 0?
sinceramente il

@trlkly a/b|0interrompe il controllo degli errori divide / modulo 0, ma ha Math.floor(a/b)funzionato
Herman L

2

Haskell , 254 byte

import Data.List.Split
import Data.List
f s=(-)<$>maximum<*>minimum$permutations(zip"+-*/%"[p(+),p(-),p(*),c$div,c$mod])>>=(s!)
p=((pure.).)
c o a b=[o a b|b/=0]
s![]=[read s]
s!((x,o):y)=case splitOn[x]s>>=(!y)of[]->[];l->l?o
[a]?_=[a]
(a:b)?o=b?o>>=o a

Provalo online!

L'input è una stringa intera, come 4 + 5 * 2. Genera tutte le permutazioni delle operazioni e per ogni permutazione divide la stringa in modo ricorsivo. Filtra le divisioni per 0 con l'elenco monade.


(%)è l'operatore del modulo. È il resto di un'operazione di divisione tra l'argomento sinistro e l'argomento destro.
maple_shaft

1

Python 2 , 262 256 254 byte

from itertools import*
def r(s,o):
 try:
  while o in s:i=s.index(o)-1;s[i:i+3]=[`eval(''.join(s[i:i+3]))`]
  return s
 except:0
def f(s):
 u=[int(v[0])for v in [reduce(r,O,s.split(' '))for O in permutations('*/%+-')]if v!=None];return abs(max(u)-min(u))

Provalo online!


Salvare alcuni byte anche usando le schede: provalo online!
Stephen,

1
Salvare un byte cambiando in [in in[(lo spazio non è necessario)
Zacharý

1

PHP , 316 byte

<?for(;$t++<54322;)count_chars($t,3)!=12345?:$p[]=$t;foreach($p as$x){for(list($z,$q)=$_GET,$b=1,$i=0;$y=strtr($x,12345,"/%*+-")[$i++];)while(-1<$k=array_flip($q)[$y]){$n=$k+1;if($b&=$z[$n]||ord($y)%10<6)eval("\$z[$k]=$z[$k]$y$z[$n]^0;");($s=array_splice)($z,$n,1);$s($q,$k,1);}$b?$r[]=$z[0]:0;}echo max($r)-min($r);

Provalo online!

Expanded
for(;$t++<54322;)
  count_chars($t,3)!=12345?:$p[]=$t;
foreach($p as$x){
  for(list($z,$q)=$_GET,$b=1,$i=0;$y=strtr($x,12345,"/%*+-")[$i++];)
    while(-1<$k=array_flip($q)[$y]){
      $n=$k+1;
      if($b&=$z[$n]||ord($y)%10<6)
        eval("\$z[$k]=$z[$k]$y$z[$n]^0;");
      ($s=array_splice)($z,$n,1);
      $s($q,$k,1);
    }
  $b?$r[]=$z[0]:0;
}
echo max($r)-min($r);

Il caso lase è 63. Il tuo errore dovuto a dare allo stesso operatore una precedenza diversa in parti diverse di un'espressione
H.Pwiz

0

Python 3 , 284 byte

Modifica: sembra che ci sia qualcosa che non va nella valutazione dell'ultimo esempio. Ci penserò domani.

Un'altra risposta Python. Non riuscivo a superare tutti gli altri, ma ho speso troppo tempo per farlo.

from itertools import*
def f(n,o):
 z=[]
 for p in permutations("+-*/%"):
  try:
   p,x,a=[*p],n[:],o[:]
   while(p):
    for i,d in enumerate(a):
     if d==p[0]:x[i+1]=str(eval(x[i]+d+x[i+1]));x.pop(i);a.pop(i)
    p.pop(0)
   z+=x
  except:0
 z=[*map(float,z)];return max(z)-min(z)

Provalo online!


1
while(p)può diventare while pper un byte salvato.
Zacharý,

0

Clojure (+ combinatoria), 342 377 + 41 = 418 byte

+35 byte a causa di un bug.

(fn[x y](let[l filter s first z #(l(fn[y]y)%)r(sort(z(for[e(q/permutations[+ - * quot mod])](try(loop[t e m y a x](if(=[]t)(s a)(let[j(s t)i(reverse(keep-indexed #(if(= j %2)%)m))](recur(rest t)(l #(not= j %)m)(loop[d 0 h a](if(=(count i)d)h(let[c(nth i d)f(inc c)](recur(inc d)(vec(z(assoc h c(j(nth h c)(nth h f))f nil)))))))))))(catch Exception _ nil)))))](-(last r)(s r))))

Provalo online!

Affinché questa funzione funzioni, è necessario disporre usedella clojure.math.combinatoricslibreria (41 byte):

(use '[clojure.math.combinatorics :as q])

nuances:

Questa funzione è una funzione anonima, il che significa che devi farlo per usarla:

((fn[x y]...) numbers operators)

Inoltre, sto usando la parola quotinvece di /(poiché Clojure fa la divisione della frazione per impostazione predefinita) e modinvece di %.

Programma non golfato:

(defn precedence [numbers operators]
  (let [results
        (sort
          (for [permute (c/permutations [+ - * / mod])]
            (loop [p-temp permute
                  o-temp operators
                  n-temp numbers]
              (if (empty? o-temp) (first n-temp)
                (let [first-p (first p-temp)
                      indices (reverse (keep-indexed #(when (= first-p %2) %) o-temp))]
                  (recur
                    (rest p-temp)
                    (filter #(not= first-p %) o-temp)
                    (loop [ind 0
                          n-through n-temp]
                      (if (= ind (count indices)) n-through
                        (let [current-ind (nth indices ind)]
                          (recur
                            (inc ind)
                            (vec
                              (filter #(not (nil? %))
                                (assoc n-through
                                  current-ind (first-p (nth n-through current-ind) (nth n-through (inc current-ind)))
                                  (inc current-ind) nil)))))))))))))]
    (- (last results) (first results))))

Penso che puoi semplicemente dire "Chiusura + Combinatoria" e non devi segnare la usedichiarazione.
Esolanging Fruit,

@ Challenger5 Credo che faresti meglio a scriverlo nella descrizione, perché di default The characters used to import the library will likely be counted codegolf.meta.stackexchange.com/questions/10225/…
Keyu Gan

@KeyuGan Hai ragione - ho frainteso quel consenso Meta. Penso che requiredebba essere incluso nel codice e la sua lunghezza debba essere aggiunta al conteggio dei byte.
Esolanging Fruit,

@ Challenger5 Quindi ho bisogno di aggiungere 41 byte nel mio bytecount, giusto? OK.
clismique,

@ Qwerp-Derp Sì, ma l'importazione fa parte del tuo codice e puoi giocare a golf.
Esolanging Fruit,

0

JavaScript (ES6), 210 byte

Input come una matrice di numeri e operatori

k=>(m=n=-k,r=(o,k,j=0)=>{for(o||(m=m>k?m:k,n=n<k?n:k);q=o[j++];(q>'%'&q<'/'||z)&&r(o.slice(0,j-1)+o.slice(j),h))for(h=[...k],z=1;i=h.indexOf(q)+1;h.splice(i-2,3,eval(a=h[i-2]+q+h[i])|0))z*=h[i]})('+-*/%',k)|m-n

Meno golf

k=>(
  m = n = NaN,
  r =(o, k, j=0) => {
    // try all operators in o
    for(;q = o[j]; j++)
    {  
      // q : current operator, 
      // look for q inside the expression to evaluate
      for(h = [...k], z = 1; i = h.indexOf(q) + 1;)
      {
        a = h[i - 2]
        b = h[i]
        z *= b // trace if any second operand is zero
        // subst subexpression with its value
        h.splice(i - 2, 3, eval(a + q + b) | 0)
      }
      // now all subexp involving current operator are evaluated
      // the result is ok if current operator is not % or /
      //  OR if no second operand was zero
      (q > '%' & q < '/' || z) && 
        // try again recursively
        // using the remaining operators and the remaining expression
        r(o.slice(0, j) + o.slice(j+1), h) 
    }
    // if no more operators to try, check max and min
    // k is an array with 1 element, can be used like a single number
    o || (
      m = m > k ? m : k, 
      n = n < k ? n : k
    )
  },
  r('+-*/%', k),
  m-n
)

Test

var F=
k=>(m=n=-k,r=(o,k,j=0)=>{for(o||(m=m>k?m:k,n=n<k?n:k);q=o[j++];(q>'%'&q<'/'||z)&&r(o.slice(0,j-1)+o.slice(j),h))for(h=[...k],z=1;i=h.indexOf(q)+1;h.splice(i-2,3,eval(a=h[i-2]+q+h[i])|0))z*=h[i]})('+-*/%',k)|m-n

function update() {
  var input = I.value.match(/\d+|\S/g)
  var result = F(input)
  O.textContent = I.value + ' -> ' + result + ' (max:'+m+' min:'+n+')'
}

update()
<input id=I value="60 / 8 % 8 * 6 % 4 * 5" oninput='update()'>
<pre id=O></pre>

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.