รูทีนย่อย

จากวิกิพีเดีย สารานุกรมเสรี
ข้ามไปที่การนำทาง ข้ามไปที่การค้นหา

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

รูทีนย่อยอาจถูกกำหนดภายในโปรแกรม หรือแยกกันในไลบรารีที่สามารถใช้ได้ในหลายโปรแกรม ในภาษาโปรแกรม ต่างๆรูทีนย่อยอาจเรียกว่ารูทีโปรแกรมย่อยฟังก์ชันเมธอดหรือโพรซีเดอร์ ในทางเทคนิค ข้อกำหนดเหล่านี้ล้วนมีคำจำกัดความที่แตกต่างกัน และศัพท์เฉพาะแตกต่างกันไปในแต่ละภาษา บางครั้งมีการใช้หน่วย callable คำศัพท์ทั่วไป [1]

ชื่อโปรแกรมย่อยแนะนำรูทีนย่อยที่ทำงานในลักษณะเดียวกับโปรแกรมคอมพิวเตอร์ที่ใช้เป็นขั้นตอนเดียวในโปรแกรมที่ใหญ่กว่าหรือโปรแกรมย่อยอื่น รูทีนย่อยมักจะถูกเข้ารหัสเพื่อให้สามารถเริ่มต้นได้หลายครั้งและจากหลาย ๆ ที่ในระหว่างการดำเนินการโปรแกรมหนึ่งครั้ง รวมถึงจากรูทีนย่อยอื่น ๆ จากนั้นแยกย่อยกลับ ( return ) ไปยังคำสั่งถัดไปหลังจากการโทรเมื่องานของรูทีนย่อยเสร็จสิ้น . แนวคิดเรื่องรูทีนย่อยเริ่มแรกเกิดขึ้นโดยJohn Mauchlyในระหว่างที่เขาทำงานเกี่ยวกับENIAC , [2]และบันทึกไว้ในการประชุมสัมมนาของ Harvard ในเดือนมกราคม ปี 1947 เรื่อง "Preparation of Problems for EDVAC-type Machines" [3] มอริส วิลค์ส ,เดวิด วีลเลอร์และสแตนลีย์ กิ ลล์ มักให้เครดิตกับการประดิษฐ์แนวคิดนี้อย่างเป็นทางการ ซึ่งพวกเขาเรียกว่ารูทีนย่อยแบบปิด[ 4] [5]ตรงกันข้ามกับ รูทีนย่อย แบบเปิดหรือมาโคร [6]อย่างไรก็ตามทัวริงได้หารือเกี่ยวกับรูทีนย่อยในกระดาษปี 1945 เกี่ยวกับข้อเสนอการออกแบบสำหรับ NPL ACEไปจนถึงการคิดค้นแนวคิดของสแต็กที่อยู่ผู้ส่ง [7]

รูทีนย่อยเป็นเครื่องมือการเขียนโปรแกรม ที่ทรงพลัง [8]และไวยากรณ์ของภาษาการเขียนโปรแกรม จำนวนมาก รวมถึงการสนับสนุนสำหรับการเขียนและการใช้งาน การใช้รูทีนย่อยอย่างรอบคอบ (เช่น ผ่าน แนวทางการ เขียนโปรแกรมแบบมีโครงสร้าง ) มักจะลดต้นทุนการพัฒนาและบำรุงรักษาโปรแกรมขนาดใหญ่ได้อย่างมาก ในขณะที่เพิ่มคุณภาพและความน่าเชื่อถือของรูทีนย่อย [9]รูทีนย่อย ซึ่งมักถูกรวบรวมไว้ในห้องสมุดเป็นกลไกสำคัญในการแบ่งปันและแลกเปลี่ยนซอฟต์แวร์ ระเบียบวินัยของการเขียนโปรแกรมเชิงวัตถุนั้นขึ้นอยู่กับวัตถุและวิธีการ(ซึ่งเป็นรูทีนย่อยที่แนบกับอ็อบเจ็กต์หรือคลาส อ็อบเจ็กต์เหล่านี้ )

ใน วิธีการ คอมไพล์ ที่ เรียกว่าthreaded codeโปรแกรมปฏิบัติการนั้นเป็นลำดับของการเรียกรูทีนย่อย

แนวคิดหลัก

เนื้อหาของรูทีนย่อยคือเนื้อความ ซึ่งเป็นส่วนของโค้ดโปรแกรมที่ดำเนินการเมื่อมีการเรียกหรือเรียกใช้รูทีนย่อย

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

อนุสัญญา คำอธิบาย การใช้งานทั่วไป
โทรตามค่า อาร์กิวเมนต์ถูกประเมินและสำเนาของค่าถูกส่งไปยังรูทีนย่อย ค่าเริ่มต้นในภาษาที่คล้าย Algol ส่วนใหญ่หลังAlgol 60เช่น Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada และอื่นๆ อีกมากมาย C, C++, Java (ค่าอ้างอิงถึงอ็อบเจ็กต์และอาร์เรย์ก็ส่งผ่านด้วยค่า)
โทรโดยอ้างอิง การอ้างอิงถึงอาร์กิวเมนต์ โดยทั่วไปแล้วที่อยู่จะถูกส่งผ่าน เลือกได้ในภาษาที่คล้าย Algol ส่วนใหญ่หลังจากAlgol 60เช่น Algol 68, Pascal, Delphi, Simula, CPL, PL/M, Modula, Oberon, Ada และอื่นๆ อีกมากมาย C++, ฟอร์ทราน, PL/I
โทรตามผล ค่าพารามิเตอร์ถูกคัดลอกกลับไปยังอาร์กิวเมนต์เมื่อส่งคืนจากรูทีนย่อย พารามิเตอร์ Ada OUT
โทรตามมูลค่าผล ค่าพารามิเตอร์จะถูกคัดลอกกลับไปยังรูทีนย่อยและอีกครั้งเมื่อส่งคืน Algol พารามิเตอร์เข้า - ออก อย่าง รวดเร็ว
เรียกชื่อ เช่นเดียวกับมาโคร – แทนที่พารามิเตอร์ด้วยนิพจน์อาร์กิวเมนต์ที่ไม่ได้ประเมิน จากนั้นประเมินอาร์กิวเมนต์ในบริบทของผู้เรียกทุกครั้งที่รูทีนที่เรียกใช้พารามิเตอร์ Algol, สกาลา
เรียกด้วยค่าคงที่ เช่นเดียวกับการเรียกตามค่า เว้นแต่ว่าพารามิเตอร์จะถือว่าเป็นค่าคงที่ พารามิเตอร์ PL/I NONASSIGNABLE, พารามิเตอร์ Ada IN

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

รูทีนย่อยสามารถเขียนโค้ดเพื่อให้เรียกตัวเองแบบเรียกซ้ำณ ที่หนึ่งแห่งหรือมากกว่าเพื่อทำงาน วิธีนี้ช่วยให้สามารถใช้งานฟังก์ชันที่กำหนดได้โดยตรงโดยการเหนี่ยวนำทางคณิตศาสตร์และ อัลกอริธึ ม การ แบ่งและพิชิต แบบเรียกซ้ำ

รูทีนย่อยที่มีจุดประสงค์เพื่อคำนวณฟังก์ชันค่าบูลีนหนึ่งฟังก์ชัน (นั่นคือ เพื่อตอบคำถามใช่/ไม่ใช่) บางครั้งเรียกว่าเพรดิเคต ใน ภาษา การเขียนโปรแกรมเชิงตรรกะมักจะ[ คลุมเครือ ]รูทีนย่อยทั้งหมดถูกเรียกว่าเพรดิเคต เนื่องจากโดยพื้นฐานแล้ว[ คลุมเครือ ] เป็น ตัวกำหนดความสำเร็จหรือความล้มเหลว [ ต้องการการอ้างอิง ]

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


ฟังก์ชั่น

ฟังก์ชัน คือรูที ย่อยที่คืนค่า [10]วัตถุประสงค์หลักของฟังก์ชันคือการแยกการคำนวณที่ซับซ้อนออกเป็นส่วนๆ ที่มีความหมายและตั้งชื่อ [11]รูทีนย่อยอาจคืนค่าที่คำนวณไปยังผู้เรียก ( ค่าที่ส่งคืน ) หรือจัดเตรียมค่าผลลัพธ์หรือพารามิเตอร์เอาต์พุตต่างๆ อันที่จริง การใช้รูทีนย่อยร่วมกันคือการใช้ฟังก์ชันทางคณิตศาสตร์ซึ่งจุดประสงค์ของรูทีนย่อยคือการคำนวณผลลัพธ์อย่างน้อยหนึ่งผลลัพธ์เท่านั้น ซึ่งค่าจะถูกกำหนดทั้งหมดโดยอาร์กิวเมนต์ที่ส่งผ่านไปยังรูทีนย่อย (ตัวอย่างอาจรวมถึงการคำนวณลอการิทึมของตัวเลขหรือดีเทอ ร์มี แนนต์ของเมทริกซ์.) ในบางภาษา ไวยากรณ์สำหรับโพรซีเดอร์ที่คืนค่าโดยพื้นฐานแล้วเหมือนกับไวยากรณ์สำหรับโพรซีเดอร์ที่ไม่คืนค่า ยกเว้นในกรณีที่ไม่มี เช่น RETURNS clause ในบางภาษา โพรซีเดอร์อาจเลือกส่งคืนแบบไดนามิกโดยมีหรือไม่มีค่า ทั้งนี้ขึ้นอยู่กับอาร์กิวเมนต์

