Direcciones de Brainf * ckish


14

Su tarea, si elige aceptarla, es crear un programa que analice y evalúe una cadena (de izquierda a derecha y de longitud arbitraria) de tokens que dan instrucciones, ya sea izquierda o derecha. Aquí están los cuatro tokens posibles y sus 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

Sin embargo, hay un inconveniente: las señales de direcciones que su programa debería poder analizar se presentarán de esta forma:

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

... en otras palabras, están concatenados, y es tarea de su programa averiguar la precedencia correcta de las direcciones y la cantidad de pasos a seguir (mirando hacia adelante). El orden de precedencia es el siguiente (de mayor a menor precedencia):

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

Si encuentra <-que no se han realizado pasos hacia la izquierda desde el inicio o desde el último reinicio, dé un solo paso hacia la izquierda. Se aplica la misma regla ->, pero luego para ir a la derecha.

Su programa debe comenzar en 0 y su resultado debe ser un entero con signo que represente la posición final final.

Puede esperar que la entrada siempre sea válida ( <--->>--<por ejemplo , nada parecido ).

Entrada de ejemplo:

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

Pasos en este ejemplo:

 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 aclarar: la salida del programa solo debe ser la posición final final como un entero con signo. La tabla de arriba está ahí para ilustrar los pasos que tomó mi ejemplo. No es necesario generar una tabla, una fila de tablas o incluso las posiciones finales de los pasos. Solo se requiere la posición final final, como un entero con signo.

El código más corto, después de una semana, gana.


44
Si entiendo las reglas de precedencia correctamente, la única vez que podría invocar <-es si es seguido inmediatamente por a <o a ->. No hay forma en este lenguaje de representar la secuencia <-entonces >, lo que sería go left the total amount of single steps that you've gone left, plus one, then go right one single step. ¿Es esto correcto y por diseño?
Adam Davis

@ AdamDavis Tienes razón. Eso fue un poco desatento de mi parte, desafortunadamente.
Dabbler decente

Respuestas:


6

GolfScript, 46 caracteres

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

Este es uno de los programas de GolfScript más lineales que he escrito: no hay una sola asignación de bucle, condicional o variable. Todo se hace mediante la manipulación de cadenas:

  • Primero, reemplazo cada aparición de ->by ). Como se garantiza que la entrada sea válida, esto garantiza que cualquier ocurrencia restante de -debe ser parte de <-.

  • A continuación, hago dos copias de la cadena. De la primera copia, elimino los caracteres <y -, dejando solo >y ). Luego duplico el resultado, elimino todos los )sy cada uno de >los últimos )de la segunda copia, los concateno y cuento los caracteres. Por lo tanto, en efecto, estoy contando:

    • 1 para cada ),
    • +1 para cada uno >después del último ), y
    • +2 para cada uno >antes del último ).
  • A continuación, hago lo mismo para la otra copia, excepto esta vez contando <y en <-lugar de >y ), y eliminando la -s antes del recuento final de caracteres. Por lo tanto, cuento:

    • 1 para cada <-,
    • +1 para cada uno <después del último <-, y
    • +2 para cada uno <antes del último <-.
  • Finalmente, resta el segundo recuento del primero y obtengo el resultado.


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

Se han realizado serios cambios en la forma en que funciona este programa. Eliminé la explicación anterior, que todavía se puede encontrar en el historial de edición de esta respuesta.

Este es asqueroso.

Funciona casi de la misma manera que otras respuestas para esta pregunta, reemplazando los caracteres en la entrada con declaraciones válidas en ese idioma y ejecutándolos. Sin embargo, hay una gran diferencia: replacees una palabra larga. Tornillo que.

A @ProgrammerDan en el chat se le ocurrió la idea de usar una tupla con la cadena ;').replace('4 veces, para usar el str.format()método previo de formateo del texto. Cuatro instancias de %sestán en la cadena en la segunda línea, cada una tomando su valor del elemento asociado de la tupla al final. Como todos son iguales, cada uno %sse reemplaza con ;').replace('. Cuando realiza las operaciones, obtiene esta cadena:

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

