Zaimplementuj notację Anyfix!

16

W notacji przedrostkowej operator znajduje się przed argumentami, więc możesz sobie wyobrazić, że operator wywołuje next()rekursywnie. W notacji infiksowej operator przechodzi między argumentami, więc możesz sobie wyobrazić to po prostu jako drzewo analizy. W notacji postfiksowej operator podąża za argumentami, więc możesz to sobie wyobrazić jako oparte na stosie.

W notacji dowolnej poprawki operator może iść w dowolne miejsce * . Jeśli pojawi się operator i nie ma wystarczającej liczby argumentów, operator czeka, aż będzie wystarczającej liczby argumentów. Aby sprostać temu wyzwaniu, musisz wdrożyć bardzo prosty ewaluator poprawek. (Pamiętaj, że anyfix to język rekreacyjny, który porzuciłem, z którym możesz się pobawić tutaj lub sprawdzić tutaj )

Będziesz musiał obsługiwać następujące polecenia:

(Arity 1)

  • duplikować
  • negatywny

(Arity 2)

  • dodanie
  • mnożenie
  • równość: zwraca 0lub 1.

Możesz użyć dowolnych pięciu symboli niebiałych dla tych poleceń. W celach demonstracyjnych użyję "jako duplikatu, ×jako zwielokrotnienia i +jako dodatku.

W przypadku literałów wystarczy obsługiwać nieujemne liczby całkowite, ale Twój tłumacz musi być w stanie zawierać wszystkie liczby całkowite (w granicach (rozsądnych) liczb całkowitych twojego języka).

Rzućmy okiem na przykład: 10+5. Pamięć powinna zachowywać się jak stos, a nie kolejka. Najpierw stos zaczyna się od [], a lista operatorów w kolejce zaczyna się od []. Następnie 10ocenia się literał, który tworzy stos [10]. Następnie operator +jest oceniany, co wymaga dwóch argumentów. Jednak na stosie jest tylko jeden argument, więc staje się lista operatorów w kolejce ['+']. Następnie 5ocenia się literał, który tworzy stos [10, 5]. W tym momencie operator '+'może zostać oceniony, tak jak robi, tworząc stos [15]i kolejkę [].

Wynik końcowy powinien być [15]za + 10 5, 10 + 5i 10 5 +.

Rzućmy okiem na twardszej np 10+". Stos i kolejka zaczynają się jako []i []. 10jest oceniany jako pierwszy, co tworzy stos [10]. Następnie +jest obliczany, co nie zmienia stosu (ponieważ nie ma wystarczającej liczby argumentów) i tworzy kolejkę ['+']. Następnie "jest oceniany. Może to zostać uruchomione natychmiast, dzięki czemu stos [10, 10]. +można teraz ocenić, aby stos stał się [20]i kolejka []. Ostateczny wynik to [20].

Co z kolejnością operacji?

Rzućmy okiem ×+"10 10. Stos i kolejka zaczynają się jako []:

  • ×: Stos pozostaje niezmieniony, a kolejka staje się ['×'].
  • +: Stos pozostaje niezmieniony, a kolejka staje się ['×', '+'].
  • ": Stos pozostaje niezmieniony, a kolejka staje się ['×', '+', '"'].
  • 10: Stos staje się [10]. Mimo że ×powinien być pierwszym operatorem, który jest oceniany, ponieważ pojawia się jako pierwszy, "może działać natychmiast i żaden z operatorów przed nim nie może, więc jest oceniany. Stos staje się [10, 10]i kolejka ['×', '+']. ×można teraz ocenić, co powoduje, że stos [100]i kolejka ['+'].
  • 10: Stos staje się [100, 10], co pozwala +na ocenę. Stos staje się [110]i kolejka [].

Ostateczny wynik to [110].

Polecenia użyte w tych demonstracjach są zgodne z poleceniami języka anyfix; jednak ostatni przykład nie zadziała z powodu błędu w moim tłumaczu. (Oświadczenie: Twoje zgłoszenia nie będą wykorzystywane w tłumaczu Anyfix)

Wyzwanie

Wybierz zestaw 5 znaków innych niż cyfry i spreparuj dowolne znaki zgodnie z powyższymi specyfikacjami. Twój program może wyprowadzić pojedynczą tablicę lub zawartą w niej wartość; gwarantuje się, że stos wartości będzie zawierał tylko jedną wartość na końcu wykonania, a kolejka operatorów będzie pusta na końcu wykonania.