รองรับภาษา

ภาษาโปรแกรมระดับสูงมักจะมีโครงสร้างเฉพาะเพื่อ:

  • กำหนดส่วนของโปรแกรม (เนื้อหา) ที่ประกอบเป็นรูทีนย่อย
  • กำหนดตัวระบุ (ชื่อ) ให้กับรูทีนย่อย
  • ระบุชื่อและประเภทข้อมูลของพารามิเตอร์และส่งกลับค่า
  • ระบุขอบเขตการตั้งชื่อ ส่วนตัว สำหรับตัวแปรชั่วคราว
  • ระบุตัวแปรนอกรูทีนย่อยที่สามารถเข้าถึงได้ภายในนั้น
  • โทรหารูทีนย่อย
  • ระบุค่าพารามิเตอร์
  • โปรแกรมหลักประกอบด้วยที่อยู่ของโปรแกรมย่อย
  • โปรแกรมย่อยประกอบด้วยที่อยู่ของคำสั่งถัดไปของการเรียกใช้ฟังก์ชันในโปรแกรมหลัก
  • ระบุค่าส่งคืนจากภายในร่างกาย
  • กลับเข้าสู่โปรแกรมโทร
  • กำจัดค่าที่ส่งคืนโดยการโทร
  • จัดการกับเงื่อนไขพิเศษ ใด ๆ ที่ พบในระหว่างการโทร
  • แพ็กเกจรูทีนย่อยลงในโมดูล , ไลบรารี , อ็อบเจ็กต์หรือclass

ภาษาโปรแกรมบาง ภาษา เช่นPascal , Fortran , Adaและภาษาถิ่น อื่นๆ ของBASICแยกความแตกต่างระหว่างฟังก์ชันหรือโปรแกรมย่อยของฟังก์ชัน ซึ่งให้ค่าส่งคืนที่ชัดเจนแก่โปรแกรมที่เรียก และรูทีนย่อยหรือโพรซีเดอร์ซึ่งไม่มี ในภาษาเหล่านั้น โดยปกติการเรียกฟังก์ชันจะฝังอยู่ในนิพจน์ (เช่นsqrtฟังก์ชันอาจถูกเรียกเป็นy = z + sqrt(x)) โพรซีเดอร์เรียกทำงานในลักษณะประโยค(เช่นprintโพรซีเดอร์อาจถูกเรียกเป็นif x > 0 then print(x)หรือถูกเรียกใช้อย่างชัดแจ้งโดยคำสั่ง เช่นCALLหรือGOSUB(เช่นcall print(x)) ) ภาษาอื่น ๆ เช่นCและLispอย่าแยกความแตกต่างระหว่างฟังก์ชันและรูทีนย่อย

ใน ภาษา การเขียนโปรแกรมที่ใช้งาน ได้จริง เช่นHaskellโปรแกรมย่อยจะไม่มีผลข้างเคียงซึ่งหมายความว่าสถานะภายในต่างๆ ของโปรแกรมจะไม่เปลี่ยนแปลง ฟังก์ชันจะส่งคืนผลลัพธ์เดียวกันเสมอหากเรียกใช้ซ้ำด้วยอาร์กิวเมนต์เดียวกัน โดยทั่วไป ภาษาดังกล่าวสนับสนุนเฉพาะฟังก์ชัน เนื่องจากรูทีนย่อยที่ไม่คืนค่าจะไม่มีประโยชน์เว้นแต่จะทำให้เกิดผลข้างเคียง

ในภาษาโปรแกรมเช่นC , C++และC#รูทีนย่อยอาจเรียกง่ายๆ ว่าฟังก์ชัน (เพื่อไม่ให้สับสนกับฟังก์ชันทางคณิตศาสตร์หรือการเขียนโปรแกรมเชิงฟังก์ชันซึ่งเป็นแนวคิดที่ต่างกัน)

คอมไพเลอร์ของภาษา มักจะแปลการเรียกโพรซีเดอร์และส่งคืนเป็นคำสั่งเครื่องตาม แบบแผนการเรียกที่กำหนดไว้อย่างดีเพื่อให้สามารถคอมไพล์รูทีนย่อยแยกจากโปรแกรมที่เรียกใช้ ลำดับของคำสั่งที่สอดคล้องกับคำสั่ง call และ return เรียกว่าprologue และ epilogueของ โพรซีเดอร์

ข้อดี

ข้อดีของการแบ่งโปรแกรมออกเป็นรูทีนย่อย ได้แก่:

  • การแยกส่วนงานการเขียนโปรแกรมที่ซับซ้อนออกเป็นขั้นตอนที่ง่ายกว่า: นี่เป็นหนึ่งในสองเครื่องมือหลักของการเขียนโปรแกรมแบบมีโครงสร้างพร้อมด้วยโครงสร้างข้อมูล
  • การลดโค้ดที่ซ้ำกันภายในโปรแกรม
  • เปิดใช้งานการใช้รหัสซ้ำในหลายโปรแกรม
  • แบ่งงานเขียนโปรแกรมขนาดใหญ่ระหว่างโปรแกรมเมอร์ต่างๆ หรือขั้นตอนต่างๆ ของโครงการ
  • การซ่อนรายละเอียดการใช้งานจากผู้ใช้รูทีนย่อย
  • ปรับปรุงความสามารถในการอ่านโค้ดโดยแทนที่บล็อกของโค้ดด้วยการเรียกใช้ฟังก์ชันที่ชื่อฟังก์ชันอธิบาย ใช้เพื่ออธิบายกลุ่มของโค้ด สิ่งนี้ทำให้รหัสการโทรกระชับและอ่านได้แม้ว่าฟังก์ชันนั้นไม่ได้มีไว้สำหรับใช้ซ้ำ
  • การปรับปรุง ความสามารถในการ ตรวจสอบย้อนกลับ (กล่าวคือ ภาษาส่วนใหญ่เสนอวิธีการรับการติดตามการโทร ซึ่งรวมถึงชื่อของรูทีนย่อยที่เกี่ยวข้องและอาจมีข้อมูลเพิ่มเติม เช่น ชื่อไฟล์และหมายเลขบรรทัด) โดยไม่แตกรหัสเป็นรูทีนย่อย การดีบักจะบกพร่องอย่างรุนแรง

ข้อเสีย

เมื่อเทียบกับการใช้โค้ดในบรรทัด การเรียกใช้รูทีนย่อยจะกำหนดโอเวอร์เฮดในการคำนวณในกลไกการโทร [ ต้องการการอ้างอิง ]

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

ประวัติ

แนวคิดของรูทีนย่อยเกิดขึ้นหลังจากที่เครื่องคำนวณมีอยู่แล้วมาระยะหนึ่งแล้ว คำแนะนำในการข้ามเลขคณิตและแบบมีเงื่อนไขได้รับการวางแผนล่วงหน้าและมีการเปลี่ยนแปลงค่อนข้างน้อย แต่คำแนะนำพิเศษที่ใช้สำหรับการเรียกโปรซีเจอร์ได้เปลี่ยนแปลงไปอย่างมากในช่วงหลายปีที่ผ่านมา คอมพิวเตอร์และไมโครโปรเซสเซอร์ที่เก่าที่สุด เช่นManchester BabyและRCA 1802ไม่มีคำสั่งการเรียกรูทีนย่อยเดียว สามารถใช้รูทีนย่อยได้ แต่โปรแกรมเมอร์ต้องการใช้ลำดับการโทร—ชุดคำสั่ง—ที่ไซต์การโทร แต่ละ แห่ง

รูทีนย่อยถูกนำมาใช้ในZ4ของKonrad Zuseในปี 1945

ในปีพ.ศ. 2488 อลัน เอ็ม. ทัวริงใช้คำว่า "ฝัง" และ "ไม่ฝัง" เป็นวิธีการเรียกและกลับจากรูทีนย่อย [12] [13]

