Kierunki Brainf * Ckish

14

Twoim zadaniem - jeśli zdecydujesz się to zaakceptować - jest zbudowanie programu, który analizuje i ocenia ciąg (od lewej do prawej i dowolnej długości) tokenów, które podają kierunki - w lewo lub w prawo. Oto cztery możliwe tokeny i ich znaczenie:

>  go right one single step
<  go left one single step
-> go right the total amount of single steps that you've gone right, plus one,
   before you previously encountered this token and reset this counter to zero
<- go left the total amount of single steps that you've gone left, plus one,
   before you previously encountered this token and reset this counter to zero

Jest jednak pewien haczyk - tokeny kierunków, które Twój program powinien być w stanie przeanalizować, zostaną przedstawione w tej formie:

<<->-><<->->>->>->

... innymi słowy, są one połączone, a Twoim zadaniem programu jest ustalenie prawidłowego pierwszeństwa wskazówek i liczby kroków do podjęcia (patrząc w przyszłość). Kolejność pierwszeństwa jest następująca (od najwyższej do najniższej):

  1. ->
  2. <-
  3. >
  4. <

Jeśli napotkasz <- gdy od początku lub od ostatniego resetu nie było żadnych kroków w lewo, wykonaj jeden krok w lewo. Ta sama zasada obowiązuje ->, ale wtedy, gdy idzie się w prawo.

Twój program powinien zaczynać się od 0, a jego wynikiem powinna być liczba całkowita ze znakiem, reprezentująca końcową pozycję końcową.

Możesz oczekiwać, że dane wejściowe będą zawsze prawidłowe (więc nic podobnego <--->>--< na przykład ).

Przykładowe dane wejściowe:

><->><-<-><-<>>->

Kroki w tym przykładzie:

 step | token | amount | end position
------+-------+--------+--------------
   1. |   >   |     +1 |           1  
   2. |   <   |     -1 |           0  
   3. |  ->   |     +2 |           2  
   4. |   >   |     +1 |           3  
   5. |   <-  |     -2 |           1  
   6. |   <   |     -1 |           0  
   7. |  ->   |     +2 |           2  
   8. |   <-  |     -2 |           0  
   9. |   <   |     -1 |          -1  
  10. |   >   |     +1 |           0  
  11. |   >   |     +1 |           1  
  12. |  ->   |     +3 |           4  

Dla wyjaśnienia: wyjściem programu powinna być tylko końcowa pozycja końcowa jako liczba całkowita ze znakiem. Powyższa tabela jest tylko po to, aby zilustrować kroki, które podjął mój przykład. Nie trzeba generować takiej tabeli, wiersza tabeli, a nawet samych pozycji krańcowych kroków. Wymagana jest tylko końcowa pozycja końcowa, jako liczba całkowita ze znakiem.

Najkrótszy kod po tygodniu wygrywa.

Przyzwoity Dabbler
źródło
4
Jeśli dobrze rozumiem zasady pierwszeństwa, jedyne, co możesz przywołać, <-to jeśli zaraz po nim następuje a <lub a ->. Nie sposób w tym języku do reprezentowania sekwencji <-następnie >- co byłoby go left the total amount of single steps that you've gone left, plus one, then go right one single step. Czy jest to poprawne i zgodne z projektem?
Adam Davis,
@AdamDavis Masz rację. Niestety było to trochę nieuważne.
Przyzwoity Dabbler

Odpowiedzi:

6

GolfScript, 46 znaków

'->'/')'*.'<-'-.')'/);+,\'>)'-.'<-'/);\'-'-+,-

