Sumando? Ese es mi fuerte!

18

Introducción

Forte es un lenguaje esotérico muy peculiar basado en el concepto de modificar los valores de los números. En Forte, los números no son constantes sino variables, puede usar la LETinstrucción para asignarles nuevos valores.

Por ejemplo, después de ejecutar a LET 2=4-1partir de ahora 2asume el valor de 3, lo que significa que cada vez que el valor 2aparece en una expresión, en su lugar, se "reemplaza" por 3. La expresión (1+1)*2ahora se evaluaría a 9.

Esta instrucción en Forte se usa tanto para almacenar información como para controlar el flujo (las líneas están numeradas y al cambiar el valor de sus números puede determinar el orden de su ejecución). En este desafío no abordaremos este segundo aspecto.

El reto

Debe escribir un intérprete para un subconjunto simplificado de las LETexpresiones de Forte .

Recibirá como entrada una serie de líneas siguiendo esta gramática:

<line>::= <number>=<expression>

<expression>::= <number>|<expression>+<number>

Nota: esta gramática no es válida Forte porque carece de números de línea, LET y paréntesis (que siempre son obligatorios)

Es decir, solo tendrá que ocuparse de sumar cálculos y asignar valores a los números. Los paréntesis no estarán presentes en la entrada, y cada expresión deberá evaluarse de izquierda a derecha: ¡tenga en cuenta que los resultados parciales se ven afectados por las redefiniciones!

Los números siempre serán enteros no negativos, hasta el límite del tipo de entero nativo de su idioma (o 2 ^ 32, el que sea mayor).

Para cada línea, debe generar el resultado de la expresión y asignar este resultado al valor (posiblemente reasignado) del primer número, lo que afectará la forma en que se interpretarán las siguientes líneas.

Este es el , ¡el código más corto (en bytes) gana!

Otras reglas

  • El formato de entrada es flexible, por ejemplo, puede tomar una sola cadena con nuevas líneas, una lista de cadenas, una lista de listas de números ... Lo mismo ocurre con la salida, siempre que esté claro cuál es el resultado de cada expresión en la entrada
  • Puede enviar una función, un programa completo o una solución para que se ejecute en un entorno REPL que lo llame una vez para cada línea.
  • Las lagunas estándar están prohibidas, en particular no puede llamar a un intérprete externo de Forte en su código.

Ejemplos

Todos estos son parte de la misma entrada. Después de cada línea, se muestra la salida esperada relativa a esa línea, a veces con un comentario que indica reasignaciones relevantes (que no forman parte de la salida requerida).

5=4
4
6=5
4        # 5 -> 4
7=1+2+5
7
7=5+2+1
4        # Order of operations matters! 5+2 -> 4+2 -> 6 -> 4
18=5+6+7
12
5=3
3        # Remember: 5 -> 4
10=6+4
3        # 6 -> 4 -> 3, 3+3 = 6 -> 3
León
fuente
Es 0un numero valido?
orlp
@orlp 0es válido ("Los números siempre serán enteros no negativos")
Leo
¿Deberíamos aceptar cualquier operador aritmético?
bacchusbeale
@bacchusbeale No, solo resumen.
Leo
¿Hay algún número máximo en particular, o es tan grande como el tipo entero nativo del idioma? Además, sería una salida no válida devolver una lista de números, uno de los cuales es el resultado, ¿verdad? (como imprimir el diccionario / mapa para qué número va a qué número)
zgrep

Respuestas:

4

Jalea , 28 bytes

®y$ÐL
ṪÇ+Ç¥/Ṅ;®⁸Ǥ;©
⁸©ḷƓÇ€¤

Pruébalo en línea!

Este es uno de los pocos programas de Jelly donde parece ser más terso recibir información de entrada estándar. Es un programa completo (escribir una función habría sido más corto pero está prohibido por las reglas PPCG, porque no se ejecutará correctamente la segunda vez). El formato de entrada se ve así:

[[5,[4]],[6,[5]],[7,[1,2,5]],[7,[5,2,1]],[18,[5,6,7]],[5,[3]],[10,[6,4]]]

Explicación

Función auxiliar 1Ŀ (traducir un número entero a su valor)

®y$ÐL
   ÐL   Repeatedly, until there are no further changes,
  $       apply the following unary function to {the input}:
 y          replace values using the mapping table
®             stored in the register.
        {Then return the eventual result.}

De manera conveniente, esta función auxiliar funcionará correctamente en un solo valor o en una lista de valores, debido a la forma en que yestá definida. Si se proporciona más de una asignación para un solo valor, tomamos la primera asignación de la tabla. La tabla de mapeo se almacena en el registro (que básicamente es solo una variable; Jelly solo tiene una variable).