ในเดือนมกราคม พ.ศ. 2490 John Mauchly ได้นำเสนอบันทึกทั่วไปที่ 'A Symposium of Large Scale Digital Calculating Machinery' ภายใต้การสนับสนุนร่วมกันของมหาวิทยาลัยฮาร์วาร์ดและสำนักสรรพาวุธ กองทัพเรือสหรัฐฯ ที่นี่เขากล่าวถึงการทำงานแบบอนุกรมและแบบขนานที่แนะนำ

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

กล่าวอีกนัยหนึ่ง หนึ่งสามารถกำหนดรูทีนย่อย A เป็นการหารและรูทีนย่อย B เป็นการคูณเชิงซ้อนและรูทีนย่อย C เป็นการประเมินข้อผิดพลาดมาตรฐานของลำดับตัวเลข และอื่นๆ ผ่านรายการรูทีนย่อยที่จำเป็นสำหรับปัญหาเฉพาะ ... รูทีนย่อยทั้งหมดเหล่านี้จะถูกจัดเก็บไว้ในเครื่อง และสิ่งที่ต้องทำคืออ้างอิงโดยย่อตามหมายเลข ตามที่ระบุไว้ในโค้ด [3]

Kay McNultyทำงานอย่างใกล้ชิดกับ John Mauchly ใน ทีม ENIACและพัฒนาแนวคิดสำหรับรูทีนย่อยสำหรับ คอมพิวเตอร์ ENIAC ที่ เธอเขียนโปรแกรมในช่วงสงครามโลกครั้งที่สอง [14]เธอและโปรแกรมเมอร์ ENIAC คนอื่นๆ ใช้รูทีนย่อยเพื่อช่วยคำนวณวิถีโคจร [14]

Goldstineและvon Neumannเขียนบทความลงวันที่ 16 สิงหาคม 1948 เกี่ยวกับการใช้รูทีนย่อย [15]

คอมพิวเตอร์และไมโครโปรเซสเซอร์ในยุคแรกๆ เช่นIBM 1620 , Intel 4004และIntel 8008และไมโครคอนโทรลเลอร์ PICมีการเรียกรูทีนย่อยแบบคำสั่งเดียวที่ใช้สแต็กฮาร์ดแวร์เฉพาะเพื่อเก็บที่อยู่ผู้ส่งคืน—ฮาร์ดแวร์ดังกล่าวรองรับเพียงไม่กี่ระดับ ของการซ้อนรูทีนย่อย แต่สามารถรองรับรูทีนย่อยแบบเรียกซ้ำได้ เครื่องก่อนกลางทศวรรษ 1960 เช่นUNIVAC I , PDP-1และIBM 1130 มัก ใช้แบบแผนการเรียกซึ่งบันทึกตัวนับคำสั่งในตำแหน่งหน่วยความจำแรกของรูทีนย่อยที่เรียกว่า ซึ่งช่วยให้สามารถซ้อนรูทีนย่อยในระดับลึกได้ตามอำเภอใจ แต่ไม่สนับสนุนรูทีนย่อยแบบเรียกซ้ำ PDP-11 (1970) เป็นหนึ่งในคอมพิวเตอร์เครื่องแรกที่ มีคำสั่งเรียกรูทีนย่อยแบบพุชแบบสแต็ก คุณลักษณะนี้สนับสนุนทั้งการซ้อนรูทีนย่อยในเชิงลึกโดยพลการและยังสนับสนุนรูทีนย่อยแบบเรียกซ้ำ [16]

รองรับภาษา

ในแอสเซมเบลอร์รุ่นแรกๆ การสนับสนุนรูทีนย่อยมีจำกัด รูทีนย่อยไม่ได้แยกจากกันอย่างชัดเจนหรือจากโปรแกรมหลัก และแน่นอนซอร์สโค้ดของรูทีนย่อยสามารถถูกสลับสับเปลี่ยนกับรูทีนย่อยอื่นๆ แอสเซม เบลอร์บางตัวจะเสนอมาโคร ที่กำหนดไว้ล่วงหน้า เพื่อสร้างลำดับการโทรและส่งคืน ในช่วงทศวรรษที่ 1960 แอสเซมเบลอร์มักจะได้รับการสนับสนุนที่ซับซ้อนกว่ามากสำหรับรูทีนย่อยทั้งแบบอินไลน์และแบบแยกจากกัน ซึ่งสามารถเชื่อมโยงเข้าด้วยกันได้

ภาษาโปรแกรมภาษาแรกที่สนับสนุนรูทีนย่อยและฟังก์ชันที่ผู้ใช้เขียนคือFORTRAN II คอมไพเลอร์ IBM FORTRAN II เปิดตัวในปี 1958 ALGOL 58และภาษาการเขียนโปรแกรมรุ่นต้นอื่นๆ ยังสนับสนุนการเขียนโปรแกรมเชิงขั้นตอนอีกด้วย

ไลบรารีรูทีนย่อย

แม้จะมีแนวทางที่ยุ่งยากนี้ รูทีนย่อยก็พิสูจน์แล้วว่ามีประโยชน์มาก ประการหนึ่ง พวกเขาอนุญาตให้ใช้รหัสเดียวกันในโปรแกรมต่างๆ มากมาย นอกจากนี้ หน่วยความจำยังเป็นทรัพยากรที่หายากมากในคอมพิวเตอร์ยุคแรกๆ และรูทีนย่อยช่วยให้ประหยัดขนาดโปรแกรมได้อย่างมาก

คอมพิวเตอร์ยุคแรกๆ จำนวนมากโหลดคำสั่งโปรแกรมลงในหน่วยความจำจากเทปกระดาษที่เจาะรู แต่ละรูทีนย่อยสามารถจัดเตรียมโดยเทปแยกชิ้น โหลดหรือต่อก่อนหรือหลังโปรแกรมหลัก (หรือ "สายหลัก" [17] ); และเทปรูทีนย่อยเดียวกันก็สามารถนำมาใช้กับโปรแกรมต่างๆ ได้มากมาย วิธีการที่คล้ายกันนี้ใช้กับคอมพิวเตอร์ที่ใช้บัตรเจาะรูสำหรับอินพุตหลัก ไลบรารีรูทีนย่อยของชื่อเดิมหมายถึงห้องสมุด ในความหมายตามตัวอักษร ซึ่งเก็บคอลเลกชันที่จัดทำดัชนีของเทปหรือสำรับไพ่ไว้ใช้ร่วมกัน

กลับโดยอ้อมกระโดด

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

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

ข้ามไปยังรูทีนย่อย

ความก้าวหน้าอีกประการหนึ่งคือการข้ามไปยังคำสั่งรูทีนย่อย ซึ่งรวมการบันทึกที่อยู่ผู้ส่งกับการข้ามการโทร ซึ่งจะช่วยลดค่าใช้จ่ายได้อย่างมาก

ตัวอย่างเช่น ใน IBM System/360คำสั่งสาขา BAL หรือ BALR ที่ออกแบบมาสำหรับการเรียกโพรซีเดอร์ จะบันทึกที่อยู่ผู้ส่งกลับในการลงทะเบียนตัวประมวลผลที่ระบุในคำสั่ง โดยการลงทะเบียนแบบแผน 14 ในการส่งคืน รูทีนย่อยเพียงดำเนินการ คำสั่งสาขาทางอ้อม (BR) ผ่านการลงทะเบียนนั้น หากรูทีนย่อยต้องการรีจิสเตอร์เพื่อวัตถุประสงค์อื่น (เช่น การเรียกรูทีนย่อยอื่น) รูทีนย่อยจะบันทึกเนื้อหาของรีจิสเตอร์ไปยังตำแหน่งหน่วยความจำส่วนตัวหรือregister stack

ในระบบเช่นHP 2100คำสั่ง JSB จะทำงานคล้ายกัน ยกเว้นที่อยู่ผู้ส่งถูกเก็บไว้ในตำแหน่งหน่วยความจำที่เป็นเป้าหมายของสาขา การดำเนินการตามขั้นตอนจริงจะเริ่มที่ตำแหน่งหน่วยความจำถัดไป ในภาษาแอสเซมบลีของ HP 2100 เราจะเขียน เช่น

       ...
       JSB MYSUB (รูทีนย่อยการโทร MYSUB)
 BB ... (จะกลับมาที่นี่หลังจาก MYSUB เสร็จ)

เพื่อเรียกรูทีนย่อยที่เรียกว่า MYSUB จากโปรแกรมหลัก รูทีนย่อยจะถูกเข้ารหัสเป็น

MYSUB NOP (ที่เก็บข้อมูลสำหรับที่อยู่ส่งคืนของ MYSUB)
 AA ... (จุดเริ่มต้นของร่าง MYSUB)
       ...
       JMP MYSUB,I (กลับไปที่โปรแกรมการโทร)

