Prosty system liczbowy


19

Pozwól, że opowiem ci o prostym systemie liczbowym. (które zrobiłem właśnie dla tego wyzwania)

System ten zawiera funkcje (), [], {}, i <>.

1. ()

Gdy ()nie podano żadnych argumentów, ocenia się na 0.

Gdy ()podany jest jeden lub więcej argumentów, następuje ich suma.

2) []

Gdy []nie podano żadnych argumentów, ocenia się na -1.

Gdy []podano jeden lub więcej argumentów, następuje ocena pierwszego argumentu minus suma pozostałych argumentów.

3) {}

Gdy {}nie podano żadnych argumentów, ocenia się na 1.

Gdy {}podano jeden lub więcej argumentów, ocenia się na iloczyn tych argumentów.

4 <>

Gdy <>nie podano żadnych argumentów, ocenia się na 1.

Gdy <>podany jest jeden lub więcej argumentów, następuje ocena liczby całkowitej pierwszego argumentu podzielonej przez iloczyn pozostałych argumentów.

Twoje zadanie

Biorąc pod uwagę ciąg znaków, który zawiera prawidłową liczbę (co oznacza, że ​​nawiasy są zrównoważone, bez dzielenia przez 0 itd.) W tym prostym systemie liczbowym, wypisz jego wartość.

Przypadki testowe

() -> 0
(()()) -> 0
([][]) -> -2
({}<>) -> 2
({}[]) -> 0
[] -> -1
[[][]] -> 0
[()<>] -> -1
{()} -> 0
{([]<>)} -> 0

Pamiętaj, to jest , więc wygrywa kod z najmniejszą liczbą bajtów.


13
Mam na to świetne imię, które całkowicie wymyśliłem i nie dostałem nigdzie indziej: płatek mózgu!
ETHprodukcje

4
@ETHproductions no
Oliver Ni


2
Czy podział zmiennoprzecinkowy czy całkowity?
xnor

1
@Oliver Jak działa dzielenie liczb całkowitych, gdy jeden lub oba operandy są ujemne? Jakie są 4 oczekiwane wyniki 5/3, 5/-3, -5/3i -5/-3?
Martin Ender,

Odpowiedzi:


2

Dyalog APL , 94 bajty

o←{(⊂(1⊃⍵),⍺⍺⊃⍵),2↓⍵}⋄u←{(⊃⍵,⍺⍺1)⍺⍺⍵⍵/1↓⍵}⋄⍎⍕'+/o' '-u+o' '×/o' '÷u×o' '(⊂⍬),'[')]}>'⍳⌽⍞],'⊂⍬'

wykorzystuje ⎕IO←0

zastępuje )]}>wywołaniem funkcji, która pobiera stos, stosuje operację na górnej ramce, usuwa ją i dołącza wynik do następnej ramki ( odo tego celu stosuje się operator monadyczny ; operator diadyczny uobsługuje bardziej skomplikowane przypadki -i ÷)