Función auxiliar 2Ŀ (evaluar una instrucción LET)

ṪÇ+Ç¥/Ṅ;®⁸Ǥ;©
Ṫ               On the last element of {the input},
 Ç              Run 1Ŀ,
     /          left fold it via
    ¥             the following binary function:
  +                 add {the two arguments}
   Ç                and run 1Ŀ on {the result},
      Ṅ         write {the result} (and a newline) to standard output,
       ;®       append the value of the register,
            ;   prepend
           ¤      the following value:
         ⁸          {the input, without its last element}
          Ç         with 1Ŀ run on it
             ©  and store that value in the register {and return it}.

Realmente no queremos un valor de retorno aquí; solo estamos ejecutando esto por sus efectos secundarios (actualizar el registro y generar el valor asignado). Sin embargo, las funciones Jelly siempre devuelven un valor, por lo que solo dejamos que se devuelva la tabla de mapeo, ya que eso es lo más terso.

Programa principal

⁸©ḷƓÇ€¤
 ©       Initialize the mapping table
⁸        with the empty string (which is also the empty list)
  ḷ      then evaluate and discard
      ¤  the following value:
   Ɠ       a line from standard input, parsed into a data structure
    Ç€     with each element transformed via 2Ŀ
         {and leave the empty string to be printed implicitly}

Normalmente, nos daría el primer argumento de línea de comandos cuando se ejecuta en este contexto, pero no hay ninguno (estamos tomando la entrada de la entrada estándar), por lo que se ejecuta en un modo alternativo que nos da la cadena nula. La necesidad de inicializar el registro (desde su valor predeterminado de 0, que se bloquea y) significa que no podemos mencionar la entrada del usuario implícitamente, lo que significa que es tan barato tomarlo de la entrada estándar ( Ɠ) como lo sería tomarlo de un argumento de línea de comando ( ³o ), y poder acceder al uso alternativo de significa que la forma de entrada inusual (para Jelly) es en realidad un byte más corto.

Es posible que esto sea mejorable. Todavía no he descubierto por qué la segunda línea necesita decir en ⁸Ǥ;lugar de solo ;@Ç: los dos deberían ser equivalentes, por lo que entiendo a Jelly, dada la falta de uso de µ/ ð/ ø, pero el último se bloquea por alguna razón. Del mismo modo, hay varias otras formas de reorganizar el programa sin perder bytes, por lo que es posible que haya perdido una forma de hacer las cosas un poco más cortas.

Por cierto, cambiar el en la última línea para ;darle una mirada interesante sobre el funcionamiento interno del programa, ya que luego generará el "historial del registro" que está implícitamente emitido por los valores de retorno de 2Ḷ.


fuente
5

Perl 5 , 92 bytes

90 bytes de código + -plbanderas.