Este es ahora un código válido de Python que se puede ejecutar con exec. Así es, bebé: Nested execs me permite usar operaciones de cadena en el código que necesita realizar operaciones de cadena en el código . Alguien por favor mátame.

El resto es bastante sencillo: cada comando se reemplaza con un código que realiza un seguimiento de tres variables: la posición actual, el número de derechos desde la última ->, y lo mismo para las izquierdas y <-. Todo se ejecuta y se imprime la posición.

Notarás que lo hago raw_input(';'), usando ';' como un aviso, en lugar de lo raw_input()que no tiene aviso. Esto ahorra caracteres de una manera poco intuitiva: si lo hiciera raw_input(), tendría que llenar la tupla ).replace(', y cada instancia de %stendría '; \' 'antes, excepto la primera . Tener un aviso crea más redundancia para que pueda guardar más caracteres en general.


2
" list.index()regresa -1cuando no puede encontrar el personaje" .. erm no. Se levanta un IndexError. Es posible que lo hayas confundido con str.find. De hecho, podría reemplazar [list('><rl').index(c)]con ['><rl'.find(c)].
Bakuriu

... Huh, lo busqué en los documentos y podría haber jurado que dijo que devolvió -1. Era específicamente la página de listas, así que no tengo idea de lo que leí. De todos modos, gracias por la ayuda, lo editaré en la respuesta.
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

Toma la entrada como una sola línea en stdin, por ejemplo:

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

o:

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

Dividí las instrucciones en operadores "derechos" (">" y "->") y operadores "izquierdos" ("<" y "<-"). Las ventajas de esto son que es más fácil explotar el paralelismo entre los operadores izquierdo y derecho, y no tenemos que hacer nada sofisticado para tokenizar la cadena. Cada "dirección" se trata como una operación de sustitución donde ajustamos el total acumulado por el número de pasos dados en esa dirección, ignorando la dirección inversa que se ocupa de la otra operación de sustitución. Aquí hay un antepasado menos golfista de este código como una especie de documentación:

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

En una iteración anterior de este código, todas las sustituciones se realizaron en una sola pasada. Esto tenía la ventaja de mantener una asignación directa entre $ p / $ pos y la posición que se devolvería en cualquier momento dado, pero tomó más bytes de código.

Si desea usar () 5.10.0, puede s / print / say / para eliminar otros 2 caracteres del conteo, pero ese no es realmente mi estilo.


4

Perl, 88 77 bytes

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

Se espera la entrada a través de STDIN, por ejemplo:

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

Actualizar

No es necesario convertir la cadena en una suma, porque s//ya está contando. :-)

Primera versión

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

Se espera la entrada a través de STDIN, por ejemplo:

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

Explicación:

La idea es convertir la cadena de dirección en una suma para que el resultado salga de manera simple print eval.

>antes de que cualquiera ->tome dos pasos, uno a la vez y el otro al siguiente ->. No importa cuál de los dos ->sigue al menos a uno de ellos. El contador interno se reinicia después del siguiente ->, por lo >que no provoca más pasos, el máximo es de dos pasos. Luego ->agrega un paso por sí mismo y también lo que queda >después del último ->.

Lo mismo vale para la dirección hacia atrás con recuentos de pasos negativos en lugar de positivos.

P.ej: ><->><-<-><-<>>->

s/->/+1/: Comience con dirección hacia adelante, porque ->tiene la mayor prioridad.
P.ej:><+1><-<+1<-<>>+1

s/>(?=.*1)/+2/g: Las asegura patrón de preanálisis que sólo el >antes de que cualquier ->se convierten.
P.ej:+2<+1+2<-<+1<-<+2+2+1

s/>/+1/g: Ahora los restantes >están cubiertos.
P.ej:+2<+1+2<-<+1<-<+2+2+1

s/<-/-1/g: Análogo a la dirección hacia atrás.
P.ej:+2<+1+2-1<+1-1<+2+2+1

s/<(?=.*-)/-2/g: En el patrón de anticipación, no es necesario completar -1el primero <-, porque no quedan -símbolos de dirección.
P.ej:+2-2+1+2-1-2+1-1<+2+2+1

s/</-1/g: Los restantes <después de los últimos <-se convierten.
P.ej:+2-2+1+2-1-2+1-1-1+2+2+1

print eval: Calcular y generar el resultado.
P.ej:4


Bueno uno Anoche estaba dando vueltas a este concepto, pero no tuve la oportunidad de intentar implementarlo hasta hoy. Lo bueno es que revisé la publicación y vi que ya tenías =)
skibrianski