คำสั่ง JSB วางที่อยู่ของคำสั่ง NEXT (กล่าวคือ BB) ในตำแหน่งที่ระบุเป็นตัวถูกดำเนินการ (เช่น MYSUB) แล้วแยกย่อยไปยังตำแหน่ง NEXT หลังจากนั้น (คือ AA = MYSUB + 1) รูทีนย่อยสามารถกลับไปที่โปรแกรมหลักโดยดำเนินการ JMP MYSUB ข้ามทางอ้อม I ซึ่งแยกสาขาไปยังตำแหน่งที่เก็บไว้ที่ตำแหน่ง MYSUB

คอมไพเลอร์สำหรับ Fortran และภาษาอื่นๆ สามารถใช้คำแนะนำเหล่านี้ได้อย่างง่ายดายเมื่อมี วิธีการนี้รองรับการโทรหลายระดับ อย่างไรก็ตาม เนื่องจากที่อยู่ส่งคืน พารามิเตอร์ และค่าส่งคืนของรูทีนย่อยถูกกำหนดตำแหน่งหน่วยความจำคงที่ จึงไม่อนุญาตให้มีการเรียกซ้ำ

อนึ่ง Lotus 1-2-3ใช้วิธีการที่คล้ายกันในช่วงต้นทศวรรษ 1980 เพื่อค้นหาการขึ้นต่อกันของการคำนวณใหม่ในสเปรดชีต กล่าวคือ มีการสงวนสถานที่ในแต่ละเซลล์เพื่อจัดเก็บที่อยู่ ผู้ ส่ง เนื่องจาก ไม่อนุญาตให้ใช้ การอ้างอิงแบบวงกลม สำหรับลำดับการคำนวณใหม่ตามธรรมชาติ จึงอนุญาตให้ทำทรีวอล์คโดยไม่ต้องจองพื้นที่สำหรับสแต็กในหน่วยความจำ ซึ่งถูกจำกัดมากในคอมพิวเตอร์ขนาด เล็ก เช่นIBM PC

สแต็คการโทร

การเรียกใช้รูทีนย่อยที่ทันสมัยส่วนใหญ่ใช้call stackซึ่งเป็นกรณีพิเศษของโครงสร้างข้อมูลสแต็ก เพื่อใช้งานการเรียกรูทีนย่อยและส่งคืน การเรียกโพรซีเดอร์แต่ละครั้งจะสร้างรายการใหม่ เรียกว่าสแต็กเฟรมที่ด้านบนของสแต็ก เมื่อโพรซีเดอร์กลับมา สแต็กเฟรมจะถูกลบออกจากสแต็ก และอาจใช้พื้นที่สำหรับการเรียกโพรซีเดอร์อื่น แต่ละสแต็กเฟรมมีข้อมูลส่วนตัวของการเรียกที่เกี่ยวข้อง ซึ่งโดยทั่วไปรวมถึงพารามิเตอร์ของโพรซีเดอร์และตัวแปรภายใน และที่อยู่ผู้ส่ง

ลำดับการเรียกสามารถดำเนินการได้โดยลำดับของคำสั่งทั่วไป (แนวทางที่ยังคงใช้ในการประมวลผลชุดคำสั่งที่ลดลง (RISC) และ สถาปัตยกรรม คำสั่งที่ยาวมาก (VLIW)) แต่เครื่องจักรแบบดั้งเดิมจำนวนมากที่ออกแบบตั้งแต่ช่วงปลายทศวรรษที่ 1960 ได้รวมคำสั่งพิเศษไว้สำหรับ จุดประสงค์นั้น

โดยปกติ call stack จะใช้เป็นพื้นที่ต่อเนื่องกันของหน่วยความจำ เป็นทางเลือกในการออกแบบโดยพลการไม่ว่าด้านล่างของสแต็กจะเป็นแอดเดรสต่ำสุดหรือสูงสุดภายในพื้นที่นี้ เพื่อให้สแต็กสามารถเติบโตไปข้างหน้าหรือข้างหลังในหน่วยความจำ อย่างไรก็ตาม สถาปัตยกรรมหลายแห่งเลือกอย่างหลัง [ ต้องการการอ้างอิง ]

การออกแบบบางอย่าง โดยเฉพาะอย่างยิ่ง การใช้งาน Forth บางส่วน ใช้สแต็กแยกกันสองชุด อันแรกใช้สำหรับข้อมูลการควบคุมเป็นหลัก (เช่น ที่อยู่ผู้ส่งและตัวนับลูป) และอีกอันสำหรับข้อมูล อดีตเคยเป็นหรือทำงานเหมือน call stack และสามารถเข้าถึงได้โดยอ้อมสำหรับโปรแกรมเมอร์ผ่านโครงสร้างภาษาอื่น ๆ ในขณะที่หลังสามารถเข้าถึงได้โดยตรงมากกว่า

เมื่อมีการแนะนำโพรซีเดอร์แบบสแต็กเป็นครั้งแรก แรงจูงใจที่สำคัญคือการบันทึกหน่วยความจำอันมีค่า [ อ้างอิงจำเป็น ]ด้วยรูปแบบนี้ คอมไพเลอร์ไม่จำเป็นต้องสำรองพื้นที่แยกในหน่วยความจำสำหรับข้อมูลส่วนตัว (พารามิเตอร์ ที่อยู่ผู้ส่ง และตัวแปรท้องถิ่น) ของแต่ละขั้นตอน ในเวลาใดก็ตาม สแต็กจะมีเพียงข้อมูลส่วนตัวของการโทรที่ใช้งานอยู่ ในปัจจุบัน (กล่าวคือ ซึ่งถูกเรียกแล้ว แต่ยังไม่ได้ส่งคืน) เนื่องจากวิธีการที่โปรแกรมมักจะประกอบขึ้นจากไลบรารีจึงไม่ใช่เรื่องแปลกที่จะค้นหาโปรแกรมที่มีรูทีนย่อยนับพันรายการซึ่งมีเพียงไม่กี่โปรแกรมเท่านั้นที่เปิดใช้งานในช่วงเวลาใดก็ตาม [ ต้องการการอ้างอิง ]สำหรับโปรแกรมดังกล่าว กลไก call stack สามารถบันทึกหน่วยความจำจำนวนมากได้ อันที่จริง กลไก call stack ถือได้ว่าเป็นวิธีการที่เร็วและง่ายที่สุดสำหรับการจัดการหน่วยความจำอัตโนมัติ

อย่างไรก็ตาม ข้อดีอีกประการของเมธอด call stack คืออนุญาตให้มีการเรียกรูทีนย่อยแบบเรียกซ้ำเนื่องจากการเรียกที่ซ้อนกันแต่ละครั้งไปยังโพรซีเดอร์เดียวกันจะได้รับอินสแตนซ์ของข้อมูลส่วนตัวแยกต่างหาก

วางซ้อนล่าช้า

ข้อเสียอย่างหนึ่งของกลไก call stack คือต้นทุนที่เพิ่มขึ้นของการเรียกโพรซีเดอร์และการส่งคืนที่ตรงกัน [ ต้องการคำชี้แจง ]ค่าใช้จ่ายเพิ่มเติมรวมถึงการเพิ่มขึ้นและลดค่าตัวชี้สแต็ก (และในบางสถาปัตยกรรม การตรวจสอบสแต็กโอเวอร์โฟ ลว์ ) และการเข้าถึงตัวแปรในเครื่องและพารามิเตอร์ด้วยแอดเดรสที่สัมพันธ์กับเฟรม แทนที่จะเป็นแอดเดรสแบบสัมบูรณ์ ค่าใช้จ่ายอาจรับรู้ได้ในเวลาดำเนินการที่เพิ่มขึ้น หรือความซับซ้อนของตัวประมวลผลที่เพิ่มขึ้น หรือทั้งสองอย่าง

ค่าโสหุ้ยนี้ชัดเจนที่สุดและไม่เหมาะสมในกระบวน งานของ leaf หรือleaf functionsซึ่งส่งคืนโดยไม่ต้องเรียกใช้ขั้นตอนใด ๆ เลย [18] [19] [20]เพื่อลดโอเวอร์เฮดนั้น คอมไพเลอร์สมัยใหม่จำนวนมากพยายามชะลอการใช้ call stack จนกว่าจะมีความจำเป็นจริงๆ [ อ้างอิงจำเป็น ]ตัวอย่างเช่น การเรียกโพรซีเดอร์Pอาจจัดเก็บที่อยู่ผู้ส่งและพารามิเตอร์ของโพรซีเดอร์ที่ถูกเรียกในรีจิสเตอร์ตัวประมวลผลบางตัว และถ่ายโอนการควบคุมไปยังโพรซีเดอร์ของโพรซีเดอร์โดยการข้ามอย่างง่าย หากโพรซีเดอร์Pส่งคืนโดยไม่ทำการเรียกอื่นใด call stack จะไม่ถูกใช้งานเลย ถ้าพี่ต้องการเรียกขั้นตอนอื่นQจากนั้นจะใช้ call stack เพื่อบันทึกเนื้อหาของการลงทะเบียนใด ๆ (เช่นที่อยู่ที่ส่งคืน) ที่จำเป็นหลังจากQส่งคืน