sub f{($b=$h{$a=pop}//$a)!=$a?f($b):$a}s%(\d+)\+(\d+)%f($1)+f$2%e&&redo;/=/;$_=$h{f$`}=f$'

Pruébalo en línea!

Utilizo la tabla hash %hpara almacenar la asignación entre números.
La función ( sub) fdevuelve el número al que se asigna su entrada (o su entrada si se asigna a ningún número):$h{$a=pop} recupera el número hacia el cual se asigna la entrada. Si no es ninguno, gracias a //$a, el valor de ($b=$h{$a=pop}//$a)es input ( $a). Nos aseguramos de que estos valores no sean la entrada en sí misma para que no se repitan infinitamente ( !=$a). Luego, llamamos recursivamente fo devolvemos la entrada.
El programa principal consta de dos pasos:
- s%(\d+)\+(\d+)%f($1)+f$2%e&&redoevalúa la primera adición en el lado derecho, mientras todavía hay una adición: reemplaza x+ypor el resultado de la evaluación de f(x)+f(y).
- /=/;$_=$h{f$`}=f$'hace la tarea:/=/permite acceder al lado izquierdo con $`y al lado derecho con $', luego $h{f$`}=f$'realiza la asignación. Y también lo asignamos a $_eso se imprime implícitamente después de cada línea.

Dada
fuente
5

JavaScript (Node.js) , 81 bytes

v=x=>(v[x]=v[x]||x,v[x]-x?v(v[x]):x)
f=x=>l=>v[v(x)]=l.reduce((p,x)=>v(v(x)+p),0)

Pruébalo en línea!

Recibe la entrada llamando a f con el valor a asignar, luego llamando al resultado con una matriz de valores para sumar. (es decir f(5)([4])) Repita para varias líneas.

vse usa como una función para calcular el valor actual real de un número, y también como un objeto para almacenar los valores reales. Primero v[x]=v[x]||xasegura que v[x]se define. v[x]-xrealiza una comparación para determinar si este es el número real o no. Si el número no se asigna a sí mismo,v(v[x]) vuelve a intentarlo recursivamente; de ​​lo contrario, vuelve x.

f realiza el cálculo y la asignación, al curry para guardar un byte, donde la segunda llamada devuelve el valor calculado.

OinkIguana
fuente
3

Haskell , 116 113 108 106 bytes

(#)=until=<<((==)=<<)
e?((n,s):r)|m<-foldl1(\a b->e#(e#a+e#b))s=m:(\x->last$m:[e#x|x/=e#n])?r
e?r=[]
(id?)

Pruébalo en línea! Cada ecuación 4=3+1+5se anota como tupla (4,[3,1,5]). La función anónima(id?) toma una lista de tales tuplas y devuelve una lista de todos los resultados intermedios.

#es una función para encontrar un punto fijo de una función dada ey un valor inicial x.

La función ?toma una función de evaluación ey resuelve recursivamente cada ecuación. foldl1(\a b->e#(e#a+e#b))sevalúa el lado derecho de una ecuación y guarda el resultado en m, por ejemplo, para 4=3+1+5calcular eval(eval(eval 3 + eval 1) + eval 5), donde cada evaluna es una aplicación de punto fijo e. Luego, la función eval se modifica para tener nen cuenta la nueva asignación de : (\x->last$m:[e#x|x/=e#n])que es lo mismo que \x -> if x == eval n then m else eval x.

La función de evaluación inicial es la idque asigna cada entero a sí mismo.


¡Gracias a Ørjan Johansen por una función de punto de fijación más corta, ahorrando 2 bytes!

Laikoni
fuente
¡Buen trabajo! Por cierto, debe devolver todos los resultados intermedios, por lo que puede abandonarlolast.
Leo
2
(#)e=until((==)=<<e)eo (#)=until=<<((==)=<<)es más corto
Ørjan Johansen
@ ØrjanJohansen Muchas gracias!
Laikoni
3

ok, 48 bytes

a:[];s:{*(a@x;x)^0N}/;f:{a[s@x]:y:{s@x+y}/s'y;y}

Uso: f[5;1 2 3] / 5=1+2+3

Pruébalo en línea!


Si no te importa tener un límite superior a los números que se pueden utilizar, por ejemplo, sólo el uso de números 0a través de 998, a continuación, las siguientes basta ( 41 bytes ± a pocos dependiendo de la máxima):

a:!999;s:(a@)/;f:{a[s@x]:y:{s@x+y}/s'y;y}

Explicación:

; separa tres definiciones

aes un diccionario / mapa de números. En el primer caso, es un diccionario real y vacío [], en el segundo caso es una lista de los números 0para 998.

ses una función que encuentra el número "resultante" cuando se le asigna un número. El /final de la función significa que se aplicará a su propia salida hasta que la salida deje de cambiar.

El último bit f, significa que:

f:{                      } /function called f, input number x, list y
                    s'y    /apply s to every number in the list
                   /       /fold through the list
            {s@x+y}        /    sum the two numbers, apply s
   a[s@x]:                 /set the s(x) to map to the final sum
          y:           ;y  /redefine y to be the final sum, then return it
zgrep
fuente
3

Python 3, 146 132 130 bytes

14 bytes guardados gracias a @Dada
2 bytes guardados gracias a @ mbomb007

d={}
g=lambda x:d.get(x)and x!=d[x]and g(d[x])or x
def f(t):
 for n,(s,*r)in t:
  for b in r:s=g(g(s)+g(b))
  d[g(n)]=s;yield g(n)

Pruébalo en línea!

Recibe entradas como tuplas de ecuaciones [ x = y + z + was (x, (y, z, w))], salidas a través del generador.

Uriel
fuente
¿Puedes mostrar un ejemplo de invocación para que se pueda probar?
Leo
1
@Leo agregó TIO.
Uriel
1
gProbablemente podría ser escrito g=lambda x:d.get(x)and d[x]!=x and g(d[x])or x. Y creo que puede usar 1 espacio para sangrar en lugar de 2. Eso debería llevarlo a [132 bytes] (¡ Pruébelo en línea! ).
Dada
1
@Dada gracias! lástima que no tengan sangría de medio espacio: P
Uriel
1
Sin sangría de medio espacio, pero otro buen truco es usar una sola pestaña en lugar de dos espacios. Siempre y cuando las sangrías sean diferentes, Python está contento (por ejemplo, podría usar espacio-tabulación y espacio-tabulación como sangrías en niveles anidados adicionales). Esto puede ahorrarle dos bytes :)
Leo