2009-06-21 23 views
19

Tôi đang giải quyết vấn đề 9 trên Project Euler. Trong giải pháp của tôi, tôi sử dụng câu lệnh "goto" để thoát khỏi hai vòng lặp. Vấn đề là như sau:Sử dụng goto một cách hợp pháp để thoát ra khỏi hai vòng?

Một bộ ba Pythagore là một tập hợp của ba số tự nhiên, abc, mà,

a^2 + b^2 = c^2

Ví dụ, 3^2 + 4^2 = 9 + 16 = 25 = 52.

Có tồn tại chính xác một bộ ba Pythagorean mà a + b + c = 1000. Tìm sản phẩm abc.

Giải pháp của tôi là trong C++:

int a,b,c; 
const int sum = 1000; 
int result = -1; 
for (a = 1; a<sum; a++){ 
    for (b = 1; b < sum; b++){ 
      c = sum-a-b; 
      if (a*a+b*b == c*c){ 
       result = a*b*c; 
       goto found; 
      } 
    } 
} 
found: 
std::cout << "a:" << a << std::endl; 
std::cout << "b:" << b << std::endl; 
std::cout << "c:" << c << std::endl; 
std::cout <<"Result:" << result << std::endl; 

Kể từ khi "goto" báo cáo không phải là rất phổ biến trong c lập trình viên ++, tôi muốn biết, nếu điều này có thể được coi là sử dụng hợp lý "goto" . Hoặc nếu có giải pháp tốt hơn cho vấn đề không cần "goto". Bằng cách đó tôi không có nghĩa là một giải pháp mà chỉ tránh "goto", nhưng mà tránh "goto" trong một cách để cải thiện các thuật toán.

+13

tôi thích các tab 8 không gian bạn đang sử dụng .... rất gợi cảm. –

+0

cảm ơn bạn, tôi cũng thích chúng :-) – Lucas

+10

Với một tab 16-không gian ở giữa ... –

Trả lời

45

return là "có cấu trúc" goto mà nhiều người lập trình thấy dễ chấp nhận hơn! Vì vậy:

