Tại sao cờ vây lại có câu lệnh "goto"


110

Tôi rất ngạc nhiên khi thấy rằng cờ vây có câu lệnh 'goto' . Tôi luôn được dạy rằng các câu lệnh 'goto' là dĩ vãng và điều xấu vì nó bao hàm luồng thực tế của một chương trình và rằng các hàm hoặc phương thức luôn là cách tốt hơn để kiểm soát luồng.

Chắc chắn là tôi đang thiếu gì đó. Tại sao Google lại đưa nó vào?


5
Có những lúc bạn thực sự cần một câu lệnh goto. Goto chỉ xấu khi được sử dụng một cách bừa bãi. Ví dụ, nếu rất khó, nếu không muốn nói là không thể, viết một trình phân tích cú pháp máy trạng thái hữu hạn với các câu lệnh goto.
xbonez

5
Nó không dành riêng cho Go, nhưng để có một cuộc thảo luận tốt về lý do tại sao các ngôn ngữ giữ lại câu lệnh và để xem các lập luận chống lại việc sử dụng nó, hãy xem bài đăng này . Có một số tài liệu tham khảo tốt được liên kết trong câu hỏi. Chỉnh sửa: đây là một cái khác .
Cᴏʀʏ

3
Để ngăn OP khỏi việc tìm kiếm thông qua các cuộc thảo luận SO được cung cấp, đây là cuộc thảo luận về LKML tóm tắt khá nhiều lý do tại sao nó lại gotohữu ích trong một số trường hợp nhất định. Đọc sau khi nghiên cứu câu trả lời của @ Kissaki.
kostix


Nó hữu ích khi triển khai một mẫu tiếp tục, nơi bạn lưu khỏi ngăn xếp, sau đó quay trở lại vị trí của bạn khi bạn muốn tiếp tục.
Justin Dennahower

Câu trả lời:


78

Khi chúng tôi thực sự kiểm tra mã nguồn của thư viện tiêu chuẩn Go, chúng tôi có thể thấy những nơi gotochúng thực sự được áp dụng tốt.

Ví dụ, trong các math/gamma.gotập tin, các gototuyên bố là đã qua sử dụng :

  for x < 0 {
    if x > -1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }
  for x < 2 {
    if x < 1e-09 {
      goto small
    }
    z = z / x
    x = x + 1
  }

  if x == 2 {
    return z
  }

  x = x - 2
  p = (((((x*_gamP[0]+_gamP[1])*x+_gamP[2])*x+_gamP[3])*x+_gamP[4])*x+_gamP[5])*x + _gamP[6]
  q = ((((((x*_gamQ[0]+_gamQ[1])*x+_gamQ[2])*x+_gamQ[3])*x+_gamQ[4])*x+_gamQ[5])*x+_gamQ[6])*x + _gamQ[7]
  return z * p / q

small:
  if x == 0 {
    return Inf(1)
  }
  return z / ((1 + Euler*x) * x)
}

Trong gototrường hợp này, chúng ta không phải đưa vào một biến (boolean) khác chỉ được sử dụng cho luồng điều khiển, được kiểm tra ở cuối. Trong trường hợp này , gotocâu lệnh làm cho mã thực sự tốt hơn để đọc và dễ theo dõi hơn (hoàn toàn trái ngược với lập luận chống lại gotobạn đã đề cập).

Cũng lưu ý rằng gotocâu lệnh có một trường hợp sử dụng rất cụ thể. Đặc tả ngôn ngữ trên goto nói rằng nó có thể không nhảy qua các biến sắp vào phạm vi (đang được khai báo) và nó không thể nhảy vào các khối (mã-) khác.


69
Trong ví dụ của bạn, tại sao không chỉ giới thiệu một hàm small(x,z)để gọi thay thế? Bằng cách đó, chúng ta không phải nghĩ về những biến nào có thể truy cập được trong small:nhãn. Tôi nghi ngờ lý do là đi vẫn thiếu một số loại hỗ trợ nội tuyến trong trình biên dịch.
Thomas Ahle