Jest to jeden z najbardziej liniowych programów GolfScript, jakie kiedykolwiek napisałem - nie ma w nim ani jednej pętli, ani warunkowego, ani zmiennego przypisania. Wszystko odbywa się za pomocą manipulacji ciągiem:

  • Po pierwsze, ja zastąpić każde wystąpienie ->przez ). Ponieważ dane wejściowe są gwarantowane jako prawidłowe, gwarantuje to, że wszelkie pozostałe wystąpienia -muszą być częścią <-.

  • Następnie wykonuję dwie kopie łańcucha. Z pierwszej kopii usuwam postacie <i -pozostawiając tylko >i ). Następnie powielam wynik, usuwam wszystkie litery )s i każdy >po ostatnim )z drugiej kopii, łączę je i liczę znaki. Tak więc w efekcie liczę:

    • +1 dla każdego ) ,
    • +1 za każdy >po ostatnim) , i
    • +2 za każde >przed ostatnim ).
  • Następnie robię to samo dla drugiej kopii, z wyjątkiem tego liczenia czasu <i<- zamiast >i )oraz usuwanie -s przed końcową liczbą znaków. Dlatego liczę:

    • +1 dla każdego <- ,
    • +1 za każdy <po ostatnim<- , i
    • +2 za każde <przed ostatnim<-.
  • Na koniec odejmuję drugą liczbę od pierwszej i wypisuję wynik.

Ilmari Karonen
źródło
6

Python 2.7 - 154 147 134 128 bajtów

l=r=p=0
exec"exec raw_input('%s->','p+=r+1;r=0%s<-','p-=l+1;l=0%s>','r+=1;p+=1%s<','l+=1;p-=1;')"%((";').replace('",)*4)
print p

Poważne zmiany zostały wprowadzone do sposobu działania tego programu. Usunąłem stare wyjaśnienie, które wciąż można znaleźć w historii edycji tej odpowiedzi.

Ten jest obrzydliwy.

Działa prawie tak samo, jak inne odpowiedzi na to pytanie, zastępując znaki w danych wejściowych prawidłowymi instrukcjami w tym języku i wykonując je. Jest jednak jedna zasadnicza różnica: replaceto długie słowo. Chrzanić to.

@ProgrammerDan na czacie wpadł na pomysł ;').replace(', aby użyć krotki z łańcuchem 4 razy, aby użyć wstępnej str.format()metody formatowania tekstu. W %sdrugim wierszu znajdują się cztery wystąpienia ciągu, z których każdy pobiera swoją wartość z powiązanego elementu krotki na końcu. Ponieważ wszystkie są takie same, każdy %sjest zastępowany przez ;').replace('. Podczas wykonywania operacji otrzymujesz następujący ciąg:

exec raw_input(';').replace('->','p+=r+1;r=0;').replace('<-','p-=l+1;l=0;').replace('>','r+=1;p+=1;').replace('<','l+=1;p-=1;')

Jest to teraz poprawny kod Pythona, który można wykonać exec. Zgadza się, kochanie: Nested execpozwala mi używać operacji na łańcuchach, które muszą wykonywać operacje na łańcuchach . Niech ktoś mnie zabije.

Reszta jest dość prosta: każde polecenie jest zastępowane kodem, który śledzi trzy zmienne: bieżącą pozycję, liczbę praw od ostatniej ->i to samo dla leworęcznych i<- . Wszystko jest uruchamiane, a pozycja jest drukowana.

