Somando? Esse é o meu forte!

18

Introdução

Forte é uma linguagem esotérica muito peculiar, baseada no conceito de modificação dos valores dos números. Em números fortes, não são constantes, mas variáveis, você pode usar a LETinstrução para atribuir novos valores a eles.

Por exemplo, depois de executar a LET 2=4-1partir de agora 2assume o valor de 3, o que significa que sempre que o valor 2aparece em uma expressão, ele é "substituído" por 3. A expressão (1+1)*2agora seria avaliada como 9.

Esta instrução em Forte é usada tanto para armazenar informações quanto para controle de fluxo (as linhas são numeradas e, alterando o valor de seus números, você pode determinar a ordem de sua execução). Neste desafio, não trataremos desse segundo aspecto.

O desafio

Você precisa escrever um intérprete para um subconjunto simplificado das LETexpressões de Forte .

Você receberá como entrada uma série de linhas seguindo esta gramática:

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

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

Nota: esta gramática não é válida Forte porque não possui números de linhas, LET e parênteses (que são sempre obrigatórios)

Ou seja, você só precisará lidar com somas de computação e atribuir valores a números. Parênteses não estarão presentes na entrada e cada expressão precisará ser avaliada da esquerda para a direita: cuidado para que resultados parciais sejam afetados por redefinições!

Os números sempre serão números inteiros não negativos, até o limite do tipo inteiro nativo do seu idioma (ou 2 ^ 32, o que for maior).

Para cada linha, você deve exibir o resultado da expressão e atribuir esse resultado ao valor (possivelmente reatribuído) do primeiro número, o que afetará como as linhas a seguir serão interpretadas.

Este é o , o código mais curto (em bytes) vence!

Outras regras

  • O formato de entrada é flexível, você pode, por exemplo, pegar uma única string com novas linhas, uma lista de strings, uma lista de listas de números ... O mesmo vale para a saída, desde que fique claro qual é o resultado de cada expressão em a entrada.
  • Você pode enviar uma função, um programa completo ou uma solução para ser executada em um ambiente REPL, chamando-o uma vez para cada linha.
  • As brechas padrão são proibidas, em particular você não pode chamar um intérprete externo Forte no seu código.

Exemplos

Tudo isso faz parte da mesma entrada. Após cada linha, a saída esperada em relação a essa linha é mostrada, às vezes com um comentário indicando redesignações relevantes (não parte da saída necessária).

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
Leo
fonte
É 0um número válido?
orlp
@orlp 0é válido ("Os números sempre serão números inteiros não negativos")
Leo
Devemos aceitar operadores aritméticos?
precisa saber é o seguinte
@bacchusbeale Não, apenas um resumo.
Leo
Existe algum número máximo em particular ou é tão grande quanto o tipo inteiro nativo do idioma? Além disso, seria uma saída inválida retornar uma lista de números, um dos quais é o resultado, certo? (como imprimir o dicionário / mapa para qual número vai para qual número)
zgrep 13/04

Respostas:

4

Gelatina , 28 bytes

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

Experimente online!

Este é um dos poucos programas Jelly em que parece mais tenso aceitar as informações padrão. É um programa completo (escrever uma função seria mais curto, mas é banido pelas regras do PPCG, porque não seria executado corretamente na segunda vez). O formato de entrada fica assim:

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

Explicação

