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()повертається, -1коли не вдається знайти символу" .. erm no. Це піднімає 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, можете с / друкувати / сказати / поголити ще два символи з рахунку, але це не дуже мій стиль.


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: Дякуємо за виправлення помилки копіювання та вставки.
Хайко Обердьєк,

Може бути golfed трохи більше: 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

Безголівки:

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

D - 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

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

З пробілом:

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 змінними в терміналах, які фіксували значення (воно виходило довше і отримувало пізніше), але це не поганий перший прохід. Досить прямий масив проходить. Приймає введення як аргумент командного рядка, виводить через повернене значення.

Вам знадобиться -std=c99прапор, щоб компілювати його з gcc.

EDIT: Так, пізно - пропустили якісь очевидні речі.


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

Є помилка: >><-дає 0 замість 1 або ><->дає 0 замість 2.
Heiko Oberdiek

Ви можете зберегти 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)

Немініфікований:

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

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

Для введення вище це перетворюється 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;

Безголівки:

$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 Виправлено!
Вереос

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.