Zauważysz, że tak raw_input(';'), używając „;” jako monit, a nie raw_input()który nie ma monitu. Zapisuje to postacie w nieintuicyjny sposób: Gdybym to zrobił raw_input(), musiałbym mieć krotkę wypełnioną ).replace(', a każda instancja %smiałaby przed nią „; \” z wyjątkiem pierwszej . Otrzymanie monitu tworzy więcej redundancji, dzięki czemu mogę ogólnie zapisać więcej postaci.

podziemny monorail
źródło
2
list.index()zwraca, -1gdy nie uda się znaleźć znaku”… nr. To podnosi IndexError. Być może pomyliłeś to str.find. W rzeczywistości można zastąpić [list('><rl').index(c)]z ['><rl'.find(c)].
Bakuriu
... Huh, sprawdziłem to w dokumentacji i mogłem przysiąc, że to zwróciło -1. Była to konkretnie strona z listami, więc nie mam pojęcia, co czytam. W każdym razie, dzięki za pomoc, zmienię to w odpowiedź.
undergroundmonorail
5

Perl, 134 131 ... 99 95 bajtów

sub f{$p+=$d;$&=~/-/?($p+=$s,$s=0):($s+=$d)}$_=<>;$d=1;s/-?>/f/eg;$s=0;$d=-1;s/<-?/f/eg;print$p

Pobiera dane wejściowe jako pojedynczy wiersz na stdin, np .:

ski@anito:~$ perl -le 'sub f{$p+=$d;$&=~/-/?($p+=$s,$s=0):($s+=$d)}$_=<>;$d=1;s/-?>/f/eg;$s=0;$d=-1;s/<-?/f/eg;print$p'
><->><-<-><-<>>->
4

lub:

ski@anito:~$ echo "><->><-<-><-<>>->" | perl -le 'sub f{$p+=$d;$&=~/-/?($p+=$s,$s=0):($s+=$d)}$_=<>;$d=1;s/-?>/f/eg;$s=0;$d=-1;s/<-?/f/eg;print$p'
4

Podzieliłem instrukcje na operatory „prawe” („>” i „->”) oraz „lewe” („<” i „<-”). Zaletą tego jest to, że łatwiej jest wykorzystać równoległość między lewym i prawym operatorem i nie musimy robić nic fantazyjnego, aby tokenizować łańcuch. Każdy „kierunek” traktowany jest jako operacja podstawienia, w której dostosowujemy sumę bieżącą o liczbę kroków wykonanych w tym kierunku, ignorując kierunek odwrotny, który jest realizowany przez inną operację podstawienia. Oto mniej doświadczony przodek tego kodu jako rodzaj dokumentacji:

sub f {
  $dir=shift;
  if($1 =~ /-/) {
    $pos+=$side+$dir;
    $side=0;
  } else {
    $pos+=$dir;
    $side+=$dir;
  }
}

$_=<>;

s/(-?>)/f(1)/eg;
$side=0;
s/(<-?)/f(-1)/eg;

print $pos

W poprzedniej iteracji tego kodu wszystkie podstawienia były wykonywane w jednym przebiegu. Miało to tę zaletę, że utrzymywało bezpośrednie odwzorowanie między $ p / $ pos a pozycją, która zostanie zwrócona w dowolnym momencie, ale zajęło więcej bajtów kodu.

Jeśli chcesz użyć () 5.10.0, możesz s / print / say /, aby zgolić kolejne 2 znaki, ale to nie w moim stylu.

skibrianski
źródło
4

Perl, 88 77 bajtów

$_=<>;print s/->/F/g+2*s/>(?=.*F)//g+s/>//g-(s/<-/B/g+2*s/<(?=.*B)//g+s/<//g)

Dane wejściowe są oczekiwane przez STDIN, na przykład:

echo '><->><-<-><-<>>->'|perl -e '$_=<>;print s/->/F/g+2*s/>(?=.*F)//g+s/>//g-(s/<-/B/g+2*s/<(?=.*B)//g+s/<//g)'
4

Aktualizacja

Nie ma potrzeby konwertowania ciągu na sumę, ponieważ s//już się liczy. :-)

Pierwsza wersja

$_=<>;s/->/+1/g;s/>(?=.*1)/+2/g;s/>/+1/g;s/<-/-1/g;s/<(?=.*-)/-2/g;s/</-1/g;print eval

Dane wejściowe są oczekiwane przez STDIN, przykład:

echo '><->><-<-><-<>>->'|perl -e '$_=<>;s/->/+1/g;s/>(?=.*1)/+2/g;s/>/+1/g;s/<-/-1/g;s/<(?=.*-)/-2/g;s/</-1/g;print eval'
4

Wyjaśnienie:

Chodzi o to, aby przekonwertować ciąg kierunku na sumę, aby wynik był wyprowadzany za pomocą prostej print eval.

>zanim którykolwiek ->zrobi dwa kroki, jeden na raz, a drugi na następny ->. Nie ma znaczenia, który z ->nich podąża za co najmniej jednym z nich. Licznik wewnętrzny jest resetowany po następnym ->, więc >nie powoduje dalszych kroków, maksimum to dwa kroki. Następnie ->dodaje jeden krok dla siebie, podobnie jak pozostałe >po ostatnim ->.

To samo dotyczy kierunku wstecznego z liczeniem kroków ujemnych zamiast pozytywnych.

Na przykład: ><->><-<-><-<>>->

s/->/+1/: Zacznij od kierunku do przodu, ponieważ ->ma najwyższy priorytet.
Na przykład:><+1><-<+1<-<>>+1

s/>(?=.*1)/+2/g: Wzorzec wyprzedzający zapewnia, że konwertowane będą tylko >przed nim ->.
Na przykład:+2<+1+2<-<+1<-<+2+2+1

s/>/+1/g: Teraz pozostałe >są objęte gwarancją.
Na przykład:+2<+1+2<-<+1<-<+2+2+1

s/<-/-1/g: Analogicznie do tyłu.
Na przykład:+2<+1+2-1<+1-1<+2+2+1

s/<(?=.*-)/-2/g: We wzorcu przewidywania pełne -1poprzednie <-nie jest potrzebne, ponieważ nie ma żadnych -symboli kierunkowych.
Na przykład:+2-2+1+2-1-2+1-1<+2+2+1

s/</-1/g: Pozostałe <po ostatnim <-są konwertowane.
Na przykład:+2-2+1+2-1-2+1-1-1+2+2+1

print eval: Oblicz i wyślij wynik.
Na przykład:4

Heiko Oberdiek
źródło
Dobry. Kopałem ten pomysł zeszłej nocy, ale nie miałem okazji go wdrożyć do dziś. Dobrze, że sprawdziłem post i zobaczyłem, że już masz =)
skibrianski
@skibrianski: Dziękujemy za naprawienie błędu kopiowania i wklejania.
Heiko Oberdiek
Można grać w golfa nieco więcej: 65 bajtów Lub bez użycia -p: 74 bajtów Zmieniłem twój s/>//gna, y/>//aby zapisać bajt w każdym przypadku, co pozwoliło również na usunięcie parens w wyrażeniu.
Xcali,
2