To jest więc wygrywa najkrótszy kod w bajtach.

Przypadki testowe

W tych przypadkach testowych duplikat jest ", ujemny jest -, dodawanie jest +, mnożenie jest ×, a równość jest =.

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)

Zasady

  • Obowiązują standardowe luki
  • Możesz wziąć oficjalnego tłumacza anyfix i zagrać w golfa, jeśli chcesz. Spodziewaj się, że okropnie przegrasz.

Dane wejściowe będą podawane jako ciąg, a dane wyjściowe jako tablica, jedna liczba całkowita, poza reprezentacją ciągu dowolnego z nich. Możesz założyć, że dane wejściowe będą zawierać tylko spacje, cyfry i 5 wybranych znaków.

* Nie aktualne

HyperNeutrino
źródło
2
Idź gdziekolwiek * ™.
Jonathan Allan,
Jaki jest wynik operatora równości? 0i 1?
Felix Palmen
1
@JonathanAllan patrz wyżej;
Usunąłem
1
@ RickHitchcock Pewnie.
HyperNeutrino,
1
Prawdopodobnie powinieneś dołączyć ×+"10 10do przypadków testowych lub innych przykładów, które 1) używają białych znaków i 2) opóźniają użycie zduplikowanego operatora (dwie rzeczy, których całkowicie pominąłem).
Arnauld,

Odpowiedzi:

5

JavaScript (ES6), 204 203 200 bajtów

Zwraca liczbę całkowitą.

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

Użyte znaki:

  • +: dodatek
  • *: mnożenie
  • #: równość
  • d: duplikować
  • -: negatywny

Przypadki testowe

Sformatowane i skomentowane

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
źródło
Nie sądzę, że to działa -1+-2. Zwraca 3 zamiast -3.
Rick Hitchcock,
1
@ RickHitchcock Rozumiem, że 2. -należy -1natychmiast zastosować .
Arnauld
Wydaje mi się, że drugi -byłby zgodny z tym, 2ponieważ przychodzi po innym operatorze. Być może @HyperNeutrino może to wyjaśnić. Operator ujemny może być niejednoznaczny w niektórych sytuacjach.
Rick Hitchcock,
3

JavaScript (ES6), 162 152 143 150 bajtów

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

Nieznacznie nie golfista:

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

Wyjaśnienie

Używam *do mnożenia i Rduplikowania. Pozostałe operatory są takie same jak w pytaniu.

N staje się tablicą liczb (w tym duplikatów).

replaceObsługuje przypadek, w którym znak ujemny przychodzi po liczbie. Na przykład zmieni się 1-na - 1i -1-na- -1 .

W ramach eval,s.match tworzy tablicę operatorów binarnych. Pamiętaj, że zawsze będzie to miało o jeden element mniej niż N.

Wynikiem tej funkcji jest eval odwzorowanie liczb i operatorów.

Oto, co jest evaledowane dla każdego z przypadków testowych:

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 

Operator przecinka w wyrażeniu JavaScript zwraca wynik ostatniego wyrażenia, więc map automatycznie zwraca użyteczne wyrażenie.

+Konieczna jest znak przed evalzmieniać truesię 1ifalse do 0.

Używanie Rzarówno jako zmiennej, jak i duplikatu operatora znacznie upraszczamap wyrażenia podrzędne.

Przypadki testowe:

Rick Hitchcock
źródło
2
Nie uważam, że replacedziała zgodnie z przeznaczeniem. Spowoduje to powrót 3do -1+--2i myślę, że 1byłoby poprawne (gdy 1zmienia się podpisać trzy razy zanim to drugi argument dla +dostępny, w wyniku -1 + 2).
Felix Palmen
Świetny chwyt, @FelixPalmen. Teraz naprawione.
Rick Hitchcock,
2

JavaScript, 321 311 bajtów

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

Wypróbuj online!

Pięć znaków jest takich samych jak w pytaniu, z wyjątkiem *mnożenia.
Skrypt jest kompresowany przy użyciu RegPack . Oryginalny skrypt jest przechowywany w zmiennej _po ocenie.


