Implémentez la notation Anyfix!

16

En notation de préfixe, l'opérateur précède les arguments, vous pouvez donc imaginer que l'opérateur appelle next()ce qui est appelé récursivement. En notation infixe, l'opérateur passe entre les arguments, vous pouvez donc l'imaginer simplement comme un arbre d'analyse. En notation postfixe, l'opérateur vient après les arguments, vous pouvez donc l'imaginer comme basé sur la pile.

Dans la notation anyfix, l'opérateur peut aller n'importe où * . Si un opérateur apparaît et qu'il n'y a pas assez d'arguments, alors l'opérateur attend qu'il y ait suffisamment d'arguments. Pour ce défi, vous devez implémenter un évaluateur anyfix très basique. (Notez que anyfix est un langage récréatif que j'ai abandonné avec lequel vous pouvez jouer ici ou vérifier ici )

Vous devrez prendre en charge les commandes suivantes:

(Arity 1)

  • dupliquer
  • négatif

(Arity 2)

  • une addition
  • multiplication
  • égalité: retourne 0ou 1.

Vous pouvez choisir d'utiliser cinq symboles non blancs pour ces commandes. À des fins de démonstration, je vais utiliser "comme doublon, ×comme multiplication et +comme ajout.

Pour les littéraux, vous devez uniquement prendre en charge les entiers non négatifs, mais votre interprète doit pouvoir contenir tous les entiers (dans la plage d'entiers (raisonnable) de votre langue).

Jetons un coup d' oeil à un exemple: 10+5. Le stockage doit se comporter comme une pile et non comme une file d'attente. Donc, tout d'abord, la pile commence à []et la liste des opérateurs en file d'attente commence à []. Ensuite, le littéral 10est évalué, ce qui fait la pile [10]. Ensuite, l'opérateur +est évalué, ce qui nécessite deux arguments. Cependant, il n'y a qu'un seul argument sur la pile, donc la liste des opérateurs en file d'attente devient ['+']. Ensuite, le littéral 5est évalué, ce qui fait la pile [10, 5]. À ce stade, l'opérateur '+'peut être évalué tel quel, créant ainsi la pile [15]et la file d'attente [].

Devrait être le résultat final [15]pour + 10 5, 10 + 5et 10 5 +.

Jetons un coup d' oeil à un exemple plus difficile: 10+". La pile et la file d'attente commencent comme []et []. 10est évalué en premier ce qui fait la pile [10]. Ensuite, +est évalué, ce qui ne change pas la pile (car il n'y a pas assez d'arguments) et fait la file d'attente ['+']. Ensuite, "est évalué. Cela peut s'exécuter immédiatement, ce qui rend la pile [10, 10]. +peut maintenant être évalué de sorte que la pile devient [20]et la file d'attente []. Le résultat final est [20].

Et l'ordre des opérations?

Jetons un coup d'oeil ×+"10 10. La pile et la file d'attente commencent à la fois comme []:

  • ×: La pile est inchangée et la file d'attente devient ['×'].
  • +: La pile est inchangée et la file d'attente devient ['×', '+'].
  • ": La pile est inchangée et la file d'attente devient ['×', '+', '"'].
  • 10: La pile devient [10]. Même s'il ×doit être le premier opérateur à être évalué car il apparaît en premier, il "peut s'exécuter immédiatement et aucun des opérateurs avant lui, il est donc évalué. La pile devient [10, 10]et la file d'attente ['×', '+']. ×peut maintenant être évalué, ce qui rend la pile [100]et la file d'attente ['+'].
  • 10: La pile devient [100, 10], ce qui permet +d'être évaluée. La pile devient [110]et la file d'attente [].

Le résultat final est [110].

Les commandes utilisées dans ces démonstrations sont cohérentes avec celles du langage anyfix; cependant, le dernier exemple ne fonctionnera pas en raison d'un bogue dans mon interprète. (Avertissement: Vos soumissions ne seront pas utilisées dans l'interpréteur anyfix)

Défi

Sélectionnez un ensemble de 5 caractères non blancs non numériques et créez un interpréteur anyfix selon les spécifications ci-dessus. Votre programme peut soit sortir le tableau singulier, soit la valeur qu'il contient; il est garanti que la pile de valeurs ne contiendra qu'une seule valeur en fin d'exécution et que la file d'attente des opérateurs sera vide en fin d'exécution.

C'est du donc le code le plus court en octets l'emporte.

Cas de test

Pour ces cas de test, le doublon est ", le négatif est -, l'addition est +, la multiplication est ×et l'égalité est =.

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)