5
@Jessta: Đó là những gì chúng tôi có khả năng hiển thị và phạm vi, phải không?
Thomas Ahle,


4
@ ogc-nick Xin lỗi, tôi không hiểu rõ, ý tôi là các hàm có thể được khai báo trong phạm vi cần thiết, vì vậy chúng không hiển thị với mã không cần đến chúng. Tôi không nói về goto và phạm vi.
Thomas Ahle

4
@MosheRevah Mã được tham chiếu không được tối ưu hóa để dễ đọc. Nó được tối ưu hóa cho hiệu suất thô, sử dụng goto kéo dài 22 dòng trong một chức năng. (Đề nghị Và Thomas Ahle là thậm chí dễ đọc hơn để mắt của tôi.)
joel.neely

30

Goto là một ý tưởng hay khi không có tính năng điều khiển tích hợp nào làm được những gì bạn muốn và khi bạn có thể thể hiện những gì bạn muốn bằng goto. (Thật tiếc trong những trường hợp này ở một số ngôn ngữ khi bạn không có goto. Bạn sẽ lạm dụng một số tính năng điều khiển, sử dụng cờ boolean hoặc sử dụng các giải pháp khác tồi tệ hơn goto.)

Nếu một số tính năng điều khiển khác (được sử dụng theo cách hợp lý rõ ràng) có thể làm những gì bạn muốn, bạn nên sử dụng nó để goto. Nếu không, hãy mạnh dạn và sử dụng goto!

Cuối cùng, cần lưu ý rằng goto của Go có một số hạn chế được thiết kế để tránh một số lỗi khó hiểu. Xem những hạn chế này trong thông số kỹ thuật.


7

Các tuyên bố của Goto đã nhận được rất nhiều sự mất uy tín kể từ thời kỳ mã Spaghetti vào những năm 60 và 70. Hồi đó có rất ít phương pháp luận phát triển phần mềm. Tuy nhiên Goto không phải là xấu xa mà tất nhiên có thể bị lạm dụng và lạm dụng bởi các lập trình viên lười biếng hoặc không có kinh nghiệm. Nhiều vấn đề với Gotos bị lạm dụng có thể được giải quyết bằng các quy trình phát triển như đánh giá mã nhóm.

gotolà các bước nhảy theo cách thức kỹ thuật giống như continue, breakreturn. Người ta có thể tranh luận rằng đây là những tuyên bố theo cách tương tự là xấu xa nhưng thực tế không phải vậy.

Tại sao đội cờ vây lại bao gồm Gotos có lẽ là vì thực tế nó là một nguyên thủy điều khiển luồng thông thường. Ngoài ra, họ hy vọng kết luận rằng phạm vi của Go loại trừ việc khiến một ngôn ngữ an toàn ngu ngốc không thể bị lạm dụng.


continue, breakreturnrất khác nhau ở một khóa cụ thể: chúng chỉ xác định "để lại phạm vi bao quanh". Họ không chỉ khuyến khích mà còn yêu cầu rõ ràng rằng nhà phát triển phải xem xét cấu trúc mã của họ và dựa trên các nguyên thủy lập trình có cấu trúc (cho vòng lặp, hàm và câu lệnh chuyển đổi). Ưu điểm tiết kiệm duy nhất của các gotocâu lệnh là chúng cho phép bạn viết assembly trong một HLL khi trình tối ưu hóa của trình biên dịch không đáp ứng được nhiệm vụ, nhưng điều này phải trả giá bằng khả năng đọc và khả năng bảo trì.
Parthian Shot vào

Lý do này là rất ngạc nhiên tìm thấy trong đi là, trong mọi trường hợp khác, nơi các nhà phát triển golang đã có một sự lựa chọn giữa mô hình lập trình có cấu trúc và "kiểm soát dòng chảy đặc biệt" / thao tác con trỏ chỉ dẫn thích setjmp, longjmp, goto, và try / except / finallyhọ đã chọn để err trên mặt thận trọng. goto, fwict, là sự đồng ý duy nhất đối với luồng điều khiển trước "lập trình có cấu trúc".
Parthian Shot vào
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.