Itinerário Brainf * ckish

14

Sua tarefa - se você optar por aceitá-la - é criar um programa que analise e avalie uma sequência (da esquerda para a direita e de comprimento arbitrário) de tokens que dão instruções - esquerda ou direita. Aqui estão os quatro tokens possíveis e seus significados:

>  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

Porém, existe um problema - os tokens de instruções que seu programa deve poder analisar serão apresentados desta forma:

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

... em outras palavras, eles são concatenados, e é tarefa do seu programa descobrir a precedência correta das direções e a quantidade de etapas a serem seguidas (olhando para o futuro). A ordem de precedência é a seguinte (da maior para a menor precedência):

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

Se você encontrar <-quando nenhuma etapa à esquerda foi feita anteriormente desde o início ou desde a última redefinição, dê um único passo à esquerda. A mesma regra se aplica a ->, mas depois para ir para a direita.

Seu programa deve iniciar em 0 e seu resultado deve ser um número inteiro assinado representando a posição final final.

Você pode esperar que a entrada seja sempre válida (assim, nada como <--->>--<, por exemplo).

Exemplo de entrada:

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

Etapas neste exemplo:

 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  

Para esclarecimento: a saída do programa deve ser apenas a posição final final como um número inteiro assinado. A tabela acima está lá apenas para ilustrar as etapas que meu exemplo deu. Não é necessário gerar uma tabela, linha de tabela ou mesmo apenas as posições finais das etapas. Somente a posição final final, como um número inteiro assinado, é necessária.

O código mais curto, depois de uma semana, vence.

Dabbler decente
fonte
4
Se eu entendo as regras de precedência corretamente, a única vez que você pode invocar <-é se for imediatamente seguido por a <ou a ->. Não há como, nessa linguagem, representar a sequência <-então >- o que seria go left the total amount of single steps that you've gone left, plus one, then go right one single step. Isso está correto e por design?
Adam Davis
@AdamDavis Você está certo. Isso foi um pouco desatento da minha parte, infelizmente.
Digno Dabbler

Respostas:

6

GolfScript, 46 caracteres

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

Este é um dos programas GolfScript mais lineares que já escrevi - não há um único loop, atribuição condicional ou variável nele. Tudo é feito usando manipulação de string:

  • Primeiro, substituo todas as ocorrências de ->por ). Como a entrada é garantida como válida, isso garante que qualquer ocorrência restante de -deva fazer parte <-.

  • Em seguida, faço duas cópias da sequência. A partir da primeira cópia, removo os caracteres <e -, deixando apenas >e ). Eu, então, duplicar o resultado, remover todo o )s e cada >após a última )da segunda cópia, concatenar-los e contar os caracteres. Assim, com efeito, estou contando:

    • +1 para cada ),
    • +1 para cada um >após o último )e
    • +2 para cada um >antes do último ).
  • Em seguida, eu fazer o mesmo para a outra cópia, só que desta contagem do tempo <e <-, em vez de >e ), e removendo os -s antes da contagem de caracteres final. Assim, conto:

    • +1 para cada <-,
    • +1 para cada um <após o último <-e
    • +2 para cada um <antes do último <-.
  • Por fim, subtraio a segunda contagem da primeira e produzo o resultado.

Ilmari Karonen
fonte
6

Python 2.7 - 154 147 134 128 bytes

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

Mudanças sérias foram feitas na maneira como este programa funciona. Removi a explicação antiga, que ainda pode ser encontrada no histórico de edições desta resposta.

Este é nojento.

Funciona da mesma maneira que outras respostas para essa pergunta, substituindo os caracteres na entrada por instruções válidas nesse idioma e executando-os. Há uma grande diferença, porém: replaceé uma palavra longa. Dane-se isso.

O @ProgrammerDan no bate-papo teve a idéia de usar uma tupla com a string ;').replace('nele 4 vezes, para usar o pré- str.format()método de formatação de texto. Quatro instâncias de %sestão na sequência na segunda linha, cada uma tendo seu valor do elemento associado da tupla no final. Como são todos iguais, cada um %sé substituído por ;').replace('. Ao executar as operações, você obtém esta sequência:

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;')