Règles

  • Les échappatoires standard s'appliquent
  • Vous pouvez prendre l'interprète officiel anyfix et le jouer au golf si vous le souhaitez. Attendez-vous à perdre horriblement.

L'entrée sera donnée sous forme de chaîne et la sortie sous forme de tableau, un entier unique, en dehors de la représentation sous forme de chaîne de l'un ou l'autre. Vous pouvez supposer que l'entrée ne contiendra que des espaces, des chiffres et les 5 caractères que vous choisissez.

* pas maintenant

HyperNeutrino
la source
2
Allez n'importe où * ™.
Jonathan Allan
Quel est le résultat de l'opérateur d'égalité? 0et 1?
Felix Palmen
1
@JonathanAllan voir ci-dessus; J'ai supprimé une commande rip
HyperNeutrino
1
@RickHitchcock Bien sûr.
HyperNeutrino
1
Vous devriez probablement inclure ×+"10 10dans les cas de test, ou tout autre exemple, 1) l'utilisation d'un espace blanc et 2) le retard de l'utilisation de l' opérateur en double (deux choses que j'ai complètement manquées).
Arnauld

Réponses:

5

JavaScript (ES6), 204 203 200 octets

Renvoie un entier.

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

Caractères utilisés:

  • +: une addition
  • *: multiplication
  • #: égalité
  • d: dupliquer
  • -: négatif

Cas de test

Formaté et commenté

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
la source
Ne pensez pas que cela fonctionne -1+-2. Renvoie 3 au lieu de -3.
Rick Hitchcock
1
@RickHitchcock Ma compréhension est que le 2e -doit être appliqué -1immédiatement.
Arnauld
Je pense que le 2e -irait avec le 2car il vient après un autre opérateur. Peut-être que @HyperNeutrino peut clarifier. L'opérateur négatif peut être ambigu dans certaines situations.
Rick Hitchcock
3

JavaScript (ES6), 162 152 143 150 150 octets

(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])}`)

Légèrement non golfé:

(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
           }
         `)

Explication

J'utilise *pour la multiplication et Rpour la duplication. Les autres opérateurs sont les mêmes que dans la question.

N devient le tableau des nombres (y compris les doublons).

Le replacegère le cas où le signe négatif vient après le nombre. Par exemple, il passera 1-à - 1et -1-à- -1 .

Dans le eval, s.matchcrée le tableau des opérateurs binaires. Notez que cela aura toujours un élément de moins que N.

Le résultat de la fonction est evalle mappage des nombres et des opérateurs.

Voici ce qui est evalédité pour chacun des cas de test:

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 

L'opérateur virgule dans une expression JavaScript renvoie le résultat de sa dernière expression, ce qui maprenvoie automatiquement une expression utilisable.

Le +signe est nécessaire avant evalde passer trueà 1et falseà 0.

L'utilisation Rà la fois de la variable et de l'opérateur en double simplifie considérablement les mapsous-expressions de.

Cas de test:

Rick Hitchcock
la source
2
Je ne pense pas que les replaceœuvres aient été conçues. Cela revient 3pour -1+--2et je pense que ce 1serait correct (les 1changements signent trois fois avant qu'il n'y ait un deuxième argument pour le +disponible, résultant en -1 + 2).
Felix Palmen, le
Bonne prise, @FelixPalmen. Maintenant corrigé.
Rick Hitchcock
2

JavaScript, 321 311 octets

_="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(_)

Essayez-le en ligne!

Les cinq caractères sont les mêmes que dans la question, sauf *pour la multiplication.
Le script est compressé à l'aide de RegPack . Le script d'origine est stocké dans la variable _après évaluation.


