Utwórz kalkulator omnifix

16

Inspiracja. Odwrotność.

Oceń dane wyrażenie omnifix.

Omnifix przypomina notację normalną matematyki, ale z dodatkowymi kopiami każdego symbolu otaczającego argumenty. Symbole zewnętrzne zastępują nawiasy, dlatego nie ma potrzeby wprowadzania dodatkowych nawiasów.

Musisz obsługiwać dodawanie, odejmowanie, mnożenie, dzielenie i dodatnie liczby rzeczywiste (liczby ujemne można zapisać -0-n-) w rozsądnym zakresie dla twojego języka.

Plus i minus muszą być +i -, ale możesz użyć *lub ×dla czasów i /lub ÷do podziału. Inne rozsądne symbole będą dozwolone na żądanie.

Punkty Brownie za wyjaśnienia i dodatkowe funkcje (takie jak dodatkowe operacje, liczby ujemne, ciągi itp.) Nawet jeśli twoja odpowiedź nie ma tych funkcji, możesz pokazać, jak to możliwe.

Podaj link, aby w miarę możliwości przetestować swoje rozwiązanie.

Przykłady

Dla jasności w poniższych objaśnieniach użyto wysokiej minus ( ¯) do wskazania liczb ujemnych. Możesz zwrócić liczby ujemne w dowolnym rozsądnym formacie.

-5-2-3

+2+×3×2×+8 ( +2+×3×2×++2+6+8)

-14--3-1--12 ( -4--3-1---14-2-12)

+2.1+×3.5×2.2×+9.8 ( +2.1+×3.5×2.2×++2.1+7.7+9.8)

×3×÷-0-6-÷2÷×-9 ( ×3×÷-0-6-÷2÷××3×÷¯6÷2÷××3ׯ3ׯ9)

÷4÷-3-÷1÷2÷-÷1.6 ( ÷4÷-3-÷1÷2÷-÷÷4÷-3-0.5-÷÷4÷2.5÷1.6)

Adám
źródło
1
The explanations below use high minus (`¯`) to indicate negative numbers.Zdecydowanie kochasz APL.
Erik the Outgolfer
@EriktheOutgolfer Masz lepszą sugestię? Ponadto TI-BASIC wykorzystuje wysoki minus.
Adám
W rzeczywistości nie, ponieważ -s można pomylić z -s, a ¯s nie można pomylić z -s.
Erik the Outgolfer
Bah, właśnie zauważyłem wymóg liczby rzeczywistej. Tyle o moim 290-bajtowym całkowitym arytmetycznym rozwiązaniu Retina ...
Neil
@Neil Dlaczego nie opublikujesz go jako odpowiedzi?
Adám

Odpowiedzi:

4

C # (.NET Core) , 198 197 188 bajtów

float O(string s){try{return float.Parse(s);}catch{var f=s[0];int i=s.IndexOf(f,1);float a=O(s.Substring(1,i-1)),b=O(s.Substring(i+1,s.Length-i-2));return f<43?a*b:f<44?a+b:f<46?a-b:a/b;}}

Wypróbuj online!

Używa *i /.

Funkcja rekurencyjna. Najpierw próbuje przeanalizować ciąg wejściowy jako float. Jeśli zawiedzie, wywołuje się rekurencyjnie, przekazując jako argumenty pierwszy i drugi operand, a następnie wykonuje wybraną operację na wynikach.

  • 1 bajt zapisany dzięki Mr. Xcoder!
  • 9 bajtów zapisanych dzięki TheLethalCoder!
Charlie
źródło
IndefOf(f, 1)może byćIndexOf(f,1)
Mr. Xcoder
1
floatZamiast tego użyj s, użyj kodów char, jeśli je masz, prawdopodobnie możesz je skrócić za pomocą >iw <kilku miejscach.
TheLethalCoder
Możesz zagrać w golfa zmieniając bajt i+1,s.Length-i-2na ++i,s.Length+~i.
Kevin Cruijssen
4

Python 3, 159 158 152 144 136 135 132 bajtów

def t(i,a=1):
 while'-'<l[i]!='/':i+=1;a=0
 if a:l[i]='(';i=t(t(i+1));l[i-1]=')'
 return-~i
*l,=input()
t(0)
print(eval(''.join(l)))

Wypróbuj online!

Nie pozwala na liczby ujemne (choć -0-5-działa oczywiście) i wymaga operatorów python.

Arfie
źródło
Czy możesz dodać link TIO?
Adám
1
while~-(l[i]in'+-*/'):i+=1;a=1i *l,=input()dla 152 bajtów
Felipe Nardi Batista
1
ze wszystkimi przypadkami testowymi: link
Felipe Nardi Batista
1
144 bajty
Felipe Nardi Batista
1
if a:l[i]='(';i=t(t(i+1));l[i-1]=')'z return-~idla 135 bajtów: P
Felipe Nardi Batista
3