@skibrianski: Gracias por corregir el error de copiar y pegar.
Heiko Oberdiek

Puede ser un poco más golfed: 65 bytes O, sin necesidad de utilizar -p: 74 bytes he cambiado su s/>//ga y/>//guardar un byte en cada caso, que también permitió la eliminación de los parens en la expresión.
Xcali

2

Rubí, 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

Sin golf:

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

Un par de victorias rápidas: l=1;r=1puede ser l=r=1y $><<opuede ser p o. Creo que podría afeitarse mucho reemplazando esa declaración de caso con algo menos voluminoso, tal vez algo en la línea deeval %w(o-=1;l+=1 o+=1;r+=1 o-=l;l=1 o+=r;r=1)['<>LR'.index c]
Paul Prestidge

De hecho, con el enfoque eval puede extraer algunos prefijos / sufijos para ahorrar aún más. Esto es 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 opodría bajar a 94 usandoruby -p
Paul Prestidge

1

D - 243

Golfizado :

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

Sin golf :

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

El resultado requerido estaba originalmente en la pregunta. Lo he resaltado ahora y he agregado más aclaraciones.
Dabbler decente

Bien, he editado mi respuesta para mostrar el resultado ahora.
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;}

Con espacios en blanco:

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

Probablemente mucho más espacio para jugar al golf. Principalmente dejé de tratar de manipular 4 variables en los ternaries que capturaban valores (seguía saliendo más y llegando más tarde), pero no fue un mal primer paso. Pase de matriz bastante sencillo. Toma la entrada como un argumento de línea de comando, sale a través del valor de retorno.

Necesitarás la -std=c99bandera para compilarlo con gcc.

EDITAR: Sí, es tarde, se perdieron algunas cosas obvias.


Puede eliminar dos espacios en la lista de argumentos de main: main(char*x,char**v). Entonces tienes 138 en lugar de 140.
Heiko Oberdiek

Hay un error: >><-da 0 en lugar de 1 o ><->da 0 en lugar de 2.
Heiko Oberdiek

Puede guardar 4 bytes si elimina espacios entre chary *, y reemplaza (*(x+1)==45)?(x++,o-=l+2,l=0):(o--,l++)con (*++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)

No minificado:

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)

Cómo funciona

Dada una entrada de cadena scomo esta:

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

Utiliza una expresión regular para reemplazar cada comando con un conjunto de instrucciones que modifican z(la posición final), l(movimientos almacenados a la izquierda) yr movimientos almacenados a la derecha. Cada expresión regular se realiza en orden de precedencia.

Para la entrada anterior, esto se convierte sen:

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

Bonita, ¿no es así?

Finalmente debemos eval(s)realizar las instrucciones y alertas zque contiene la posición final.


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

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)

Probablemente se acorte un poco más ...


0

PHP, 284 282

Sin expresiones regulares.

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

Sin golf:

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

Puede ganar 2 caracteres con str_split($i)( 1es el valor predeterminado para el segundo argumento). Y $iprobablemente debería ser $c, ¿correcto?
Dabbler decente

La primera línea estaba mal (estaba $i): P ¡Lo arregló!
Vereos

0

Otra solución perl, 113 caracteres

Ya hay dos respuestas que superan esto, es solo por risitas. Utiliza un enfoque basado en la observación de Ilmari sobre el valor de los tokens:

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

Explotó un poco:

$_=<>;
chomp;
s/->/#/g;
s/<-/%/g;
s/>(?=.*#)/?/g;
s/<(?=.*%)/;/g;
s/#/>/g;
s/%/</g;
$t+=ord for split//;
print$t-61*length
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.