Sommer? C'est mon fort!

18

introduction

Forte est un langage ésotérique très particulier basé sur le concept de modification des valeurs des nombres. Dans Forte, les nombres ne sont pas des constantes mais des variables, vous pouvez utiliser l' LETinstruction pour leur attribuer de nouvelles valeurs.

Par exemple, après avoir exécuté à LET 2=4-1partir de maintenant, il 2prend la valeur de 3, ce qui signifie que chaque fois que la valeur 2apparaît dans une expression, elle est plutôt "remplacée" par 3. L'expression (1+1)*2serait désormais évaluée à 9.

Cette instruction dans Forte est utilisée à la fois pour stocker des informations et pour contrôler le flux (les lignes sont numérotées et en modifiant la valeur de leurs nombres, vous pouvez déterminer l'ordre de leur exécution). Dans ce défi, nous ne traiterons pas de ce deuxième aspect.

Le défi

Vous devez écrire un interpréteur pour un sous-ensemble simplifié d' LETexpressions de Forte .

Vous recevrez en entrée une série de lignes suivant cette grammaire:

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

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

Remarque: cette grammaire n'est pas valide en Forte car elle manque de numéros de ligne, de LET et de parenthèses (qui sont toujours obligatoires)

Autrement dit, vous n'aurez qu'à gérer le calcul des sommations et l'attribution de valeurs aux nombres. Les parenthèses ne seront pas présentes dans l'entrée, et chaque expression devra être évaluée de gauche à droite: attention aux résultats partiels affectés par les redéfinitions!

Les nombres seront toujours des entiers non négatifs, jusqu'à la limite du type d'entier natif de votre langue (ou 2 ^ 32, selon le plus élevé des deux).

Pour chaque ligne, vous devez sortir le résultat de l'expression et affecter ce résultat à la valeur (éventuellement réaffectée) du premier nombre, ce qui affectera la façon dont les lignes suivantes seront interprétées.

C'est le , le code le plus court (en octets) gagne!

Autres règles

  • Le format d'entrée est flexible, vous pouvez par exemple prendre une seule chaîne avec des sauts de ligne, une liste de chaînes, une liste de listes de nombres ... Il en va de même pour la sortie, tant qu'il est clair quel est le résultat de chaque expression dans l'entrée.
  • Vous pouvez soumettre une fonction, un programme complet ou une solution à exécuter dans un environnement REPL en l'appelant une fois pour chaque ligne.
  • Les failles standard sont interdites, en particulier vous ne pouvez pas appeler un interprète Forte externe dans votre code.

Exemples

Ils font tous partie de la même entrée. Après chaque ligne, la sortie attendue par rapport à cette ligne est affichée, parfois avec un commentaire indiquant les réaffectations pertinentes (ne faisant pas partie de la sortie requise).

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
la source
Est 0un numéro valide?
orlp
@orlp 0est valide ("Les nombres seront toujours des entiers non négatifs")
Leo
Faut-il accepter des opérateurs arithmétiques?
bacchusbeale
@bacchusbeale Non, juste une sommation.
Leo
Y a-t-il un nombre maximum particulier ou est-il aussi grand que le type entier natif de la langue? En outre, ce serait une sortie non valide de renvoyer une liste de nombres, dont l'un est le résultat, non? (comme imprimer le dictionnaire / la carte pour quel numéro va à quel numéro)
zgrep

Réponses:

4

Gelée , 28 octets

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

Essayez-le en ligne!

C'est l'un des rares programmes Jelly où il semble plus difficile de prendre des entrées à partir des entrées standard. C'est un programme complet (l'écriture d'une fonction aurait été plus courte mais est interdite par les règles PPCG, car elle ne s'exécuterait pas correctement la deuxième fois). Le format d'entrée ressemble à ceci:

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

Explication

Fonction d'assistance 1Ŀ (traduire un entier en sa valeur)

®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.}