Siatkówka , 290 287 286 bajtów

\d+
¦$&$*
¯¦
¯
{`\+([¯¦]1*)\+([¯¦]1*)\+
-$1-¯$2-
-(¯|¦)(1*)-([¯¦]+1*\2)-
-¯$3-¯$1$2-
(×|÷)¯(1*\1)([¯¦]1*\1)
$1¦$2¯$3
צ×[¯¦]1*×|¯¯¦?
¦
¯¦|¦¯
¯
+`-((¯|¦)1*)(1*)-\2\3-
$1
-([¯¦]1*)-[¯¦](1*)-
$1$2
צ1(1*)×([¯¦]1*)×
+צ$1×$2×+$2+
}`÷¦(?=1*÷(¯|¦)(1+)÷)(\2)*1*÷\1\2÷
$1$#3$*
((¯)|¦)(1*)
$2$.3

Wypróbuj online! Uwaga: Może tylko arytmetyki liczb całkowitych, więc niektóre przypadki testowe zostały usunięte. Akceptuje i zwraca liczby ujemne przy użyciu ¯prefiksu. Edycja: Zapisano 3 4 bajty dzięki @Cowsquack. Wyjaśnienie:

\d+
¦$&$*

Potrzebowałem jakiegoś sposobu obsługi zera, więc używam go ¦jako prefiksu liczby dodatniej. Liczby są następnie konwertowane na unary.

¯¦
¯

Ale liczby ujemne wymagają tylko ¯prefiksu.

{`\+([¯¦]1*)\+([¯¦]1*)\+
-$1-¯$2-

Cytowanie +s staje się brzydkie, więc zmieniam dodawanie w odejmowanie.

-(¯|¦)(1*)-([¯¦]+1*\2)-
-¯$3-¯$1$2-

Jeśli wartość bezwzględna LHS odejmowania jest mniejsza niż RHS, przełącz je i neguj obie strony.

(×|÷)¯(1*\1)([¯¦]1*\1)
$1¦$2¯$3

Także jeśli LHS mnożenia lub dzielenia jest ujemne, zaneguj obie strony.

צ×[¯¦]1*×|¯¯¦?
¦

Również jeśli LHS mnożenia wynosi zero, to wynik wynosi zero. Ponadto dwa minusy stanowią plus.

¯¦|¦¯
¯

Ale minus i plus (lub odwrotnie) tworzą minus.

+`-((¯|¦)1*)(1*)-\2\3-
$1

Odejmij dwie liczby tego samego znaku. Rób to wielokrotnie, aż nie pozostaną takie odejmowania.

-([¯¦]1*)-[¯¦](1*)-
$1$2

Jeśli nadal występuje odejmowanie, znaki muszą być różne, więc dodaj liczby razem. (Ale zrób to tylko raz, ponieważ może to ponownie ujawnić odejmowanie dwóch liczb tego samego znaku).

צ1(1*)×([¯¦]1*)×
+צ$1×$2×+$2+

Wykonaj mnożenie przez wielokrotne dodawanie.

}`÷¦(?=1*÷(¯|¦)(1+)÷)(\2)*1*÷\1\2÷
$1$#3$*

Dokonaj podziału na liczby całkowite. Jeden z powyższych kroków uprości wyrażenie, więc zapętlaj wstecz, dopóki nie pozostaną żadne operacje.

((¯)|¦)(1*)
$2$.3

Konwertuj z powrotem na dziesiętne.

