Brainf * ckish направления


14

Ваша задача - если вы решите принять ее - состоит в том, чтобы создать программу, которая анализирует и оценивает строку (слева направо и произвольной длины) токенов, которые дают указания - влево или вправо. Вот четыре возможных токена и их значения:

>  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

Однако есть одна загвоздка - токены направлений, которые ваша программа должна уметь анализировать, будут представлены в следующем виде:

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

... другими словами, они объединены, и ваша программа должна определить правильный приоритет направлений и количество шагов, которые нужно предпринять (заглядывая в будущее). Порядок приоритета выглядит следующим образом (от высшего к низшему приоритету):

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

Если вы столкнулись <-с тем, что с момента запуска или с момента последнего сброса ранее не было сделано никаких шагов влево, сделайте один единственный шаг влево. То же правило применяется к ->, но затем для перехода вправо.

Ваша программа должна начинаться с 0, а ее результатом должно быть целое число со знаком, представляющее конечную конечную позицию.

Вы можете ожидать, что ввод всегда будет действительным (например, ничего подобного <--->>--<).

Пример ввода:

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

Шаги в этом примере:

 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  

Для пояснения: выходные данные программы должны быть только конечной конечной позицией в виде целого числа со знаком. Приведенная выше таблица как раз иллюстрирует шаги, которые предпринял мой пример. Нет необходимости выводить такую ​​таблицу, строку таблицы или даже просто конечные позиции шагов. Требуется только конечная конечная позиция в виде целого числа со знаком.

Самый короткий код, через неделю, выигрывает.


4
Если я правильно понимаю правила приоритета, то единственный раз, когда вы можете ссылаться на <-него, - это сразу после него <или a или a ->. Там нет никакого способа на этом языке , чтобы представить последовательность , <-то >- что будет go left the total amount of single steps that you've gone left, plus one, then go right one single step. Это правильно и по замыслу?
Адам Дэвис

@AdamDavis Вы правы. Это было немного невнимательно с моей стороны, к сожалению.
Приличный Дабблер

Ответы:


6

GolfScript, 46 символов

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

Это одна из самых линейных программ на GolfScript, которую я когда-либо писал - в ней нет ни одного цикла, условного или переменного присваивания. Все делается с помощью манипуляции со строками:

  • Во-первых, я заменяю каждое вхождение ->на ). Поскольку входные данные гарантированно действительны, это гарантирует, что любое оставшееся вхождение -должно быть частью <-.

  • Затем я делаю две копии строки. Из первого экземпляра я удаляю символы <и- , оставляя только >и ). Затем я дублирую результат, удаляю все )s и каждый >следующий за последним )из второй копии, объединяю их и подсчитываю символы. Таким образом, по сути, я считаю:

    • +1 за каждого ),
    • +1 за каждого >после последнего )и
    • +2 за каждого > перед последним ).
  • Далее я делаю то же самое для другой копии, за исключением этого времени, считая <и <-вместо >и ), и удаляя -s до окончательного подсчета символов. Таким образом, я считаю:

    • +1 за каждого <-,
    • +1 за каждого <после последнего <-и
    • +2 за каждого< перед последним <-.
  • Наконец, я вычитаю второй отсчет из первого и вывожу результат.


6

Python 2,7 - 154 147 134 128 байт

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

Серьезные изменения были внесены в работу этой программы. Я удалил старое объяснение, которое все еще можно найти в истории редактирования этого ответа.

Это грубо.

Он работает почти так же, как и другие ответы на этот вопрос, заменяя символы на входе действительными утверждениями на этом языке и выполняя их. Однако есть одно существенное отличие: replaceэто длинное слово. Да пошло оно.

В чате @ProgrammerDan возникла идея использовать кортеж со строкой ;').replace('в нем 4 раза, чтобы использовать предварительный str.format()метод форматирования текста. Четыре экземпляра %sнаходятся в строке во второй строке, каждый из которых берет свое значение из связанного элемента кортежа в конце. Поскольку они все одинаковые, каждый %sзаменяется на ;').replace('. Когда вы выполняете операции, вы получаете эту строку:

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

