Un sistema numérico simple

19

Déjame contarte sobre un sistema numérico simple. (que hice solo para este desafío)

Este sistema contiene las funciones (), [], {}, y <>.

1) ()

Cuando no ()se le dan argumentos, se evalúa como 0.

Cuando ()se le dan uno o más argumentos, se evalúa como la suma de los argumentos.

2) []

Cuando no []se le dan argumentos, se evalúa como -1.

Cuando []se le dan uno o más argumentos, se evalúa como el primer argumento menos la suma de los otros argumentos.

3) {}

Cuando no {}se le dan argumentos, se evalúa como 1.

Cuando {}se le dan uno o más argumentos, se evalúa el producto de esos argumentos.

4) <>

Cuando no <>se le dan argumentos, se evalúa como 1.

Cuando <>se le dan uno o más argumentos, se evalúa en el primer entero de argumento dividido por el producto de los otros argumentos.

Tu tarea

Dada una cadena que contiene un número válido (lo que significa que los corchetes están equilibrados, no hay división por 0, etc.) en este sistema numérico simple, imprima su valor.

Casos de prueba

() -> 0
(()()) -> 0
([][]) -> -2
({}<>) -> 2
({}[]) -> 0
[] -> -1
[[][]] -> 0
[()<>] -> -1
{()} -> 0
{([]<>)} -> 0

Recuerde, este es el , por lo que gana el código con la menor cantidad de bytes.

Oliver Ni
fuente
13
Tengo un gran nombre para esto, que acabo de inventar y no obtuve de ningún otro lado: ¡Brain-coke!
ETHproductions
44
@ETHproductions no
Oliver Ni
Algo
2
¿La división es división flotante o división entera?
xnor
1
@Oliver ¿Cómo debería funcionar la división de enteros cuando uno o ambos operandos son negativos? ¿Cuáles son los resultados esperados para 4 5/3, 5/-3, -5/3y -5/-3?
Martin Ender

Respuestas:

2

Dyalog APL , 94 bytes

o←{(⊂(1⊃⍵),⍺⍺⊃⍵),2↓⍵}⋄u←{(⊃⍵,⍺⍺1)⍺⍺⍵⍵/1↓⍵}⋄⍎⍕'+/o' '-u+o' '×/o' '÷u×o' '(⊂⍬),'[')]}>'⍳⌽⍞],'⊂⍬'

usos ⎕IO←0

reemplaza )]}>con una llamada de función que toma una pila, aplica una operación en su marco superior, la elimina y agrega el resultado a su siguiente marco (el operador monádico ose usa para eso; el operador diádico umaneja los casos más complicados de -y ÷)