ตัวอย่าง C และ C++

ใน ภาษาการเขียนโปรแกรม CและC++โปรแกรมย่อยเรียกว่าฟังก์ชัน (จัดประเภทเพิ่มเติมเป็นฟังก์ชันสมาชิกเมื่อเชื่อมโยงกับคลาสหรือฟังก์ชันอิสระ[21]หากไม่ใช่) ภาษาเหล่านี้ใช้คีย์เวิร์ดพิเศษvoidเพื่อระบุว่าฟังก์ชันไม่คืนค่าใดๆ โปรดทราบว่าฟังก์ชัน C/C++ สามารถมีผลข้างเคียง รวมถึงการปรับเปลี่ยนตัวแปรใดๆ ที่แอดเดรสถูกส่งผ่านเป็นพารามิเตอร์ ตัวอย่าง:

โมฆะFunction1 () { /* บางรหัส */ }    

ฟังก์ชันไม่คืนค่าและต้องถูกเรียกเป็นฟังก์ชันแบบสแตนด์อโลน เช่นFunction1();

int Function2 () {  
  กลับ5 ; 
}

ฟังก์ชันนี้ส่งคืนผลลัพธ์ (หมายเลข 5) และการเรียกอาจเป็นส่วนหนึ่งของนิพจน์ เช่นx + Function2()

ถ่านFunction3 ( จำนวนint ) {   
  การเลือกถ่าน[] = { 'S' , 'M' , 'T' , 'W' , 'T' , 'F' , 'S' };         
  ส่งคืนการเลือก[ หมายเลข]; 
}

ฟังก์ชันนี้จะแปลงตัวเลขระหว่าง 0 ถึง 6 ให้เป็นตัวอักษรเริ่มต้นของวันที่ตรงกันในสัปดาห์ นั่นคือ 0 ถึง 'S', 1 ถึง 'M', ..., 6 ถึง 'S' ผลลัพธ์ของการเรียกมันอาจถูกกำหนดให้กับตัวแปร เช่นnum_day = Function3(number);.

เป็นโมฆะFunction4 ( int * pointer_to_var ) {   
  ( * pointer_to_var ) ++ ;
}

ฟังก์ชันนี้ไม่คืนค่า แต่แก้ไขตัวแปรที่ส่งที่อยู่เป็นพารามิเตอร์ มันจะถูกเรียกด้วยFunction4(&variable_to_increment);.

ตัวอย่างพื้นฐานเล็กๆ

ตัวอย่าง()                                ' เรียกรูทีนย่อย

 ตัวอย่าง                             ย่อย' เริ่มรูทีน
    ย่อยTextWindow WriteLine ( "นี่คือตัวอย่างของรูทีนย่อยใน Microsoft Small Basic" ) ' รูทีนย่อยทำอะไรEndSub ' สิ้นสุดรูทีนย่อย  
                                  

ในตัวอย่างข้างต้นExample()เรียกรูทีนย่อย [22]ในการกำหนดรูทีนย่อยจริงSubต้องใช้คีย์เวิร์ด โดยมีชื่อรูทีนย่อยตามSubหลัง หลังจากเนื้อหาตาม EndSubจะต้องพิมพ์

ตัวอย่าง Visual Basic 6

ใน ภาษา Visual Basic 6โปรแกรมย่อยจะเรียกว่าฟังก์ชันหรือย่อย (หรือเมธอดเมื่อเชื่อมโยงกับคลาส) Visual Basic 6 ใช้คำต่างๆ ที่เรียกว่าtypesเพื่อกำหนดสิ่งที่กำลังส่งผ่านเป็นพารามิเตอร์ โดยค่าเริ่มต้น ตัวแปรที่ไม่ระบุจะถูกลงทะเบียนเป็นประเภทตัวแปรและสามารถส่งผ่านเป็นByRef (ค่าเริ่ม ต้น) หรือByVal นอกจากนี้ เมื่อมีการประกาศฟังก์ชันหรือย่อย จะมีการกำหนดสถานะสาธารณะ ส่วนตัว หรือเพื่อน ซึ่งกำหนดว่าจะสามารถเข้าถึงได้ภายนอกโมดูลหรือโครงการที่มีการประกาศไว้หรือไม่

  • ตามค่า [ByVal] – วิธีการส่งค่าของอาร์กิวเมนต์ไปยังโพรซีเดอร์โดยส่งสำเนาของค่า แทนที่จะส่งที่อยู่ ด้วยเหตุนี้ ค่าจริงของตัวแปรจึงไม่สามารถเปลี่ยนแปลงได้โดยขั้นตอนที่ส่งผ่าน
  • โดยการอ้างอิง [ByRef] – วิธีการส่งผ่านค่าของอาร์กิวเมนต์ไปยังโพรซีเดอร์โดยส่งที่อยู่ของตัวแปร แทนที่จะส่งสำเนาของค่าของมัน ซึ่งช่วยให้ขั้นตอนการเข้าถึงตัวแปรจริง ด้วยเหตุนี้ ค่าจริงของตัวแปรจึงสามารถเปลี่ยนแปลงได้ตามขั้นตอนที่ส่งผ่าน เว้นแต่จะระบุไว้เป็นอย่างอื่น อาร์กิวเมนต์จะถูกส่งโดยการอ้างอิง
  • สาธารณะ (เป็นทางเลือก) – ระบุว่าขั้นตอนการทำงานสามารถเข้าถึงได้โดยขั้นตอนอื่นๆ ทั้งหมดในโมดูลทั้งหมด หากใช้ในโมดูลที่มีตัวเลือกส่วนตัว กระบวนงานจะไม่พร้อมใช้งานนอกโครงการ
  • ส่วนตัว (ไม่บังคับ) – ระบุว่าโพรซีเดอร์ฟังก์ชันสามารถเข้าถึงได้เฉพาะโพรซีเดอร์อื่นในโมดูลที่มีการประกาศ
  • เพื่อน (เป็นทางเลือก) – ใช้ในโมดูลคลาสเท่านั้น ระบุว่าโพรซีเดอร์ของฟังก์ชันสามารถมองเห็นได้ตลอดทั้งโปรเจ็กต์ แต่จะไม่ปรากฏให้ผู้ควบคุมอินสแตนซ์ของอ็อบเจ็กต์เห็น
 ฟังก์ชั่นฟังก์ชั่น ส่วนตัว1 ( ) 
    ' รหัสบาง
ฟังก์ชั่นสิ้นสุด ที่นี่ 

ฟังก์ชันไม่คืนค่าและต้องถูกเรียกเป็นฟังก์ชันแบบสแตนด์อโลน เช่นFunction1

 ฟังก์ชันฟังก์ชัน ส่วนตัว2 ()  เป็น ฟังก์ชันจำนวนเต็ม
    2 = 5 ฟังก์ชันสิ้นสุด  
 

ฟังก์ชันนี้ส่งคืนผลลัพธ์ (หมายเลข 5) และการเรียกอาจเป็นส่วนหนึ่งของนิพจน์ เช่นx + Function2()

Private  Function  Function3 ( ByVal  intValue  เป็น Integer )  เป็น String 
    Dim  strArray ( 6 )  เป็น String 
    strArray  =  Array ( "M" ,  "T" ,  "W" ,  "T" ,  "F" ,  "S" ,  "S" ) 
    Function3  =  straArray ( intValue ) 
สิ้นสุด ฟังก์ชัน

ฟังก์ชันนี้จะแปลงตัวเลขระหว่าง 0 ถึง 6 ให้เป็นตัวอักษรเริ่มต้นของวันที่ตรงกันในสัปดาห์ นั่นคือ 0 ถึง 'M', 1 ถึง 'T', ..., 6 ถึง 'S' ผลลัพธ์ของการเรียกมันอาจถูกกำหนดให้กับตัวแปร เช่นnum_day = Function3(number).

 ฟังก์ชั่นฟังก์ชั่น ส่วนตัว4 ( ByRef  intValue  เป็น Integer ) 
    intValue  =  intValue  +  1 
End  Function

ฟังก์ชันนี้ไม่คืนค่า แต่แก้ไขตัวแปรที่ส่งที่อยู่เป็นพารามิเตอร์ มันจะเรียกด้วย " Function4(variable_to_increment)"

ตัวอย่าง PL/I

