ทำไม Go จึงมีคำสั่ง "goto"


110

ฉันประหลาดใจที่พบว่า Go มีคำสั่ง'goto' ฉันได้รับการสอนเสมอว่าคำสั่ง 'goto' เป็นเรื่องของอดีตและความชั่วร้ายเพราะมันเกิดขึ้นจากขั้นตอนที่แท้จริงของโปรแกรมและฟังก์ชันหรือวิธีการเป็นวิธีที่ดีกว่าในการควบคุมโฟลว์

ฉันต้องขาดอะไรบางอย่าง ทำไม Google จึงรวมไว้ด้วย?


5
มีหลายครั้งที่คุณต้องการคำสั่ง goto จริงๆ Goto เป็นสิ่งชั่วร้ายเฉพาะเมื่อใช้อย่างไม่เลือกปฏิบัติ ตัวอย่างเช่นหากเป็นเรื่องยากมากหากไม่สามารถทำได้ให้เขียนตัวแยกวิเคราะห์เครื่อง Finite state ด้วยคำสั่ง goto
xbonez

5
ไม่เฉพาะเจาะจงสำหรับ Go แต่สำหรับการสนทนาที่ดีว่าเหตุใดภาษาจึงยังคงใช้คำสั่งและหากต้องการดูข้อโต้แย้งเกี่ยวกับการใช้งานโปรดดูโพสต์นี้ มีการอ้างอิงที่ดีบางส่วนที่เชื่อมโยงอยู่ในคำถาม แก้ไข: นี่อีก
Cᴏʀʏ

3
ในการบันทึก OP จากการ grepping ผ่านการสนทนา SO ที่ให้มานี่คือการอภิปรายเกี่ยวกับ LKMLซึ่งสรุปได้ค่อนข้างมากว่าเหตุใดจึงgotoมีประโยชน์ในบางกรณี อ่านหลังเรียนคำตอบของ @ Kissaki
kostix

1
ที่เกี่ยวข้อง: programmers.stackexchange.com/q/566/33478 (และดูคำตอบของฉัน )
Keith Thompson

มันมีประโยชน์ในการใช้รูปแบบการต่อเนื่องซึ่งคุณบันทึกออกจากสแต็กแล้วกลับไปที่ที่คุณอยู่เมื่อคุณต้องการดำเนินการต่อ
Justin Dennahower

คำตอบ:


78

เมื่อเราตรวจสอบซอร์สโค้ดของไลบรารีมาตรฐาน Go เราจะเห็นว่าgotos ถูกนำไปใช้จริงที่ไหน

ยกตัวอย่างเช่นในmath/gamma.goไฟล์คำสั่งที่ใช้ :goto

  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)
}

gotoในกรณีนี้เราประหยัดจากการแนะนำอื่น (บูล) ตัวแปรที่ใช้เพียงเพื่อการควบคุมการไหลของการตรวจสอบในตอนท้าย ในกรณีนี้ที่gotoคำสั่งทำให้โค้ดจริงดีกว่าที่จะอ่านและปฏิบัติตามได้ง่ายขึ้น (ค่อนข้างขัดกับข้อโต้แย้งกับgotoคุณกล่าวถึง)

นอกจากนี้โปรดทราบว่าgotoข้อความดังกล่าวมีลักษณะการใช้งานที่เฉพาะเจาะจงมาก ข้อกำหนดภาษาโกโตะกล่าวว่ามันอาจจะไม่กระโดดข้ามตัวแปรเข้ามาในขอบเขต (การประกาศ) และมันอาจจะไม่ได้กระโดดลงไปในเรื่องอื่น ๆ (Code-) บล็อก


69
ในตัวอย่างของคุณทำไมไม่แนะนำฟังก์ชันที่small(x,z)จะเรียกแทน ด้วยวิธีนี้เราไม่ต้องคิดว่าตัวแปรใดบ้างที่สามารถเข้าถึงได้ในsmall:ป้ายกำกับ ฉันสงสัยว่าสาเหตุคือ go ยังขาดการรองรับการซับในบางประเภทในคอมไพเลอร์
Thomas Ahle

5
@Jessta: นั่นคือสิ่งที่เรามีการมองเห็นและขอบเขตใช่ไหม?
Thomas Ahle

6
@ThomasAhle Go ไม่อนุญาตให้gotoชี้ไปที่ป้ายกำกับหลังจากมีการนำตัวแปรใหม่มาใช้ การเรียกใช้คำสั่ง "goto" จะต้องไม่ทำให้ตัวแปรใด ๆ เข้ามาในขอบเขตที่ไม่ได้อยู่ในขอบเขตที่จุด goto
km6zla

4
@ ogc-nick ขออภัยฉันไม่ชัดเจนฉันหมายความว่าฟังก์ชันสามารถประกาศได้ในขอบเขตที่จำเป็นดังนั้นจึงไม่สามารถมองเห็นโค้ดที่ไม่ต้องการได้ ฉันไม่ได้พูดถึง goto และขอบเขต
Thomas Ahle