Neil
źródło
Wow, to znaczy - epickie. Największy post Retina na PPCG? Jednak zwykle rozwiązania QuadR i Retina są do siebie bardzo podobne. Czy mogę zainspirować?
Adám
W tej linii +`-(([¯¦])1*)(1*)-\2\3-, [¯¦]może stać się¯|¦
Kritixi Lithos
@ Cowsquack W rzeczywistości dzieje się trzy razy, dzięki!
Neil
Jest jeden, za którym tęskniłeś ([×÷]);)
Kritixi Lithos
1
@ Cowsquack Lepiej nie znajdź innego, w przeciwnym razie będę musiał wykreślić 4 ...
Neil
2

PHP , 116 114 109 bajtów

-5 dzięki Martin Ender

for($s=$argv[$o=1];$o!=$s;)$s=preg_replace('#([*+/-])(([\d.]+|(?R))\1(?3))\1#','($2)',$o=$s);eval("echo$s;");

Zastosowania *do mnożenia i /dzielenia. Liczby ujemne zdarzają się, pomimo tego, że nie podejmuję żadnych konkretnych prób.

Wypróbuj online!

Nieoznakowany i wyjaśniony

for($s=$argv[$o=1];   # Initialize with $s = input and $o = 1;
    $o!=$s;)          # While $o != $s
    # Set $o to $s and set $s to be $s after this regex replacement:
    $s=preg_replace('#([*+/-])(([\d.]+|(?R))\1(?3))\1#','($2)',$o=$s);
    # i.e., continue to do this replacement until the result is the same on two consecutive
    # steps (no replacement was made)
# Once $o == $s (i.e. no more replacement can be made), eval the result and print
eval("echo$s;"); 

Wyjaśnię również wyrażenie regularne, ponieważ jest to trochę magiczne:

([*+/-])(([\d.]+|(?R))\1(?3))\1


([*+/-])

Po pierwsze, chcemy dopasować dowolny z czterech operatorów: *+/-

([\d.]+|(?R))

Następnie musimy dopasować liczbę [\d.]+lub inne prawidłowe wyrażenie omnifix (?R).

\1

Następnie dopasowujemy tego samego operatora, który był na początku.

(?3)

Następnie robimy to samo, co w grupie 3: dopasowujemy liczbę lub wyrażenie dookólne.

\1

Na koniec ponownie dopasuj początkowego operatora.

Cokolwiek pasuje to zostanie zastąpione przez ($2). To bierze część wewnątrz otaczających operatorów i umieszcza ją w nawiasach, więc wygląda jak normalna notacja infiksowa.

Business Cat
źródło
2

QuadR , 33 32 27 bajtów

-1 dzięki Cows Quack . -5 dzięki Erikowi Outgolfer .

((.)[\d¯\.]+){2}\2
⍎¯11↓⍵M

z argumentem / flagą

Wypróbuj online!

Jest to równoważne z 40-bajtowym rozwiązaniem Dyalog APL:

'((.)[\d¯\.]+){2}\2'R{⍕⍎1↓¯1↓⍵.Match}⍣≡

Wypróbuj online!

Wyjaśnienie

(tekst w nawiasach odnosi się do Dyalog APL zamiast QuadR)

(){2}\2 Następujący wzór dwa razy, a całość również dwukrotnie:
  (.) dowolny znak
  []+ po którym następuje co najmniej jeden z następujących zestawów znaków:
   \dd igits,
   ¯ high minus (znak ujemny)
   \.  okres

( ⎕R jest R eplaced z :)

( {} Wynik następującej anonimowej funkcji zastosowanej do przestrzeni nazw ⍵ :)

⍵M ( ⍵.Match) tekst litery M
¯1↓  upuść ostatni znak (symbol + - ×lub ÷)
1↓ upuść pierwszy znak (symbol)
 wykonaj jako kod APL
 (  stringify)

 ( ⍣≡) powtarzaj zamianę, dopóki nie nastąpią żadne zmiany

Adám
źródło
Myślę, że możesz upuścić
Kritixi Lithos
@Cowsquack Masz rację co do QuadR. ⎕Rnie może działać na danych liczbowych. Dzięki.
Adám
Krótszy regex; -5 bajtów.
Erik the Outgolfer
1

Haskell , 132 znaki

(134 bajty, ponieważ ×i ÷wziąć dwa bajty w UTF-8)

f y|(n@(_:_),r)<-span(`elem`['.'..'9'])y=(read n,r)
f(c:d)|(l,_:s)<-f d,(m,_:r)<-f s=(o[c]l m,r)
o"+"=(+)
o"-"=(-)
o"×"=(*)
o"÷"=(/)

Wypróbuj online!

fanalizuje jak najwięcej danych wejściowych i daje wynik, a także pozostały ciąg (który jest pusty w przypadkach testowych). Jeśli nie jest to zgodne z regułami, usuń nie dający się oddzielić ciąg reszt

Haskell , 139 znaków

...
g=fst.f
przestał się obracać w lewo
źródło
0

Perl, 64 53 bajtów

Uwzględnij +1dla-p

perl -pe 's%([*-/])(([\d.]+|(?0))\1(?3))\1%($2)%&&redo;$_=eval' <<< "/4/-3-/1/2/-/"

Przypadkowo implementuje także ,(wyrzuca pierwszy argument), a czasem .(dołącza argumenty razem). .nie działa jednak niezawodnie, ponieważ koliduje z kropką dziesiętną zarówno na poziomie analizy, jak i na poziomie oceny

Ton Hospel
źródło
0

Java 8, 205 200 bajtów

float O(String s){try{return new Float(s);}catch(Exception e){int f=s.charAt(0),i=s.indexOf(f,1);float a=O(s.substring(1,i)),b=O(s.substring(i+1,s.length()-1));return f<43?a*b:f<44?a+b:f<46?a-b:a/b;}}

Port odpowiedzi C # @Charlie .
-5 bajtów dzięki @ceilingcat .

Wypróbuj online.

Kevin Cruijssen
źródło