Создайте программу с одним простым GOTO


25

XKCD GOTO комикс

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

Примечание: я не несу никакой ответственности за атаки велоцираптора, пытаясь выполнить этот вызов.


2
Одна Гото кажется проблематичным. Каждый C-код, который я могу представить с использованием одного goto, может быть тривиально изменен для использования структурированных конструкций. Многократные gotos однако ...
Pubby

Претензия @ Pubby, похоже, против нынешних двух решений. Замена gotoна switchкажется возможной для обоих.
Угорен

@Pubby Сколько Gotos вам нужно, чтобы создать работоспособное решение? Если проблема, о которой говорилось в настоящее время, невозможна, я могу создать альтернативную проблему.
Джо З.

Я думаю, что вам разрешено вставлять мультфильм, если есть ссылка.
luser droog

Ответы:


11

C fizzbuzz

Это решение основано на идее прерываний и меток переменных (только gcc, извините). Программа устанавливает таймер, который периодически вызывает main, и мы переходим к тому месту, где было указано последнее выполнение нашего обработчика прерываний (main).

Я никогда раньше не использовал таймеры или переменные меток, так что я думаю, что здесь есть что добавить.

#include <sys/time.h>
#include <signal.h>
#include <stdio.h>

int main(int argc)
{
    static int run = 1;
    static int* gotoloc = &&init;
    static int num = 0;
    static int limit = 50;

    goto *gotoloc;
init:
    signal(SIGVTALRM, (void (*)(int)) main);
    gotoloc = &&loop;

    struct itimerval it_val;

    it_val.it_value.tv_sec = 0;
    it_val.it_value.tv_usec = 100000;
    it_val.it_interval.tv_sec = 0;
    it_val.it_interval.tv_usec = 100000;
    setitimer(ITIMER_VIRTUAL, &it_val, NULL);

    while(run);

loop:
    num = num + 1;
    run = num < limit;
    gotoloc = &&notfizz + (&&fizz - &&notfizz) * !(num % 3);
    return 1;

fizz:
    printf("fizz");
    gotoloc = &&notbuzz + (&&buzz - &&notbuzz) * !(num % 5);
    return 1;

notfizz:
    gotoloc = &&notfizzbuzz + (&&buzz - &&notfizzbuzz) * !(num % 5);
    return 1;

buzz:
    printf("buzz\n");
    gotoloc = &&loop;
    return 1;

notbuzz:
    printf("\n");
    gotoloc = &&loop;
    return 1;

notfizzbuzz:
    printf("%d\n", num);
    gotoloc = &&loop;
    return 1;
}

runдолжен быть объявлен volatile, иначе while(run)может быть "оптимизирован" для while(1). Или вместо этого просто зайдите куда-нибудь, что звонит exit.
Угорен

@ugoren Хороший вопрос. Я включил оптимизации (O1, O2 и Os), и все они сломали программу. К сожалению, просто добавив 'volatile' перед запуском, gotoloc и num не смогли это исправить. Возможно, gcc не создан для оптимизации такого рода кода.
Шион

Определение volatile int numвне основного должно сделать это. С static, gcc думает, что знает, кто может связываться с этим.
Угорен

к сожалению, я не могу создать gotoloc за пределами основного, или я мог бы, но мне пришлось бы установить его на ноль за пределами, а затем сбросить только в начале основного, если он равен нулю. И апелляция статистика исчезнет. Так что я думаю, что лучше сказать, что я использую C неправильно, gcc по праву не оптимизирует его правильно, поэтому не пытайтесь.
Шион

5

Perl

Я не очень хорош в боулинге, но подозреваю, что это может заинтересовать ОП. Это сито Эратосфена с использованием переменной goto. Если бы это было «рефакторингом», я сомневаюсь, что что-то из этого можно будет использовать повторно, кроме, возможно, первых нескольких строк. Когда сито заканчивается, все остальные 1s в @primesмассиве соответствуют простым значениям.

Для дополнительного удовольствия не используются операторы and, or, ternaries, условные операторы или операторы сравнения любого вида.

@primes[2..1e4]=(1)x9999;
$a=2;
Y:
  $b=$a*~-$a;
X:
  $primes[$b+=$a+=$c=$a/100%2+$b/1e4%2]=0;
  goto"$c"^h;
Z:

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

В случае, если есть какая-то путаница относительно того, какой вопрос я разместил, это был вопрос о построении кода, используя только GOTO, а не один.
Джо З.

1
@JoeZeng Первоначально у меня было три, но я сократил их до одного, чтобы это также было правильным решением этой проблемы.
Примо

3

С

Мое использование макросов, возможно, не делает его "одним GOTO".
И он довольно короткий, поэтому «полностью перестроен» не так уж и много.
Но вот моя попытка в любом случае.

Считывает число из стандартного ввода, печатает его по модулю 3.

int main() {
    char s[100], *p, r=0;
    void *pl[] = { &&a, &&b, &&c, &&d, &&e, &&f, &&g, &&h, &&i, &&j, &&a, &&b, &&x, &&y, &&z }, *p1;
    p = gets(s);
    #define N(n) (pl+n)[!*p*60+*p-48];p++;goto *p1
    a: p1=N(0);
    b: p1=N(1);
    c: p1=N(2);
    d: p1=N(0);
    e: p1=N(1);
    f: p1=N(2);
    g: p1=N(0);
    h: p1=N(1);
    i: p1=N(2);
    j: p1=N(0);
    z: r++;
    y: r++;
    x: printf("%d\n", r);

    return 0;
}

1
Да, использование таких макросов не является "одним GOTO". Но даже тогда вам нужно будет провести реструктуризацию программы без использования GOTO. Удаление заявлений не добавляет к вашему счету.
Джо З.

Распечатать число по модулю 3 было бы легко, просто используя printfи scanf. Оценка вашего решения, скорее всего, будет около 2 или 3.
Джо З.

1
Честная оценка. Хотя я не могу понять, почему кто-то захочет запрограммировать то, что печатается n%3таким образом. Это должна быть программа, которая становится запутанной при удалении GOTO , а не при его введении .
Джо З.

2
"Зачем?" не имеет отношения к этому сайту - он полон глупых способов делать глупости. Если вы удалите goto, программа не будет работать. Но что вы ожидали - что программа станет запутанной только удалением?
Угорен

1
Путем удаления и последующей реструктуризации, да. Простым примером может быть использование goto для выхода из нескольких вложенных циклов.
Джо З.
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.