reemplaza ([{<con código que agrega un marco a la pila ( (⊂⍬),)

ejecuta la expresión resultante (invertida, para que coincida con el orden de ejecución de APL) con una pila inicial de un marco vacío ( ⊂⍬)

ngn
fuente
5

Haskell, 357 306 277 251 228 224 188 185 180 bytes

Un analizador basado en tokens con una pila explícita. (%)toma una pila de tokens y un carácter y empuja (código de operación, número predeterminado) o (0, número) para ({[<, o saca los números más altos y un código de operación y empuja la respuesta para )}]>. Los códigos de operación están codificados por un hack de enumeración ascii.

Felicitaciones a @ChristianSievers por su gran respuesta de la que tomé prestadas algunas ideas.

¡Un trazador de líneas!

s%c|elem c"([<{",g<-div(fromEnum c)25=(g,[0,0,1,-1,1]!!g):s|(a,(o,n):b)<-span((==0).fst)s=(0,[foldr1(flip$[(+),quot,(-),(*)]!!(o-1))$snd<$>a,n]!!(0^length a)):b
snd.head.foldl(%)[]

¡Ahora con menos manejo de errores! Uso:

*Main> map (snd.head.foldl(%)[]) ["()","(()())","([][])","({}<>)","({}[])","[]","[[][]]","[()<>]","{()}","{([]<>)}"]
[0,0,-2,2,0,-1,0,-1,0,0]

¡Gracias @ChristianSievers por guardar 14 + 3 bytes!

¡Gracias @Zgarb por guardar algunos + 4 bytes!

Angs
fuente
1
¿Qué tal (0,[0,0,1,-1,1]!!o):sen la quinta línea?
Christian Sievers
@ChristianSievers, por supuesto!
Angs
Cambie las definiciones de !, para que pueda hacerlo (s:_)!_=d scomo el segundo caso. Además, creo que podrías unirte x<-p$d<$>init a,y<-d$last aen el último caso de %.
Zgarb
@ Zgarb gracias! Encontré una manera de unificar aún más la evaluación.
Angs
1
En la tercera línea de %puede colocar parens alrededor _:by g c.
Zgarb
3

Python 2, 292 265 248 235 223 206 204 bytes

r=reduce
s=input()
for n in')]}>':s=s.replace(n,'),')
for a in'(*x:sum(x)','[a=-1,*x:a-sum(x)','{*x:r(int.__mul__,x,1)','<a=1,*x:r(int.__div__,x,a)':s=s.replace(a[0],'(lambda %s)('%a[1:])
print eval(s)[0]

Reemplaza todos los corchetes con un lambda que hace lo que hace el corchete, luego evalúa el código Python resultante. Requiere su entrada entre comillas, como '[<><>([]{})]'.

Este programa almacena el tipo de paréntesis como el primer carácter en cada cadena en el for, y todo lo que sigue a la palabra clave lambdacomo el resto. Luego usa el primer carácter para sustituir; el resto se combina en una lambda como (lambda*x:sum(x))().

Pruébalo en Ideone!

Cobre
fuente
3

PEG.js (ES6) , 132 bytes

x=a:[([{<]b:x*[)\]}>]{var x='([<'.indexOf(a)
b.length^1||b.push(0)
return~~eval(b.length?b.join(('+-/'[x]||'*')+' '):~-('10'[x]||2))}

Debería arreglarse ahora.

Explicación

Más legible:

x=a:[([{<]
  b:x*
  [)\]}>]
{
  var x='([<'.indexOf(a)
  b.length^1||b.push(0)
  return ~~eval(
    b.length?
      b.join(('+-/'[x]||'*')+' ')
    :~-('10'[x]||2))
  )
}

PEG.js es una versión extendida de Javascript específicamente diseñada para el análisis. Es MUY estricto, por eso tuve que usarvar . Además, parece haber un error con corchetes dentro de las cadenas, que hinchó el código de manera significativa.

Para comenzar, definimos una regla xque coincida con cualquier paréntesis aque pueda o no contener múltiples expresiones que coincidan con la reglax .

Para que coincida cada regla x, empujamos un 0 a la matriz de la coincidencia interna bsib la longitud es 1.

Si bla longitud es> 0, entonces encontramos el índice de ain ([<y obtenemos un carácter al +-/usar ese índice. Si el resultado no está definido (lo que significa que aera {), entonces convertimos el resultado en *. Finalmente, añadimos un espacio y nos unimosb al resultado.

Si bla longitud es = 0, entonces encontramos el índice de ain ([<y obtenemos un carácter al 10usar ese índice. Si el resultado no está definido (lo que significa que aera {o <), convertimos el resultado en 2. Finalmente, simplemente disminuimos.

Al final, podemos evaluar la expresión y poner el resultado al suelo.

Mama Fun Roll
fuente
3

Perl, 113 + 2 = 115 bytes

Ejecutar con -lp(penalización de 2 bytes).

/\W/,eval"sub $`\{\$#_?(shift)$&&$'1}"for qw'a+a:1- b-a:- c*c: d/c:';y/([{</a-d/;s/\W/0),/g;s/\pL\K/(/g;$_=eval

Más legible (nota: esta "versión más legible" en realidad no se ejecutará, porque pongo comentarios en lugares que no están permitidos sintácticamente):

              # -p option: read a line of input into $_ at program start
              # -l option: remove the final newline whenever reading
do {          # for each element of a list, given later:
  /\W/;       # place an initial identifier in $`, the next character in
              # $&, and the rest of the element in $'
  eval qq{    # then evaluate the following template, with substitutions:
    sub $` {  # define a subroutine named $`, that does this:
      \$#_ ?  # if there is more than one argument                   
      (shift) # then return the first argument $&-ed with
      $& &$'  # the result of a recursive call with the tail of the arguments
              # else (the "else" is a colon taken from $', not the template)
      1       # return (the remainder of $' applied to) 1
    }
  }
} for qw'     # specify the list by splitting the following on whitespace:        
  a+a:1-      # a(head,tail) = len(tail>1) ? head+a(tail) : 1-1
  b-a:-       # b(head,tail) = len(tail>1) ? head-a(tail) : -1
  c*c:        # c(head,tail) = len(tail>1) ? head*c(tail) : 1
  d/c:        # d(head,tail) = len(tail>1) ? head/c(tail) : 1
';
y/([{</a-d/;  # replace ( [ { < with a b c d in $_
s/\W/0),/g;   # replace whitespace, punctuation in $_ with the string "0),"
s/\pL\K/(/g;  # place a ( after (\K) each letter (\pL) in $_
$_=eval       # evaluate $_ as a Perl program, storing the result back in $_
              # -p option: print $_ to the user at program end
              # -l option: output a newline whenever printing

La idea básica es que estamos convirtiendo una entrada similar [()<>]al programa Perl a b(a(0),d(0),0),través del procesamiento de texto; Perl está bien con la coma final. Anteriormente, definimos las funcionesa , b, c, dpara tener el mismo efecto que el (), [], {}, <>construcciones del lenguaje que estamos implementando; cada uno ignora su último argumento (el 0 al final), que se incluye para garantizar que todas las entradas se analicen correctamente, y trabajan utilizando la implementación comúnmente vista en la programación funcional donde la cabeza y la cola se procesan por separado. Porque b(e,f,g,0)significa e-f-g, es decir, trata su primer argumento especialmente, mientras que atrata sus argumentos simétricamente ( a(e,f,g,0)significa e+f+g), implementamosarecursivamente y a btravés de llamadas a. cy dtener una relación similar Las cuatro funciones son muy similares, por lo que las generamos en tiempo de ejecución en lugar de implementarlas por separado; almacenamos una plantilla que se aplica a las cuatro funciones en una cadena, luego generamos las funciones sustituyendo caracteres en la plantilla.

Como Perl /realiza la división de punto flotante, la implementa{} también lo hace. Supongo que esto no es un problema en sí mismo o que -Minteger(al seleccionar una variante de idioma donde todas las operaciones aritméticas son operaciones enteras) es gratis, porque de lo contrario tendría que gastar bytes adicionales escribiendo una división entera en Perl, que no parece ser de lo que se trata el problema fundamentalmente. (Creo que tendrías que gastar cuatro bytes (shift)para cambiar int+(shift); no he probado esto).


fuente
2

Octava, 215 206 198 bytes

S='sum(x(:))-sum(x(2:end))';Y='(@(x)isempty(x)';c=']) ';[~,~,u]=unique(['()<>[]{}',input('')]);eval([{'sum([',c,[Y,'+fix((',S,')/prod(x(2:end))))(['],c,[Y,'*-1+',S,'*2)(['],c,'prod([',c}{u(9:end)}])

pruébalo en línea

rahnema1
fuente
2

PHP, 315 300 285 258 250 244 bytes

for($s=$argv[1];$r=!$r;)foreach(["(+)1","[-]0","{*}2","</>2]as$p)if(preg_match("#$e$p[0]([-_\d]*)$e$p[2]#",$s,$m)){if(""==$v=strtok($m[1],_))$v=$p[3]-1;while(""<$n=strtok(_))eval("\$v$p[1]=$n;");$s=strtr($s,[$m[$r=0]=>_.$v]);}echo substr($s,1);

reemplaza las subexpresiones con guión bajo + valor; el bucle se rompe cuando la iteración no reemplaza.

19 años desde que conocí a C, 17 años trabajando con PHP;
esta es la primera vez questrtok tiene sentido ... ¡ayuda a ahorrar 24 bytes!

Descompostura

for($s=$argv[1];    // take input from argument
    $r=!$r;)        // toggle $r; loop until no replacement has taken place
    foreach(["(+)1","[-]0","{*}2","</>2]as$p) // loop through operations
        if(preg_match("#$e$p[0]([-_\d]*)$e$p[2]#",$s,$m))   // find a match
        {
            if(""==$v=strtok($m[1],_))  // init $v with first token from sub-match
                $v=$p[3]-1;             // if no token, init with default value
            while(""<$n=strtok(_))      // loop through tokens
                eval("\$v$p[1]=$n;");       // operate
            $s=strtr($s,[$m[$r=0]=>_.$v]);  // reset $r; replace match with underscore+value
        }
echo substr($s,1);  // print result
Titus
fuente
@ Oliver no está ganando a nadie aquí; pero gracias por la diversion!
Tito el
2

ES6 (Javascript), 250, 171, 154, 149147 bytes

Una versión pura de Javascript.

La "metaprogramación" (como la mayoría de las otras respuestas aquí), convierte el texto del programa de entrada en un programa Javascript correspondiente, aplicando una serie de sustituciones de texto directo (es decir, manteniendo la estructura del programa como está).

Probablemente se pueda jugar más al golf.

ACTUALIZACIÓN (v2.1)

  • Menos dos bytes (paréntesis eliminado en la expresión ternaria)
  • Golfó 5 bytes más, usando variables para la extracción de resultados, y deshaciéndose de "[]" extra

ACTUALIZACIÓN (v2)

Me acabo de dar cuenta de que las comas pendientes en las matrices ES son totalmente válidas, por lo que todo el código de normalización de comas podría eliminarse. También siguió un excelente consejo de @Titus sobre la optimización de la búsqueda alfabética.

ACTUALIZACIÓN (v1)

Se eliminó el alias duplicado "reemplazar".

ACTUALIZACIÓN (v1)

  • Utilice un alfabeto mejor: () => 1+ [] => 0 {} => 2 * <> => 2 / (cada carácter se puede reutilizar directamente como un valor u operador)

  • Reemplazado reduce () con replace () (mapeo alfabético)

  • Fusión constante en línea, abrir y cerrar el procesamiento del soporte en un solo paso

Golfizado (v2.1)

s=>eval("o="+s.replace(/./g,r=>"2+1-3*3/"["()[]{}<>".indexOf(r)]).replace(/\d\D?|\D/g,r=>r[1]?r[0]-2+",":r*1?'([':`].reduce((r,a)=>r${r}a)),`)+"o

Golfizado (v1)

(s,A="(2)+[1]-{3}*<3>/")=>eval(s[R="replace"](/./g,r=>A[A.indexOf(r)+1])[R](/\d\D?|\D/g,r=>r[1]?r[0]-2+",":(r[0]*1?'([':`].reduce((r,a)=>r${r}a)),`))[R](/,(\])|,$/g,"$1"))    

Golfizado (v0)

([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))

Explicado (v0)

//BEGIN 

//s - input text, A - alphabet, R - "String.replace()" alias
E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(

//Replace input alphabet by a more friendly one, to avoid too much escaping and quoting
// () - ab, [] -cd, {} - ef, <> - gh
s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')

//Replace no-arg invocations with a corresponding constant value
// () => 0, [] => -1, {} => 1, <> => 1      
[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')

//Replace opening brackets with "(["
[R](/[aceg]/g,"([")

//Replace closing brackets with "].reduce(...)),"
//An arithmetic operation to apply (+-*/) is chosen based on the bracket type 
//and is substituted into the template 
[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)

//Strip excessive commas
[R](/,(\])|,$/g,"$1")
);

//END: eval() the result


Example:
E("{([]<>()<>{})(<><>)}")
=> eval("([([-1,1,0,1,1].reduce((r,a)=>r+a)),([1,1].reduce((r,a)=>r+a))].reduce((r,a)=>r*a))")
=> 4

Prueba

E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))

T=(s,a)=>{
    console.log(s,r=E(s),r==a?"OK":"NOT OK");
}

T("()",0)
T("(()())",0) 
T("([][])",-2)
T("({}<>)",2) 
T("({}[])",0) 
T("[]",-1)
T("[[][]]",0) 
T("[()<>]",-1) 
T("{()}",0) 
T("{([]<>)}",0)

Prueba de salida

() 0 OK
(()()) 0 OK
([][]) -2 OK
({}<>) 2 OK
({}[]) 0 OK
[] -1 OK
[[][]] 0 OK
[()<>] -1 OK
{()} 0 OK
{([]<>)} 0 OK
zepelín
fuente
1
¿Podría su versión v0 ir con s.reduce((r,c)=>r+="abcdefgh"["()[]{}<>".indexOf(c)],'')(-5)? Si es así, puede recordar indexOfen una variable y tomar el operador de un tercer literal de cadena.
Tito el
2

Haskell, 184 179 172 161 160 159 151 148 145 bytes

s%(c:i)|elem c")}]>"=([1?(*),sum,1?quot,(-1)?(-)]!!mod(fromEnum c)5$s,i)|(r,j)<-[]%i=(s++[r])%j
[v]%e=(v,e)
(v?_)[]=v
(_?o)s=foldl1 o s
fst.([]%)

Descenso recursivo, enhebrando la entrada porque Haskell. Como de costumbre, la última línea no es una definición, pero establece la función que resuelve el problema. Entonces, para probar, coloque las líneas excepto la última en un archivo, cárguela y haga algo como esto:

*Main> fst.([]%) $ "{([][][])([][])}"
6

Gracias a @Zgarb por la inspiración y muchos consejos detallados, y a @Angs por la inspiración de su solución y otros consejos.

No se especificó cómo debería comportarse la división con enteros negativos. De todos modos, usar repetidamente divparece incorrecto, ya que no es lo mismo que usar divuna vez con el producto de los valores restantes. Ahora usando quot, obtengo los mismos resultados para <{}([][])[]>y <{}{([][])[]}>.

Para un código agradable, casi legible, mira la primera versión. Las versiones intermedias contienen todo tipo de código agradable e intimidante y ayudan a comprender esta versión.

Christian Sievers
fuente
Creo que puede guardar un par de bytes definiendo (!)=(,)y utilizando en !lugar de tuplas explícitas.
Zgarb
Si define m xy d xcomo 1#xy 0#x, puede fusionar los casos m[x]y d[x]en uno, lo que creo que también guarda algunos bytes.
Zgarb
@ Zgarb ¡Gracias! Casi me pierdo el último par, sin el cual el !truco no paga. Su segunda sugerencia es malvada, ahí va mi código casi legible ... ¡Listo!
Christian Sievers
Heh, me acabo de dar cuenta de que definir s%(c:i)=(s?c,i)y s?')'=sum setc. será mucho más corto, ya que puedes deshacerte de los repetidos is. ..No espere, eso probablemente no funcionará debido al s%(_:i)caso.
Zgarb
1
Pierda los backticks elemy diveso debería ahorrar algunos bytes más.
Zgarb
1

JavaScript (ES6), no eval, 156 bytes

f=s=>s==(s=s.replace(/(.) ?([- \d]*)[\]})>]/,(_,c,s)=>` `+(s?s.split` `.reduce((l,r)=>c<`<`?l- -r:c<`[`?l/r|0:c<`{`?l-r:l*r):c==`[`?-1:c==`(`?0:1)))?+s:f(s)

Explicación: La expresión regular encuentra el primer corchete de cierre no procesado y coincide con el corchete de apertura correspondiente (presumiblemente) y cualquier argumento intermedio. Los argumentos se dividen y se reducen de acuerdo con la operación (lamentablemente '(' y '[' no son el par óptimo para +y -), o si no hay argumentos, se calcula el valor apropiado (una vez más, la coincidencia de caracteres con los valores es subóptima desde mi punto de vista). El resultado se sustituye con un espacio inicial para separarlo de otros resultados. La función se llama a sí misma recursivamente hasta que no haya más cambios que realizar, en cuyo caso devuelve el valor resultante. Ejemplo:

[()<>]
[ 0<>]
[ 0 1]
 -1
Neil
fuente
f ("([] [])") => 0 (en lugar de 2)
zepelín
Algunas otras pruebas también me están fallando (puede probar el código de prueba en mi respuesta ), probablemente debido a: f ("[]") => 0, ya que "[]" es parte de cada prueba que falla.
zepelín
@zeppelin Vaya, eso se debió a un mal golf. He vuelto a una versión anterior, pero lamentablemente eso me cuesta un par de bytes.
Neil