Implementar la notación Anyfix!

16

En la notación de prefijo, el operador viene antes que los argumentos, por lo que puede imaginar que el operador llama a lo next()que se llama de forma recursiva. En notación infija, el operador va entre los argumentos, por lo que puede imaginarlo simplemente como un árbol de análisis. En la notación postfix, el operador viene después de los argumentos, por lo que puede imaginarlo como basado en la pila.

En cualquier notación, el operador puede ir a cualquier parte * . Si aparece un operador y no hay suficientes argumentos, entonces el operador espera hasta que haya suficientes argumentos. Para este desafío, debe implementar un evaluador anyfix muy básico. (Tenga en cuenta que anyfix es un lenguaje recreativo que abandoné con el que puede jugar aquí o consultar aquí )

Deberá admitir los siguientes comandos:

(Arity 1)

  • duplicar
  • negativo

(Arity 2)

  • adición
  • multiplicación
  • igualdad: devoluciones 0o 1.

Puede elegir usar cinco símbolos que no sean espacios en blanco para estos comandos. Con fines demostrativos, usaré "como duplicado, ×como multiplicación y +como suma.

Para literales, solo necesita admitir enteros no negativos, pero su intérprete debe poder contener todos los enteros (dentro del rango de enteros (razonable) de su idioma).

Vamos a echar un vistazo a un ejemplo: 10+5. El almacenamiento debe comportarse como una pila, no como una cola. Entonces, primero, la pila comienza en [], y la lista de operadores en cola comienza en []. Luego, 10se evalúa el literal que hace la pila [10]. A continuación, +se evalúa el operador , que requiere dos argumentos. Sin embargo, solo hay un argumento en la pila, por lo que se convierte en la lista de operadores en cola ['+']. Luego, 5se evalúa el literal que hace la pila [10, 5]. En este punto, el operador '+'puede ser evaluado para que así sea, haciendo que la pila [15]y la cola[] .

El resultado final debe ser [15]de + 10 5, 10 + 5, y 10 5 +.

Vamos a echar un vistazo a un ejemplo más difícil: 10+". La pila y la cola comienzan como []y []. 10se evalúa primero lo que hace la pila [10]. A continuación, +se evalúa, lo que no cambia la pila (porque no hay suficientes argumentos) y hace la cola ['+']. Entonces, "se evalúa. Esto puede ejecutarse inmediatamente, por lo que es así, haciendo la pila [10, 10]. +ahora se puede evaluar para que la pila se convierta [20]y la cola []. El resultado final es [20].

¿Qué pasa con el orden de las operaciones?

Echemos un vistazo ×+"10 10. La pila y la cola comienzan como []:

  • ×: La pila no cambia y la cola se convierte ['×'].
  • +: La pila no cambia y la cola se convierte ['×', '+'].
  • ": La pila no cambia y la cola se convierte ['×', '+', '"'].
  • 10: La pila se convierte [10]. Aunque ×debería ser el primer operador en ser evaluado, ya que aparece primero, "puede ejecutarse inmediatamente y ninguno de los operadores antes que puede, por lo que se evalúa. La pila se convierte [10, 10]y la cola ['×', '+']. ×ahora se puede evaluar, lo que hace que la pila[100] y la cola ['+'].
  • 10: La pila se convierte [100, 10], lo que permite +ser evaluada. La pila se convierte [110]y la cola[] .

El resultado final es [110] .

Los comandos utilizados en estas demostraciones son consistentes con los del lenguaje anyfix; sin embargo, el último ejemplo no funcionará debido a un error en mi intérprete. (Descargo de responsabilidad: sus envíos no se utilizarán en el intérprete anyfix)

Desafío

Seleccione un conjunto de 5 caracteres que no sean espacios en blanco y que no sean dígitos y cree un intérprete anyfix de acuerdo con las especificaciones anteriores. Su programa puede generar la matriz singular o el valor que contiene; se garantiza que la pila de valores solo contendrá un valor único al final de la ejecución y que la cola de operadores estará vacía al final de la ejecución.

Esto es por lo que gana el código más corto en bytes.

Casos de prueba

Para estos casos de prueba, duplicado es ", negativo es -, suma es +, multiplicación es ×e igualdad es =.

