อ่าน 7 นาที
ฟังก์ชันซ้อน
ใน การเขียนโปรแกรมคอมพิวเตอร์ ฟังก์ชัน ซ้อน (หรือ ขั้นตอนย่อย หรือ รูทีนย่อย ) คือ ฟังก์ชัน ที่มีชื่อ ซึ่งถูกกำหนดไว้ภายในบล็อกอื่น (บล็อกที่ครอบคลุม) และมี ขอบเขตการ...
ฟังก์ชันซ้อน
ในการเขียนโปรแกรมคอมพิวเตอร์ฟังก์ชันซ้อน (หรือขั้นตอนย่อยหรือรูทีนย่อย ) คือฟังก์ชันที่มีชื่อ ซึ่งถูกกำหนดไว้ภายในบล็อกอื่น (บล็อกที่ครอบคลุม) และมีขอบเขตการใช้งานเฉพาะภายในบล็อกที่ครอบคลุมนั้น – หมายความว่าสามารถเรียกใช้ได้เฉพาะโดยใช้ชื่อภายในตัวบล็อกที่ครอบคลุมเท่านั้น และสามารถใช้ตัวระบุที่ประกาศไว้ในบล็อก ภายนอก รวมถึงฟังก์ชันภายนอกได้ บล็อกที่ครอบคลุมนั้นโดยทั่วไปแล้วจะเป็นฟังก์ชันอื่น แต่ก็ไม่เสมอไป
การรองรับฟังก์ชันซ้อนกันใน ภาษาโปรแกรมนั้นแตกต่างกันไป สำหรับ ภาษา โปรแกรมเชิงโครงสร้างนั้น ภาษาที่ล้าสมัยบางภาษา เช่นALGOL , Simula 67และPascal รวมถึง JavaScriptที่ใช้กันทั่วไปนั้น รองรับฟังก์ชันนี้ โดยทั่วไปแล้ว ภาษาโปรแกรม แบบไดนามิกและเชิงฟังก์ชันจะรองรับฟังก์ชันนี้ อย่างไรก็ตาม ภาษาที่ใช้กันทั่วไปบางภาษา เช่น CและC++มาตรฐาน กลับไม่รองรับฟังก์ชันนี้
เทคโนโลยีการเขียนโปรแกรมอื่นๆ ก็ให้ประโยชน์ที่คล้ายคลึงกัน ตัวอย่างเช่นฟังก์ชันแลมบ์ดา (lambda function ) ก็อนุญาตให้กำหนดฟังก์ชันภายในฟังก์ชัน (รวมถึงที่อื่นๆ) และอนุญาตให้มีการซ่อนและการห่อหุ้มข้อมูลที่คล้ายคลึงกัน ที่สำคัญคือ ฟังก์ชันแลมบ์ดาไม่มีชื่อ (เป็นฟังก์ชันนิรนาม) ดังนั้นจึงไม่สามารถเรียกใช้ด้วยชื่อได้ และไม่มีลักษณะการมองเห็น (visibility aspect)
คุณลักษณะ
ขอบเขตของฟังก์ชันซ้อนกันคือบล็อกที่บรรจุฟังก์ชันนั้นอยู่ ไม่ว่าจะเป็นบล็อกฟังก์ชันหรือบล็อกภายในตัวฟังก์ชันก็ตาม ฟังก์ชันนั้นจะไม่สามารถมองเห็นได้ (ไม่สามารถเรียกใช้โดยชื่อได้) จากภายนอกบล็อกที่บรรจุ อยู่
ฟังก์ชันซ้อนกันสามารถใช้ตัวระบุ (เช่น ชื่อของฟังก์ชัน ตัวแปร ชนิดข้อมูล คลาส) ที่ประกาศไว้ในบล็อกที่ครอบคลุมอยู่ได้ ยกเว้นในกรณีที่ตัวระบุเหล่านั้นถูกบดบังด้วยการประกาศภายในที่มีชื่อเดียวกัน
ฟังก์ชันซ้อนสามารถประกาศภายในฟังก์ชันซ้อนอีกทีได้แบบเรียกซ้ำ เพื่อสร้างโครงสร้างที่ซ้อนกันอย่างลึกซึ้ง ฟังก์ชันที่ซ้อนกันอย่างลึกซึ้งสามารถเข้าถึงตัวระบุที่ประกาศไว้ในบล็อกที่ครอบคลุมทั้งหมด รวมถึงฟังก์ชันที่ครอบคลุมเหล่านั้นด้วย
ในบางสถานการณ์ ฟังก์ชันซ้อนกันอาจนำไปสู่การสร้าง Closure ได้หากฟังก์ชันซ้อนสามารถหลุดพ้นจากฟังก์ชันที่ครอบคลุมได้ เช่น หากฟังก์ชันเป็นFirst-Class Objectและฟังก์ชันซ้อนถูกส่งต่อไปยังฟังก์ชันอื่นหรือถูกส่งคืนจากฟังก์ชันที่ครอบคลุม Closure จะถูกสร้างขึ้น และการเรียกใช้ฟังก์ชันนี้สามารถเข้าถึงสภาพแวดล้อมของฟังก์ชันดั้งเดิมได้ เฟรมของฟังก์ชันที่ครอบคลุมโดยตรงจะต้องคงอยู่จนกว่า Closure ที่อ้างอิงถึงตัวสุดท้ายจะตายลง และตัวแปรอัตโนมัติที่ไม่ใช่ตัวแปรโลคอล ที่อ้างอิงใน Closure จึงไม่สามารถจัดสรรหน่วยความจำบน Stack ได้ในภาษาโปรแกรมที่อนุญาตให้ Closure คงอยู่เกินกว่าอายุของบล็อกที่ครอบคลุม นี่คือปัญหา funargและเป็นเหตุผลสำคัญที่ทำให้ฟังก์ชันซ้อนกันไม่ได้ถูกนำไปใช้ในบางภาษาโปรแกรมที่ง่ายกว่า เนื่องจากมันทำให้การสร้างและการวิเคราะห์โค้ดซับซ้อนขึ้นอย่างมาก โดยเฉพาะอย่างยิ่งเมื่อฟังก์ชันซ้อนกันในหลายระดับและใช้ส่วนต่างๆ ของสภาพแวดล้อมร่วมกัน
ค่า
เทคโนโลยีฟังก์ชันซ้อนช่วยให้นักเขียนโปรแกรมสามารถเขียนโค้ดที่มีคุณสมบัติที่เป็นประโยชน์ เช่นการซ่อนข้อมูลการห่อหุ้มและการแบ่งส่วนนักเขียนโปรแกรมสามารถแบ่งงานออกเป็นงานย่อย ซึ่งมีความหมายเฉพาะภายในบริบทของงานหลักเท่านั้น โดยที่ฟังก์ชันของงานย่อยจะถูกซ่อนไว้จากผู้เรียกใช้ที่ไม่ได้รับการออกแบบมาเพื่อใช้งานฟังก์ชันเหล่านั้น
ขอบเขตของบล็อกช่วยให้ฟังก์ชันสามารถแบ่งปันสถานะของบล็อกที่ครอบคลุม (รวมถึงฟังก์ชันที่ครอบคลุม) โดยไม่ต้องส่งพารามิเตอร์หรือใช้ตัวแปรส่วนกลาง[ 1 ]
การใช้งาน
ผู้ช่วย
โดยทั่วไป ฟังก์ชันซ้อนกันมักทำหน้าที่เป็นฟังก์ชันช่วยหรือฟังก์ชันเรียกซ้ำ
การควบคุมการไหล
ฟังก์ชันซ้อนกันสามารถใช้สำหรับการควบคุมการไหลของโปรแกรม ที่ไม่เป็นโครงสร้างได้ โดยใช้คำสั่ง return สำหรับการควบคุมการไหลของโปรแกรมที่ไม่เป็นโครงสร้างทั่วไป วิธีนี้สามารถใช้สำหรับการควบคุมที่ละเอียดกว่าที่ทำได้ด้วยคุณสมบัติในตัวอื่นๆ ของภาษา ตัวอย่างเช่น สามารถอนุญาตให้ยุติลูป for ก่อนกำหนดได้หากbreakไม่มีเงื่อนไข หรือยุติลูป for ซ้อนbreak กันก่อนกำหนดได้หาก ไม่มีเงื่อนไข หลายระดับ หรือข้อยกเว้น
ฟังก์ชันลำดับสูงกว่า
ในบางภาษา สามารถสร้างฟังก์ชันซ้อนที่เข้าถึงชุดพารามิเตอร์จากฟังก์ชันภายนอกได้ ซึ่งก็คือclosureและให้ฟังก์ชันนั้นเป็นค่าส่งคืนของฟังก์ชันภายนอก ดังนั้นจึงสามารถส่งคืนฟังก์ชันที่ตั้งค่าไว้เพื่อทำงานบางอย่างโดยมีพารามิเตอร์เพิ่มเติมเพียงเล็กน้อยหรือไม่ให้เลย ซึ่งสามารถเพิ่มประสิทธิภาพได้อย่างมาก[ 2 ]
ตัวอย่าง
ตัวอย่างง่ายๆ
ตัวอย่างง่ายๆ ในภาษาปาสคาล:
ฟังก์ชันE ( x : real ) : real ; ฟังก์ชันF ( y : real ) : real ; begin F := x + y end ; begin E := F ( 3 ) + F ( 4 ) end ;ฟังก์ชันนี้Fซ้อนอยู่ภายในE. โปรดสังเกตว่าEพารามิเตอร์ของxก็สามารถมองเห็นได้ในF(เช่นเดียวFกับที่เป็นส่วนหนึ่งของE) ในขณะที่xและyนั้นมองไม่เห็นจากภายนอกEและFตามลำดับ
ในทำนองเดียวกัน ในStandard ML :
fun e ( x : real ) = let fun f y = x + y in f 3 + f 4 end ;ในภาษาฮัสเคลล์ :
e :: Float -> Float e x = f 3 + f 4 โดยที่f y = x + ye: ขั้นตอน(x) ส่งคืนค่า(float); ประกาศตัวแปร x เป็น float; f: ขั้นตอน(y) ส่งคืนค่า(float); ประกาศตัวแปร y เป็น float; ส่งคืน x + y จบ; คืนค่า f(3.0) + f(4.0); จบ;
ในภาษา Python :
def e ( x : float ) -> float : def f ( y : float ) -> float : return x + y return f ( 3.0 ) + f ( 4.0 )ในGNU C [ 3 ] – ซึ่งขยาย C มาตรฐานด้วยฟังก์ชันซ้อนกัน:
float e ( float x ) { float f ( float y ) { return x + y ; } return f ( 3.0f ) + f ( 4.0f ); }เรียงลำดับเร็ว
ต่อไปนี้เป็นการใช้งานquicksort : [ 4 ]
void sort ( int * items , int size ) { void quickSort ( int first , int last ) { void swap ( int p , int q ) { int tmp = items [ p ]; items [ p ] = items [ q ]; items [ q ] = tmp ; } int partition () { int pivot = items [ first ]; int index = first ; swap ( index , last ); for ( int i = first ; i < last ; i ++ ) { if ( items [ i ] < pivot ) { swap ( index ++ , i ); } } swap ( index , last ); return index ; }ถ้า( first < last ) { int pivotIndex = partition (); quickSort ( first , pivotIndex - 1 ); quickSort ( pivotIndex + 1 , last ); } } quickSort ( 0 , size - 1 ); }ต่อไปนี้คือการใช้งาน อัลก อริทึมการเรียงลำดับแบบเร็ว (quicksort) ที่ใช้การแบ่งพาร์ติชันของ Hoareโดยใช้ไวยากรณ์นิพจน์แลมบ์ดาในC++11 ซึ่งเป็นเทคโนโลยีทางเลือกที่ช่วยให้สามารถซ่อนฟังก์ชันไว้ภายในฟังก์ชันได้:
// Iter แทนเทมเพลต ตัววนซ้ำแบบเข้าถึงแบบสุ่ม < typename Iter > void sort ( Iter begin , Iter end ) { auto partition = [ & ]() -> Iter { // รูปแบบการแบ่งพาร์ติชันของ Hoare Iter & pivot = * begin ; Iter forwardCursor = begin ; Iter backwardCursor = end - 1 ; Iter partitionPositionFound = false ;auto locatePartitionPosition = [ & ]() -> void { while ( * forwardCursor < pivot ) { ++ forwardCursor ; } while ( pivot < * backwardCursor ) { -- backwardCursor ; } if ( forwardCursor >= backwardCursor ) { partitionPositionFound = true ; } else swap ( * forwardCursor , * backwardCursor ); } };// ฟังก์ชันช่วยแบบง่ายๆauto moveOnAndTryAgain = [ & ]() -> void { ++ forwardCursor ; -- backwardCursor ; };// โครงร่างโดยย่อของกระบวนการแบ่งพาร์ติชันจริงในขณะที่( true ) { locatePartitionPosition (); ถ้า( partitionPositionFound ) ให้คืนค่าbackwardCursor + 1 ; มิฉะนั้น{ moveOnAndTryAgain (); } } };// โครงร่างโดยย่อของอัลกอริธึม quicksort ถ้า( begin < end - 1 ) { Iter partitionPosition = partition (); sort ( begin , partitionPosition ); sort ( partitionPosition , end ); } }ภาษา
ภาษาโปรแกรมที่โดดเด่นซึ่งรองรับฟังก์ชันซ้อนกัน ได้แก่:
- ภาษาโปรแกรมที่ใช้ ALGOLเป็นพื้นฐาน เช่นALGOL 68 , Simula , Pascal , Modula-2 , Modula-3 , Oberon , PL/IและAda
- ภาษา Lispเวอร์ชันสมัยใหม่(ที่มีขอบเขตทางคำศัพท์) เช่นSchemeและCommon Lisp
- ECMAScript ( JavaScriptและActionScript )
- ลูกดอก[ 5 ]
- Kotlin (ฟังก์ชันท้องถิ่น[ 6 ] )
- สนิม
- Scala (ฟังก์ชันซ้อนกัน[ 7 ] )
- มีการรองรับในระดับต่างๆ กันในภาษาสคริปต์ เช่นRuby , Python , Lua , PHPและPerl
- GCCรองรับฟังก์ชันซ้อนกันในภาษา C ซึ่งเป็นส่วนขยายของภาษา[ 8 ]
- C#เริ่มตั้งแต่ C# 7.0 เป็นต้นไป
- ภาษาDเป็นภาษาที่เกี่ยวข้องกับภาษา C โดยมีฟังก์ชันซ้อนกันอยู่
- ภาษาฟอร์ทรานตั้งแต่ฟอร์ทราน-90 เป็นต้นไป รองรับซับรูทีนและฟังก์ชันที่ ซ้อนกัน เพียงระดับเดียว ( CONTAINed )
- MATLAB (รองรับอย่างเต็มรูปแบบ)
- ภาษาวูลฟรัม
- Golang (การปิดฟังก์ชัน[ 9 ] )
ภาษาเชิงฟังก์ชัน
ใน ภาษา การเขียนโปรแกรมเชิง ฟังก์ชันส่วนใหญ่ เช่น Scheme ฟังก์ชันซ้อนกันเป็นวิธีทั่วไปในการใช้งานอัลกอริทึม ที่มีลูปอยู่ภายใน โดยจะสร้างฟังก์ชันภายใน แบบเรียกซ้ำอย่างง่าย ( tail recursive ) ซึ่งทำหน้าที่เป็นลูปหลักของอัลกอริทึม ในขณะที่ฟังก์ชันภายนอกจะทำการเริ่มต้นการทำงานซึ่งจำเป็นต้องทำเพียงครั้งเดียวเท่านั้น ในกรณีที่ซับซ้อนกว่านั้น อาจมีการสร้างฟังก์ชันแบบเรียกซ้ำซึ่งกันและกันหลายฟังก์ชันเป็นฟังก์ชันภายใน
ทางเลือกอื่นๆ
สามารถใช้เทคนิคทางเลือกต่างๆ เพื่อให้ได้ผลลัพธ์การเขียนโปรแกรมที่คล้ายคลึงกันกับการใช้ฟังก์ชันซ้อนกันได้
ความเป็นโมดูล
อีกทางเลือกหนึ่งที่นิยมใช้คือการใช้ประโยชน์จากเทคโนโลยีการแบ่งส่วนโมดูลของภาษา ฟังก์ชันบางอย่างจะเปิดให้ใช้งานจากภายนอกโมดูล และบางฟังก์ชันจะมองเห็นได้เฉพาะภายในโมดูลเท่านั้น
ในภาษา C สามารถนำไปใช้ได้โดยการประกาศฟังก์ชันและตัวแปรเป็นแบบstaticเพื่อซ่อนจากโค้ดภายนอกไฟล์[ 10 ]วิธีนี้ช่วยให้สามารถซ่อนข้อมูล ห่อหุ้ม และแยกส่วนได้ แต่ในระดับความละเอียด ที่แตกต่าง จากฟังก์ชันที่ซ้อนกัน ความเป็นโมดูลาร์นี้ไม่รองรับการซ้อนกันมากกว่าหนึ่งระดับ
ในภาษาโปรแกรมเชิงวัตถุคลาสโดยทั่วไปจะให้ขอบเขตที่สามารถซ่อนฟังก์ชันและสถานะจากผู้ใช้งานคลาสได้ แต่ยังคงสามารถเข้าถึงได้ภายในคลาสเอง บางภาษาอนุญาตให้มีคลาสซ้อนกันได้
พารามิเตอร์
เพื่อนำการซ่อนข้อมูลมาใช้ ฟังก์ชันสามารถส่งข้อมูลที่ใช้ร่วมกันเป็นพารามิเตอร์ได้ แต่จะทำให้การเรียกฟังก์ชันมีความซับซ้อนมากขึ้น[ 1 ]
ในภาษา C โดยทั่วไปจะใช้วิธีส่งพอยเตอร์ไปยังโครงสร้างที่มีข้อมูลที่ใช้ร่วมกัน[ 10 ]
แลมบ์ดา
ในPHPและภาษาโปรแกรมอื่นๆ แลมบ์ดาเป็นอีกทางเลือกหนึ่ง ฟังก์ชันจะถูกกำหนดในคำสั่งโค้ดแทนที่จะประกาศด้วยไวยากรณ์ฟังก์ชันแบบปกติ ฟังก์ชันนี้ไม่มีชื่อ แต่สามารถเรียกใช้งานได้ผ่านการอ้างอิงฟังก์ชัน ฟังก์ชันดังกล่าวสามารถกำหนดได้ทั้งภายในฟังก์ชันและในขอบเขตอื่นๆ หากต้องการใช้ตัวแปรโลคอลในฟังก์ชันนิรนาม ให้ใช้โคลเชอร์
ทางเลือกตามภาษา
ภาษาโปรแกรมต่อไปนี้มีคุณสมบัติที่คล้ายคลึงกับฟังก์ชันซ้อนกัน:
- C++ – คลาสช่วยให้สามารถซ่อนและห่อหุ้มข้อมูลได้ในลักษณะเดียวกัน การกำหนดคลาสภายในคลาสจะให้โครงสร้างที่คล้ายกัน (ดูอ็อบเจ็กต์ฟังก์ชันใน C++ )
- ภาษา Eiffelห้ามการซ้อนฟังก์ชันอย่างชัดเจนเพื่อให้ภาษามีความเรียบง่าย แต่ก็อนุญาตให้ใช้ตัวแปรพิเศษ " Result"เพื่อระบุผลลัพธ์ของฟังก์ชัน (ที่ส่งค่ากลับ)
- C#และVisual Basic – ผ่านนิพจน์แลมบ์ดา
- Java – ตั้งแต่ Java 8 เป็นต้นไป ผ่านทางนิพจน์แลมบ์ดา[ 12 ]และในเวอร์ชันเก่ากว่านั้น ผ่านทางคลาสนิรนามที่มีเมธอดเดียว
การดำเนินการ
การใช้งานฟังก์ชันซ้อนกันอาจซับซ้อนกว่าที่เห็น เนื่องจากการอ้างอิงถึงฟังก์ชันซ้อนกันที่อ้างอิงถึงตัวแปรที่ไม่ใช่ตัวแปรโลคอลจะสร้างโคลเชอร์ขึ้นมา ด้วยเหตุนี้ ฟังก์ชันซ้อนกันจึงไม่ได้รับการสนับสนุนในบางภาษา เช่น C, C++ หรือ Java เนื่องจากทำให้คอมไพเลอร์ใช้งานได้ยากขึ้น[ 10 ] [ 13 ]อย่างไรก็ตาม คอมไพเลอร์บางตัวก็รองรับฟังก์ชันซ้อนกันในฐานะส่วนขยายเฉพาะของคอมไพเลอร์ ตัวอย่างที่รู้จักกันดีคือ การใช้งาน C ใน GNU Cซึ่งใช้โค้ดร่วมกับคอมไพเลอร์สำหรับภาษาต่างๆ เช่น Pascal, Ada และ Modula
การเข้าถึงวัตถุที่ไม่ใช่ในพื้นที่
มีหลายวิธีในการใช้งานขั้นตอนย่อยแบบซ้อนกันในภาษาที่มีขอบเขตตามคำศัพท์ แต่โดยทั่วไปแล้ววิธีแบบดั้งเดิมมีดังนี้:
- วัตถุที่ไม่ใช่โลคอลใดๆX สามารถเข้าถึงได้ผ่านลิงก์การเข้าถึงในเฟรมการทำงานบนสแต็กของเครื่อง ผู้เรียก C ช่วยเหลือขั้นตอนที่ถูกเรียก P โดยการส่ง ลิงก์ โดยตรงไปยัง การทำงาน ล่าสุดของการห่อหุ้มคำศัพท์ทันทีของ P (P) ก่อนการเรียกนั้นเอง จากนั้น P สามารถค้นหาการทำงานที่ถูกต้องสำหรับ X ที่กำหนดได้อย่างรวดเร็วโดยการติดตามลิงก์จำนวนคงที่ (P.depth – X.depth) (โดยปกติจะเป็นจำนวนน้อย)
- ผู้เรียกสร้างลิงก์โดยตรงนี้โดย (ตัวมันเอง) ติดตามลิงก์เก่ากว่า C.depth – P.depth + 1 ลิงก์ ไปจนถึงการเปิดใช้งานล่าสุดของ (P) จากนั้นจึง เชื่อมต่อลิงก์เหล่านี้ ชั่วคราวด้วยลิงก์โดยตรงไปยังการเปิดใช้งานนั้น ลิงก์นี้จะหายไปพร้อมกับ P ในภายหลัง ซึ่งลิงก์เก่ากว่าที่อยู่ด้านล่างอาจกลับมาใช้งานได้อีกครั้ง
- โปรดทราบว่า P สามารถมองเห็นได้ และอาจถูกเรียกโดย C ได้ หาก (P) = C / (C) / ((C)) / เป็นต้น
วิธีการดั้งเดิมนี้เร็วกว่าที่หลายคนคิด แต่โดยทั่วไปแล้วมักถูกปรับปรุงให้เหมาะสมที่สุดในคอมไพเลอร์สมัยใหม่ (โดยใช้การแสดงผลหรือเทคนิคที่คล้ายกัน)
อีกวิธีหนึ่งในการใช้งานฟังก์ชันซ้อนกัน ซึ่งใช้โดยคอมไพเลอร์บางตัว คือการแปลง ("ยก") ฟังก์ชันซ้อนกันให้เป็นฟังก์ชันที่ไม่ซ้อนกัน (โดยที่พารามิเตอร์เพิ่มเติมที่ซ่อนอยู่จะเข้ามาแทนที่ลิงก์การเข้าถึง) โดยใช้กระบวนการที่เรียกว่าการยกแลมบ์ดา (lambda lifting)ในขั้นตอนกลางของการคอมไพล์
ฟังก์ชันในฐานะค่า
เพื่อให้ฟังก์ชันโลคอลที่มีขอบเขตเชิงคำศัพท์ที่ไม่ใช่โล คอล สามารถส่งผ่านเป็นผลลัพธ์ได้ โค้ดรันไทม์ของภาษาจะต้องส่งผ่านสภาพแวดล้อม (ข้อมูล) ที่ฟังก์ชันเห็นภายในฟังก์ชันที่ห่อหุ้มโดยปริยายด้วย เพื่อให้สามารถเข้าถึงได้แม้ว่าการทำงานปัจจุบันของฟังก์ชันที่ห่อหุ้มจะไม่มีอยู่อีกต่อไปแล้ว[ 14 ]ซึ่งหมายความว่าสภาพแวดล้อมจะต้องถูกจัดเก็บไว้ในพื้นที่หน่วยความจำอื่นที่ไม่ใช่ (ส่วนที่ถูกเรียกคืนในภายหลังของ) สแต็กการดำเนินการตามลำดับเวลา ซึ่งในทางกลับกันหมายถึงการจัดสรรหน่วยความจำแบบไดนามิก อย่างอิสระ ภาษาที่ใช้ Algol รุ่นเก่าหลายภาษา (หรือภาษาถิ่นของภาษาเหล่านั้น) จึงไม่อนุญาตให้ส่งผ่านฟังก์ชันโลคอลที่เข้าถึงที่ไม่ใช่โลคอลเป็นค่าส่งคืน หรือไม่อนุญาตให้ส่งผ่านฟังก์ชันเป็นค่าส่งคืนเลย แม้ว่าการส่งผ่านฟังก์ชันดังกล่าวเป็นอาร์กิวเมนต์อาจยังคงเป็นไปได้
สแต็กที่ไม่สามารถดำเนินการได้
การใช้งานฟังก์ชันซ้อนกันของGCC ในภาษา C ทำให้เกิดการสูญเสีย สแต็กที่ไม่สามารถเรียกใช้งานได้ (NX stacks) ดังนั้น ซอฟต์แวร์ที่ออกแบบโดยใช้Secure Development Lifecycleมักจะไม่อนุญาตให้ใช้ฟังก์ชันซ้อนกันของ GCC [ 15 ] โดยค่าเริ่มต้น GCC จะแจ้งเตือนสั้นๆ เมื่อไฟล์ออบเจ็กต์ที่สร้างขึ้นต้องการสแต็กที่สามารถเรียกใช้งานได้ สำหรับคำเตือนที่ชัดเจนเมื่อคอมไพล์โค้ด C ให้ใช้ตัวเลือก ‑Wtrampolines
ปัญหาเกิดขึ้นเนื่องจาก GCC ใช้ "แทรมโพลีน" (โค้ดที่สามารถเรียกใช้งานได้บนสแต็ก) เพื่อกระโดดไปยังฟังก์ชันที่ซ้อนกัน[ 16 ]โครงการ GCC มีวิธีการอื่นที่สามารถจัดเตรียมฟังก์ชันที่ซ้อนกันผ่านตัวอธิบายที่ไม่สามารถเรียกใช้งานได้ อย่างไรก็ตาม ปัจจุบันมีการใช้งานเฉพาะในคอมไพเลอร์ Ada ของ GCC เท่านั้น
ดูเพิ่มเติม
- สแต็กการเรียก
- การปิด (วิทยาการคอมพิวเตอร์)
- การประกอบฟังก์ชัน (วิทยาการคอมพิวเตอร์)
- ชั้นใน
- การซ้อน (ทางด้านคอมพิวเตอร์)
ลิงก์ภายนอก
- คำถามที่พบบ่อยเกี่ยวกับ comp.lang.c: ฟังก์ชันซ้อนกัน
- "6.4 ขั้นตอนย่อยและฟังก์ชันแบบซ้อนกัน"เอกสารประกอบของ FreePascal
สรุปเนื้อหา
ข้อมูลสำคัญจากบทความ
ข้อมูลสำคัญเกี่ยวกับ ฟังก์ชันซ้อน
ใน การเขียนโปรแกรมคอมพิวเตอร์ ฟังก์ชัน ซ้อน (หรือ ขั้นตอนย่อย หรือ รูทีนย่อย ) คือ ฟังก์ชัน ที่มีชื่อ ซึ่งถูกกำหนดไว้ภายในบล็อกอื่น (บล็อกที่ครอบคลุม) และมี ขอบเขตการ...
คุณลักษณะ
ขอบเขตของฟังก์ชันซ้อนกันคือบล็อกที่บรรจุฟังก์ชันนั้นอยู่ ไม่ว่าจะเป็นบล็อกฟังก์ชันหรือบล็อกภายในตัวฟังก์ชันก็ตาม ฟังก์ชันนั้นจะไม่สามารถมองเห็นได้ (ไม่สามารถเรียกใช้โดยชื่อได้) จากภายนอกบล็อกที่บรรจุ อยู่
ค่า
เทคโนโลยีฟังก์ชันซ้อนช่วยให้ นักเขียนโปรแกรม สามารถเขียน โค้ด ที่มีคุณสมบัติที่เป็นประโยชน์ เช่น การซ่อนข้อมูล การ ห่อหุ้ม และ การแบ่งส่วน นักเขียนโปรแกรมสามารถแบ่งงานออกเป็นงานย่อย ซึ่งมีความหมายเฉพาะภายในบริบทของงานหลักเท่านั้น...
ผู้ช่วย
โดยทั่วไป ฟังก์ชันซ้อนกันมักทำหน้าที่เป็น ฟังก์ชันช่วย หรือ ฟังก์ชันเรียก ซ้ำ