Теперь это действительный код Python, который можно выполнить с помощью exec. Правильно, детка: вложенные execs позволяют мне использовать строковые операции над кодом, которые должны выполнять строковые операции над кодом . Кто-нибудь, пожалуйста, убейте меня.

Все остальное довольно просто: каждая команда заменяется кодом, который отслеживает три переменные: текущую позицию, количество прав с момента последней -> и то же для левых и<- . Все это выполняется, и позиция печатается.

Вы заметите, что я делаю raw_input(';'), используя ';' скорее, чемraw_input() как подсказка. Это сохраняет символы не интуитивно понятным способом: если бы я это сделал raw_input(), мне пришлось бы заполнить кортеж ).replace(', и каждый экземпляр %sимел бы «; \» перед ним, кроме первого . Наличие подсказки создает больше избыточности, поэтому я могу сохранить больше символов в целом.


2
list.index()msgstr " возвращается, -1когда не удается найти символ" .. нет. Это поднимает IndexError. Вы, возможно, перепутали это с str.find. На самом деле вы могли бы заменить [list('><rl').index(c)]на ['><rl'.find(c)].
Бакуриу

... Да, я посмотрел это в документах и ​​мог поклясться, что сказал, что вернул -1. Это была определенно страница для списков, так что я понятия не имею, что я читаю. В любом случае, спасибо за помощь, я отредактирую это в ответе.
подземный

5

Perl 134 131 ... 99 95 байт

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

Принимает ввод в виде одной строки в stdin, например:

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

или:

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

Я разделил инструкции на «правые» операторы («>» и «->») и «левые» операторы («<» и «<-»). Преимущества этого состоят в том, что проще использовать параллелизм между левым и правым операторами, и нам не нужно делать ничего необычного для токенизации строки. Каждое «направление» рассматривается как операция замещения, в которой мы корректируем промежуточную сумму на количество шагов, предпринятых в этом направлении, игнорируя обратное направление, о котором заботится другая операция замещения. Вот менее преданный этому кодексу предок этого рода документации:

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

В предыдущей итерации этого кода все замены выполнялись за один проход. Преимуществом этого было сохранение прямого отображения между $ p / $ pos и позицией, которая будет возвращена в любой данный момент времени, но занимала больше байтов кода.

Если вы хотите использовать () 5.10.0, вы можете s / print / say / убрать еще 2 символа, но это не совсем мой стиль.


4

Perl, 88 77 байт

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

Ввод ожидается через STDIN, например:

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

Обновить

Нет необходимости преобразовывать строку в сумму, потому что s//она уже считается. :-)

Первая версия

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

Ввод ожидается через STDIN, пример:

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

Объяснение:

Идея состоит в том, чтобы преобразовать строку направления в сумму, чтобы результат выводился простым print eval.

>прежде чем любой ->делает два шага, один за раз, а другой за другим ->. Неважно, какой из ->них следует за хотя бы одним из них. Внутренний счетчик сбрасывается после следующего ->, поэтому >не вызывает дальнейших шагов, максимум составляет два шага. Затем ->добавьте один шаг для себя и сделайте все остальное >после последнего ->.

То же самое относится к обратному направлению с отрицательным, а не положительным количеством шагов.

Например: ><->><-<-><-<>>->

s/->/+1/: Начните с направления вперед, потому что ->имеет наивысший приоритет.
Например:><+1><-<+1<-<>>+1

s/>(?=.*1)/+2/g: Шаблон прогнозирования гарантирует, что преобразуются только те, которые >прежде ->.
Например:+2<+1+2<-<+1<-<+2+2+1

s/>/+1/g: Теперь остальные >покрыты.
Например:+2<+1+2<-<+1<-<+2+2+1

s/<-/-1/g: Аналог в обратном направлении.
Например:+2<+1+2-1<+1-1<+2+2+1

s/<(?=.*-)/-2/g: В прогнозной схеме полное -1из первых <-не требуется, поскольку не осталось -символов направления.
Например:+2-2+1+2-1-2+1-1<+2+2+1

s/</-1/g: Оставшиеся <после последнего <-конвертируются.
Например:+2-2+1+2-1-2+1-1-1+2+2+1

print eval: Рассчитать и вывести результат.
Например:4


Хороший. Вчера вечером я осматривал эту концепцию, но до сегодняшнего дня у меня не было возможности ее реализовать. Хорошо, что я проверил пост и увидел, что вы уже имели =)
skibrianski