Input -> Output
1+2×3 -> 9
1"+"+ -> 4
2"××" -> 16
3"×+" -> 18
3"+×" -> 36
123"= -> 1 ("= always gives 1)
1+2=3 -> 1
1"=2+ -> 3
1-2-+ -> -3
-1-2+ -> 3 (hehe, the `-1` becomes `+1` at the `-` rather than making the `2` a `-1`)
+×"10 10 -> 200 (after the 10 is duplicated (duplication is delayed), 10 + 10 is performed and then 20 * 10, giving 200)

Reglas

  • Se aplican lagunas estándar
  • Puede tomar el intérprete oficial anyfix y jugarlo si lo desea. Espera perder horriblemente.

La entrada se dará como una cadena y la salida como una matriz, un solo entero, fuera de la representación de la cadena de cualquiera. Puede suponer que la entrada solo contendrá espacios, dígitos y los 5 caracteres que elija.

* no realmente

Hiperneutrino
fuente
2
Ve a cualquier lugar * ™.
Jonathan Allan
¿Cuál es el resultado del operador de igualdad? 0y 1?
Felix Palmen
1
@ JonathanAllan ver arriba;
Eliminé
1
@RickHitchcock Claro.
HyperNeutrino
1
Probablemente debería incluir ×+"10 10en los casos de prueba, o cualquier otro ejemplo que esté 1) usando un espacio en blanco y 2) retrasando el uso del operador duplicado (dos cosas que omití por completo).
Arnauld

Respuestas:

5

JavaScript (ES6), 204 203 200 bytes

Devuelve un entero.

e=>e.replace(/\d+|\S/g,v=>{for((1/v?s:v>','?u:b)[U='unshift'](v);!!u[0]/s[0]?s[U](u.pop()>'c'?s[0]:-S()):!!b[0]/s[1]?s[U](+eval(S(o=b.pop())+(o<'$'?'==':o)+S())):0;);},s=[],u=[],b=[],S=_=>s.shift())|s

Caracteres utilizados:

  • +: adición
  • *: multiplicación
  • #: igualdad
  • d: duplicar
  • -: negativo

Casos de prueba

Formateado y comentado

e => e.replace(                     // given an expression e, for each value v matching
  /\d+|\S/g, v => {                 // a group of digits or any other non-whitespace char.
    for(                            //   this loop processes as many operators as possible
      (                             //   insert v at the beginning of the relevant stack:
        1 / v ? s : v > ',' ? u : b //     either value, unary operator or binary operator
      )[U = 'unshift'](v);          //     (s[], u[] or b[] respectively)
      !!u[0] / s[0] ?               //   if there's at least 1 value and 1 unary operator:
        s[U](                       //     unshift into s[]:
          u.pop() > 'c' ?           //       for the duplicate operator:
            s[0]                    //         a copy of the last value
          :                         //       else, for the negative operator:
            -S()                    //         the opposite of the last value
        ) :                         //     end of unshift()
      !!b[0] / s[1] ?               //   if there's at least 2 values and 1 binary operator:
        s[U](                       //     unshift into s[]:
          +eval(                    //       the result of the following expression:
            S(o = b.pop()) +        //         the last value, followed by the
            (o < '$' ? '==' : o) +  //         binary operator o with '#' replaced by '=='
            S()                     //         followed by the penultimate value
          )                         //       end of eval()
        ) : 0;                      //     end of unshift()
    );                              //   end of for()
  },                                // end of replace() callback
  s = [],                           // initialize the value stack s[]
  u = [],                           // initialize the unary operator stack u[]
  b = [],                           // initialize the binary operator stack b[]
  S = _ => s.shift()                // S = helper function to shift from s[]
) | s                               // return the final result
Arnauld
fuente
No pienses que esto funciona -1+-2. Devuelve 3 en lugar de -3.
Rick Hitchcock
1
@RickHitchcock Entiendo que el segundo -debe aplicarse -1inmediatamente.
Arnauld
Creo que el segundo -iría con el 2ya que viene después de otro operador. Quizás @HyperNeutrino pueda aclarar. El operador negativo puede ser ambiguo en algunas situaciones.
Rick Hitchcock
3

JavaScript (ES6), 162 152 143 150 bytes

(s,N=s.replace(/(-?\d+)-|-(-)/g,'- $1 ').match(/(- ?)*?\d+|R/g))=>+eval(`R=${N[0]>'9'?N[1]:N[0]},${s.match(/[+*=]/g).map((o,i)=>'R=R'+o+'='+N[i+1])}`)

Ligeramente incólume:

(s,
 N=s.replace(/(-?\d+)-|-(-)/g,'- $1 ').      //change postfix negatives to prefix,
                                             //and change "--" to "- - "
     match(/(- ?)*?\d+|R/g)                  //grab numbers and duplicates
)=>+eval(`R=${N[0] > '9' ?  N[1] : N[0]},    //handle a delayed duplicate
          ${s.match(/[+*=]/g).               //grab the operators
              map((o,i)=>'R=R'+o+'='+N[i+1]) //create a comma-separated list of assignments
           }
         `)

Explicación

Estoy usando *para multiplicar y Rpara duplicar. Los otros operadores son los mismos que en la pregunta.

N se convierte en la matriz de los números (incluidos los duplicados).

El replacemaneja el caso donde el signo negativo viene después del número. Por ejemplo, se cambiará 1-a - 1y -1-a- -1 .

Dentro de eval, s.matchcrea la matriz de operadores binarios. Tenga en cuenta que esto siempre tendrá uno menos elementos que N.

El resultado de la función es evalla asignación de los números y operadores.

Esto es lo que se evaledita para cada uno de los casos de prueba:

0+2*3        R=0,R=R+=2,R=R*=3        = 6 
1+2*3        R=1,R=R+=2,R=R*=3        = 9 
1R+R+        R=1,R=R+=R,R=R+=R        = 4 
2R**R        R=2,R=R*=R,R=R*=R        = 16 
3R*+R        R=3,R=R*=R,R=R+=R        = 18 
3R+*R        R=3,R=R+=R,R=R*=R        = 36 
123R=        R=123,R=R==R             = 1 
1+2=3        R=1,R=R+=2,R=R==3        = 1 
1R=2+        R=1,R=R==R,R=R+=2        = 3 
1-2-+        R=- 1,R=R+=- 2           = -3 
-1-2+        R=1,R=R+=2               = 3 
*+R10 10     R=10,R=R*=10,R=R+=10     = 110 
+*R10 10     R=10,R=R+=10,R=R*=10     = 200 
-1+--2       R=-1,R=R+=- -2           = 1 
-1+-2        R=-1,R=R+=-2             = -3 

El operador de coma en una expresión de JavaScript devuelve el resultado de su última expresión, por lo que mapautomáticamente devuelve una expresión utilizable.

El +signo es necesario antes evalde cambiar truea 1y falsepara 0.

Usar Rtanto la variable como el operador duplicado simplifica enormemente lamap subexpresiones de.

Casos de prueba:

Rick Hitchcock
fuente
2
No creo que replacefuncione como estaba previsto. Esto devuelve 3para -1+--2y creo que 1sería correcto (los 1cambios firman tres veces antes de que haya un segundo argumento de la +disposición, dando como resultado -1 + 2).
Felix Palmen
Gran captura, @FelixPalmen. Ahora arreglado.
Rick Hitchcock
2

JavaScript, 321 311 bytes

_="f=a=>(a.replace(/\\d+|./g,mq!~(b='+*=\"- '.indexOf(a))|b>2j3j4j5&&o+aw1/y?(y*=-1wcz:1/y?oywcz:hz,rql.map(z=>m(lki,1)[0],i)),hq1/s[1]?sk0,2,+eval(`y${a=='='?a+a:a}s[1]`)):cz,cqlki,0,a),s=[],l=[],e='splice'),s)z(a,i)ys[0]w)^r``:q=z=>os.unshift(k[e](j?b-";for(i of"jkoqwyz")with(_.split(i))_=join(pop());eval(_)

Pruébalo en línea!

Los cinco caracteres son los mismos que en la pregunta, excepto *para la multiplicación.
El script se comprime usando RegPack . El script original se almacena en la variable _después de la evaluación.


fuente
No creo que esto funcione -1+-2. Devuelve 3 en lugar de -3.
Rick Hitchcock
@RickHitchcock. ¿Por qué crees que debería regresar en -3lugar de 3?
Puedo estar malentendiendo al operador negativo. En general, -1 + -2es -3, pero ¿debería analizarse como en su --1 + 2lugar?
Rick Hitchcock
1
@RickHitchcock. Estoy bastante seguro de que el resultado es 3. 2Incluso antes de llegar a la pila, -se evalúa el segundo y, por lo tanto, tenemos 1 2 +cuál es realmente 3. Ah, y probablemente también tengas que editar tu respuesta.
Probablemente tengas razón. Usted y Arnauld obtienen la misma respuesta, y le he pedido al OP que me aclare algo. Te votaría de nuevo si pudiera.
Rick Hitchcock
1