Assez commodément, cette fonction d'assistance fonctionnera correctement soit sur une seule valeur, soit sur une liste de valeurs, en raison de la façon dont yest définie. Si plusieurs mappages sont donnés pour une seule valeur, nous prenons le premier mappage de la table. La table de mappage est stockée dans le registre (qui n'est fondamentalement qu'une variable; Jelly n'a qu'une seule variable).

Fonction d'aide 2Ŀ (évaluer une instruction 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}.

Nous ne voulons pas vraiment de valeur de retour ici; nous exécutons simplement cela pour ses effets secondaires (mise à jour du registre et sortie de la valeur assignée). Les fonctions Jelly retournent toujours une valeur, cependant, nous laissons simplement la table de mappage être renvoyée, car c'est le plus tersest.

Programme 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}

Normalement, cela nous donnerait le premier argument de ligne de commande lorsqu'il est exécuté dans ce contexte, mais il n'y en a pas (nous prenons l'entrée à partir de l'entrée standard), donc il s'exécute dans un mode alternatif qui nous donne la chaîne nulle. La nécessité d'initialiser le registre (à partir de sa valeur par défaut de 0, qui se bloque y) signifie que nous ne pouvons pas mentionner implicitement l'entrée de l'utilisateur, ce qui signifie qu'il est aussi bon marché de le prendre à partir de l'entrée standard ( Ɠ) que de le prendre à partir d'un argument de ligne de commande ( ³ou ), et être en mesure d'accéder à l'utilisation alternative des moyens que la forme inhabituelle (pour Jelly) d'entrée est en fait un octet plus court.

Il est possible que cela puisse être amélioré. Je n'ai toujours pas compris pourquoi la deuxième ligne doit dire ⁸Ǥ;plutôt que juste ;@Ç- les deux devraient être équivalentes, pour autant que je comprends Jelly, étant donné le manque d'utilisation de µ/ ð/ ø- mais ce dernier se bloque pour une raison quelconque. De même, il existe plusieurs autres façons de réorganiser le programme sans perdre d'octets, il est donc possible que j'ai manqué un moyen de raccourcir les choses.

Soit dit en passant, la modification de la dernière ligne ;vous donne un aperçu intéressant du fonctionnement interne du programme, car il affichera alors l '"historique du registre" qui est implicitement émis par les valeurs de retour de 2Ḷ.


la source
5

Perl 5 , 92 octets

90 octets de code + -pldrapeaux.

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

Essayez-le en ligne!

J'utilise la table %hde hachage pour stocker le mappage entre les nombres.
La fonction ( sub) frenvoie le numéro auquel son mappage d'entrée (ou son entrée si elle n'est mappée à aucun numéro): $h{$a=pop}récupère le numéro vers lequel le mappage d' entrée. Si ce n'est pas le cas, grâce à //$a, la valeur de ($b=$h{$a=pop}//$a)est l'entrée ( $a). Nous nous assurons que cette valeur n'est pas l'entrée elle-même pour ne pas boucler indéfiniment ( !=$a). Ensuite, nous appelons récursivement fou renvoyons l'entrée.
Le programme principal se compose de deux étapes:
- s%(\d+)\+(\d+)%f($1)+f$2%e&&redoévalue le premier ajout sur le côté droit, alors qu'il y a encore un ajout: il remplace permet d'accéder au côté gauche avec et au côté droit avec , puisx+y par le résultat de l'évaluation de f(x)+f(y).
- /=/;$_=$h{f$`}=f$'la mission:/=/$`$'$h{f$`}=f$' fait l'affectation. Et nous l'affectons également à ce $_qui est implicitement imprimé après chaque ligne.

Dada
la source
5

JavaScript (Node.js) , 81 octets

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)

Essayez-le en ligne!

Reçoit l'entrée en appelant f avec la valeur à affecter, puis en appelant le résultat de cela avec un tableau de valeurs à additionner. (ie f(5)([4])) Répétez pour plusieurs lignes.