@skibrianski: Спасибо за исправление ошибки копирования и вставки.
Хайко Обердик

Может быть в гольф немного больше: 65 байт или без использования-p : 74 байт Я изменил ваш , s/>//gчтобы y/>//сохранить байты в каждом случае , который также позволил для удаления из скобок в выражении.
Xcali

2

Рубин, 141 байт

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

Пара быстрых побед: l=1;r=1может быть l=r=1и $><<oможет быть p o. Я думаю, что вы могли бы сильно побриться, заменив это заявление случая чем-то менее громоздким, возможно, чем-то вродеeval %w(o-=1;l+=1 o+=1;r+=1 o-=l;l=1 o+=r;r=1)['<>LR'.index c]
Пола Престиж

На самом деле, используя подход eval, вы можете извлечь некоторые префиксы / суффиксы, чтобы сэкономить еще больше. Это 98 символов: 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вы можете перейти к 94, используяruby -p
Пол Престиж

1

Д - 243

Гольф :

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

Без гольфа :

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

Требуемый вывод изначально был в вопросе. Я выделил это сейчас и добавил дополнительные пояснения.
Приличный Дабблер

Хорошо, я отредактировал свой ответ, чтобы вывести результат сейчас.
Тони Эллис

1

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

С пробелами:

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

Наверное, гораздо больше возможностей для игры в гольф. Я в основном отказался от попыток манипулировать 4 переменными в троицах, которые захватывали lvalues ​​(он продолжал появляться дольше и становился позже), но неплохой первый проход. Довольно простой проход массива. Принимает ввод в качестве аргумента командной строки, выводит через возвращаемое значение.

Вам понадобится -std=c99 флаг, чтобы скомпилировать его с помощью gcc.

РЕДАКТИРОВАТЬ: Да, уже поздно - пропустил некоторые очевидные вещи.


Вы можете удалить два пробела в списке аргументов main: main(char*x,char**v). Тогда у вас есть 138 вместо 140.
Хейко Обердик

Есть ошибка: >><-дает 0 вместо 1 или ><->0 вместо 2.
Хейко Обердик

Вы можете сохранить 4 байта, если удалите пробелы между charи *и заменить (*(x+1)==45)?(x++,o-=l+2,l=0):(o--,l++)на (*++x==45)?(o-=l+2,l=0):(x--,o--,l++).
Матье Родик,

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)

Как это устроено

Приведенный строковый ввод выглядит sпримерно так:

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

Он использует регулярное выражение для замены каждой команды набором команд, которые изменяют z(конечную позицию), l(сохраненные движения влево) и rсохраненные движения вправо. Каждое регулярное выражение выполняется в порядке приоритета.

Для ввода выше это преобразуется sв:

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

Довольно, не правда ли?

Наконец, мы eval(s)должны выполнить инструкции и предупреждения, zкоторые содержат конечную позицию.


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 байт]

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)

Вероятно, это может быть сокращено немного больше ...


0

PHP, 284 282

Нет регулярных выражений

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

Вы можете выиграть 2 символа с str_split($i)( 1по умолчанию для второго аргумента.) И$i вероятно, должно быть $c, правильно?
Приличный Дабблер

Первая строка была неправильной (это было $i): P Исправлено!
Vereos

0

Другое решение Perl, 113 символов

Уже есть два ответа, которые побеждают это, это только для хихиканья. Он использует подход, основанный на наблюдениях Ильмари о значении токенов:

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

Немного взорвался:

$_=<>;
chomp;
s/->/#/g;
s/<-/%/g;
s/>(?=.*#)/?/g;
s/<(?=.*%)/;/g;
s/#/>/g;
s/%/</g;
$t+=ord for split//;
print$t-61*length
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.