Haskell , 251 bytes

(#([],[]))
(x:r)#(o,n)|x>'9'=r#h(o++[x],n)|[(a,t)]<-lex$x:r=t#h(o,read a:n)
_#(o,n:_)=n
h=e.e
e(o:s,n:m:r)|o>'N'=e(s,g[o]n m:r)
e(o:s,n:r)|o=='D'=e(s,n:n:r)|o=='N'=e(s,-n:r)
e(o:s,n)|(p,m)<-e(s,n)=(o:p,m)
e t=t
g"a"=(+)
g"m"=(*)
g"q"=(((0^).abs).).(-)

Pruébalo en línea! Utiliza los siguientes caracteres: apara sumar, mmultiplicar, igualar q, Dduplicar y Nnegar. (Quería usar epara igualdad, pero me encontré con el problema que se lexanaliza 2e3como un número). Ejemplo de uso: (#([],[])) "2a3 4m"rendimientos 20.

Laikoni
fuente
1

Código de máquina 6502 (C64), 697 bytes

00 C0 A2 00 86 29 86 26 86 27 86 28 86 4B 86 4C 86 CC 20 E4 FF F0 FB C9 0D F0
10 C9 20 30 F3 A6 27 9D B7 C2 20 D2 FF E6 27 D0 E7 C6 CC A9 20 20 1C EA A9 0D
20 D2 FF 20 95 C0 20 09 C1 20 95 C0 A6 26 E4 27 F0 4E BD B7 C2 E6 26 C9 20 F0
E8 C9 2D D0 09 A6 4C A9 01 9D B7 C3 D0 32 C9 22 D0 09 A6 4C A9 02 9D B7 C3 D0
25 C9 2B D0 09 A6 4C A9 81 9D B7 C3 D0 18 C9 2A D0 09 A6 4C A9 82 9D B7 C3 D0
0B C9 3D D0 0D A6 4C A9 83 9D B7 C3 E6 28 E6 4C D0 A3 4C 8A C2 A6 29 F0 6F A4
28 F0 6B CA F0 4B C6 28 A6 4B E6 4B BD B7 C3 F0 F5 30 14 AA CA D0 0B 20 7B C2
20 E1 C1 20 4D C2 D0 D9 20 5C C2 D0 D4 29 0F AA CA D0 0B 20 6D C2 20 08 C2 20
4D C2 D0 C3 CA D0 0B 20 6D C2 20 16 C2 20 4D C2 D0 B5 20 6D C2 20 F4 C1 20 4D
C2 D0 AA A4 4B B9 B7 C3 F0 02 10 AC C8 C4 4C F0 0F B9 B7 C3 F0 F6 30 F4 AA A9
00 99 B7 C3 F0 A6 60 A0 00 A6 26 E4 27 F0 15 BD B7 C2 C9 30 30 0E C9 3A 10 0A
E6 26 99 37 C4 C8 C0 05 D0 E5 C0 00 F0 08 A9 00 99 37 C4 20 39 C2 60 A2 FF E8
BD 37 C4 D0 FA A0 06 88 CA 30 0A BD 37 C4 29 0F 99 37 C4 10 F2 A9 00 99 37 C4
88 10 F8 A9 00 8D 3D C4 8D 3E C4 A2 10 A0 7B 18 B9 BD C3 90 02 09 10 4A 99 BD
C3 C8 10 F2 6E 3E C4 6E 3D C4 CA D0 01 60 A0 04 B9 38 C4 C9 08 30 05 E9 03 99
38 C4 88 10 F1 30 D2 A2 06 A9 00 9D 36 C4 CA D0 FA A2 10 A0 04 B9 38 C4 C9 05
30 05 69 02 99 38 C4 88 10 F1 A0 04 0E 3D C4 2E 3E C4 B9 38 C4 2A C9 10 29 0F
99 38 C4 88 10 F2 CA D0 D6 C0 05 F0 06 C8 B9 37 C4 F0 F6 09 30 9D 37 C4 E8 C8
C0 06 F0 05 B9 37 C4 90 F0 A9 00 9D 37 C4 60 A9 FF 45 FC 85 9F A9 FF 45 FB 85
9E E6 9E D0 02 E6 9F 60 A2 00 86 9F A5 FB C5 FD D0 07 A5 FC C5 FE D0 01 E8 86
9E 60 A5 FB 18 65 FD 85 9E A5 FC 65 FE 85 9F 60 A9 00 85 9E 85 9F A2 10 46 FC
66 FB 90 0D A5 FD 18 65 9E 85 9E A5 FE 65 9F 85 9F 06 FD 26 FE CA 10 E6 60 20
33 C1 A6 29 AD 3D C4 9D 3F C4 AD 3E C4 9D 3F C5 E6 29 60 A6 29 A5 9E 9D 3F C4
A5 9F 9D 3F C5 E6 29 60 A6 29 BD 3E C4 9D 3F C4 BD 3E C5 9D 3F C5 E6 29 60 C6
29 A6 29 BD 3F C4 85 FD BD 3F C5 85 FE C6 29 A6 29 BD 3F C4 85 FB BD 3F C5 85
FC 60 A6 29 BD 3E C5 10 13 20 7B C2 20 E1 C1 20 4D C2 A9 2D 20 D2 FF A6 29 BD
3E C5 8D 3E C4 BD 3E C4 8D 3D C4 20 8B C1 A9 37 A0 C4 4C 1E AB

Demostración en línea

Uso sys49152 , luego ingrese la expresión anyfix y presione Intro.

  • casi sin verificación de errores, así que espere salidas divertidas en expresiones inválidas.
  • El símbolo para la multiplicación es *, todos los demás como se sugiere.
  • la longitud máxima de entrada es de 256 caracteres, no puede haber más de 127 operadores en cola.
  • La rutina de entrada no maneja caracteres de control, así que no escriba mal;)
  • los enteros están firmados a 16 bits, el desbordamiento se envolverá en silencio.
  • El recuento de bytes es un poco grande porque esta CPU ni siquiera sabe multiplicación y el sistema operativo C64 OS / ROM no le da ningún tipo de aritmética o conversiones enteras a / desde cadenas decimales.

Aquí está el código fuente legible del ensamblador de estilo ca65 .

Felix Palmen
fuente
1

VB.NET (.NET 4.5) 615 576 bytes

-39 bytes gracias a Felix Palmen al cambiar \r\na\n

Requiere Imports System.Collections.Generic(incluido en el recuento de bytes)

Dim s=New Stack(Of Long),q=New List(Of String),i=Nothing,t=0,c=0
Function A(n)
For Each c In n
If Long.TryParse(c,t)Then
i=i &"0"+t
Else
If c<>" "Then q.Add(c)
If i<>A Then s.Push(i)
i=A
End If
If i=A Then E
Next
If i<>A Then s.Push(i)
E
A=s
End Function
Sub E()
c=s.Count
If c=0Then Exit Sub
For Each op In q
If op="-"Or op="d"Or c>1Then
Select Case op
Case"*"
s.Push(s.Pop*s.Pop)
Case"+"
s.Push(s.Pop+s.Pop)
Case"="
s.Push(-(s.Pop=s.Pop))
Case"-"
s.Push(-s.Pop)
Case"d"
s.Push(s.Peek)
End Select
q.Remove(op)
E
Exit Sub
End If
Next
End Sub

El punto de entrada es Function A, que toma una cadena como entrada y devuelve a Stack(Of Long).

Símbolos:

  • Además - +
  • Multiplicación *
  • Igualdad - =
  • Negación -
  • Duplicación d

Visión general:

La función Atoma la entrada y la tokeniza. Utiliza a Long?para hacer un total acumulado para enteros de varios dígitos, queNothing significa que actualmente no estamos leyendo un entero.

La subrutina Etoma la pila de enteros y la cola de operadores, y evalúa la notación anyfix. Se llama recursivamente hasta que no quedan acciones.

Declaro parámetros globales de una vez para guardar bytes en la declaración y el paso de parámetros.

El valor de retorno de Ase establece asignando un valor a la variable coincidente A.

VB Truees equivalente a -1, de modo que la operación tiene que negar el resultado para obtener el valor correcto.

Pruébalo en línea!

Brian J
fuente
sugiera agregar ¡ Pruébelo en línea!
Felix Palmen
por cierto, con el Imports, solo obtengo un recuento de bytes 576: ¿podría haber contado mal?
Felix Palmen
@FelixPalmen con el que conté en \r\nlugar de \n, así que ahí es donde está la discrepancia.
Brian J
@FelixPalmen Agregó TIO, ¡gracias por recordármelo! :) (Oh, no me di cuenta de que ya lo hiciste para esta publicación)
Brian J