ในPL/Iโพรซีเดอร์ที่เรียกอาจถูกส่งผ่านdescriptorโดยให้ข้อมูลเกี่ยวกับอาร์กิวเมนต์ เช่น ความยาวของสตริงและขอบเขตอาร์เรย์ ซึ่งจะช่วยให้ขั้นตอนเป็นแบบทั่วไปมากขึ้นและไม่จำเป็นต้องให้โปรแกรมเมอร์ส่งข้อมูลดังกล่าว โดยค่าเริ่มต้น PL/I ส่งผ่านอาร์กิวเมนต์โดยการอ้างอิง รูทีนย่อย (เล็กน้อย) เพื่อเปลี่ยนเครื่องหมายของแต่ละองค์ประกอบของอาร์เรย์สองมิติอาจมีลักษณะดังนี้:

  change_sign: ขั้นตอน (อาร์เรย์);
    ประกาศ array(*,*) float;
    อาร์เรย์ = -array;
    สิ้นสุด change_sign;

สามารถเรียกด้วยอาร์เรย์ต่างๆ ได้ดังนี้

  /* ขอบเขตอาร์เรย์แรกตั้งแต่ -5 ถึง +10 และ 3 ถึง 9 */
  ประกาศ array1 (-5:10, 3:9) ลอย;
  /* ขอบเขตอาร์เรย์ที่สองตั้งแต่ 1 ถึง 16 และ 1 ถึง 16 */
  ประกาศ array2 (16,16) ลอย;
  โทร change_sign(array1);
  โทร change_sign(array2);

ตัวอย่าง Python

ในPythonมีการใช้คีย์เวิร์ดdefเพื่อกำหนดฟังก์ชัน คำสั่งที่ประกอบเป็นเนื้อความของฟังก์ชันต้องดำเนินต่อไปในบรรทัดเดียวกันหรือเริ่มต้นในบรรทัดถัดไปและเยื้อง [23]โปรแกรมตัวอย่างต่อไปนี้จะพิมพ์ว่า "สวัสดีชาวโลก!" ตามด้วย "วิกิพีเดีย" ในบรรทัดถัดไป

def  simple_function (): 
    พิมพ์( 'สวัสดีชาวโลก!' ) 
    พิมพ์( 'วิกิพีเดีย' ) 
simple_function ()

ตัวแปรท้องถิ่น การเรียกซ้ำและการกลับเข้ามาใหม่

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

โปรแกรมย่อยอาจมีหมายเลขและลักษณะของไซต์การโทร หากการเรียกซ้ำได้รับการสนับสนุน โปรแกรมย่อยอาจเรียกตัวเองได้ด้วยซ้ำ ทำให้การดำเนินการหยุดชั่วคราวในขณะที่ การดำเนินการที่ ซ้อนกัน อื่น ของโปรแกรมย่อยเดียวกันเกิดขึ้น การเรียกซ้ำเป็นวิธีที่มีประโยชน์ในการทำให้อัลกอริธึมที่ซับซ้อนบางตัวง่ายขึ้นและแยกย่อยปัญหาที่ซับซ้อน โดยทั่วไปแล้วภาษาแบบเรียกซ้ำจะจัดเตรียมสำเนาใหม่ของตัวแปรท้องถิ่นในการเรียกแต่ละครั้ง หากโปรแกรมเมอร์ต้องการให้ค่าของตัวแปรโลคัลยังคงเหมือนเดิมระหว่างการเรียก ก็สามารถประกาศให้คงที่ในบางภาษา หรือใช้ค่าส่วนกลางหรือพื้นที่ทั่วไปได้ นี่คือตัวอย่างของรูทีนย่อยแบบเรียกซ้ำใน C/C++ เพื่อค้นหา ตัวเลข ฟีโบนักชี :

int Fib ( int n ) {   
  ถ้า( n <= 1 ) {    
    กลับn ; 
  }
  ส่งคืนFib ( n - 1 ) + Fib ( n - 2 );       
}

ภาษายุคแรกอย่างFortranไม่สนับสนุนการเรียกซ้ำเนื่องจากตัวแปรได้รับการจัดสรรแบบสแตติก เช่นเดียวกับที่ตั้งสำหรับที่อยู่ผู้ส่ง คอมพิวเตอร์ส่วนใหญ่ก่อนช่วงปลายทศวรรษ 1960 เช่นPDP-8ไม่รองรับการลงทะเบียนสแต็กฮาร์ดแวร์ [ ต้องการการอ้างอิง ]

ภาษาสมัยใหม่หลังALGOLเช่นPL/IและCมักใช้สแต็กซึ่งมักจะได้รับการสนับสนุนโดยชุดคำสั่งคอมพิวเตอร์ที่ทันสมัยที่สุดเพื่อให้บันทึกการเปิดใช้งานใหม่สำหรับการเรียกใช้โปรแกรมย่อยทุกครั้ง ด้วยวิธีนี้ การดำเนินการแบบซ้อนจะมีอิสระในการแก้ไขตัวแปรในเครื่องโดยไม่ต้องคำนึงถึงผลกระทบต่อการดำเนินการที่ระงับอื่นๆ ที่กำลังดำเนินการอยู่ เมื่อมีการสะสมการ เรียกซ้อน โครงสร้าง call stackจะถูกสร้างขึ้น ซึ่งประกอบด้วยเรกคอร์ดการเปิดใช้งานหนึ่งรายการสำหรับแต่ละโปรแกรมย่อยที่ถูกระงับ อันที่จริง โครงสร้างสแต็กนี้มีอยู่ทั่วไป ดังนั้นเร็กคอร์ดการเปิดใช้งานจึงมักเรียกว่า สแต็ เฟรม

บางภาษา เช่นPascal , PL/I และAdaยังสนับสนุนรูทีนย่อยแบบซ้อนซึ่งเป็นรูทีนย่อยที่เรียกได้ภายในขอบเขตของรูทีนย่อยภายนอก (พาเรนต์) เท่านั้น รูทีนย่อย Inner มีการเข้าถึงตัวแปรโลคัลของรูทีนย่อยภายนอกที่เรียกใช้ ซึ่งทำได้โดยการจัดเก็บข้อมูลบริบทเพิ่มเติมไว้ในบันทึกการเปิดใช้งาน ซึ่งเรียกอีกอย่างว่า display

หากโปรแกรมย่อยสามารถดำเนินการได้อย่างถูกต้อง แม้ว่าจะมีการดำเนินการอื่นของโปรแกรมย่อยเดียวกันอยู่แล้ว โปรแกรมย่อยนั้นจะเรียกว่าreentrant โปรแกรมย่อยแบบเรียกซ้ำจะต้องกลับเข้ามาใหม่ โปรแกรมย่อย Reentrant ยังมีประโยชน์ใน สถานการณ์ แบบมัลติเธรดเนื่องจากหลายเธรดสามารถเรียกโปรแกรมย่อยเดียวกันโดยไม่ต้องกลัวว่าจะรบกวนซึ่งกันและกัน ใน ระบบประมวล ผลธุรกรรมIBM CICS quasi-reentrantมีข้อกำหนดที่จำกัดน้อยกว่าเล็กน้อย แต่คล้ายคลึงกันสำหรับโปรแกรมแอปพลิเคชันที่แชร์โดยเธรดจำนวนมาก

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

โอเวอร์โหลด

ในภาษาที่มีการพิมพ์สูง บางครั้งควรมีฟังก์ชันหลายอย่างที่มีชื่อเดียวกัน แต่ใช้งานกับข้อมูลประเภทต่างๆ หรือมีโปรไฟล์พารามิเตอร์ต่างกัน ตัวอย่างเช่น ฟังก์ชันสแควร์รูทอาจถูกกำหนดให้ทำงานกับจำนวนจริง ค่าที่ซับซ้อน หรือเมทริกซ์ อัลกอริธึมที่ใช้ในแต่ละกรณีไม่เหมือนกัน และผลลัพธ์ที่ได้อาจแตกต่างกัน ด้วยการเขียนฟังก์ชันแยกกันสามฟังก์ชันในชื่อเดียวกัน โปรแกรมเมอร์จึงไม่ต้องจำชื่อที่แตกต่างกันสำหรับข้อมูลแต่ละประเภท นอกจากนี้ หากสามารถกำหนดประเภทย่อยสำหรับจำนวนจริง เพื่อแยกจำนวนจริงที่เป็นค่าบวกและค่าลบ ฟังก์ชันสองฟังก์ชันสามารถเขียนได้สำหรับจำนวนจริง ฟังก์ชันหนึ่งจะคืนค่าจำนวนจริงเมื่อพารามิเตอร์เป็นค่าบวก และอีกฟังก์ชันหนึ่งเพื่อคืนค่าจำนวนจริงเมื่อพารามิเตอร์เป็นค่าบวก เชิงลบ.

ในการเขียนโปรแกรมเชิงวัตถุเมื่อชุดของฟังก์ชันที่มีชื่อเดียวกันสามารถรับโปรไฟล์พารามิเตอร์หรือพารามิเตอร์ประเภทต่างๆ ที่แตกต่างกันได้ แต่ละฟังก์ชันจะถือว่าโอเวอร์ โหลด