vest utilisé comme une fonction pour calculer la valeur actuelle réelle d'un nombre, et également comme un objet pour stocker les valeurs réelles. v[x]=v[x]||xS'assure d' abord que v[x]c'est défini. v[x]-xeffectue une comparaison pour déterminer s'il s'agit du nombre réel ou non. Si le nombre ne correspond pas à lui-même, v(v[x])réessayez récursivement, sinon retournez x.

f effectue le calcul et l'affectation, curry pour enregistrer un octet, où le deuxième appel renvoie la valeur calculée.

OinkIguana
la source
3

Haskell , 116113108106 octets

(#)=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?)

Essayez-le en ligne! Chaque équation 4=3+1+5est notée comme tuple (4,[3,1,5]). La fonction anonyme(id?) prend une liste de ces tuples et renvoie une liste de tous les résultats intermédiaires.

#est une fonction permettant de trouver un point fixe d'une fonction donnée eet une valeur de départ x.

La fonction ?prend une fonction d'évaluation eet résout récursivement chaque équation. foldl1(\a b->e#(e#a+e#b))sévalue le côté droit d'une équation et enregistre le résultat m, par exemple pour 4=3+1+5le calcul eval(eval(eval 3 + eval 1) + eval 5), où chacun evalest une application de point fixe de e. Ensuite, la fonction eval est modifiée pour prendre en compte la nouvelle affectation de n: (\x->last$m:[e#x|x/=e#n])qui est la même que \x -> if x == eval n then m else eval x.

La fonction d'évaluation initiale consiste à idmapper chaque entier sur lui-même.


Merci à Ørjan Johansen pour une fonction de point fixe plus courte, économisant 2 octets!

Laikoni
la source
Bon travail! Au fait, vous devez retourner tous les résultats intermédiaires, vous pouvez donc abandonnerlast.
Leo
2
(#)e=until((==)=<<e)eou (#)=until=<<((==)=<<)est plus courte.
Ørjan Johansen
@ ØrjanJohansen Merci beaucoup!
Laikoni
3

oK, 48 octets

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

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

Essayez-le en ligne!


Si vous ne me dérange pas d' avoir une limite supérieure aux chiffres que vous pouvez utiliser, comme en utilisant uniquement des nombres à 0travers 998, puis les suivantes suffixes ( 41 octets ± quelques en fonction du maximum):

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

Explication:

; sépare trois définitions.

aest un dictionnaire / carte de nombres. Dans le premier cas, c'est un véritable dictionnaire vide [], dans le second cas, c'est une liste des numéros 0à 998.

sest une fonction qui trouve le nombre "résultant" lorsqu'il reçoit un nombre. La /fin de la fonction signifie qu'elle s'appliquera à sa propre sortie jusqu'à ce que la sortie cesse de changer.

Le dernier bit,, fsignifie 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
la source
3

Python 3, 146 132 130 130 octets

14 octets enregistrés grâce à @Dada
2 octets enregistrés grâce à @ 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)

Essayez-le en ligne!

Reçoit l'entrée sous forme de tuples d'équations [ x = y + z + was (x, (y, z, w))], les sorties via le générateur.

Uriel
la source
Pouvez-vous montrer un exemple d'appel afin qu'il puisse être testé?
Leo
1
@Leo a ajouté TIO.
Uriel
1
gpourrait probablement être écrit g=lambda x:d.get(x)and d[x]!=x and g(d[x])or x. Et je pense que vous pouvez utiliser 1 espace pour mettre en retrait au lieu de 2. Cela devrait vous amener à [132 octets] ( Essayez-le en ligne! ).
Dada
1
@Dada merci! dommage qu'ils n'aient pas de retrait demi-espace: P
Uriel
1
Pas de retrait d'un demi-espace, mais une autre astuce consiste à utiliser un seul onglet au lieu de deux espaces. Tant que les indentations sont différentes, Python est content (vous pouvez par exemple utiliser espace-tabulation et tabulation-espace comme indentations sur des niveaux imbriqués supplémentaires). Cela peut vous faire économiser deux octets :)
Leo