Agora é um código python válido que pode ser executado com exec. Isso mesmo, querida: s aninhados execme permitem usar operações de string no código que precisa executar operações de string no código . Alguém por favor me mate.

O restante é bem direto: cada comando é substituído por um código que monitora três variáveis: A posição atual, o número de direitos desde o último ->e o mesmo para esquerdas e <-. A coisa toda é executada e a posição é impressa.

Você notará que sim raw_input(';'), usando ';' como um prompt, em vez de raw_input()que não tem nenhum prompt. Isso salva os caracteres de uma maneira não intuitiva: se eu fizesse raw_input(), teria que preencher a tupla ).replace('e todas as instâncias %steriam '; \' 'antes dela, exceto a primeira . Ter um prompt cria mais redundância para que eu possa salvar mais caracteres em geral.

undergroundmonorail
fonte
2
" list.index()retorna -1quando falha em encontrar o personagem" .. erm no. Isso gera um IndexError. Você pode ter confundido isso str.find. Na verdade, você pode substituir [list('><rl').index(c)]por ['><rl'.find(c)].
Bakuriu
... Huh, procurei nos documentos e poderia jurar que ele retornou -1. Era especificamente a página de listas, então não faço ideia do que li. De qualquer forma, obrigado pela ajuda, vou editar isso na resposta.
Undergroundmonorail
5

Perl, 134 131 ... 99 95 bytes

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

Recebe a entrada como uma única linha no stdin, por exemplo:

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

ou:

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

Dividi as instruções nos operadores "direito" (">" e "->") e "esquerdo" ("<" e "<-"). As vantagens disso são que é mais fácil explorar o paralelismo entre operadores esquerdo e direito, e não precisamos fazer nada sofisticado para tokenizar a string. Cada "direção" é tratada como uma operação de substituição, onde ajustamos o total de corrida pelo número de etapas executadas nessa direção, ignorando a direção reversa que é tratada pela outra operação de substituição. Aqui está um ancestral menos influente desse código como uma espécie de documentação:

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

Em uma iteração anterior desse código, todas as substituições foram feitas em uma única passagem. Isso tinha a vantagem de manter um mapeamento direto entre $ p / $ pos e a posição que seria retornada em um determinado momento, mas consumia mais bytes de código.

Se você deseja usar () 5.10.0, pode / print / say / raspar outros 2 caracteres da contagem, mas esse não é realmente o meu estilo.

skibrianski
fonte
4

Perl, 88 bytes

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

A entrada é esperada via STDIN, por exemplo:

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

Atualizar

Não há necessidade de converter a sequência em um somatório, porque s//já está contando. :-)

Primeira versão

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

A entrada é esperada via STDIN, exemplo:

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

Explicação:

A idéia é converter a sequência de direção em um somatório para que o resultado seja gerado de forma simples print eval.

>antes de qualquer dar ->dois passos, um de uma vez e outro no seguinte ->. Não importa, qual das vezes ->segue pelo menos um deles. O contador interno é redefinido após o próximo ->, portanto >, não causa mais etapas, o máximo é duas etapas. Em seguida, ->adiciona um passo para si e os demais >após o último ->.

O mesmo vale para a direção inversa com contagem de passos negativa em vez de positiva.

Por exemplo: ><->><-<-><-<>>->

s/->/+1/: Comece com a direção para frente, porque ->tem maior precedência.
Por exemplo:><+1><-<+1<-<>>+1

s/>(?=.*1)/+2/g: O padrão de previsão assegura que apenas o >antes de qualquer ->seja convertido.
Por exemplo:+2<+1+2<-<+1<-<+2+2+1

s/>/+1/g: Agora o restante >está coberto.
Por exemplo:+2<+1+2<-<+1<-<+2+2+1

s/<-/-1/g: Analógico na direção inversa.
Por exemplo:+2<+1+2-1<+1-1<+2+2+1

s/<(?=.*-)/-2/g: No padrão de antecipação, o total -1do primeiro <-não é necessário, porque não existem -símbolos de direção restantes.
Por exemplo:+2-2+1+2-1-2+1-1<+2+2+1

s/</-1/g: O restante <após o último <-é convertido.
Por exemplo:+2-2+1+2-1-2+1-1-1+2+2+1

print eval: Calcule e produza o resultado.
Por exemplo:4

Heiko Oberdiek
fonte
Um bom. Eu estava discutindo esse conceito ontem à noite, mas não tive chance de implementá-lo até hoje. Ainda bem que eu verifiquei o posto e viu que já tinha =)
skibrianski
@skibrianski: Obrigado por corrigir o erro de copiar e colar.
Heiko Oberdiek
Pode ser golfed um pouco mais: 65 bytes Ou, sem o uso de -p: 74 bytes I mudou a sua s/>//gpara y/>//salvar um byte em cada caso, o que também permitiu a remoção dos parênteses na expressão.
Xcali 6/06/19
2