นี่คือตัวอย่างของการโอเวอร์โหลดรูทีนย่อยในC++ :

#include <iostream> 

double Area ( double h , double w ) { กลับh * w ; }          

double Area ( double r ) { return r * r * 3.14 ; }          

int หลัก() {  
  double สี่เหลี่ยมผืนผ้า_area = พื้นที่( 3 , 4 );    
  double circle_area = พื้นที่( 5 );   

  std :: cout << "พื้นที่ของรูปสี่เหลี่ยมผืนผ้าคือ" << สี่เหลี่ยมผืนผ้า _area << std :: endl ;      
  std :: cout << "พื้นที่ของวงกลมคือ" << circle_area << std :: endl ;      
}

ในโค้ดนี้ มีสองฟังก์ชันในชื่อเดียวกัน แต่มีพารามิเตอร์ต่างกัน

อีกตัวอย่างหนึ่ง รูทีนย่อยอาจสร้างอ็อบเจ็กต์ที่จะยอมรับทิศทาง และติดตามพาธไปยังจุดเหล่านี้บนหน้าจอ มีพารามิเตอร์มากมายที่สามารถส่งผ่านไปยังคอนสตรัคเตอร์ได้ (สีของการติดตาม, พิกัด x และ y เริ่มต้น, ความเร็วของการติดตาม) หากโปรแกรมเมอร์ต้องการให้ Constructor ยอมรับเฉพาะพารามิเตอร์สี เขาก็สามารถเรียก Constructor อื่นที่ยอมรับเฉพาะสี ซึ่งจะเรียก Constructor ด้วยพารามิเตอร์ทั้งหมดที่ส่งผ่านชุดค่าเริ่มต้นสำหรับพารามิเตอร์อื่นๆ ทั้งหมด ( โดยทั่วไปแล้ว X และ Y จะอยู่ที่กึ่งกลางหน้าจอหรือวางไว้ที่จุดเริ่มต้น และความเร็วจะถูกตั้งค่าเป็นค่าอื่นที่ผู้เขียนโค้ดเลือก)

PL/I มีGENERICแอตทริบิวต์ในการกำหนดชื่อทั่วไปสำหรับชุดของการอ้างอิงรายการที่ถูกเรียกด้วยอาร์กิวเมนต์ประเภทต่างๆ ตัวอย่าง:

  ประกาศ gen_name GENERIC(
                      ชื่อ เมื่อไร (ไบนารีคงที่)
                      เปลวไฟเมื่อ (FLOAT)
                      ชื่อพาธ อย่างอื่น
                           );

อาจมีการระบุคำจำกัดความอาร์กิวเมนต์หลายรายการสำหรับแต่ละรายการ การเรียก "gen_name" จะส่งผลให้มีการเรียก "name" เมื่ออาร์กิวเมนต์เป็น FIXED BINARY, "flame" เมื่อ FLOAT" ฯลฯ หากอาร์กิวเมนต์ตรงกับตัวเลือก "pathname" จะไม่ถูกเรียก

ปิด ทำการ

การปิดเป็นโปรแกรมย่อยร่วมกับค่าของตัวแปรบางตัวที่จับได้จากสภาพแวดล้อมที่สร้างขึ้น การปิดบัญชีเป็นคุณลักษณะเด่นของภาษาการเขียนโปรแกรม Lisp ซึ่งแนะนำโดยJohn McCarthy การปิดสามารถใช้เป็นกลไกสำหรับผลข้างเคียงทั้งนี้ขึ้นอยู่กับการนำไปใช้

อนุสัญญา

มีการพัฒนาอนุสัญญาจำนวนมากสำหรับการเข้ารหัสของรูทีนย่อย เกี่ยวกับการตั้งชื่อของพวกเขา นักพัฒนาหลายคนได้นำแนวทางที่ชื่อของรูทีนย่อยควรเป็นกริยาเมื่อทำงานบางอย่าง และคำคุณศัพท์เมื่อทำการสอบถาม และคำนามเมื่อใช้แทนตัวแปร

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

ผู้เสนอโปรแกรมโมดูลาร์ (modularizing code) สนับสนุนว่าแต่ละรูทีนย่อยควรมีการพึ่งพาโค้ดอื่นๆ น้อยที่สุด ตัวอย่างเช่น การใช้ตัวแปรโกลบอลโดยทั่วไปถือว่าไม่ฉลาดโดยผู้สนับสนุนสำหรับเปอร์สเปคทีฟนี้ เพราะมันจะเพิ่มการมีเพศสัมพันธ์ที่แน่นแฟ้นระหว่างรูทีนย่อยและตัวแปรโกลบอลเหล่านี้ หากไม่จำเป็นต้องใช้คัปปลิ้ง คำแนะนำของพวกเขาคือปรับโครงสร้างรูทีนย่อยใหม่เพื่อยอมรับพารามิเตอร์ที่ ส่งผ่าน แทน อย่างไรก็ตาม การเพิ่มจำนวนพารามิเตอร์ที่ส่งผ่านไปยังรูทีนย่อยอาจส่งผลต่อความสามารถในการอ่านโค้ด

รหัสคืน

นอกจาก เอฟเฟกต์ หลักหรือปกติแล้ว รูทีนย่อยอาจจำเป็นต้องแจ้งโปรแกรมที่เรียกเกี่ยวกับ เงื่อนไข พิเศษที่อาจเกิดขึ้นระหว่างการดำเนินการ ในบางภาษาและมาตรฐานการเขียนโปรแกรม การดำเนินการนี้มักจะทำผ่านโค้ดส่งคืนซึ่งเป็นค่าจำนวนเต็มที่วางโดยรูทีนย่อยในตำแหน่งมาตรฐานบางตำแหน่ง ซึ่งเข้ารหัสเงื่อนไขปกติและข้อยกเว้น

ในIBM System/360ซึ่งคาดว่าโค้ดส่งคืนจากรูทีนย่อย ค่าส่งคืนมักจะได้รับการออกแบบให้เป็นผลคูณของ 4 เพื่อให้สามารถใช้เป็น ดัชนี ตารางสาขา โดยตรง ไปยังตารางสาขาซึ่งมักจะตั้งอยู่หลัง เรียกใช้คำสั่งเพื่อหลีกเลี่ยงการทดสอบแบบมีเงื่อนไขเพิ่มเติม และปรับปรุงประสิทธิภาพต่อไป ในภาษาแอสเซมบลีSystem/360 หนึ่งจะเขียนเช่น:

           BAL 14, SUBRTN01 ไปที่รูทีนย่อย จัดเก็บที่อยู่ผู้ส่งใน R14
           B TABLE(15) ใช้ค่าที่ส่งคืนใน reg 15 เพื่อสร้างดัชนีตารางสาขา
* แยกสาขาไปยังสาขาที่เหมาะสม
ตาราง B รหัสส่งคืนตกลง =00 GOOD }
           รหัสส่งคืน BAD =04 อินพุตไม่ถูกต้อง } ตารางสาขา
           B ERROR รหัสส่งคืน =08 เงื่อนไขที่ไม่คาดคิด }

การเพิ่มประสิทธิภาพการเรียกรูทีนย่อย

มีค่าใช้จ่าย รันไทม์ที่สำคัญ ในการเรียกรูทีนย่อย รวมถึงการส่งต่ออาร์กิวเมนต์ การแตกแขนงไปยังโปรแกรมย่อย และการแตกแขนงกลับไปยังผู้เรียก โอเวอร์เฮดมักจะรวมถึงการบันทึกและกู้คืนรีจิสเตอร์ตัวประมวลผลบางตัว การจัดสรรและเรียกคืนที่เก็บข้อมูลเฟรมการโทร ฯลฯ[ ตัวอย่างที่จำเป็น ]ในบางภาษา การเรียกรูทีนย่อยแต่ละครั้งยังหมายถึงการทดสอบอัตโนมัติของโค้ดส่งคืนของรูทีนย่อย หรือการจัดการข้อยกเว้นที่อาจเพิ่มขึ้น . แหล่งที่มาของค่าใช้จ่ายที่สำคัญในภาษาเชิงวัตถุคือการใช้ไดนามิก อย่างเข้มข้น สำหรับการเรียกใช้เมธอด