static int findit(int sum, int* pa, int* pb, int* pc) 
{ 
    for (int a = 1; a<sum; a++) { 
     for (int b = 1; b < sum; b++) { 
      int c = sum-a-b; 
      if (a*a+b*b == c*c) { 
       *pa = a; *pb = b; *pc = c; 
       return a*b*c; 
     } 
    } 
    return -1;  
} 

int main() { 
    int a, b, c; 
    const int sum = 1000; 
    int result = findit(sum, &a, &b, &c); 
    if (result == -1) { 
     std::cout << "No result!" << std::endl; 
     return 1; 
    } 
    std::cout << "a:" << a << std::endl; 
    std::cout << "b:" << b << std::endl; 
    std::cout << "c:" << c << std::endl; 
    std::cout <<"Result:" << result << std::endl; 
    return 0; 
} 
+21

+1: nếu nó đủ phức tạp để xem xét một goto, nó đủ phức tạp để đóng gói trong một hàm và tránh goto. –

+2

Đúng. Đừng làm phức tạp các vòng lặp. Chỉ cần ra ngoài. Giải pháp hoàn hảo. Nhưng bạn có thể vượt qua a, b, c bằng cách tham chiếu. Sau đó, bạn không cần phải lộn xộn xung quanh với con trỏ. –

+0

Cảm ơn bạn, đây là một giải pháp tốt đẹp. Nó không làm cho mã ít đọc được hơn. – Lucas

4

Tôi không thể nghĩ ra giải pháp thay thế tốt hơn. Nhưng có một sự thay thế không sử dụng goto sẽ thay đổi người đầu tiên for -loop:

for (a = 1; a<sum && result == -1; a++){ 

Sau đó break ra khỏi thứ hai for -loop. Điều đó sẽ làm việc giả định kết quả sẽ không bao giờ là -1 sau khi vòng lặp for thứ hai đã bị hỏng bởi break.

+2

Tôi không thấy điều này là tốt hơn. Bạn đang làm cho mã khó đọc hơn khi bạn đang thêm nhiều điều kiện hơn vào thử nghiệm. –

+0

Và bạn nói đúng. Như tôi đã nói, tôi không thể nghĩ ra một lựa chọn tốt hơn, tôi chỉ cho thấy một cách để tránh vòng lặp goto mà không làm thay đổi mã đáng kể. – Blixt

+0

Tôi muốn thêm rằng giải pháp của Alex Martelli là tất nhiên tốt hơn. Đóng gói nó trong một hàm có lẽ là giải pháp tốt nhất trong trường hợp này. – Blixt

4

Bạn có thể khai báo một bool found = false ở phía trên và sau đó thêm && !found để bạn cho vòng lặp điều kiện (sau a < sumb < sum) và sau đó thiết lập được tìm thấy đúng nơi goto hiện tại của bạn là. Sau đó, làm cho đầu ra của bạn có điều kiện trên tìm thấy là đúng sự thật.

3

Tôi chỉ tìm thấy điều này trên thanh bên "Có liên quan". Một chủ đề thú vị tổng thể, nhưng đặc biệt, this là câu trả lời cho câu hỏi của tôi.

6

Xem this question về việc thoát khỏi 2 vòng lặp. Có nhiều câu trả lời tốt hơn được cung cấp hơn là sử dụng goto.

Câu trả lời hay nhất được cung cấp là đặt vòng lặp thứ hai của bạn vào một hàm và gọi hàm đó từ bên trong vòng lặp đầu tiên của bạn.

mã được sao chép từ phản ứng mquander của

public bool CheckWhatever(int whateverIndex) 
{ 
    for(int j = 0; j < height; j++) 
    { 
     if(whatever[whateverIndex][j]) return false; 
    } 

    return true; 
} 

public void DoubleLoop() 
{ 
    for(int i = 0; i < width; i++) 
    { 
     if(!CheckWhatever(i)) break; 
    } 
} 

Dù tôi cảm thấy rằng việc sử dụng một goto trong trường hợp này không phải là khá là xấu như giết chết mèo con. Nhưng nó gần rồi.

+10

Làm thế nào mà tất cả chúng ta đều có bộ não được rửa sạch? :) Cấu trúc kết quả của mã này phức tạp hơn việc sử dụng goto (do bổ sung 'if-statement' trong DoubleLoop). Việc tách mã ra cũng có thể làm giảm tập hợp các tối ưu hóa mà trình biên dịch có thể thực hiện - ví dụ một biến cục bộ để "CheckWhatever" có khả năng có thể được tối ưu hóa để nằm ngoài vòng lặp kèm theo. –

+0

Phiên bản goto đơn giản hơn rất nhiều. – jv110

18

Theo ý kiến ​​của tôi, bạn có thể sử dụng goto trong trường hợp như thế này.

Btw, rao giảng hạ mình chống lại goto thường xuất phát từ những người chỉ vẹt những gì họ nghe người khác nói hoặc đọc ở đâu đó ..

+1

Java đã đặt tên cho các vòng lặp, khá giống với số này – Casebash

+3

@Casebash: Chắc chắn rồi. Nhưng làm thế nào điều này liên quan đến câu trả lời hoặc câu hỏi? –

1
int a,b,c,sum = 1000; 
for (a = 1; a<sum; ++a) 
for (b = 1; b<sum; ++b){ 
    c = sum-a-b; 
    if (a*a+b*b == c*c) sum = -a*b*c; 
} 
printf("a: %d\n",a-1); 
printf("b: %d\n",b-1); 
printf("c: %d\n",c); 
printf("Result: %d\n",-sum); 

Cũng tối ưu hóa kết quả ra ..: P

Dù sao tôi yêu gotos!

Các vấn đề liên quan