Ruby, 141 bytes

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

Ungolfed:

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.
fonte
Um par de vitórias rápidas: l=1;r=1pode ser l=r=1e $><<opode ser p o. Eu acho que você poderia se barbear muito substituindo a declaração do caso por algo menos volumoso, talvez algo comoeval %w(o-=1;l+=1 o+=1;r+=1 o-=l;l=1 o+=r;r=1)['<>LR'.index c]
Paul Prestidge 25/02/2014
De fato, com a abordagem eval, você pode usar alguns prefixos / sufixos para economizar ainda mais. Esta é de 98 caracteres: l=r=1;o=0;gets.gsub('->',??).scan(/<-|./){eval"o+=#{%w[-1;l+ -l;l 1;r+ r;r][$&[-1].ord%4]}=1"};p o, você poderia ir até 94 usandoruby -p
Paul Prestidge
1

D - 243

Golfe :

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;}}

Sem golfe :

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
fonte
A saída necessária estava originalmente na pergunta. Eu o destaquei agora e adicionei mais esclarecimentos.
Digno Dabbler
Certo, editei minha resposta para gerar o resultado agora.
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;}

Com espaço em branco:

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;
}

Provavelmente muito mais espaço para jogar isso. Eu desisti de tentar manipular 4 variáveis ​​em ternários que capturavam lvalues ​​(continuava saindo por mais tempo e ficando mais tarde), mas não era um primeiro passo ruim. Passe de matriz bastante direto. Recebe a entrada como argumento da linha de comando e gera o valor de retorno.

Você precisará da -std=c99bandeira para compilá-la com o gcc.

EDIT: Sim, é tarde - perdeu algumas coisas óbvias.

Comintern
fonte
Você pode remover dois espaços na lista de argumentos de main: main(char*x,char**v). Então você tem 138 em vez de 140.
Heiko Oberdiek
Há um erro: >><-dá 0 em vez de 1 ou ><->dá 0 em vez de 2.
Heiko Oberdiek
Você pode salvar 4 bytes se remover espaços entre chare *e substituir (*(x+1)==45)?(x++,o-=l+2,l=0):(o--,l++)por(*++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)

Desminificado:

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)

Como funciona

Dada uma entrada de string da seguinte sforma:

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

Ele usa um Regex para substituir cada comando por um conjunto de instruções que modificam z(a posição final), l(movimentos da esquerda armazenados) e rmovimentos da direita armazenados. Cada Regex é executado em ordem de precedência.

Para a entrada acima, isso converte sem:

"++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;"

Bonito, não é?

Finalmente nós eval(s) executar as instruções e alertas zque contêm a posição final.

George Reith
fonte
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
fonte
0

JavaScript [217 bytes]

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)

Provavelmente pode ser encurtado um pouco mais ...

Visão
fonte
0

PHP, 284 282

Sem regex.

$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;

Ungolfed:

$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
fonte
Você pode ganhar 2 caracteres com str_split($i)( 1é o padrão para o segundo argumento). E $iprovavelmente deve estar $c, correto?
Decent Dabbler
A primeira linha estava errada (estava $i): P Corrigida!
Vereos
0

Outra solução perl, 113 caracteres

Já existem duas respostas que superam isso, é apenas para rir. Ele usa uma abordagem baseada na observação de Ilmari sobre o valor dos tokens:

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

Explodiu um pouco:

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