la source
Ne pensez pas que cela fonctionne -1+-2. Renvoie 3 au lieu de -3.
Rick Hitchcock
@RickHitchcock. Pourquoi pensez-vous qu'il devrait revenir -3au lieu de 3?
Je peux mal comprendre l'opérateur négatif. , En général -1 + -2est -3, mais doit - il être analysé comme --1 + 2lieu?
Rick Hitchcock
1
@RickHitchcock. Je suis sûr que le résultat est 3. Avant 2même de venir sur la pile, la seconde -est évaluée, et donc nous avons 1 2 +qui l'est en effet 3. Ah, et vous devez probablement aussi modifier votre réponse.
Tu as probablement raison. Vous et Arnauld obtenez la même réponse, et j'ai demandé des éclaircissements au PO. Je voterais à nouveau si je le pouvais.
Rick Hitchcock
1

Haskell , 251 octets

(#([],[]))
(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).).(-)

Essayez-le en ligne! Utilise les caractères suivants: apour l'addition, mpour la multiplication, qpour l'égalité, Dpour le double et Npour la négation. (Je voulais utiliser epour l'égalité, mais je suis tombé sur le problème qui lexanalyse 2e3un nombre.) Exemple d'utilisation: (#([],[])) "2a3 4m"rendements 20.

Laikoni
la source
1

6502 code machine (C64), 697 octets

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

Démo en ligne

Utilisation sys49152 , puis entrez l'expression anyfix et appuyez sur Retour.

  • pas de vérification d'erreur, attendez-vous donc à des sorties amusantes sur des expressions invalides.
  • le symbole de la multiplication est *, tous les autres comme suggéré.
  • la longueur d'entrée maximale est de 256 caractères, il ne peut y avoir plus de 127 opérateurs en file d'attente.
  • La routine d'entrée ne gère les caractères de contrôle, donc ne saisissez pas mal;)
  • les entiers sont signés 16 bits, le débordement s'enroulera silencieusement.
  • le nombre d'octets est un peu élevé car ce processeur ne connaît même pas la multiplication et le système d'exploitation / ROM C64 ne vous donne aucune arithmétique entière ou conversion vers / depuis des chaînes décimales.

Voici le code source de l'assembleur de style ca65 lisible .

Felix Palmen
la source
1

VB.NET (.NET 4.5) 615 576 octets

-39 octets grâce à Felix Palmen en passant \r\nà\n

Requiert Imports System.Collections.Generic(inclus dans le nombre d'octets)

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

Le point d'entrée est Function A, qui prend une chaîne en entrée et renvoie a Stack(Of Long).

Symboles:

  • Une addition - +
  • Multiplication - *
  • Égalité - =
  • Négation - -
  • Duplication - d

Aperçu:

La fonction Aprend l'entrée et la tokenise. Il utilise un Long?pour faire un total cumulé pour les entiers à plusieurs chiffres, quiNothing signifie que nous ne lisons pas actuellement un entier.

La sous-routine Eprend la pile d'entiers et la file d'attente d'opérateurs et évalue la notation anyfix. Il s'appelle récursivement jusqu'à ce qu'il ne reste aucune action.

Je déclare les paramètres globaux en une seule fois pour économiser des octets à la fois sur la déclaration et le passage des paramètres.

La valeur de retour de Aest définie en affectant une valeur à la variable correspondante A.

VB Trueest équivalent à-1 , de sorte que l'opération doit annuler le résultat pour obtenir la valeur correcte.

Essayez-le en ligne!

Brian J
la source
suggérer d'ajouter Essayez-le en ligne!
Felix Palmen du
btw, avec le Imports, j'obtiens un nombre d'octets de seulement 576- auriez-vous pu mal compté?
Felix Palmen du
@FelixPalmen avec lequel j'ai compté au \r\nlieu de \n, c'est donc là que se situe la différence.
Brian J
@FelixPalmen Ajouté TIO, merci de me le rappeler! :) (Oh, je ne savais pas que tu étais déjà arrivé pour ce post)
Brian J