อ่าน 15 นาที
กลยุทธ์การประเมินผล
ใน ภาษาโปรแกรม กลยุทธ์ การประเมิน คือชุดของกฎสำหรับการประเมินนิพจน์ [ 1 ] คำ นี้มักใช้เพื่ออ้างถึงแนวคิดที่เฉพาะเจาะจงมากขึ้นของ กลยุทธ์การส่งผ่านพารามิเตอร์ [ 2 ]...
กลยุทธ์การประเมินผล
| กลยุทธ์การประเมินผล |
|---|
ในภาษาโปรแกรมกลยุทธ์การประเมินคือชุดของกฎสำหรับการประเมินนิพจน์[ 1 ] คำนี้มักใช้เพื่ออ้างถึงแนวคิดที่เฉพาะเจาะจงมากขึ้นของกลยุทธ์การส่งผ่านพารามิเตอร์[ 2 ]ซึ่งกำหนดชนิดของค่าที่ส่งผ่านไปยังฟังก์ชันสำหรับแต่ละพารามิเตอร์ ( กลยุทธ์การผูก ) [ 3 ]และว่าจะประเมินพารามิเตอร์ของการเรียกฟังก์ชันหรือไม่ และถ้าใช่ จะประเมินในลำดับใด ( ลำดับการประเมิน ) [ 4 ]แนวคิดของกลยุทธ์การลดรูปนั้นแตกต่างกัน[ 5 ]แม้ว่าผู้เขียนบางคนจะรวมสองคำนี้เข้าด้วยกัน และคำจำกัดความของแต่ละคำก็ยังไม่เป็นที่ยอมรับกันอย่างกว้างขวาง[ 6 ] กลยุทธ์การประเมินของภาษาโปรแกรมเป็นส่วนหนึ่งของ ความหมายระดับสูงบางภาษา เช่นPureScriptมีตัวแปรที่มีกลยุทธ์การประเมินที่แตกต่างกัน บางภาษาเชิงประกาศเช่นDatalogรองรับกลยุทธ์การประเมินหลายแบบ
เช่นเดียวกับในคณิตศาสตร์ การประเมินค่าคือกระบวนการค้นหาค่าที่สอดคล้องกับนิพจน์[ 7 ] [ 8 ]
ข้อกำหนดในการเรียกใช้ฟังก์ชันประกอบด้วยรายละเอียดระดับต่ำเฉพาะแพลตฟอร์มเกี่ยวกับการส่งผ่านพารามิเตอร์
ตัวอย่าง
เพื่อแสดงให้เห็น การเรียกใช้ฟังก์ชันf(a, b)อาจประเมินอาร์กิวเมนต์ก่อนaจากนั้นbเก็บผลลัพธ์ไว้ในตัวอ้างอิงหรือตำแหน่งหน่วยความจำref_aจากref_bนั้นประเมินเนื้อหาของฟังก์ชันด้วยตัวอ้างอิงที่ส่งผ่านเข้ามา วิธีนี้ทำให้ฟังก์ชันสามารถค้นหาค่าอาร์กิวเมนต์ดั้งเดิมที่ส่งผ่านเข้ามาได้โดยการดีรีเฟอร์เรชันพารามิเตอร์ (บางภาษาใช้ตัวดำเนินการเฉพาะเพื่อทำเช่นนี้) เพื่อแก้ไขค่าเหล่านั้นผ่านการกำหนดค่าราวกับว่าเป็นตัวแปรโลคอล และส่งคืนค่าผ่านตัวอ้างอิง นี่คือกลยุทธ์การประเมินแบบเรียกโดยอ้างอิง[ 9 ]
โต๊ะ
นี่คือตารางกลยุทธ์การประเมินและภาษาตัวแทนตามปีที่นำมาใช้ ภาษาตัวแทนจะเรียงลำดับตามลำดับเวลา โดยเริ่มจากภาษาที่นำกลยุทธ์มาใช้ และตามด้วยภาษาที่โดดเด่นที่ใช้กลยุทธ์นั้น[ 10 ] : 434
| กลยุทธ์การประเมินผล | ภาษาตัวแทน | ปีที่เริ่มเปิดตัวครั้งแรก |
|---|---|---|
| ติดต่อโดยอ้างอิง | ฟอร์ทราน II, PL/I | 1958 |
| เรียกตามค่า | ALGOL , C , [ 11 ] Scheme , MATLAB [ 12 ] | 1960 |
| เรียกชื่อตามที่กำหนด | ALGOL 60 , ซิมูล่า | 1960 |
| เรียกใช้โดยการกู้คืนสำเนา | Fortran IV , Ada [ 13 ] | พ.ศ. 2505 |
| เรียกโดยการรวม | บทนำ | พ.ศ. 2508 [ 14 ] [ 15 ] |
| เรียกใช้ตามความจำเป็น | SASL , [ 16 ] Haskell , R [ 17 ] | พ.ศ. 2514 [ 18 ] |
| โทรโดยการแชร์ | CLU , Java , Python , Ruby , จูเลีย | พ.ศ. 2517 [ 19 ] |
| เรียกใช้พารามิเตอร์อ้างอิง | C++ , PHP , [ 20 ] C# , [ 21 ] Visual Basic .NET [ 22 ] | พ.ศ. 2528 [ 23 ] |
| เรียกโดยอ้างอิงถึงรัฐธรรมนูญ | ซี++ , ซี | พ.ศ. 2528 [ 23 ] |
คำสั่งประเมินผล
ลำดับการดำเนินการจะกำหนดโครงสร้างต้นไม้ไวยากรณ์นามธรรม ของนิพจน์ ในขณะที่ลำดับการประเมินจะกำหนดลำดับในการประเมินนิพจน์ ตัวอย่างเช่น โปรแกรม Python
def f ( x ): print ( x , end = "" ) return xพิมพ์( f ( 1 ) + f ( 2 ))ผลลัพธ์123ที่ได้เกิดจากลำดับการประเมินจากซ้ายไปขวาของ Python แต่โปรแกรมที่คล้ายกันในOCaml มีดังนี้ :
let f x = print_int x ; x ;; print_int ( f 1 + f 2 )ผลลัพธ์213ที่เกิดจากลำดับการประเมินจากขวาไปซ้ายของ OCaml
ลำดับการประเมินส่วนใหญ่มักปรากฏให้เห็นในโค้ดที่มีผลข้างเคียงแต่ยังส่งผลต่อประสิทธิภาพของโค้ดด้วย เนื่องจากลำดับที่ตายตัวจะขัดขวางการจัดตารางคำสั่งด้วยเหตุนี้ มาตรฐานภาษาต่างๆ เช่น C++ จึงมักไม่ระบุลำดับการประเมิน แม้ว่าภาษาต่างๆ เช่น Java และ C# จะกำหนดลำดับการประเมินเป็นจากซ้ายไปขวา[ 10 ] : 240–241 และ มาตรฐาน C++17ได้เพิ่มข้อจำกัดเกี่ยวกับลำดับการประเมิน[ 24 ]
การประเมินอย่างเข้มงวด
ลำดับการประยุกต์ใช้เป็นตระกูลของลำดับการประเมินที่อาร์กิวเมนต์ของฟังก์ชันจะถูกประเมินอย่างสมบูรณ์ก่อนที่จะนำฟังก์ชันไปใช้ [ 25 ]ซึ่งมีผลทำให้ฟังก์ชันเป็นแบบเข้มงวดกล่าวคือ ผลลัพธ์ของฟังก์ชันจะไม่ถูกกำหนดหากอาร์กิวเมนต์ใดๆ ไม่ได้ถูกกำหนด ดังนั้นการประเมินลำดับการประยุกต์ใช้จึงมักเรียกว่าการประเมินแบบเข้มงวดนอกจากนี้ การเรียกฟังก์ชันจะเกิดขึ้นทันทีที่พบในขั้นตอน ดังนั้นจึงเรียกว่าการประเมินแบบกระตือรือร้นหรือ การ ประเมินแบบโลภ[ 26 ] [ 27 ]ผู้เขียนบางคนเรียกการประเมินแบบเข้มงวดว่า "การเรียกโดยค่า" เนื่องจากกลยุทธ์การผูกค่าแบบเรียกต้องใช้การประเมินแบบเข้มงวด[ 4 ]
Common Lisp , Eiffelและ Java ประเมินอาร์กิวเมนต์ของฟังก์ชันจากซ้ายไปขวา C ไม่กำหนดลำดับ[ 28 ] Scheme กำหนดให้ลำดับการดำเนินการเป็นการดำเนินการตามลำดับของการเรียงสับเปลี่ยนอาร์กิวเมนต์ที่ไม่ระบุ[ 29 ] OCamlก็ไม่ระบุลำดับเช่นกัน แต่ในทางปฏิบัติจะประเมินอาร์กิวเมนต์จากขวาไปซ้ายเนื่องจากการออกแบบเครื่องจักรนามธรรม[ 30 ]ทั้งหมดนี้เป็นการประเมินแบบเข้มงวด
การประเมินแบบไม่เข้มงวด
ลำดับการประเมินที่ไม่เข้มงวดคือ ลำดับการประเมินที่ไม่เข้มงวด กล่าวคือ ฟังก์ชันอาจส่งคืนผลลัพธ์ก่อนที่อาร์กิวเมนต์ทั้งหมดจะได้รับการประเมินอย่างสมบูรณ์[ 31 ] : 46–47 ตัวอย่างต้นแบบคือการประเมินตามลำดับปกติซึ่งจะไม่ประเมินอาร์กิวเมนต์ใดๆ จนกว่าจะจำเป็นต้องใช้ในส่วนของฟังก์ชัน[ 32 ]การประเมินตามลำดับปกติมีคุณสมบัติที่ว่ามันจะสิ้นสุดโดยไม่มีข้อผิดพลาดเมื่อใดก็ตามที่ลำดับการประเมินอื่นๆ จะสิ้นสุดโดยไม่มีข้อผิดพลาด[ 33 ]ชื่อ "ลำดับปกติ" มาจากแคลคูลัสแลมบ์ดา ซึ่งการลดตามลำดับปกติจะพบรูปแบบปกติหากมีอยู่ (เป็นกลยุทธ์การลดแบบ "ทำให้เป็นปกติ" ) [ 34 ]การประเมินแบบขี้เกียจถูกจัดประเภทในบทความนี้เป็นเทคนิคการผูกมัดมากกว่าลำดับการประเมิน แต่ความแตกต่างนี้ไม่ได้ปฏิบัติตามเสมอไป และผู้เขียนบางคนกำหนดการประเมินแบบขี้เกียจเป็นการประเมินตามลำดับปกติหรือในทางกลับกัน[ 25 ] [ 35 ]หรือสับสนระหว่างความไม่เข้มงวดกับการประเมินแบบขี้เกียจ[ 31 ] : 43–44
นิพจน์บูลีนในหลายภาษาใช้รูปแบบการประเมินแบบไม่เข้มงวดที่เรียกว่าการประเมินแบบลัดวงจรซึ่งการประเมินจะประเมินนิพจน์ด้านซ้าย แต่อาจข้ามนิพจน์ด้านขวาหากสามารถกำหนดผลลัพธ์ได้ เช่น ในนิพจน์แบบแยก (OR) ที่trueพบ หรือในนิพจน์แบบเชื่อม (AND) ที่falseพบ และอื่นๆ[ 35 ]นิพจน์เงื่อนไขก็ใช้การประเมินแบบไม่เข้มงวดเช่นกัน โดยจะประเมินเพียงสาขาเดียวเท่านั้น[ 31 ]
การเปรียบเทียบการประเมินตามลำดับการประยุกต์ใช้และการประเมินตามลำดับปกติ
ด้วยการประเมินลำดับปกติ นิพจน์ที่มีการคำนวณที่มีราคาแพง ข้อผิดพลาด หรือลูปอนันต์จะถูกละเว้นหากไม่จำเป็น[ 4 ]ทำให้สามารถระบุโครงสร้างการควบคุมการไหลที่ผู้ใช้กำหนดเองได้ ซึ่งเป็นสิ่งอำนวยความสะดวกที่ไม่มีในการประเมินลำดับแบบประยุกต์ การประเมินลำดับปกติใช้โครงสร้างที่ซับซ้อน เช่นthunksสำหรับนิพจน์ที่ยังไม่ได้ประเมิน เมื่อเทียบกับสแต็กการเรียกที่ใช้ในการประเมินลำดับแบบประยุกต์[ 36 ]ในอดีต การประเมินลำดับปกติขาดเครื่องมือดีบักที่ใช้งานได้เนื่องจากความซับซ้อน[ 37 ]
กลยุทธ์การผูกมัดอย่างเข้มงวด
เรียกตามค่า
ในการส่งค่าแบบเรียก (หรือส่งผ่านค่า) ค่าที่ประเมินแล้วของนิพจน์อาร์กิวเมนต์จะถูกผูกกับตัวแปรที่สอดคล้องกันในฟังก์ชัน (บ่อยครั้งโดยการคัดลอกค่าไปยังพื้นที่หน่วยความจำใหม่) หากฟังก์ชันหรือขั้นตอนสามารถกำหนดค่าให้กับพารามิเตอร์ได้ จะมีการกำหนดค่าให้กับ ตัวแปรโลคอล เท่านั้น นั่นคือ สิ่งใดก็ตามที่ส่งผ่านเข้าไปในการเรียกฟังก์ชันจะไม่มีการเปลี่ยนแปลงใน ขอบเขตของผู้เรียกเมื่อฟังก์ชันส่งคืนค่า ตัวอย่างเช่น ในภาษา Pascalการส่งอาร์เรย์แบบส่งผ่านค่าจะทำให้อาร์เรย์ทั้งหมดถูกคัดลอก และการเปลี่ยนแปลงใดๆ ต่ออาร์เรย์นี้จะมองไม่เห็นสำหรับผู้เรียก: [ 38 ]
โปรแกรมหลัก; ใช้crt ;ขั้นตอนPrintArray ( a : อาร์เรย์ของจำนวนเต็ม) ; var i : จำนวนเต็ม; begin for i := Low ( a ) to High ( a ) do Write ( a [ i ]) ; WriteLn () ; end ;ขั้นตอนการแก้ไข( แถว: อาร์เรย์ของจำนวนเต็ม) ; เริ่มต้น พิมพ์อาร์เรย์( แถว) ; // 123 แถว[ 1 ] := 4 ; พิมพ์อาร์เรย์( แถว) ; // 143 สิ้นสุด;ตัวแปรA : อาร์เรย์ของจำนวนเต็ม; begin A := [ 1 , 2 , 3 ] ; PrintArray ( A ) ; // 123 Modify ( A ) ; PrintArray ( A ) ; // 123 end .การเบี่ยงเบนความหมาย
กล่าวอย่างเคร่งครัด ภายใต้การส่งค่าแบบ Call by Value จะไม่มีการดำเนินการใดๆ ที่ดำเนินการโดยรูทีนที่ถูกเรียกสามารถมองเห็นได้โดยผู้เรียก นอกเหนือจากเป็นส่วนหนึ่งของค่าที่ส่งคืน[ 19 ]ซึ่งหมายถึงรูปแบบของการเขียนโปรแกรมเชิงฟังก์ชันอย่างแท้จริงในความหมายของการใช้งาน อย่างไรก็ตาม คำว่า "Call by Value ที่ค่าเป็น Reference" ได้กลายเป็นเรื่องปกติในบางภาษา เช่น ชุมชน Java [ 39 ]เมื่อเปรียบเทียบกับการส่งค่าแบบ Pass by Value แบบดั้งเดิม ค่าที่ส่งผ่านไม่ใช่ค่าตามความหมายปกติของคำว่าค่า เช่น จำนวนเต็มที่สามารถเขียนเป็นค่าคงที่ได้ แต่เป็น ตัวจัดการ Reference ภายในการใช้งาน การเปลี่ยนแปลงของตัวจัดการ Reference นี้จะมองเห็นได้ในผู้เรียก เนื่องจากมีการเปลี่ยนแปลงที่มองเห็นได้ รูปแบบของ "Call by Value" นี้จึงควรเรียกว่า Call by Sharingมากกว่า[ 19 ]
ในภาษาโปรแกรมเชิงฟังก์ชันล้วนๆค่าและโครงสร้างข้อมูลนั้นไม่สามารถเปลี่ยนแปลงได้ ดังนั้นจึงไม่มีความเป็นไปได้ที่ฟังก์ชันจะแก้ไขอาร์กิวเมนต์ใดๆ ด้วยเหตุนี้ โดยทั่วไปจึงไม่มีความแตกต่างทางความหมายระหว่างการส่งค่าและการส่งอ้างอิงหรือตัวชี้ไปยังโครงสร้างข้อมูลและการใช้งานภายในมักใช้การเรียกโดยอ้างอิงเพื่อประโยชน์ด้านประสิทธิภาพ ถึงกระนั้น ภาษาเหล่านี้มักถูกอธิบายว่าเป็นภาษาที่เรียกโดยค่า
ติดต่อโดยอ้างอิง
การเรียกโดยอ้างอิง (หรือการส่งผ่านโดยอ้างอิง) เป็นกลยุทธ์การประเมินผลที่พารามิเตอร์ถูกผูกไว้กับการอ้างอิงโดยปริยายไปยังตัวแปรที่ใช้เป็นอาร์กิวเมนต์ แทนที่จะเป็นสำเนาของค่าของตัวแปรนั้น โดยทั่วไปแล้วหมายความว่าฟังก์ชันสามารถแก้ไข (เช่นกำหนดค่าให้กับ ) ตัวแปรที่ใช้เป็นอาร์กิวเมนต์ได้ ซึ่งผู้เรียกจะเห็นการเปลี่ยนแปลงนี้ ดังนั้น การเรียกโดยอ้างอิงจึงสามารถใช้เพื่อเพิ่มช่องทางการสื่อสารระหว่างฟังก์ชันที่ถูกเรียกและฟังก์ชันที่เรียกได้ การส่งผ่านโดยอ้างอิงสามารถปรับปรุงประสิทธิภาพได้อย่างมาก: การเรียกฟังก์ชันที่มีโครงสร้างขนาดหลายเมกะไบต์เป็นอาร์กิวเมนต์ไม่จำเป็นต้องคัดลอกโครงสร้างขนาดใหญ่ แต่คัดลอกเฉพาะการอ้างอิงไปยังโครงสร้างเท่านั้น (ซึ่งโดยทั่วไปเป็นคำของเครื่องและมีเพียงไม่กี่ไบต์) อย่างไรก็ตาม ภาษาที่ใช้การเรียกโดยอ้างอิงทำให้โปรแกรมเมอร์ติดตามผลกระทบของการเรียกฟังก์ชันได้ยากขึ้น และอาจทำให้เกิดข้อผิดพลาดเล็กน้อยได้
เนื่องจากไวยากรณ์ที่แตกต่างกัน ความแตกต่างระหว่างการเรียกโดยอ้างอิง (ซึ่งประเภทการอ้างอิงเป็นโดยปริยาย) และการเรียกโดยการแบ่งปัน (ซึ่งประเภทการอ้างอิงเป็นไปอย่างชัดเจน) มักจะไม่ชัดเจนในตอนแรก การทดสอบง่ายๆ คือ เป็นไปได้หรือไม่ที่จะเขียนswap(a, b)ฟังก์ชันแบบดั้งเดิมในภาษา[ 39 ]ตัวอย่างเช่นใน Fortran:
โปรแกรมMain implicit none integer :: a = 1 integer :: b = 2 call Swap ( a , b ) print * , a , b ! 2 1 contains subroutine Swap ( a , b ) integer , intent ( inout ) :: a , b integer :: temp temp = a a = b b = temp end subroutine Swap end program Mainดังนั้นinoutเจตนารมณ์ของ Fortran คือการใช้การเรียกแบบอ้างอิง (call-by-reference) กล่าวคือ ตัวแปรใดๆ ก็สามารถแปลงเป็นตัวจัดการอ้างอิงได้โดยปริยาย ในทางตรงกันข้าม สิ่งที่ใกล้เคียงที่สุดใน Java คือ:
public class Main { static class Box { int value ; public Box ( int value ) { this . value = value ; } }static void swap ( Box a , Box b ) { int temp = a . value ; a . value = b . value ; b . value = temp ; }public static void main ( String [] args ) { Box a = new Box ( 1 ) ; Box b = new Box ( 2 ) ; swap ( a , b ) ; System.out.printf ( " a = % d, b = %d%n" , a.value , b.value ) ; // output: a = 2, b = 1 } }โดยBoxต้องใช้ประเภทที่ระบุอย่างชัดเจนเพื่อแนะนำแฮนเดิล Java ใช้การเรียกแบบแบ่งปัน แต่ไม่ใช่การเรียกแบบอ้างอิง[ 39 ]
เรียกใช้โดยการกู้คืนสำเนา
การเรียกแบบคัดลอก-คืนค่า—หรือที่รู้จักกันในชื่อ "copy-in copy-out", "call by value result", "call by value return" (ตามคำเรียกใน ชุมชน Fortran )—เป็นรูปแบบหนึ่งของการเรียกแบบอ้างอิง ด้วยการเรียกแบบคัดลอก-คืนค่า เนื้อหาของอาร์กิวเมนต์จะถูกคัดลอกไปยังตัวแปรใหม่ภายในฟังก์ชันที่เรียกใช้ จากนั้นฟังก์ชันอาจแก้ไขตัวแปรนี้ได้เช่นเดียวกับการเรียกแบบอ้างอิง แต่เนื่องจากตัวแปรเป็นตัวแปรภายใน การแก้ไขจึงมองไม่เห็นจากภายนอกฟังก์ชันที่เรียกใช้ในระหว่างการเรียก เมื่อฟังก์ชันส่งค่ากลับ เนื้อหาที่อัปเดตของตัวแปรนี้จะถูกคัดลอกกลับไปเขียนทับอาร์กิวเมนต์เดิม ("คืนค่า") [ 40 ]
ความหมายของการเรียกโดยการคัดลอกและคืนค่าจะคล้ายคลึงกับการเรียกโดยการอ้างอิงในหลายกรณี แต่จะแตกต่างกันเมื่ออาร์กิวเมนต์ของฟังก์ชันสองตัวขึ้นไปใช้ชื่อแทนกัน (เช่น ชี้ไปยังตัวแปรเดียวกันในสภาพแวดล้อมของผู้เรียก) ภายใต้การเรียกโดยการอ้างอิง การเขียนไปยังอาร์กิวเมนต์หนึ่งจะส่งผลต่ออาร์กิวเมนต์อื่นในระหว่างการทำงานของฟังก์ชัน ภายใต้การเรียกโดยการคัดลอกและคืนค่า การเขียนไปยังอาร์กิวเมนต์หนึ่งจะไม่ส่งผลต่ออาร์กิวเมนต์อื่นในระหว่างการทำงานของฟังก์ชัน แต่เมื่อสิ้นสุดการเรียก ค่าของอาร์กิวเมนต์ทั้งสองอาจแตกต่างกัน และไม่ชัดเจนว่าอาร์กิวเมนต์ใดถูกคัดลอกกลับมาก่อน ดังนั้นตัวแปรของผู้เรียกจะได้รับค่าใด[ 41 ]ตัวอย่างเช่น Ada ระบุว่าการกำหนดค่าแบบคัดลอกออกสำหรับแต่ละin outพารามิเตอร์outเกิดขึ้นในลำดับที่กำหนด[ 42 ]จากโปรแกรมต่อไปนี้ (ผิดกฎหมายใน Ada 2012) [ 43 ]จะเห็นได้ว่าพฤติกรรมของGNATคือการคัดลอกตามลำดับจากซ้ายไปขวาเมื่อส่งคืน:
with Ada.Text_IO ; use Ada.Text_IO ;ขั้นตอนTest_Copy_Restore คือขั้นตอนModify ( A , B : in out Integer ) คือbegin A := A + 1 ; B := B + 2 ; end Modify ; X : Integer := 0 ; begin Modify ( X , X ); Put_Line ( "X = " & Integer ' Image ( X )); end Test_Copy_Restore ; -- $ gnatmake -gnatd.E test_copy_restore.adb; ./test_copy_restore -- test_copy_restore.adb:12:10: คำเตือน: ค่าจริงที่เขียนได้สำหรับ "A" ทับซ้อนกับค่าจริงสำหรับ "B" [-gnatw.i] -- X = 2ถ้าโปรแกรมส่งค่ากลับเป็น 1 แสดงว่าเป็นการคัดลอกจากขวาไปซ้าย และภายใต้หลักการเรียกแบบอ้างอิง (call by reference semantics) โปรแกรมจะส่งค่ากลับเป็น 3
เมื่อมีการส่งค่าอ้างอิงไปยังผู้เรียกโดยที่ยังไม่ได้กำหนดค่าเริ่มต้น (ตัวอย่างเช่นoutพารามิเตอร์ในภาษา Ada ซึ่งแตกต่างจากin outพารามิเตอร์ทั่วไป) กลยุทธ์การประเมินนี้อาจเรียกว่า "เรียกตามผลลัพธ์" (call by result)
กลยุทธ์นี้ได้รับความสนใจในการประมวลผลแบบมัลติโปรเซส ซิ่ง และการเรียกใช้ขั้นตอนระยะไกล[ 44 ] เนื่องจากไม่เหมือนกับ call-by-reference ตรงที่ไม่จำเป็นต้องสื่อสารบ่อยครั้งระหว่างเธรดการดำเนินการเพื่อเข้าถึงตัวแปร
โทรโดยการแชร์
การเรียกโดยการแบ่งปัน (หรือที่รู้จักกันในชื่อ "pass by sharing", "call by object" หรือ "call by object-sharing") เป็นกลยุทธ์การประเมินค่าที่อยู่ระหว่างการเรียกโดยค่าและการเรียกโดยการอ้างอิง แทนที่จะเปิดเผยตัวแปรทุกตัวเป็นการอ้างอิง มีเพียงค่าบางประเภทเท่านั้นที่เรียกว่า "การอ้างอิง" " ประเภทกล่อง " หรือ "วัตถุ" ที่มีความหมายของการอ้างอิง และที่อยู่ของตัวชี้เหล่านี้จะถูกส่งไปยังฟังก์ชัน เช่นเดียวกับการเรียกโดยค่า ค่าของที่อยู่ส่งผ่านเป็นสำเนา และการกำหนดค่าโดยตรงให้กับพารามิเตอร์ของฟังก์ชันจะเขียนทับสำเนาและมองไม่เห็นสำหรับฟังก์ชันที่เรียก เช่นเดียวกับการเรียกโดยการอ้างอิง การเปลี่ยนแปลงเป้าหมายของตัวชี้จะมองเห็นได้สำหรับฟังก์ชันที่เรียก การเปลี่ยนแปลงของวัตถุที่เปลี่ยนแปลงได้ภายในฟังก์ชันจะมองเห็นได้สำหรับผู้เรียกเนื่องจากวัตถุไม่ได้ถูกคัดลอกหรือโคลน แต่ถูกแบ่งปันดังนั้นจึงเรียกว่า "call by sharing" [ 19 ]
เทคนิคนี้ได้รับการกล่าวถึงครั้งแรกโดยBarbara Liskovในปี 1974 สำหรับภาษาCLU [ 19 ]มีการใช้ในภาษาสมัยใหม่หลายภาษา เช่นPython (ค่าที่ใช้ร่วมกันเรียกว่า "อ็อบเจ็กต์") [ 45 ] Java (อ็อบเจ็กต์), Ruby (อ็อบเจ็กต์ ), JavaScript (อ็อบเจ็กต์), Scheme (โครงสร้างข้อมูลเช่นเวกเตอร์) [ 46 ] AppleScript (รายการ บันทึก วันที่ และอ็อบเจ็กต์สคริปต์), OCaml และML (การอ้างอิง บันทึก อาร์เรย์ อ็อบเจ็กต์ และประเภทข้อมูลผสมอื่นๆ), Maple (rtables และตาราง) และTcl (อ็อบเจ็กต์) [ 47 ]คำว่า "call by sharing" ที่ใช้ในบทความนี้ไม่ได้ใช้กันทั่วไป คำศัพท์ไม่สอดคล้องกันในแหล่งข้อมูลต่างๆ ตัวอย่างเช่น ในชุมชน Java พวกเขากล่าวว่า Java คือการเรียกโดยค่า[ 39 ]
สำหรับวัตถุที่เปลี่ยนแปลงไม่ได้จะไม่มีความแตกต่างที่แท้จริงระหว่างการเรียกโดยการแบ่งปันและการเรียกโดยค่า เว้นแต่ว่าเอกลักษณ์ของวัตถุจะมองเห็นได้ในภาษา การใช้การเรียกโดยการแบ่งปันกับวัตถุที่เปลี่ยนแปลงได้เป็นทางเลือกแทนพารามิเตอร์อินพุต/เอาต์พุต : พารามิเตอร์จะไม่ถูกกำหนด (อาร์กิวเมนต์จะไม่ถูกเขียนทับและเอกลักษณ์ของวัตถุจะไม่เปลี่ยนแปลง) แต่วัตถุ (อาร์กิวเมนต์) จะถูกเปลี่ยนแปลง[ 48 ]
ตัวอย่างเช่น ในภาษา Python ลิสต์สามารถเปลี่ยนแปลงได้และส่งผ่านการเรียกใช้แบบแชร์ ดังนั้น:
def f ( l : list [ int ]) -> None : l . append ( 1 )m : รายการ[ int ] = [] f ( m ) พิมพ์( m )ผลลัพธ์ที่ได้เกิด จากเมธอด [1]ดังกล่าวappendทำการแก้ไขอ็อบเจ็กต์ที่ถูกเรียกใช้
ในทางตรงกันข้าม การกำหนดค่าภายในฟังก์ชันจะไม่ปรากฏให้ผู้เรียกใช้สังเกตเห็น ตัวอย่างเช่น โค้ดนี้ผูกอาร์กิวเมนต์อย่างเป็นทางการเข้ากับอ็อบเจ็กต์ใหม่ แต่ผู้เรียกใช้จะไม่เห็นการเปลี่ยนแปลงนั้น เนื่องจากไม่ได้ทำการแก้ไขค่าเดิมa_list:
def f ( l : list [ int ]) -> None : l = l + [ 1 ] print ( l ) # [1]m : รายการ[ int ] = [] f ( m ) พิมพ์( m ) # []โทรตามที่อยู่
การเรียกโดยที่อยู่การส่งผ่านโดยที่อยู่ หรือการเรียก/ส่งผ่านโดยตัวชี้เป็นวิธีการส่งผ่านพารามิเตอร์ที่ส่งผ่านที่อยู่ของอาร์กิวเมนต์เป็นพารามิเตอร์อย่างเป็นทางการ ภายในฟังก์ชัน ที่อยู่ (ตัวชี้) อาจถูกใช้เพื่อเข้าถึงหรือแก้ไขค่าของอาร์กิวเมนต์ ตัวอย่างเช่น การดำเนินการสลับสามารถนำไปใช้ได้ดังต่อไปนี้ในภาษา C: [ 49 ]
#include <stdio.h>void swap ( int * a , int * b ) { int temp = * a ; * a = * b ; * b = temp ; }int main () { int a = 1 ; int b = 2 ; swap ( &a a , & b ); printf ( "%d %d \n " , a , b ); // 2 1 return 0 ; }ผู้เขียนบางคนถือว่า&เป็นส่วนหนึ่งของไวยากรณ์ของการเรียกswapภายใต้มุมมองนี้ C สนับสนุนกลยุทธ์การส่งผ่านพารามิเตอร์แบบเรียกโดยอ้างอิง[ 50 ]ผู้เขียนคนอื่นๆ มีมุมมองที่แตกต่างออกไป โดยมองว่าการใช้งานที่นำเสนอswapใน C เป็นเพียงการจำลองการเรียกโดยอ้างอิงโดยใช้พอยเตอร์[ 51 ]ภายใต้มุมมอง "การจำลอง" นี้ ตัวแปรที่เปลี่ยนแปลงได้ใน C ไม่ใช่ตัวแปรชั้นหนึ่ง (นั่นคือ ค่า l ไม่ใช่นิพจน์) แต่ประเภทพอยเตอร์ต่างหากที่เป็น ในมุมมองนี้ โปรแกรมสลับที่นำเสนอเป็นเพียงรูปแบบไวยากรณ์สำหรับโปรแกรมที่ใช้พอยเตอร์ตลอดทั้งโปรแกรม[ 52 ]ตัวอย่างเช่น โปรแกรมนี้ ( readและ ได้ถูกเพิ่มเข้ามาเพื่อเน้นความคล้ายคลึงกับ โปรแกรมการเรียกโดยแบ่งปันของassignJava ข้างต้น ): Box
#include <stdio.h>อ่านค่าint ( int * p ) { คืนค่า* p ; }void assign ( int * p , int v ) { * p = v ; }void swap ( int * a , int * b ) { int temp_storage ; int * temp = & temp_storage ; assign ( temp , read ( a )); assign ( a , read ( b )); assign ( b , read ( temp )); }int main () { int a_storage ; int * a = & a_storage ; int b_storage ; int * b = & b_storage ; assign ( a , 1 ); assign ( b , 2 ); swap ( a , b ); printf ( "%d %d \n " , read ( a ), read ( b )); // 2 1 return 0 ; }เนื่องจากในโปรแกรมนี้swapฟังก์ชันจะทำงานกับตัวชี้ และไม่สามารถเปลี่ยนแปลงตัวชี้เองได้ แต่สามารถเปลี่ยนได้เฉพาะค่าที่ตัวชี้ชี้ไปเท่านั้น มุมมองนี้จึงกล่าวว่า กลยุทธ์การประเมินค่าหลักของภาษา C นั้นคล้ายคลึงกับ call-by-sharing มากกว่า
C++ ทำให้เรื่องนี้สับสนยิ่งขึ้นไปอีกโดยอนุญาตswapให้ประกาศและใช้งานด้วยไวยากรณ์ "อ้างอิง" ที่เบามาก: [ 53 ]
void swap ( int & a , int & b ) { int temp = a ; a = b ; b = temp ; }int main () { int a = 1 ; int b = 2 ; swap ( a , b ); std :: println ( "{} {}" , a , b ); // 2 1 return 0 ; }ในเชิงความหมายแล้ว นี่เทียบเท่ากับตัวอย่างในภาษาซี ดังนั้น ผู้เขียนหลายคนจึงพิจารณาว่าการเรียกพารามิเตอร์โดยระบุที่อยู่ (call-by-address) เป็นกลยุทธ์การส่งผ่านพารามิเตอร์ที่ไม่เหมือนใคร ซึ่งแตกต่างจากการเรียกพารามิเตอร์โดยระบุค่า (call-by-value), การเรียกพารามิเตอร์โดยอ้างอิง (call-by-reference) และการเรียกพารามิเตอร์โดยการแบ่งปัน (call-by-sharing)
เรียกโดยการรวม
ในการเขียนโปรแกรมเชิงตรรกะการประเมินนิพจน์อาจสอดคล้องกับการรวมเทอมที่เกี่ยวข้องเข้าด้วยกัน พร้อมกับการประยุกต์ใช้การแก้ปัญหา บางรูปแบบ การรวมเทอมต้องจัดเป็นกลยุทธ์การผูกมัดที่เข้มงวด เนื่องจากเป็นการดำเนินการที่สมบูรณ์ อย่างไรก็ตาม การรวมเทอมยังสามารถดำเนินการกับตัวแปรที่ไม่มีขอบเขตได้ ดังนั้นการเรียกใช้จึงไม่จำเป็นต้องกำหนดค่าสุดท้ายให้กับตัวแปรทั้งหมดเสมอไป
กลยุทธ์การผูกมัดที่ไม่เข้มงวด
เรียกชื่อตามที่กำหนด
การเรียกใช้ฟังก์ชันโดยระบุชื่อ (Call by name) เป็นกลยุทธ์การประเมินผลที่ตัวแปรของฟังก์ชันจะไม่ถูกประเมินก่อนที่จะเรียกใช้ฟังก์ชัน แต่จะถูกแทนที่ลงในส่วนเนื้อหาของฟังก์ชันโดยตรง (โดยใช้การแทนที่แบบหลีกเลี่ยงการจับค่า ) แล้วจึงค่อยประเมินค่าเมื่อใดก็ตามที่ปรากฏในฟังก์ชัน หากตัวแปรใดไม่ได้ถูกใช้ในส่วนเนื้อหาของฟังก์ชัน ตัวแปรนั้นจะไม่ถูกประเมินค่าเลย แต่ถ้าถูกใช้หลายครั้ง ตัวแปรนั้นจะถูกประเมินค่าใหม่ทุกครั้งที่ปรากฏ (ดู เทคนิคการเขียนโปรแกรมที่ใช้ประโยชน์จากสิ่งนี้ได้ใน Jensen's device )
การประเมินค่าแบบเรียกตามชื่อ (call-by-name) บางครั้งอาจดีกว่าการประเมินค่าแบบเรียกตามค่า (call-by-value) หากอาร์กิวเมนต์ของฟังก์ชันไม่ได้ถูกใช้ในฟังก์ชันนั้น การประเมินค่าแบบเรียกตามชื่อจะช่วยประหยัดเวลาโดยไม่ต้องประเมินค่าอาร์กิวเมนต์ ในขณะที่การประเมินค่าแบบเรียกตามค่าจะประเมินค่าอาร์กิวเมนต์นั้นเสมอ หากอาร์กิวเมนต์เป็นการคำนวณที่ไม่สิ้นสุด ข้อดีก็จะยิ่งมากขึ้น อย่างไรก็ตาม เมื่ออาร์กิวเมนต์ของฟังก์ชันถูกใช้ การประเมินค่าแบบเรียกตามชื่อมักจะช้ากว่า จึงต้องใช้กลไกอย่างเช่นthunkมาช่วย
ภาษา .NETสามารถจำลองการเรียกใช้ฟังก์ชันโดยใช้ชื่อ (call by name) โดยใช้ดีเลเกต (delegates) หรือExpression<T>พารามิเตอร์ ซึ่งในกรณีหลังจะทำให้ฟังก์ชันมีโครงสร้างต้นไม้ไวยากรณ์นามธรรม (abstract syntax tree) ส่วน Eiffelนั้นมีเอเจนต์ (agents) ซึ่งเป็นตัวแทนของการดำเนินการที่จะถูกประเมินเมื่อจำเป็น โปรแกรม Javaก็สามารถทำการประเมินแบบเลซี่ (lazy evaluation) ในลักษณะเดียวกันได้โดยใช้แลมบ์ดาเอ็กซ์เพรสชัน (lambda expressions)และ อินเทอร์เฟซ ( java.util.function.Supplier<T>interface)
เรียกใช้ตามความจำเป็น
การเรียกใช้ฟังก์ชันโดยต้องการ (Call by need) เป็น รูปแบบหนึ่งของการเรียกใช้ฟังก์ชันโดยระบุชื่อ (Call by name) ที่ใช้การจดจำค่า (memoization ) โดยหากมีการประเมินค่าอาร์กิวเมนต์ของฟังก์ชัน ค่าดังกล่าวจะถูกเก็บไว้เพื่อใช้ในภายหลัง หากอาร์กิวเมนต์เป็นแบบบริสุทธิ์ (เช่น ไม่มีผลข้างเคียง) จะให้ผลลัพธ์เช่นเดียวกับการเรียกใช้ฟังก์ชันโดยระบุชื่อ ช่วยประหยัดค่าใช้จ่ายในการคำนวณอาร์กิวเมนต์ใหม่
Haskellเป็นภาษาโปรแกรมที่รู้จักกันดีซึ่งใช้การประเมินค่าแบบเรียกตามความต้องการ (call-by-need evaluation) เนื่องจากการประเมินค่าของนิพจน์อาจเกิดขึ้นในขั้นตอนการคำนวณที่ไกลออกไป Haskell จึงรองรับเฉพาะผลข้างเคียง (เช่นการเปลี่ยนแปลงค่า ) ผ่านการใช้โมนาด (monads ) เท่านั้น ซึ่งจะช่วยขจัดพฤติกรรมที่ไม่คาดคิดจากตัวแปรที่มีค่าเปลี่ยนแปลงก่อนการประเมินค่าแบบล่าช้า
ในการใช้งานฟังก์ชัน call by need ของ R นั้น อาร์กิวเมนต์ทั้งหมดจะถูกส่งผ่าน ซึ่งหมายความว่า R อนุญาตให้เกิดผลข้างเคียงใดๆ ก็ได้
การประเมินแบบขี้เกียจ (Lazy evaluation)เป็นการใช้งานที่พบได้บ่อยที่สุดของความหมายการเรียกใช้ตามความจำเป็น (call-by-need semantics) แต่ก็มีรูปแบบอื่นๆ เช่นการประเมินแบบมองโลกในแง่ดี (optimistic evaluation ) ภาษา .NETใช้ประเภท (type Lazy<T>) ในการใช้งานการเรียกใช้ตามความจำเป็น
การลดกราฟเป็นการนำการประเมินแบบเลซี่มาใช้อย่างมีประสิทธิภาพ
เรียกโดยการขยายมาโคร
การเรียกใช้โดยการขยายมาโครนั้นคล้ายกับการเรียกใช้โดยชื่อ แต่ใช้การแทนที่ข้อความแทน การแทนที่เพื่อ หลีกเลี่ยงการจับค่าตัวแปรดังนั้น การแทนที่มาโครอาจส่งผลให้เกิดการจับค่าตัวแปร ซึ่งนำไปสู่ข้อผิดพลาดและพฤติกรรมที่ไม่พึงประสงค์มาโครแบบสะอาดจะหลีกเลี่ยงปัญหานี้โดยการตรวจสอบและแทนที่ตัวแปรที่ถูกซ่อนไว้ซึ่งไม่ใช่พารามิเตอร์
เรียกโดยอนาคต
"Call by future" หรือที่รู้จักกันในชื่อ "parallel call by name" หรือ "lenient evaluation" [ 54 ]เป็น กลยุทธ์การประเมิน พร้อมกันที่ผสมผสานความหมายที่ไม่เข้มงวดเข้ากับการประเมินแบบกระตือรือร้น วิธีนี้ต้องการการจัดตารางเวลาแบบไดนามิกและการซิงโครไนซ์แบบละเอียด แต่เหมาะสำหรับเครื่องจักรแบบขนานขนาดใหญ่
กลยุทธ์นี้สร้างอนาคต (สัญญา)สำหรับเนื้อหาของฟังก์ชันและอาร์กิวเมนต์แต่ละตัว อนาคตเหล่านี้จะถูกคำนวณพร้อมกันกับการไหลของส่วนที่เหลือของโปรแกรม เมื่ออนาคต A ต้องการค่าของอนาคต B ที่ยังไม่ได้คำนวณ อนาคต A จะหยุดรอจนกว่าอนาคต B จะคำนวณเสร็จและมีค่า หากอนาคต B คำนวณเสร็จแล้ว ค่าจะถูกส่งคืนทันที เงื่อนไขจะหยุดรอจนกว่าเงื่อนไขจะได้รับการประเมิน และแลมบ์ดาจะไม่สร้างอนาคตจนกว่าจะถูกนำไปใช้อย่างสมบูรณ์[ 55 ]
หากนำไปใช้กับกระบวนการหรือเธรด การสร้างฟิวเจอร์จะสร้างกระบวนการหรือเธรดใหม่หนึ่งรายการหรือมากกว่า (สำหรับคำสัญญา) การเข้าถึงค่าจะซิงโครไนซ์กระบวนการหรือเธรดเหล่านี้กับเธรดหลัก และการยุติการคำนวณของฟิวเจอร์จะเทียบเท่ากับการยุติคำสัญญาที่กำลังคำนวณค่าของมัน หากนำไปใช้กับโครูทีนเช่นใน.NET async/awaitการสร้างฟิวเจอร์จะเรียกโครูทีน (ฟังก์ชันอะซิงโครนัส) ซึ่งอาจส่งต่อไปยังผู้เรียก และในทางกลับกันจะถูกส่งกลับมาเมื่อมีการใช้ค่า ทำให้เกิดการทำงานแบบมัลติทาสก์ร่วมกัน
กลยุทธ์นี้ไม่แน่นอน เนื่องจากผลการประเมินสามารถเกิดขึ้นได้ทุกเวลาตั้งแต่การสร้างอนาคต (เช่น เมื่อมีการกำหนดนิพจน์) จนถึงการใช้ค่าของอนาคตนั้น กลยุทธ์นี้ไม่เข้มงวด เพราะตัวฟังก์ชันอาจส่งคืนค่าก่อนที่จะมีการประเมินอาร์กิวเมนต์ อย่างไรก็ตาม ในการใช้งานส่วนใหญ่ การทำงานอาจยังคงติดขัดอยู่กับการประเมินอาร์กิวเมนต์ที่ไม่จำเป็น ตัวอย่างเช่น โปรแกรม
f x = 1 / x g y = 1 main = print ( g ( f 0 ))อาจgเสร็จสิ้นก่อนfและส่งออก 1 หรืออาจส่งผลให้เกิดข้อผิดพลาดเนื่องจากการ1/0ประเมิน[ 31 ]
การเรียกใช้แบบอนาคต (Call-by-future) คล้ายกับการเรียกใช้แบบความต้องการ (Call-by-need) ตรงที่ค่าจะถูกคำนวณเพียงครั้งเดียวเท่านั้น ด้วยการจัดการข้อผิดพลาดและการไม่สิ้นสุดอย่างระมัดระวัง โดยเฉพาะอย่างยิ่งการยุติอนาคตกลางคันหากพบว่าไม่จำเป็นต้องใช้ การเรียกใช้แบบอนาคตจึงมีคุณสมบัติการสิ้นสุดเช่นเดียวกับการประเมินแบบเรียกใช้ตามความต้องการ[ 55 ]อย่างไรก็ตาม การเรียกใช้แบบอนาคตอาจทำงานคาดการณ์ที่ไม่จำเป็นเมื่อเทียบกับการเรียกใช้แบบความต้องการ เช่น การประเมินโครงสร้างข้อมูลแบบขี้เกียจอย่างละเอียด[ 31 ]สามารถหลีกเลี่ยงสิ่งนี้ได้โดยการใช้อนาคตแบบขี้เกียจที่ไม่เริ่มการคำนวณจนกว่าจะแน่ใจว่าจำเป็นต้องใช้ค่า
การประเมินในแง่ดี
การประเมินแบบมองโลกในแง่ดีเป็นรูปแบบหนึ่งของการเรียกใช้ตามความต้องการ โดยที่อาร์กิวเมนต์ของฟังก์ชันจะถูกประเมินบางส่วนในรูปแบบการเรียกใช้โดยค่าเป็นระยะเวลาหนึ่ง (ซึ่งอาจปรับได้ในระหว่างการทำงาน ) หลังจากเวลาดังกล่าวผ่านไป การประเมินจะถูกยกเลิกและฟังก์ชันจะถูกนำไปใช้โดยใช้การเรียกใช้ตามความต้องการ[ 56 ]แนวทางนี้หลีกเลี่ยงค่าใช้จ่ายในการทำงานบางส่วนของการเรียกใช้ตามความต้องการ ในขณะที่ยังคงรักษาลักษณะการสิ้นสุดที่ต้องการไว้
ดูเพิ่มเติม
- รูปแบบปกติของเบต้า
- การเปรียบเทียบภาษาโปรแกรม
- เดอ เรแอนด์เดอ ดิกโต
- ประเมิน
- แคลคูลัสแลมบ์ดา
- เรียกโดยค่าพุช
- การประเมินบางส่วน
อ่านเพิ่มเติม
- Baker-Finch, Clem; King, David; Hall, Jon; Trinder, Phil (1999-03-10). "ความหมายเชิงปฏิบัติการสำหรับการเรียกแบบขนานตามความต้องการ" (ps) รายงานการวิจัย 99 ( 1) . คณะคณิตศาสตร์และวิทยาการคอมพิวเตอร์ มหาวิทยาลัยเปิด
- Ennals, Robert; Peyton Jones, Simon (สิงหาคม 2003). การประเมินแบบมองโลกในแง่ดี: กลยุทธ์การประเมินอย่างรวดเร็วสำหรับโปรแกรมที่ไม่เข้มงวด (PDF) . การประชุมนานาชาติว่าด้วยการเขียนโปรแกรมเชิงฟังก์ชัน. สำนักพิมพ์ ACM. เก็บถาวรจากต้นฉบับ(PDF)เมื่อวันที่ 22 สิงหาคม 2025.
- Ludäscher, Bertram (2001-01-24). "บันทึกการบรรยาย CSE 130" . CSE 130: ภาษาโปรแกรม: หลักการและแบบแผน .
- เพียร์ซ, เบนจามิน ซี. (2002). ประเภทและภาษาโปรแกรม . สำนักพิมพ์ MIT . ISBN 0-262-16209-1.
- Sestoft, Peter (2002). "การสาธิตการลดรูปแคลคูลัสแลมบ์ดา" (PDF)ใน Mogensen, T; Schmidt, D; Sudborough, IH (บรรณาธิการ). สาระสำคัญของการคำนวณ: ความ ซับซ้อนการวิเคราะห์ การแปลงรูป บทความที่อุทิศให้กับ Neil D. Jonesบันทึกการบรรยายในวิทยาการคอมพิวเตอร์ เล่มที่ 2566 Springer-Verlag หน้า 420–435 ISBN 3-540-00326-6.
- "การเรียกค่าโดยการส่งค่าและการเรียกค่าโดยการส่งอ้างอิงในภาษาซี"คำอธิบายเกี่ยวกับการเรียกค่าโดยการส่งค่าและการเรียกค่าโดยการส่งอ้างอิงในภาษาซี
{{cite web}}: CS1 maint: บริการเก็บถาวรที่เลิกใช้แล้ว ( ลิงก์ )
ลิงก์ภายนอก
- โปรแกรมแสดงภาพเรขาคณิตปฏิสัมพันธ์ แบบออนไลน์เชิงโต้ตอบซึ่งใช้เครื่องจักรแบบกราฟสำหรับกลยุทธ์การประเมินผลทั่วไปหลายประการ
สรุปเนื้อหา
ข้อมูลสำคัญจากบทความ
ข้อมูลสำคัญเกี่ยวกับ กลยุทธ์การประเมินผล
ใน ภาษาโปรแกรม กลยุทธ์ การประเมิน คือชุดของกฎสำหรับการประเมินนิพจน์ [ 1 ] คำ นี้มักใช้เพื่ออ้างถึงแนวคิดที่เฉพาะเจาะจงมากขึ้นของ กลยุทธ์การส่งผ่านพารามิเตอร์ [ 2 ]...
ตัวอย่าง
เพื่อแสดงให้เห็น การเรียกใช้ฟังก์ชัน f(a, b) อาจประเมินอาร์กิวเมนต์ก่อน a จากนั้น b เก็บผลลัพธ์ไว้ใน ตัวอ้างอิง หรือตำแหน่งหน่วยความจำ ref_a จาก ref_b นั้นประเมินเนื้อหาของฟังก์ชันด้วยตัวอ้างอิงที่ส่งผ่านเข้ามา...
โต๊ะ
นี่คือตารางกลยุทธ์การประเมินและภาษาตัวแทนตามปีที่นำมาใช้ ภาษาตัวแทนจะเรียงลำดับตามลำดับเวลา โดยเริ่มจากภาษาที่นำกลยุทธ์มาใช้ และตามด้วยภาษาที่โดดเด่นที่ใช้กลยุทธ์นั้น [ 10 ] : 434
คำสั่งประเมินผล
ลำดับการดำเนินการ จะกำหนด โครงสร้างต้นไม้ไวยากรณ์นามธรรม ของนิพจน์ ในขณะที่ลำดับการประเมินจะกำหนดลำดับในการประเมินนิพจน์ ตัวอย่างเช่น โปรแกรม Python