4
@MosheRevah โค้ดอ้างอิงไม่ได้รับการปรับให้สามารถอ่านได้ ได้รับการปรับให้เหมาะสมสำหรับประสิทธิภาพดิบโดยใช้ goto ที่ครอบคลุม 22 บรรทัดภายในฟังก์ชันเดียว (และข้อเสนอของ Thomas Ahle ก็ยิ่งอ่านได้มากขึ้นสำหรับสายตาของฉัน)
joel.neely

30

Goto เป็นความคิดที่ดีเมื่อไม่มีคุณสมบัติการควบคุมในตัวที่ทำในสิ่งที่คุณต้องการได้เลยและเมื่อใดที่คุณสามารถแสดงสิ่งที่คุณต้องการด้วย goto (เป็นเรื่องที่น่าเสียดายในบางภาษาเมื่อคุณไม่มี goto คุณต้องใช้คุณสมบัติการควบคุมบางอย่างในทางที่ผิดโดยใช้แฟล็กบูลีนหรือใช้โซลูชันอื่นที่แย่กว่า goto)

หากคุณลักษณะการควบคุมอื่น ๆ (ใช้ในวิธีที่ชัดเจนพอสมควร) สามารถทำสิ่งที่คุณต้องการได้คุณควรใช้คุณลักษณะนี้ในการ goto ถ้าไม่กล้าใช้ goto!

ในที่สุดก็น่าสังเกตว่าโกโตะของโกมีข้อ จำกัด บางอย่างที่ออกแบบมาเพื่อหลีกเลี่ยงข้อบกพร่องบางอย่างที่คลุมเครือ ดูข้อ จำกัด เหล่านี้ในข้อมูลจำเพาะ


7

ข้อความของ Goto ได้รับความเสื่อมเสียอย่างมากตั้งแต่ยุคของSpaghetti codeในทศวรรษที่ 60 และ 70 ในตอนนั้นมีวิธีการพัฒนาซอฟต์แวร์ที่แย่มากถึงไม่มีเลย อย่างไรก็ตาม Goto ไม่ใช่สิ่งชั่วร้ายโดยกำเนิด แต่แน่นอนว่าสามารถนำไปใช้ในทางที่ผิดและถูกทำร้ายโดยโปรแกรมเมอร์ที่ขี้เกียจหรือไม่มีทักษะ ปัญหามากมายเกี่ยวกับ Gotos ที่ถูกละเมิดสามารถแก้ไขได้ด้วยกระบวนการพัฒนาเช่นการตรวจสอบโค้ดทีม

gotoมีการกระโดดในลักษณะเช่นเดียวกับทางด้านเทคนิคcontinue, และbreak returnอาจโต้แย้งได้ว่าข้อความเหล่านี้เป็นข้อความที่ชั่วร้ายในลักษณะเดียวกัน แต่ไม่ใช่

ทำไมทีม Go จึงรวม Gotos ไว้ด้วยอาจเป็นเพราะความจริงที่ว่ามันเป็นแบบดั้งเดิมของการควบคุมการไหลทั่วไป นอกจากนี้พวกเขาหวังว่าจะได้ข้อสรุปว่าขอบเขตของ Go ไม่รวมถึงการทำให้ภาษางี่เง่าปลอดภัยไม่สามารถละเมิดได้


continue, breakและreturnมีความแตกต่างกันมากในหนึ่งที่สำคัญโดยเฉพาะอย่างยิ่งพวกเขาระบุเพียงแค่ "ปล่อยขอบเขตการปิดล้อม" พวกเขาไม่เพียง แต่สนับสนุน แต่ต้องการอย่างชัดเจนให้นักพัฒนาพิจารณาโครงสร้างของโค้ดของพวกเขาและอาศัยพื้นฐานการเขียนโปรแกรมที่มีโครงสร้าง (สำหรับคำสั่งลูปฟังก์ชันและสวิตช์) หนึ่งเดียวและช่วยประหยัดgotoงบได้คืออนุญาตให้คุณเขียนแอสเซมบลีใน HLL เมื่อเครื่องมือเพิ่มประสิทธิภาพของคอมไพเลอร์ไม่ตรงกับงาน แต่สิ่งนี้ต้องเสียค่าใช้จ่ายในการอ่านและการบำรุงรักษา
Parthian Shot

เหตุผลที่เป็นเช่นนี้น่าแปลกใจที่จะพบในการเดินทางคือว่าในทุกกรณีอื่น ๆ ที่พัฒนา golang มีทางเลือกระหว่างการเขียนโปรแกรมแบบโครงสร้างและ "การควบคุมการไหลพิเศษ" A / กิจวัตรตัวชี้สอนชอบsetjmp, longjmp, gotoและtry / except / finallyพวกเขาเลือกที่จะทำผิดพลาดในด้าน ข้อควรระวัง goto, fwict คือการยอมรับ แต่เพียงผู้เดียวในขั้นตอนการควบคุมก่อน "การเขียนโปรแกรมเชิงโครงสร้าง"
Parthian Shot
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.