มีการเพิ่มประสิทธิภาพการเรียกโพรซีเดอร์ที่ดูเหมือนชัดเจนบางอย่างที่ไม่สามารถใช้ได้หากโพรซีเดอร์อาจมีผลข้างเคียง ตัวอย่างเช่น ในนิพจน์ต้องเรียก(f(x)-1)/(f(x)+1)ใช้ฟังก์ชัน สองครั้ง เนื่องจากการเรียกสองครั้งอาจส่งคืนผลลัพธ์ที่แตกต่างกัน fนอกจากนี้xจะต้องดึงค่าของอีกครั้งก่อนการโทรครั้งที่สอง เนื่องจากการโทรครั้งแรกอาจมีการเปลี่ยนแปลง การพิจารณาว่าโปรแกรมย่อยอาจมีผลข้างเคียงหรือไม่นั้นยากมาก (แน่นอน ตัดสินไม่ได้โดยอาศัยทฤษฎีบทของข้าว ) ดังนั้นในขณะที่การปรับให้เหมาะสมเหล่านั้นปลอดภัยในภาษาการเขียนโปรแกรมที่ใช้งานได้จริง คอมไพเลอร์ของการเขียนโปรแกรมที่จำเป็นทั่วไปมักจะต้องถือว่าแย่ที่สุด

อินไลน์

เมธอดที่ใช้ในการกำจัดโอเวอร์เฮดนี้คือการขยายแบบอินไลน์หรืออินไลน์ของเนื้อหาของโปรแกรมย่อยที่ไซต์การโทร แต่ละไซต์ สิ่งนี้ไม่เพียงแต่หลีกเลี่ยงโอเวอร์เฮดการโทร แต่ยังช่วยให้คอมไพเลอร์เพิ่มประสิทธิภาพเนื้อหาของโพรซีเดอร์ได้อย่างมีประสิทธิภาพมากขึ้นโดยคำนึงถึงบริบทและข้อโต้แย้งในการเรียกนั้น คอมไพเลอร์สามารถเพิ่มประสิทธิภาพเนื้อหาที่แทรกได้ อย่างไรก็ตาม Inlining มักจะเพิ่มขนาดโค้ด เว้นแต่โปรแกรมจะมีเพียงการเรียกรูทีนย่อยเพียงครั้งเดียว

ดูเพิ่มเติม

อ้างอิง

  1. ^ คณะกรรมการช่วยเหลือการเลือกตั้งของสหรัฐอเมริกา (2007) "คำจำกัดความของคำที่มีความหมายพิเศษ" . แนวทางระบบการลงคะแนนโดยสมัครใจ เก็บถาวรจากต้นฉบับเมื่อ 8 ธันวาคม 2555 . สืบค้นเมื่อ14 มกราคม 2556 .
  2. สุบราตา ทัสคุปต์ (7 มกราคม 2014). เริ่มด้วย Babbage: กำเนิดวิทยาการคอมพิวเตอร์ สำนักพิมพ์มหาวิทยาลัยอ็อกซ์ฟอร์ด. หน้า 155–. ISBN 978-0-19-930943-6.
  3. ^ a b J.W. Mauchly, "Preparation of Problems for EDVAC-type Machines" (1947), in Brian Randell (Ed.), The Origins of Digital Computers, Springer, 1982.
  4. ^ วีลเลอร์ ดีเจ (1952) "การใช้รูทีนย่อยในโปรแกรม" (PDF) . การดำเนินการของการประชุมระดับชาติ ACM ปี 1952 (พิตต์สเบิร์ก) ใน - ACM '52 หน้า 235. ดอย : 10.1145/609784.609816 .
  5. วิลค์ส เอ็มวี; วีลเลอร์ ดีเจ; กิลล์, เอส. (1951). การเตรียมโปรแกรมสำหรับคอมพิวเตอร์ดิจิตอลอิเล็กทรอนิกส์ แอดดิสัน-เวสลีย์.
  6. ^ ไดนิธ, จอห์น (2004). ""เปิดรูทีนย่อย" พจนานุกรมคอมพิวเตอร์" . Encyclopedia.com . สืบค้นเมื่อ14 มกราคม 2556 .
  7. ทัวริง, อลัน เอ็ม. (1945), รายงานโดย ดร. น. ทัวริงเกี่ยวกับข้อเสนอสำหรับการพัฒนาเครื่องคอมพิวเตอร์อัตโนมัติ (ACE): ส่งไปยังคณะกรรมการบริหารของ NPL ในเดือนกุมภาพันธ์ พ.ศ. 2489พิมพ์ซ้ำในCopeland, BJ , ed. (2005). เครื่องคำนวณ อัตโนมัติของ Alan Turing อ็อกซ์ฟอร์ด: สำนักพิมพ์มหาวิทยาลัยอ็อกซ์ฟอร์ด หน้า 383. ISBN 0-19-856593-3.
  8. ^ โดนัลด์ อี. คนุธ (1997). ศิลปะแห่งการเขียนโปรแกรมคอมพิวเตอร์ เล่มที่ 1: อัลกอริธึมพื้นฐาน แอดดิสัน-เวสลีย์. ISBN 0-201-89683-4.
  9. ^ อ.-เจ. ดาห์ล; อีดับเบิลยู ไดค์สตรา; รถยนต์ Hoare (1972) การเขียนโปรแกรม แบบมีโครงสร้าง สื่อวิชาการ. ISBN 0-12-200550-3.
  10. วิลสัน, เลสลี่ บี. (2001). ภาษาโปรแกรมเปรียบเทียบ ฉบับที่ 3 แอดดิสัน-เวสลีย์. หน้า 140. ISBN 0-201-71012-9.
  11. สตรูสทรัป, บียาร์น (2013). ภาษาการเขียนโปรแกรม C++ รุ่นที่สี่ แอดดิสัน-เวสลีย์. หน้า 307. ISBN 978-0-321-56384-2.
  12. ทัวริง, อลัน แมธิสัน (19 มีนาคม ค.ศ. 1946) [1945], ข้อเสนอเพื่อการพัฒนาในแผนกคณิตศาสตร์ของกลไกคอมพิวเตอร์อัตโนมัติ (ACE)(NB. นำเสนอเมื่อ 1946-03-19 ต่อหน้าคณะกรรมการบริหารของ National Physical Laboratory (บริเตนใหญ่).)
  13. ^ ช่างไม้ ไบรอัน เอ็ดเวิร์ด ; Doran, Robert William (1 มกราคม 2520) [ตุลาคม 2518] "เครื่องทัวริงอีกเครื่อง" . วารสารคอมพิวเตอร์ . 20 (3): 269–279. ดอย : 10.1093/comjnl/20.3.269 .(11 หน้า)
  14. อรรถเป็น ไอแซคสัน, วอลเตอร์ (18 กันยายน 2014). "วอลเตอร์ ไอแซคสัน กับสตรีแห่ง ENIAC" . ฟอร์จูน . เก็บถาวรจากต้นฉบับเมื่อ 12 ธันวาคม 2018 . สืบค้นเมื่อ14 ธันวาคม 2018 .
  15. การวางแผนและการเข้ารหัสของปัญหาสำหรับเครื่องมือคอมพิวเตอร์อิเล็กทรอนิกส์, Pt 2, Vol. 3 https://library.ias.edu/files/pdfs/ecp/planningcodingof0103inst.pdf (ดูหน้า 163 ของ pdf สำหรับหน้าที่เกี่ยวข้อง)
  16. Guy Lewis Steele Jr. AI Memo 443 'การหักล้างตำนาน "การเรียกขั้นตอนราคาแพง"; หรือการดำเนินการเรียกขั้นตอนถือว่าเป็นอันตราย" . ส่วน "ค. เหตุใดการเรียกกระบวนการจึงมีชื่อเสียงไม่ดี"
  17. ^ แฟรงค์ โธมัส เอส. (1983) บทนำสู่ PDP-11 และภาษาแอสเซมบลี ชุดซอฟต์แวร์ Prentice-Hall ศิษย์ฮอลล์. หน้า 195. ISBN  9780134917047. สืบค้นเมื่อ6 กรกฎาคม 2559 . เราสามารถจัดหาสำเนาซอร์สโค้ดให้กับพนักงานประกอบของเราสำหรับรูทีนย่อยที่มีประโยชน์ทั้งหมดของเรา จากนั้นเมื่อนำเสนอโปรแกรมหลักสำหรับแอสเซมบลีแก่เขา บอกเขาว่ารูทีนย่อยใดจะถูกเรียกในเมนไลน์ [... ]
  18. ^ "ศูนย์ข้อมูล ARM" . Infocenter.arm.com . สืบค้นเมื่อ29 กันยายน 2556 .
  19. ^ "การใช้สแต็ กx64" ไมโครซอฟต์ดอคส์ . ไมโครซอฟต์. สืบค้นเมื่อ5 สิงหาคม 2019 .
  20. ^ "ประเภทฟังก์ชัน" . Msdn.microsoft.com . สืบค้นเมื่อ29 กันยายน 2556 .
  21. ^ "ฟังก์ชันฟรีหมายถึงอะไร" .
  22. ^ "Microsoft Small Basic" . www.smallbasic.com .
  23. ^ "4. More Control Flow Tools — เอกสาร Python 3.9.7 "