Zbuduj program za pomocą jednego prostego GOTO

25

Komiks XKCD GOTO

Twoim zadaniem jest zbudowanie jak największego programu, który używa dokładnie jednego GOTO, bez którego cały program (lub przynajmniej jego duża część) musi zostać całkowicie zrestrukturyzowany. Wynik jest liczony jako liczba instrukcji w kodzie, które zmieniają miejsca lub są nowo wprowadzone (usunięcie instrukcji nie dodaje do wyniku), gdy kod jest restrukturyzowany bez GOTO (inni mogą zakwestionować twoją restrukturyzację, przedstawiając więcej elegancki). Ponieważ jest to kręgle kodu, najwyższy wynik wygrywa.

Uwaga: nie ponoszę żadnej odpowiedzialności za ataki welociraptorów, podejmując wyzwanie.

Joe Z.
źródło
2
Jeden goto wydaje się problematyczne. Każdy kod C, o którym myślę, że korzysta z jednego goto, może być trywialnie zmieniany, aby używać strukturalnych struktur. Wiele gotów jednak ...
Pubby
Roszczenie @ Pubby'ego wydaje się przeciwstawiać obecnym dwóm rozwiązaniom. Zastąpienie gotoprzez switchwydaje się możliwe dla obu.
ugoren
@Pubby Ile gotów potrzebujesz, aby stworzyć wykonalne rozwiązanie? Jeśli problem, jak obecnie stwierdzono, jest niemożliwy, mogę stworzyć problem alternatywny.
Joe Z.
Myślę, że możesz osadzić kreskówkę, o ile istnieje również link.
luser droog
1
Nie kwalifikuje się, ale naprawdę to zrobiłem .
luser droog

Odpowiedzi:

11

C fizzbuzz

To rozwiązanie działa na zasadzie przerwań i etykiet zmiennych (tylko gcc, przepraszam). Program ustawia licznik czasu, który okresowo wywołuje main, gdzie mamy miejsce, w którym znajduje się ostatnie wykonanie naszego programu obsługi przerwań (main), który powiedział nam, że powinniśmy.

Nigdy wcześniej nie korzystałem z timerów ani zmiennych etykiet, więc myślę, że jest tu wiele do zrobienia.

#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;
}
shiona
źródło
runnależy zadeklarować volatile, w przeciwnym razie while(run)można go „zoptymalizować” do while(1). Lub zamiast tego, po prostu mam gdzieś to woła exit.
ugoren
@ugoren Dobry punkt. Włączyłem optymalizacje (O1, O2 i Os) i wszystkie one złamały program. Niestety dodanie „niestabilności” przed biegiem, gotoloc i num nie naprawiły tego. Być może gcc nie jest zbudowany do optymalizacji tego rodzaju kodu.
shiona
Zdefiniowanie volatile int numpoza głównym powinno to zrobić. Z staticgcc uważa, że ​​wie, kto może z tym zadzierać.
ugoren
niestety nie mogę stworzyć gotoloc poza głównym, lub mógłbym, ale musiałbym ustawić go na zero na zewnątrz, a następnie zresetować na początku main, jeśli wynosi zero. I statystyki odwołania zanikają. Myślę więc, że najlepiej powiedzieć, że używam C źle, gcc słusznie nie optymalizuje go poprawnie, więc nie próbuj.
shiona
5

Perl

Nie jestem zbyt dobry w kręgle, ale podejrzewam, że może to zainteresować OP. To sito Eratostenesa wykorzystujące zmienną goto. Gdyby to „refaktoryzować”, wątpię, by którykolwiek z nich nadawałby się do ponownego wykorzystania, może poza kilkoma pierwszymi liniami. Kiedy sito się kończy, wszystkie pozostałe 1s w @primestablicy odpowiadają wartościom podstawowym.

Dla dodatkowej zabawy używa się żadnych znaków „and”, „or”, trójek, warunkowych lub operatorów porównania.

@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:
primo
źródło
W przypadku jakichkolwiek nieporozumień, dlaczego zamieszczam to tutaj, w osobnym pytaniu (teraz usuniętym), OP stwierdził, że „to było pytanie, które naprawdę chciał zadać”, ale nie był pewien, czy jest to możliwe .
primo,
W przypadku jakichkolwiek wątpliwości co do tego, jakie pytanie zadałem, było to pytanie dotyczące budowania kodu przy użyciu tylko GOTO, a nie tylko jednego.
Joe Z.
1
@JoeZeng Miałem pierwotnie trzy, ale zredukowałem je do jednego, aby było to również prawidłowe rozwiązanie tego problemu.
primo
3

do

Moje użycie makr prawdopodobnie nie czyni go „jednym GOTO”.
I jest dość krótki, więc „całkowicie zrestrukturyzowany” to niewiele.
Ale i tak oto moja próba.

Odczytuje liczbę ze standardowego wejścia, drukuje ją modulu 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;
}
ugoren
źródło
1
Tak, używanie takich makr nie jest „jednym GOTO”. Ale nawet wtedy trzeba będzie przeprowadzić restrukturyzację programu bez korzystania z GOTO. Usunięcie wyciągów nie dodaje do wyniku.
Joe Z.
Drukowanie liczby modulo 3 byłoby łatwe, wystarczy użyć a printfi scanf. Wynik twojego rozwiązania najprawdopodobniej wynosiłby około 2 lub 3.
Joe Z.
1
Uczciwy punkt. Nie mogę jednak wymyślić, dlaczego ktokolwiek miałby chcieć zaprogramować coś, co drukuje n%3w ten sposób. Powinien to być program, który staje się zawiły, gdy GOTO zostanie usunięte , a nie kiedy zostanie wprowadzone .
Joe Z.
2
"Czemu?" jest nieistotna dla tej witryny - jest pełna głupich sposobów robienia głupich rzeczy. Jeśli usuniesz goto, program nie będzie działać. Ale czego się spodziewałeś - że program zostałby zawiły przez samo usunięcie?
ugoren
1
Przez usunięcie i późniejszą restrukturyzację tak. Prostym przykładem może być użycie goto do wyrwania się z wielu zagnieżdżonych pętli.
Joe Z.