Rubin, 141 bajtów

l=1;r=1;o=0
gets.gsub('->',?R).gsub('<-',?L).chars{|c|case c
when'<';o-=1;l+=1
when'>';o+=1;r+=1
when'L';o-=l;l=1
when'R';o+=r;r=1
end}
$><<o

Nie golfowany:

parsed = gets.gsub('->', 'R')
             .gsub('<-', 'L')
countL = 1
countR = 1
result = 0
parsed.each_char do |c|
    case c
    when '<'
        result -= 1
        countL += 1
    when '>'
        result += 1
        countR += 1
    when 'L'
        result -= countL
        countL = 1
    when 'R'
        result += countR
        countR = 1
    end
end
puts result
Tim S.
źródło
Kilka szybkich zwycięstw: l=1;r=1może być l=r=1i $><<omoże być p o. Myślę, że możesz się dużo golić, zastępując to oświadczenie sprawy mniej masywnym, może coś w stylueval %w(o-=1;l+=1 o+=1;r+=1 o-=l;l=1 o+=r;r=1)['<>LR'.index c]
Paul Prestidge
W rzeczywistości dzięki podejściu eval możesz wyciągnąć niektóre prefiksy / sufiksy, aby zaoszczędzić jeszcze więcej. To 98 znaków: l=r=1;o=0;gets.gsub('->',??).scan(/<-|./){eval"o+=#{%w[-1;l+ -l;l 1;r+ r;r][$&[-1].ord%4]}=1"};p omożesz zejść do 94 używającruby -p
Paul Prestidge
1

D - 243

Gra w golfa :

import std.regex,std.stdio;void main(string[]a){int s,c,v;auto t=a[1].matchAll("->|<-(?!>)|>|<".regex);foreach(m;t){auto r=m.hit;if(r=="->"){s+=c+1;c=0;}else if(r=="<-"){s-=v+1;v=0;}else if(r==">"){++s;++c;}else if(r=="<"){--s;++v;}}s.write;}}

Bez golfa :

import std.regex, std.stdio;