Função auxiliar 1Ŀ (converter um número inteiro para seu 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 maneira conveniente, essa função auxiliar funcionará corretamente em um único valor ou em uma lista de valores, devido à maneira como yé definido. Se mais de um mapeamento for fornecido para um único valor, pegaremos o primeiro mapeamento da tabela. A tabela de mapeamento é armazenada no registro (que é basicamente apenas uma variável; o Jelly possui apenas uma variável).

Função auxiliar 2Ŀ (avalie uma instrução 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 não queremos um valor de retorno aqui; estamos apenas executando isso por seus efeitos colaterais (atualizando o registro e emitindo o valor atribuído). As funções Jelly sempre retornam um valor, portanto, apenas permitimos que a tabela de mapeamento seja retornada, pois é estressante.

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 daria o primeiro argumento da linha de comando quando executado neste contexto, mas não há um (estamos recebendo informações da entrada padrão), portanto, ele é executado em um modo alternativo que nos fornece a cadeia nula. A necessidade de inicializar o registro (de seu valor padrão 0, que trava y) significa que não podemos mencionar implicitamente a entrada do usuário, o que significa que é tão barato retirá-lo da entrada padrão ( Ɠ) quanto seria retirá-lo de um argumento de linha de comando ( ³ou ) e ser capaz de acessar o uso alternativo significa que a forma incomum (para Jelly) de entrada é na verdade um byte mais curto.

É possível que isso seja improvável. Ainda não entendi por que a segunda linha precisa dizer, ⁸Ǥ;e não apenas ;@Ç- as duas devem ser equivalentes, tanto quanto eu entendo Jelly, dada a falta de uso de µ/ ð/ ø-, mas a última falha por algum motivo. Da mesma forma, existem várias outras maneiras de reorganizar o programa sem perder bytes; portanto, é possível que eu tenha perdido uma maneira de tornar as coisas um pouco mais curtas.

Aliás, alterar a última linha para ;fornecer uma visão interessante do funcionamento interno do programa, pois ele exibirá o "histórico do registro" que é implicitamente gerado pelos valores de retorno de 2Ḷ.


fonte
5

Perl 5 , 92 bytes

90 bytes de código + -plsinalizadores.

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

Experimente online!

Eu uso a hashtable %hpara armazenar o mapeamento entre números.
A função ( sub) fretorna o número para o qual seu mapa de entrada (ou sua entrada se não estiver mapeado para nenhum número): $h{$a=pop}recupera o número para o qual a entrada é mapeada. Se não for nenhum, graças a //$a, o valor de ($b=$h{$a=pop}//$a)é a entrada ( $a). Garantimos que esses valores não sejam a própria entrada para não fazer um loop infinito ( !=$a). Então, chamamos recursivamente fou retornamos a entrada.
O programa principal consiste em duas etapas:
- s%(\d+)\+(\d+)%f($1)+f$2%e&&redoavalia a primeira adição no lado direito, enquanto ainda há uma adição: ela substitui x+ypelo resultado da avaliação de f(x)+f(y).
- /=/;$_=$h{f$`}=f$'faz a tarefa:/=/permite acessar o lado esquerdo com $`e o lado direito com $', em seguida, $h{f$`}=f$'faz a atribuição. E também atribuímos a ele $_que é implicitamente impresso após cada linha.

dada
fonte
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)

Experimente online!

Recebe entrada chamando f com o valor a ser atribuído e, em seguida, chamando o resultado disso com uma matriz de valores para adicionar. (ie f(5)([4])) Repita para várias linhas.

vé usado como uma função para calcular o valor atual real de um número e também como um objeto para armazenar os valores reais. Primeiro v[x]=v[x]||xgarante que v[x]está definido. v[x]-xrealiza uma comparação para determinar se esse é o número real ou não. Se o número não for mapeado para si mesmo, v(v[x])tente recursivamente novamente, caso contrário, retorne x.

f executa o cálculo e a atribuição, com curry para salvar um byte, onde a segunda chamada retorna o valor calculado.

OinkIguana
fonte
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?)

Experimente online! Cada equação 4=3+1+5é anotada como tupla (4,[3,1,5]). A função anônima (id?)pega uma lista dessas tuplas e retorna uma lista de todos os resultados intermediários.

#é uma função para encontrar um ponto fixo de uma determinada função ee um valor inicial x.

A função ?assume uma função de avaliação ee resolve recursivamente cada equação. foldl1(\a b->e#(e#a+e#b))savalia o lado direito de uma equação e salva o resultado em m, por exemplo, para 4=3+1+5computar eval(eval(eval 3 + eval 1) + eval 5), onde cada evalum é um aplicativo de ponto de correção e. Em seguida, a função eval é modificada para levar em consideração a nova atribuição n: (\x->last$m:[e#x|x/=e#n])que é a mesma que \x -> if x == eval n then m else eval x.

A função de avaliação inicial é idque mapeia cada número inteiro para si mesmo.


Obrigado a Ørjan Johansen por uma função de ponto fixo mais curta, economizando 2 bytes!

Laikoni
fonte
Bom trabalho! Pela maneira que você é obrigado a devolver todos os resultados intermediários, assim você pode soltarlast.
Leo
2
(#)e=until((==)=<<e)eou (#)=until=<<((==)=<<)é mais curto.
Ørjan Johansen
@ ØrjanJohansen Muito obrigado!
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

Experimente online!


Se você não se importa de ter um limite superior para os números que você pode usar, como usar apenas números 0através de 998, em seguida, os seguintes sufixos ( 41 bytes ± a poucos dependendo do máximo):

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

Explicação:

; separa três definições.

aé um dicionário / mapa de números. No primeiro caso, é um dicionário real e vazio [], no segundo caso, é uma lista dos números 0para 998.

sé uma função que encontra o número "resultante" quando recebe um número. O /final da função significa que ela se aplicará a sua própria saída até que a saída pare de mudar.

O ú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
fonte
3

Python 3, 146 132 130 bytes

14 bytes salvos graças a @Dada
2 bytes salvos graças 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)

Experimente online!

Recebe entrada como tuplas de equações [ x = y + z + wcomo (x, (y, z, w))], saídas via gerador.

Uriel
fonte
Você pode mostrar um exemplo de chamada para que possa ser testado?
Leo
1
@Leo adicionou TIO.
Uriel
1
gprovavelmente poderia ser escrito g=lambda x:d.get(x)and d[x]!=x and g(d[x])or x. E acho que você pode usar 1 espaço para recuar em vez de 2. Isso deve levá-lo a [132 bytes] ( Experimente online! ).
Dada
1
@Dada thanks! muito ruim que eles não têm travessão semi-espaço: P
Uriel
1
Sem recuo de meio espaço, mas outro truque interessante é usar uma única guia em vez de dois espaços. Desde que as indentações sejam diferentes, o Python fica feliz (você pode, por exemplo, usar espaço-tab e espaço-tab como indentações em níveis aninhados adicionais). Isso pode poupar dois bytes :)
Leo