źródło
Nie sądzę, że to działa -1+-2. Zwraca 3 zamiast -3.
Rick Hitchcock,
@RickHitchcock. Dlaczego uważasz, że powinien on powrócić -3zamiast 3?
Mogę nie rozumieć operatora negatywnego. Zasadniczo -1 + -2jest -3, ale czy powinno się go parsować jak --1 + 2zamiast tego?
Rick Hitchcock,
1
@RickHitchcock. Jestem całkiem pewien, że wynik jest 3. Zanim 2jeszcze wejdzie na stos, drugi -jest oceniany i dlatego mamy to, 1 2 +co rzeczywiście jest 3. Ach, i prawdopodobnie musisz również edytować swoją odpowiedź.
Pewnie masz rację. Ty i Arnauld otrzymacie tę samą odpowiedź, a ja poprosiłem PO o wyjaśnienie. Zagłosowałbym ponownie, gdybym mógł.
Rick Hitchcock,
1

Haskell , 251 bajtów

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

Wypróbuj online! Używa następujących znaków: ado dodawania, mdo mnożenia, qdo równości, Ddo duplikowania i Ndo negacji. (Chciałem użyć edla równości, ale natknąłem się na problem, który lexanalizuje 2e3jako liczbę.) Przykład użycia: (#([],[])) "2a3 4m"daje 20.

Laikoni
źródło
1

Kod maszynowy 6502 (C64), 697 bajtów

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

Demo online

Użycie sys49152 , a następnie wprowadź dowolne wyrażenie i naciśnij klawisz Return.

  • prawie bez sprawdzania błędów, więc oczekuj śmiesznych wyników dla niepoprawnych wyrażeń.
  • symbolem pomnożenia jest *, wszystkie inne, jak sugerowano.
  • maksymalna długość wejściowa wynosi 256 znaków, w kolejce nie może być więcej niż 127 operatorów.
  • Procedura wejściowa nie obsługuje znaków kontrolnych, więc nie wprowadzaj błędów;)
  • liczby całkowite są 16-bitowe, przepełnienie po cichu się zawinie.
  • liczba bajtów jest nieco duża, ponieważ ten procesor nawet nie zna mnożenia, a system operacyjny / ROM C64 nie zapewnia żadnej arytmetyki liczb całkowitych ani konwersji do / z ciągów dziesiętnych.

Oto czytelny kod źródłowy asemblera w stylu ca65 .

Felix Palmen
źródło
1

VB.NET (.NET 4.5) 615 576 bajtów

-39 bajtów dzięki Felixowi Palmenowi po zmianie \r\nna\n

Wymaga Imports System.Collections.Generic(uwzględniony w liczbie bajtów)

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

Punktem wejściowym jest Funkcja A, która pobiera ciąg znaków jako dane wejściowe i zwraca aStack(Of Long) .

Symbolika:

  • Dodawanie - +
  • Mnożenie - *
  • Równość =
  • Negacja - -
  • Duplikacja - d

Przegląd:

Funkcja Apobiera dane wejściowe i tokenizuje je. Używa a Long?do wykonania sumy bieżącej dla liczb całkowitych wielocyfrowych, co Nothingoznacza, że ​​obecnie nie czytamy liczb całkowitych.

Podprogram Epobiera stos liczb całkowitych i kolejkę operatorów i ocenia notację poprawek. Wywołuje się rekurencyjnie, dopóki nie pozostaną żadne działania.

Deklaruję globalne parametry naraz, aby zaoszczędzić bajty zarówno podczas przekazywania deklaracji, jak i parametrów.

Zwracana wartość z Ajest ustawiana poprzez przypisanie wartości do dopasowanej zmiennej A.

VB Truejest równoważne z-1 , więc operacja musi zanegować wynik, aby uzyskać poprawną wartość.

Wypróbuj online!

Brian J.
źródło
zaproponuj dodanie Wypróbuj online!
Felix Palmen
btw, dzięki Imports, mam tylko bajt 576- czy mógłbyś przeliczyć?
Felix Palmen
@ FelixPalmen liczyłem \r\nzamiast \n, więc tam jest rozbieżność.
Brian J
@FelixPalmen Dodano TIO, dzięki za przypomnienie! :) (Och, nie zdawałem sobie sprawy, że udało ci się to zrobić dla tego postu)
Brian J