void main( string[] a )
{
    int s, c, v;
    auto t = a[1].matchAll( "->|<-(?!>)|>|<".regex );

    foreach( m; t )
    {
        auto r = m.hit;

        if( r == "->" )
        {
            s += c + 1;
            c = 0;
        }
        else if( r == "<-" )
        {
            s -= v + 1;
            v = 0;
        }
        else if( r == ">" )
        {
            ++s;
            ++c;
        }
        else if( r == "<" )
        {
            --s;
            ++v;
        }
    }

    s.write;
}
Tony Ellis
źródło
Wymagana wydajność była pierwotnie w pytaniu. Podkreśliłem to teraz i dodałem dalsze wyjaśnienia.
Przyzwoity dabbler
Tak, zredagowałem moją odpowiedź, aby uzyskać wynik teraz.
Tony Ellis,
1

C, 148 141 140

140:

r,l,o;main(char *x,char **v){for(x=v[1];*x;x++)(*x^45)?(*x^60)?(r++,o++):(*(x+1)==45)?(x++,o-=l+2,l=0):(o--,l++):(o+=r+1,r=0,x++);return o;}

141:

r,l,o;main(char *x,char **v){for(x=v[1];*x;x++)(*x^45)?(*x^60)?(r++,o++):(*(x+1)==45)?(x++,o=o-l-2,l=0):(o--,l++):(o+=r+1,r=0,x++);return o;}

148:

r,l,o;main(char *x,char **v){for(x=v[1];*x;x++){if(*x^45){if(*x^60)r++,o++;else{o--,l++;if(*(x+1)==45)x++,o-=l,l=0;}}else o+=r+1,r=0,x++;}return o;}

Z białymi znakami:

r,l,o;
main(char *x,char **v) 
{
    for(x=v[1];*x;x++)
    (*x^45) ?
        (*x^60) ?
            (r++,o++)
            :
            (*(x+1)==45) ?
                (x++,o-=l+2,l=0)
            :(o--,l++)
        :(o+=r+1,r=0,x++);
    return o;
}

Prawdopodobnie dużo więcej miejsca na golfa. W większości zrezygnowałem z próby manipulowania 4 zmiennymi w trójskładnikach, które przechwytywały wartości lv (wciąż wychodziły dłużej i dostawały się później), ale nie było to złe pierwsze przejście. Całkiem proste przejście do tablicy. Pobiera dane wejściowe jako argument wiersza polecenia, dane wyjściowe za pośrednictwem wartości zwracanej.

Potrzebujesz -std=c99flagi, aby skompilować ją z gcc.

EDYCJA: Tak, jest późno - przegapiłem oczywiste rzeczy.

Komintern
źródło
Można usunąć dwie spacje w liście argumentów main: main(char*x,char**v). Potem masz 138 zamiast 140.
Heiko Oberdiek
Wystąpił błąd: >><-daje 0 zamiast 1 lub ><->daje 0 zamiast 2.
Heiko Oberdiek
Możesz zapisać 4 bajty, jeśli usuniesz spacje między chari *, i zastąpisz (*(x+1)==45)?(x++,o-=l+2,l=0):(o--,l++)je (*++x==45)?(o-=l+2,l=0):(x--,o--,l++).
Mathieu Rodic
1

JavaScript, 136

z=0;l=r=1;c=["--z;++l;",/</g,"++z;++r;",/>/g,"z-=l;l=1;",/<-/g,"z+=r;r=1;",/->/g];for(a=8;a--;s=s.replace(c[a--],c[a]));eval(s);alert(z)

Unminified:

s="><->><-<-><-<>>->";
z=0;
l=r=1;
c=[
    "--z;++l;", /</g,
    "++z;++r;", />/g,
    "z-=l;l=1;", /<-/g,
    "z+=r;r=1;", /->/g
];
for(a=8;a--;s=s.replace(c[a--],c[a]));
eval(s);
alert(z) // Output (4)

Jak to działa

Biorąc pod uwagę ciąg znaków stak:

s="><->><-<-><-<>>->";

Wykorzystuje Regex, aby zastąpić każde polecenie zestawem instrukcji, które modyfikują z(położenie końcowe), l(zapisane ruchy w lewo) i rzapisane ruchy w prawo. Każdy Regex jest wykonywany w kolejności pierwszeństwa.

Powyższe dane wejściowe są konwertowane sna:

"++z;++r;--z;++l;z+=r;r=1;++z;++r;z-=l;l=1;--z;++l;z+=r;r=1;z-=l;l=1;--z;++l;++z;++r;++z;++r;z+=r;r=1;"

Ładne, prawda?

Na koniec musimy eval(s)wykonać instrukcje i ostrzeżenie, zktóre zawiera pozycję końcową.

George Reith
źródło
1

JavaScript (116, 122 , 130 )

116:

for(l=r=p=i=0;c='<>-0'.indexOf(a.replace(/->/g,0)[i++])+1;p--)c-4?c-3?c-2?l++:(r++,p+=2):(p-=l-2,l=0):(p+=r+2,r=0);p

122:

for(l=r=p=i=0,a=a.replace(/->/g,0);c='<>-0'.indexOf(a[i])+1;i++,p--)c-4?c-3?c-2?l++:(r++,p+=2):(p-=l-2,l=0):(p+=r+2,r=0);p

130:

for(l=r=p=i=0;c='<>-'.indexOf(a[i])+1;i++,p--)c-3?c-1?(r++,p+=2):a[i+1]=='-'?a[i+2]=='>'?l++:(p-=l,l=0,i++):l++:(p+=r+2,r=0,i++);p
mowwwalker
źródło
0

JavaScript [217 bajtów]

prompt(x=l=r=0,z='replace',f='$1 $2 ')[z](/(>.*?)(->)/g,f)[z](/(<.*?)(<-)/g,f)[z](/(<|>)(<|>)/g,f)[z](/<-?|-?>/g,function(c){c=='>'&&(x++,r++),c=='<'&&(x--,l++),c=='->'&&(x+=++r,r*=0),c=='<-'&&(x-=++l,l*=0)}),alert(x)

Prawdopodobnie można go nieco skrócić ...

Wizja
źródło
0

PHP, 284 282

Brak wyrażenia regularnego.

$i=fgets(STDIN);$c=$a=0;$s=str_split($i);while($c<count($s)){switch($s[$c]){case"<":if($s[$c+1]=="-"){if($s[$c+2]==">"){$c+=3;$a+=$rr;$rr=0;$ll++;}else{$c+=2;$a+=-($ll+1);$ll=0;}}else{$c++;$a--;$ll++;}break;case">":$c++;$a++;$rr++;break;case"-":$c+=2;$a+=$rr+1;$rr=0;break;}}echo$a;

Nie golfowany:

$i=fgets(STDIN);
$c=$a=0;
$s=str_split($i);
while($c<count($s)){
    switch($s[$c]){
    case "<":
        if($s[$c+1]=="-"){
            if($s[$c+2]==">"){
                $c+=3;$a+=$rr;$rr=0;$ll++;
            }
            else{
                $c+=2;$a+=-($ll+1);$ll=0;
            }
        }
        else{
            $c++;$a--;$ll++;
        }
    break;
    case ">":
        $c++;$a++;$rr++;
        break;
    case "-":
        $c+=2;$a+=$rr+1;$rr=0;
        break;
    }
}
echo $a;
Vereos
źródło
Możesz wygrać 2 znaki za pomocą str_split($i)( 1domyślnie jest to drugi argument). $iPrawdopodobnie powinno być $c, prawda?
Przyzwoity dabbler
Pierwszy wiersz był nieprawidłowy (to było $i): P Naprawiono!
Vereos
0

Kolejne rozwiązanie perla, 113 znaków

Istnieją już dwie odpowiedzi, które to pokonały, to tylko na chichoty. Wykorzystuje podejście oparte na obserwacji Ilmari dotyczącej wartości tokenów:

$_=<>;chomp;s/->/#/g;s/<-/%/g;s/>(?=.*#)/?/g;s/<(?=.*%)/;/g;s/#/>/g;s/%/</g;$t+=ord for split//;print$t-61*length

Trochę eksplodował:

$_=<>;
chomp;
s/->/#/g;
s/<-/%/g;
s/>(?=.*#)/?/g;
s/<(?=.*%)/;/g;
s/#/>/g;
s/%/</g;
$t+=ord for split//;
print$t-61*length
skibrianski
źródło