Xây dựng chương trình với một GOTO đơn giản


25

Truyện tranh XKCD GOTO

Nhiệm vụ của bạn là xây dựng chương trình lớn nhất mà bạn có thể sử dụng chính xác một GOTO, mà không có toàn bộ chương trình (hoặc ít nhất là một đoạn lớn của chương trình) phải được cấu trúc lại hoàn toàn. Điểm số được tính là số lượng câu lệnh trong mã của bạn thay đổi địa điểm hoặc mới được giới thiệu (xóa câu lệnh không thêm vào điểm số của bạn) khi mã được cơ cấu lại mà không có GOTO (những người khác được phép thách thức cơ cấu lại của bạn bằng cách trình bày thêm thanh lịch một). Vì đây là mã bowling, điểm số cao nhất sẽ thắng.

Lưu ý: Tôi không yêu cầu bất kỳ trách nhiệm pháp lý nào đối với các cuộc tấn công bằng Velociraptor bằng cách thử thách này.


2
Một goto duy nhất có vẻ có vấn đề. Mỗi mã C tôi có thể nghĩ về việc sử dụng một goto duy nhất có thể được thay đổi một cách tầm thường để sử dụng các cấu trúc có cấu trúc. Nhiều gotos tuy nhiên ...
Pubby

Yêu cầu của @ Pubby dường như chống lại hai giải pháp hiện tại. Thay thế gotobằng switchdường như có thể cho cả hai.
ugoren

@Pubby Bạn cần bao nhiêu goto để tạo ra một giải pháp khả thi? Nếu vấn đề như đã nêu hiện tại là không thể, tôi có thể tạo ra một vấn đề khác.
Joe Z.

Tôi nghĩ bạn được phép nhúng phim hoạt hình, miễn là có một liên kết.
luser droog

1
Nó không đủ điều kiện, nhưng tôi thực sự đã làm điều này .
luser droog

Câu trả lời:


11

C fizzbuzz

Giải pháp này chạy xung quanh ý tưởng về các ngắt và các biến nhãn (chỉ gcc, xin lỗi). Chương trình thiết lập một bộ đếm thời gian gọi định kỳ chính, trong đó chúng tôi sẽ thực hiện lần thực hiện cuối cùng của trình xử lý ngắt (chính) cho chúng tôi biết chúng ta nên làm gì.

Tôi chưa bao giờ sử dụng bộ định thời hoặc biến nhãn trước đây, vì vậy tôi nghĩ rằng có nhiều thứ để tô ở đây.

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

runnên được khai báo volatile, nếu không while(run)có thể được "tối ưu hóa" thành while(1). Hoặc thay vào đó, chỉ cần goto ở đâu đó mà gọi exit.
ugoren

@ugoren Điểm tốt. Tôi đã bật tối ưu hóa (O1, O2 và Os) và tất cả những thứ đã phá vỡ chương trình. Thật không may, chỉ thêm 'dễ bay hơi' trước khi chạy, gotoloc và num đã không sửa nó. Nó có thể là gcc không được xây dựng để tối ưu hóa loại mã này.
shiona

Xác định volatile int numbên ngoài chính nên làm điều đó. Với static, gcc nghĩ rằng nó biết ai có thể gây rối với nó.
xấu xí

Thật không may, tôi không thể tạo gotoloc bên ngoài chính, hoặc tôi có thể, nhưng tôi sẽ phải đặt nó về 0 bên ngoài và sau đó chỉ đặt lại ở đầu chính nếu nó bằng không. Và các chỉ số kháng cáo để mờ dần. Vì vậy, tôi nghĩ tốt nhất nên nói rằng tôi đang sử dụng C một cách tồi tệ, gcc đúng là không tối ưu hóa nó một cách chính xác vì vậy đừng thử.
shiona

5

Perl

Tôi không giỏi chơi bowling, nhưng tôi nghi ngờ điều này có thể khiến OP quan tâm. Đây là một sàng của Eratosthenes bằng cách sử dụng một goto biến. Đây có phải là 'tái cấu trúc' không, tôi nghi ngờ bất kỳ thứ nào trong số đó sẽ có thể tái sử dụng, ngoại trừ vài dòng đầu tiên. Khi sàng kết thúc, tất cả các 1s còn lại trong @primesmảng tương ứng với các giá trị nguyên tố.

Để thêm phần thú vị, không có ands, ors, ternary, điều kiện hoặc toán tử so sánh nào được sử dụng.

@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:

Trong trường hợp có bất kỳ sự nhầm lẫn nào về lý do tại sao tôi đăng bài này ở đây, trong một câu hỏi riêng (hiện đã bị xóa), OP tuyên bố rằng "đây là câu hỏi mà anh ấy thực sự muốn hỏi", nhưng không chắc là có thể không .
primo 15/03/13

Trong trường hợp có bất kỳ sự nhầm lẫn nào về câu hỏi tôi đã đăng, đó là câu hỏi về việc xây dựng mã chỉ sử dụng GOTO, thay vì chỉ một câu hỏi .
Joe Z.

1
@JoeZeng Ban đầu tôi có ba, nhưng tôi đã giảm nó xuống còn một để nó cũng là một giải pháp hợp lệ cho vấn đề này.
primo 15/03/13

3

C

Việc sử dụng macro của tôi có lẽ không làm cho nó trở thành "một GOTO".
Và nó khá ngắn, nên "tái cấu trúc hoàn toàn" không nhiều.
Nhưng đây là nỗ lực của tôi.

Đọc một số từ đầu vào tiêu chuẩn, in nó 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;
}

1
Vâng, sử dụng các macro như thế không phải là "một GOTO". Nhưng ngay cả khi đó, bạn cần cung cấp cấu trúc lại chương trình mà không cần sử dụng GOTO. Xóa các tuyên bố không thêm vào điểm số của bạn.
Joe Z.

In một số modulo 3 sẽ dễ dàng chỉ bằng cách sử dụng một printfscanf. Điểm số của giải pháp của bạn rất có thể là khoảng 2 hoặc 3.
Joe Z.

1
Điểm công bằng. Mặc dù vậy, tôi không thể nghĩ tại sao mọi người lại muốn lập trình một cái gì đó in n%3theo cách đó. Nó phải là một chương trình trở nên phức tạp khi GOTO bị xóa , không phải khi nó được giới thiệu .
Joe Z.

2
"Tại sao?" không liên quan đến trang web này - nó chứa đầy những cách ngu ngốc để làm những điều ngu ngốc. Nếu bạn loại bỏ goto, chương trình sẽ không hoạt động. Nhưng bạn đã mong đợi điều gì - rằng chương trình sẽ trở nên hỗn độn chỉ bởi việc gỡ bỏ?
ugoren

1
Bằng cách loại bỏ và tái cấu trúc tiếp theo, có. Một ví dụ đơn giản có thể là việc sử dụng goto để thoát ra khỏi nhiều vòng lặp lồng nhau.
Joe Z.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.