zastępuje ([{<kodem, który dodaje ramkę do stosu ( (⊂⍬),)

wykonuje wynikowe wyrażenie (odwrócone, aby dopasować kolejność wykonywania APL) z początkowym stosem jednej pustej ramki ( ⊂⍬)


5

Haskell, 357 306 277 251 228 224 188 185 180 bajtów

Parser oparty na tokenach z jawnym stosem. (%)pobiera stos tokenów i znak i albo wypycha (kod operacji, numer domyślny) lub (0, liczba) ({[<, albo wyskakuje z najwyższych liczb i jednego kodu operacji i przesuwa odpowiedź na )}]>. Kody są kodowane przez hack wyliczenia ascii.

Wyrazy uznania dla @ChristianSievers za wspaniałą odpowiedź , od której pożyczyłem kilka pomysłów.

One-liner!

s%c|elem c"([<{",g<-div(fromEnum c)25=(g,[0,0,1,-1,1]!!g):s|(a,(o,n):b)<-span((==0).fst)s=(0,[foldr1(flip$[(+),quot,(-),(*)]!!(o-1))$snd<$>a,n]!!(0^length a)):b
snd.head.foldl(%)[]

Teraz z mniejszą obsługą błędów! Stosowanie:

*Main> map (snd.head.foldl(%)[]) ["()","(()())","([][])","({}<>)","({}[])","[]","[[][]]","[()<>]","{()}","{([]<>)}"]
[0,0,-2,2,0,-1,0,-1,0,0]

Dzięki @ChristianSievers za oszczędność 14 + 3 bajtów!

Dzięki @Zgarb za uratowanie niektórych + 4 bajtów!


1
A może (0,[0,0,1,-1,1]!!o):sw piątej linii?
Christian Sievers

@ChristianSievers oczywiście!
Angs,

Zmień definicje !, abyś mógł zrobić (s:_)!_=d stak jak w drugim przypadku. Myślę też, że mógłbyś związać x<-p$d<$>init a,y<-d$last aw ostatnim przypadku %.
Zgarb,

@Zgarb dzięki! Znalazłem sposób na jeszcze bardziej ujednolicenie oceny.
Angs,

1
W trzecim wierszu %możesz upuścić pareny wokół _:bi g c.
Zgarb

3

Python 2, 292 265 248 235 223 206 204 bajtów

r=reduce
s=input()
for n in')]}>':s=s.replace(n,'),')
for a in'(*x:sum(x)','[a=-1,*x:a-sum(x)','{*x:r(int.__mul__,x,1)','<a=1,*x:r(int.__div__,x,a)':s=s.replace(a[0],'(lambda %s)('%a[1:])
print eval(s)[0]

Zamienia wszystkie nawiasy na lambda, które robi to, co robi nawias, a następnie ocenia wynikowy kod Python. Wymaga danych wejściowych otoczonych cudzysłowami, np '[<><>([]{})]'.

Ten program przechowuje typ nawiasu jako pierwszy znak w każdym ciągu w for, a wszystko po słowie kluczowym lambdajako resztę. Następnie używa pierwszego znaku do zastąpienia; reszta jest połączona w lambda (lambda*x:sum(x))().

Wypróbuj na Ideone!


3

PEG.js (ES6) , 132 bajty

x=a:[([{<]b:x*[)\]}>]{var x='([<'.indexOf(a)
b.length^1||b.push(0)
return~~eval(b.length?b.join(('+-/'[x]||'*')+' '):~-('10'[x]||2))}

Powinien zostać teraz naprawiony.

Wyjaśnienie

Bardziej czytelny:

x=a:[([{<]
  b:x*
  [)\]}>]
{
  var x='([<'.indexOf(a)
  b.length^1||b.push(0)
  return ~~eval(
    b.length?
      b.join(('+-/'[x]||'*')+' ')
    :~-('10'[x]||2))
  )
}

PEG.js to rozszerzona wersja Javascript stworzona specjalnie do analizy. Jest BARDZO ścisły, dlatego musiałem go użyć var. Ponadto wydaje się, że występuje błąd z nawiasami wewnątrz ciągów, co znacznie rozszerzyło kod.

Na początek definiujemy regułę xpasującą do dowolnego nawiasu, aktóry może zawierać lub nie zawierać wielu wyrażeń pasujących do reguły x.

Dla każdego dopasowania do reguły x, wypychamy 0 do tablicy dopasowania wewnętrznego, bjeśli bdługość wynosi 1.

Jeśli bdługość> 0, to znajdujemy indeks aw ([<i otrzymujemy znak z +-/tego indeksu. Jeśli wynik jest niezdefiniowany (to znaczy, że abył {), to przekształcamy wynik w *. Na koniec zajmujemy się spacją i łączymy się bz wynikiem.

Jeśli bdługość = 0, to znajdujemy indeks aw ([<i otrzymujemy znak z 10tego indeksu. Jeśli wynik jest niezdefiniowany (co oznacza, że abył {lub <), wówczas zamieniamy wynik na 2. Wreszcie, po prostu zmniejszamy.

Na koniec możemy po prostu ocenić wyrażenie i podać wynik.


3

Perl, 113 + 2 = 115 bajtów

Uruchom z -lp(kara 2 bajty).

/\W/,eval"sub $`\{\$#_?(shift)$&&$'1}"for qw'a+a:1- b-a:- c*c: d/c:';y/([{</a-d/;s/\W/0),/g;s/\pL\K/(/g;$_=eval

Bardziej czytelne (uwaga: ta „bardziej czytelna wersja” tak naprawdę nie działa, ponieważ umieszczam komentarze w miejscach, w których nie są dozwolone składniowo):

              # -p option: read a line of input into $_ at program start
              # -l option: remove the final newline whenever reading
do {          # for each element of a list, given later:
  /\W/;       # place an initial identifier in $`, the next character in
              # $&, and the rest of the element in $'
  eval qq{    # then evaluate the following template, with substitutions:
    sub $` {  # define a subroutine named $`, that does this:
      \$#_ ?  # if there is more than one argument                   
      (shift) # then return the first argument $&-ed with
      $& &$'  # the result of a recursive call with the tail of the arguments
              # else (the "else" is a colon taken from $', not the template)
      1       # return (the remainder of $' applied to) 1
    }
  }
} for qw'     # specify the list by splitting the following on whitespace:        
  a+a:1-      # a(head,tail) = len(tail>1) ? head+a(tail) : 1-1
  b-a:-       # b(head,tail) = len(tail>1) ? head-a(tail) : -1
  c*c:        # c(head,tail) = len(tail>1) ? head*c(tail) : 1
  d/c:        # d(head,tail) = len(tail>1) ? head/c(tail) : 1
';
y/([{</a-d/;  # replace ( [ { < with a b c d in $_
s/\W/0),/g;   # replace whitespace, punctuation in $_ with the string "0),"
s/\pL\K/(/g;  # place a ( after (\K) each letter (\pL) in $_
$_=eval       # evaluate $_ as a Perl program, storing the result back in $_
              # -p option: print $_ to the user at program end
              # -l option: output a newline whenever printing

Podstawową ideą jest to, że przekształcamy dane wejściowe podobne [()<>]do programu Perl b(a(0),d(0),0),poprzez przetwarzanie tekstu; Perl jest w porządku z przecinkiem. Wcześniej, mamy zdefiniowane funkcje a, b, c, daby mieć taki sam skutek jak (), [], {}, <>konstrukcje z języka jesteśmy wykonawczego; każdy z nich ignoruje swój ostatni argument (0 na końcu), który jest zawarty w celu zapewnienia, że ​​wszystkie dane wejściowe są poprawnie analizowane, i działa z wykorzystaniem implementacji powszechnie występującej w programowaniu funkcjonalnym, w którym głowa i ogon są przetwarzane osobno. Ponieważ b(e,f,g,0)środki e-f-g, tj. Traktuje swój pierwszy argument specjalnie, podczas gdy ajego argumenty traktuje symetrycznie ( a(e,f,g,0)środki e+f+g), implementujemyarekurencyjnie i bza pośrednictwem połączeń a. ci dmają podobny związek. Wszystkie cztery z tych funkcji są bardzo podobne, więc generujemy je w czasie wykonywania, a nie wdrażamy osobno; przechowujemy szablon, który dotyczy wszystkich czterech funkcji w ciągu, a następnie generujemy funkcje, zastępując znaki w szablonie.

Ponieważ Perl /wykonuje dzielenie zmiennoprzecinkowe, to również implementuje {}. Zakładam, że albo nie jest to problem sam w sobie, albo -Minteger(wybór wariantu językowego, w którym wszystkie operacje arytmetyczne są operacjami liczb całkowitych) jest bezpłatny, ponieważ w przeciwnym razie musiałbym spędzić dodatkowe bajty na pisaniu podziału na liczby całkowite w Perlu, wydaje się, że nie na tym polega problem. (Myślę, że trzeba wydać cztery bajty zmienia (shift)się int+(shift);. Nie testowałem tego)


2

Oktawa, 215 206 198 bajtów

S='sum(x(:))-sum(x(2:end))';Y='(@(x)isempty(x)';c=']) ';[~,~,u]=unique(['()<>[]{}',input('')]);eval([{'sum([',c,[Y,'+fix((',S,')/prod(x(2:end))))(['],c,[Y,'*-1+',S,'*2)(['],c,'prod([',c}{u(9:end)}])

spróbuj online


2

PHP, 315 300 285 258 250 244 bajtów

for($s=$argv[1];$r=!$r;)foreach(["(+)1","[-]0","{*}2","</>2]as$p)if(preg_match("#$e$p[0]([-_\d]*)$e$p[2]#",$s,$m)){if(""==$v=strtok($m[1],_))$v=$p[3]-1;while(""<$n=strtok(_))eval("\$v$p[1]=$n;");$s=strtr($s,[$m[$r=0]=>_.$v]);}echo substr($s,1);

zamienia podwyrażenia na podkreślenie + wartość; pętla pęka, gdy iteracja nie zastępuje.

19 lat od pierwszego spotkania z C, 17 lat w PHP;
to pierwszy raz, który strtokma sens ... pomaga zaoszczędzić 24 bajty!

awaria

for($s=$argv[1];    // take input from argument
    $r=!$r;)        // toggle $r; loop until no replacement has taken place
    foreach(["(+)1","[-]0","{*}2","</>2]as$p) // loop through operations
        if(preg_match("#$e$p[0]([-_\d]*)$e$p[2]#",$s,$m))   // find a match
        {
            if(""==$v=strtok($m[1],_))  // init $v with first token from sub-match
                $v=$p[3]-1;             // if no token, init with default value
            while(""<$n=strtok(_))      // loop through tokens
                eval("\$v$p[1]=$n;");       // operate
            $s=strtr($s,[$m[$r=0]=>_.$v]);  // reset $r; replace match with underscore+value
        }
echo substr($s,1);  // print result

@ Oliver nie bije tutaj nikogo; ale dzięki za zabawę!
Tytus

2

ES6 (JavaScript), 250, 171, 154, 149, 147 bajtów

Czysta wersja Javascript.

„Metaprogramowanie” (jak większość innych odpowiedzi tutaj) konwertuje tekst programu wejściowego na odpowiedni program Javascript, stosując do niego szereg bezpośrednich podstawień tekstu (tj. Zachowując aktualną strukturę programu).

Prawdopodobnie można dalej grać w golfa.

AKTUALIZACJA (v2.1)

  • Minus dwa bajty (usunięto nawias w wyrażeniu potrójnym)
  • Grał o 5 bajtów więcej, używając zmiennej do ekstrakcji wyników i pozbywając się dodatkowego „[]”

AKTUALIZACJA (v2)

Właśnie zdałem sobie sprawę, że oczekujące przecinki w tablicach ES są całkowicie poprawne, więc cały kod normalizacji przecinków można usunąć. Postępował również zgodnie ze świetną radą @Titus, dotyczącą optymalizacji wyszukiwania alfabetu.

AKTUALIZACJA (v1)

Usunięto zduplikowany alias „zamień”.

AKTUALIZACJA (v1)

  • Użyj lepszego alfabetu: () => 1+ [] => 0 {} => 2 * <> => 2 / (każdy znak może być bezpośrednio ponownie użyty jako wartość lub operator)

  • Zamienione zmniejsz () na replace () (odwzorowanie alfabetu)

  • Scalona stała obróbka nachylenia, otwierania i zamykania zamka w jeden krok

Gra w golfa (v2.1)

s=>eval("o="+s.replace(/./g,r=>"2+1-3*3/"["()[]{}<>".indexOf(r)]).replace(/\d\D?|\D/g,r=>r[1]?r[0]-2+",":r*1?'([':`].reduce((r,a)=>r${r}a)),`)+"o

Gra w golfa (v1)

(s,A="(2)+[1]-{3}*<3>/")=>eval(s[R="replace"](/./g,r=>A[A.indexOf(r)+1])[R](/\d\D?|\D/g,r=>r[1]?r[0]-2+",":(r[0]*1?'([':`].reduce((r,a)=>r${r}a)),`))[R](/,(\])|,$/g,"$1"))    

Gra w golfa (v0)

([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))

Wyjaśnione (v0)

//BEGIN 

//s - input text, A - alphabet, R - "String.replace()" alias
E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(

//Replace input alphabet by a more friendly one, to avoid too much escaping and quoting
// () - ab, [] -cd, {} - ef, <> - gh
s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')

//Replace no-arg invocations with a corresponding constant value
// () => 0, [] => -1, {} => 1, <> => 1      
[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')

//Replace opening brackets with "(["
[R](/[aceg]/g,"([")

//Replace closing brackets with "].reduce(...)),"
//An arithmetic operation to apply (+-*/) is chosen based on the bracket type 
//and is substituted into the template 
[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)

//Strip excessive commas
[R](/,(\])|,$/g,"$1")
);

//END: eval() the result


Example:
E("{([]<>()<>{})(<><>)}")
=> eval("([([-1,1,0,1,1].reduce((r,a)=>r+a)),([1,1].reduce((r,a)=>r+a))].reduce((r,a)=>r*a))")
=> 4

Test

E=([...s],A="(a)b[c]d{e}f<g>h",R="replace")=>eval(s.reduce((r,c)=>r+=A[A.indexOf(c)+1],'')[R](/ab|cd|ef|gh/g,r=>({d:-1,b:'0'}[r[1]]||1) + ',')[R](/[aceg]/g,"([")[R](/[bdfh]/g,r=>`].reduce((r,a)=>r${"+*-/"["bfdh".indexOf(r)]}a)),`)[R](/,(\])|,$/g,"$1"))

T=(s,a)=>{
    console.log(s,r=E(s),r==a?"OK":"NOT OK");
}

T("()",0)
T("(()())",0) 
T("([][])",-2)
T("({}<>)",2) 
T("({}[])",0) 
T("[]",-1)
T("[[][]]",0) 
T("[()<>]",-1) 
T("{()}",0) 
T("{([]<>)}",0)

Wyjście testowe

() 0 OK
(()()) 0 OK
([][]) -2 OK
({}<>) 2 OK
({}[]) 0 OK
[] -1 OK
[[][]] 0 OK
[()<>] -1 OK
{()} 0 OK
{([]<>)} 0 OK

1
Czy twoja wersja v0 może współpracować z s.reduce((r,c)=>r+="abcdefgh"["()[]{}<>".indexOf(c)],'')(-5)? jeśli tak, możesz zapamiętać indexOfzmienną i pobrać operator z literału trzeciego ciągu.
Tytus

2

Haskell, 184 179 172 161 160 159 151 148 145 bajtów

s%(c:i)|elem c")}]>"=([1?(*),sum,1?quot,(-1)?(-)]!!mod(fromEnum c)5$s,i)|(r,j)<-[]%i=(s++[r])%j
[v]%e=(v,e)
(v?_)[]=v
(_?o)s=foldl1 o s
fst.([]%)

Rekurencyjne zejście, nawlekanie danych wejściowych, ponieważ Haskell. Jak zwykle ostatni wiersz nie jest definicją, ale określa funkcję, która rozwiązuje problem. Aby przetestować, umieść wiersze oprócz ostatniego w pliku, załaduj go i zrób coś takiego:

*Main> fst.([]%) $ "{([][][])([][])}"
6

Podziękowania dla @Zgarb za inspirację i wiele szczegółowych wskazówek oraz @Angs za inspirację z jego rozwiązania i dalszych wskazówek.

Nie określono, jak powinien zachowywać się podział z ujemnymi liczbami całkowitymi. W każdym razie wielokrotne używanie divwydaje się nieprawidłowe, ponieważ nie jest to to samo, co divjednorazowe użycie z iloczynem pozostałych wartości. Teraz używam quot, otrzymuję te same wyniki dla <{}([][])[]>i <{}{([][])[]}>.

Aby uzyskać ładny, prawie czytelny kod, zobacz pierwszą wersję. Wersje pośrednie zawierają wszelkiego rodzaju ładny i onieśmielający kod i pomagają zrozumieć tę wersję.


Myślę, że możesz zaoszczędzić kilka bajtów, definiując (!)=(,)i używając !zamiast jawnych krotek.
Zgarb,

Jeśli zdefiniujesz m xi d xjako 1#xi 0#x, możesz scalić przypadkim[x] i d[x]do jednego, który moim zdaniem zbyt zapisuje niektóre bajty.
Zgarb,

@Zgarb Thanks! Prawie przegapiłem ostatnią parę, bez której! lewa się nie opłaca. Twoja druga sugestia jest zła, pojawia się mój prawie czytelny kod ... Sprytnie!
Christian Sievers

Heh, właśnie zdałem sobie z tego sprawę s%(c:i)=(s?c,i) i s?')'=sum sitp. Będą znacznie krótsze, ponieważ możesz pozbyć się powtarzających się iliter. ..Nie czekaj, to prawdopodobnie nie zadziała z powodu s%(_:i)sprawy.
Zgarb,

1
Tracisz backty elem i divto powinno zaoszczędzić trochę więcej bajtów.
Zgarb

1

JavaScript (ES6), nie eval, 156 bajtów

f=s=>s==(s=s.replace(/(.) ?([- \d]*)[\]})>]/,(_,c,s)=>` `+(s?s.split` `.reduce((l,r)=>c<`<`?l- -r:c<`[`?l/r|0:c<`{`?l-r:l*r):c==`[`?-1:c==`(`?0:1)))?+s:f(s)

Objaśnienie: Wyrażenie regularne znajduje pierwszy nieprzetworzony nawias zamykający i dopasowuje (przypuszczalnie) odpowiedni nawias otwierający i wszelkie argumenty pomiędzy nimi. Argumenty są dzielone i redukowane zgodnie z operacją (niestety „(” i „[” nie są optymalną parą dla +i -), lub jeśli nie ma argumentów, wówczas obliczana jest odpowiednia wartość (ponownie dopasowanie znaków do wartości jest nieoptymalne z mojego punktu widzenia). Następnie wynik zastępuje się spacją wiodącą, aby oddzielić go od innych wyników. Następnie funkcja wywołuje się rekurencyjnie, dopóki nie będzie więcej zmian do wprowadzenia, w którym to przypadku zwraca wartość wynikową. Przykład:

[()<>]
[ 0<>]
[ 0 1]
 -1

f ("([] [])") => 0 (zamiast 2)
zeppelin

Niektóre inne testy również dla mnie zawodzą (możesz wypróbować kod testowy w mojej odpowiedzi ), prawdopodobnie z powodu: f („[]”) => 0, ponieważ „[]” jest częścią każdego testu zakończonego niepowodzeniem.
zeppelin,

@zeppelin Ups, to było spowodowane złym golfem. Wróciłem do poprzedniej wersji, ale niestety kosztuje mnie to kilka bajtów.
Neil,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.