Procedures, functions and structured programming
แนะนำใหม่| English | ไทย |
|---|---|
| subroutines/ˈsʌbruːtiːnz/ | ซับรูทีน |
| function/ˈfʌŋkʃn/ | ฟังก์ชัน |
| procedure/prəˈsiːdʒə/ | ขั้นตอน |
| structured programming/ˈstrʌktʃəd ˈprəʊɡræmɪŋ/ | การเขียนโปรแกรมเชิงโครงสร้าง |
| decomposition/ˌdiːkɒmpəˈzɪʃn/ | การแยกย่อย |
| parameters/pəˈræmɪtəz/ | พารามิเตอร์ |
| arguments/ˈɑːɡjuːmənts/ | อาร์กิวเมนต์ |
| signature/ˈsɪɡnɪtʃə/ | ลายมือชื่อ (signature) |
| pass by value/pæs baɪ ˈvæljuː/ | ส่งค่าผ่านค่า |
| pass by reference/pæs baɪ ˈrefrəns/ | ส่งค่าผ่านอ้างอิง |
| global variable/ˈɡləʊbl ˈveərɪəbl/ | ตัวแปรสเกล globale |
| local variable/ˈləʊkl ˈveərɪəbl/ | ตัวแปรสเกล local |
| scope/skəʊp/ | ขอบเขต (scope) |
เทปของ routines ที่ใครก็ยืมได้
- ถึงปี 1951 ทีม EDSAC ในเคมบริดจ์เก็บ library ของ subroutines ไว้บนเทปกระดาษเจาะรู: รากที่สอง, การพิมพ์, logaritms โปรแกรมใด ๆ สามารถอ่านเข้าไปและเรียกใช้ได้
- สิ่งที่ยากคือการกลับมา David Wheeler's trick, "Wheeler jump", เก็บที่อยู่การคืนค่าเพื่อให้ routine กลับไปที่จุดที่ถูกเรียกจากได้
- ทุก function call ที่คุณเขียนวันนี้ทำสิ่งเดียวกัน, และทุก language library สืบเนื่องมาจากเทปนั้น
- บทเรียนนี้คือ procedures และ functions: วิธีนิยาม, วิธีส่งค่าเข้าออก, และวิธีเขียนหนึ่งแบบที่ Paper 2 ให้คะแนน
Procedures และ functions
- Structured programming สร้างโปรแกรมจาก subroutines ขนาดเล็กที่มีชื่อ, แต่ละตัวมีงานเดียว
- ** Procedure ** คือบล็อกที่มีชื่อซึ่งดำเนินการและไม่ส่งค่ากลับ:
PROCEDURE Greet(Name : STRING) … ENDPROCEDURE, เรียกใช้ด้วยCALL Greet("Ada"). - ** Function ** ส่งค่ากลับมาซึ่งเป็นส่วนหนึ่งของนิพจน์:
FUNCTION Square(X : INTEGER) RETURNS INTEGER … RETURN X * X … ENDFUNCTION, ใช้ในResult ← Square(5) + 1.

Workspace (กระบวนการ) ทำสิ่งใดสิ่งหนึ่ง; Function (ฟังก์ชัน) ส่งค่ากลับคืน
Call stack: push when calling, pop when returning
การเรียกใช้ subroutines จะดัน frame ใหม่ขึ้นไปด้านบน; การกลับคืนจะดึง frame นั้นออกและส่งค่ากลับไปยังผู้เรียก การใช้ call ที่กำลังทำงานอยู่จะเป็นเสมอ frame อยู่ด้านบน
ความแตกต่างหลักระหว่าง procedure และ function คือ function:
function ส่งค่ากลับมา (ซึ่งนำไปใช้ในนิพจน์); procedure ทำ action และไม่ส่งค่าใดกลับมา
A function Square(x) ผลตอบแทน x * x. What does the call Square(5) return?
5 × 5 = 25 — ค่าที่ฟังก์ชันส่งกลับไปยังผู้เรียก (frame ที่ถูกดึงออกจาก call stack)
เมื่อใดควรใช้แต่ละแบบ
- ** Procedure ** เมื่อต้องการชุดขั้นตอนเดียวกันหลายจุด: ตรวจสอบข้อมูลเข้า, แสดงเมนู, สลับค่าสองตัว เขียนขั้นตอนครั้งเดียวแล้วเรียกตามชื่อ
- ** Function ** เมื่อต้องคำนวณค่าหนึ่งแล้วนำไปใช้ในนิพจน์: ผลรวม, ค่า
TRUE/FALSE, หรือค่าที่มากกว่าของสองจำนวน ค่าที่ส่งกลับมาจะแทนที่การเรียกใช้ - ใช้ Subroutine เมื่อมีตรรกะปรากฏในหลายที่, บล็อกมีวัตถุประสงค์ที่ชัดเจน, โปรแกรมซับซ้อนพอที่ต้องการ ** Decomposition **, หรือเมื่อต้องการทดสอบชิ้นส่วนหนึ่งโดยอิสระ
เหตุผลที่ดีในการเขียน subroutine คือ:
Subroutines ช่วยลดความซ้ำซ้อน ให้มีวัตถุประสงค์ที่มีชื่อ และสามารถทดสอบแยกต่างหากได้
Parameters, Arguments และ Interface
- ** Parameters ** คือตัวแปรที่ Subroutine ประกาศเพื่อรับค่า ส่วนค่าที่ผู้เรียกให้มาคือ ** Arguments **
- ** Header ** คือบรรทัดแรก:
PROCEDURE Name(Param : TYPE)หรือFUNCTION Name(Param : TYPE) RETURNS TYPE** Interface ** หรือ ** Signature ** คือชื่อ, parameters ตามลำดับพร้อมชนิด, และชนิดที่ส่งกลับมา: ทุกสิ่งที่ผู้เรียกต้องรู้ - ** Return value ** คือสิ่งที่ Function ส่งกลับมาพร้อมกับ
RETURN
จับคู่แต่ละคำศัพท์กับความหมาย
Function vs procedure = ส่งค่าหรือไม่; by value vs by reference = สำเนาหรือต้นฉบับ
ตัวอย่างฝึกหัด: อธิบายแต่ละคำใน Header
FUNCTION Pass2(Count : INTEGER) RETURNS BOOLEAN.FUNCTION: Subroutine ที่ส่งค่ากลับPass2:.Identifier ที่ใช้ในการเรียกใช้มันCount: Parameter, Identifier ที่รับ Argument ที่ส่งเข้ามาINTEGER: ชนิดข้อมูลของ Parameter นั้นRETURNS BOOLEAN: ชนิดข้อมูลของค่าที่ Function ส่งกลับมา ห้าส่วน คะแนนละหนึ่ง
ใน FUNCTION Pass2(Count : INTEGER) RETURNS BOOLEAN, identifier Count คือ ____.
Parameter รับ argument ที่ผู้เรียกส่งเข้ามาและใช้งานภายในฟังก์ชันเหมือนตัวแปรท้องถิ่น
Pass by Value และ Pass by Reference
- ** Pass by Value **: Routine ได้รับ ** Copy **, ดังนั้นการเปลี่ยนแปลงภายในจะไม่ส่งผลต่อผู้เรียก ใช้สำหรับ Input ที่ Routine อ่านเพียงอย่างเดียว
- ** Pass by Reference **:Routine ได้รับ Ref ของตัวแปรของผู้เรียกเอง ดังนั้นการเปลี่ยนแปลง ** จะ ** ส่งผลต่อผู้เรียก ใช้เมื่อ Routine ต้องอัปเดต Argument เช่นใน
Swap - Cambridge เขียน Mode ใน Header,
BYVALหรือBYREF, ก่อนแต่ละ Parameter หากไม่เขียนอะไรBYVALจะถูกสมมติไว้

สำเนา, หรือลิงก์ไปยังตัวแปรของผู้เรียกใช้
ตัวอย่างฝึกหัด: ผลลัพธ์คืออะไร?
PROCEDURE Adjust(BYREF X : INTEGER, BYVAL Y : INTEGER)
X <- X + Y
Y <- Y * 2
ENDPROCEDURE
A <- 5
B <- 3
CALL Adjust(A, B)
OUTPUT A, B
Xเป็น Ref ของA, ดังนั้นAจึงเป็น 8Yเป็น Copy ของB, ดังนั้นการคูณสองYก็ทำให้Bยังคงอยู่ที่ 3- ผลลัพธ์คือ
8, 3หาก Header บอกว่าBYVAL X,Aจะยังคงเป็น 5
ในตัวอย่างที่แสดงผลลัพธ์แล้ว ค่าของ A หลัง CALL Adjust(A, B) คืออะไร?
X ถูกส่ง BYREF ดังนั้น X ← X + Y จึงเพิ่ม 3 เข้ากับ A ของผู้เรียก: 5 + 3 = 8 B ยังคงเป็น 3 เพราะ Y เป็นสำเนา
ตัวแปร Local และ Global
- ** Local variable ** ถูกประกาศภายใน Subroutine และมีอยู่เฉพาะขณะทำงาน; ** Global variable ** ถูกประกาศภายนอกและมองเห็นได้ทุกที่ พื้นที่ที่ชื่อนั้นมองเห็นได้เรียกว่า ** Scope ** ของมัน
- แนะนำให้ใช้ Locals:同一 Identifier สามารถใช้ซ้ำในที่อื่นโดยไม่ขัดแย้ง, ค่าไม่สามารถเปลี่ยนโดยไม่ตั้งใจโดยส่วนอื่นของโปรแกรม, หน่วยความจำจะถูกปลดปล่อยเมื่อกลับคืน, และ Subroutine มีความสมบูรณ์ในตัวเอง
- Local ถูกสร้างใหม่ทุกครั้งที่เรียกใช้ ดังนั้นจึงไม่สามารถเก็บค่าระหว่างครั้งเรียกได้ Routine ที่สร้าง string ต่อเนื่องหลายครั้งจำเป็นต้องใช้ string นั้นเป็น Global หรือส่งเข้า
BYREF

Global (ตัวแปรสากล) เข้าถึงได้ทุกที่; Local (ตัวแปรเฉพาะที่) มีอยู่ภายใน subroutine ของมันเองเท่านั้น
ตัวแปรท้องถิ่นมีอยู่เฉพาะภายใน subroutine ที่ประกาศไว้ ในขณะที่ตัวแปร global สามารถมองเห็นได้ทุกที่ในโปรแกรม
การเก็บตัวแปรให้เป็นท้องถิ่นจะจำกัดขอบเขต (scope) ของมัน หลีกเลี่ยงการชนชื่อกัน และทำให้ subroutine ทดสอบแยกต่างหากได้ — ควรหลีกเลี่ยงการใช้ globals尽可能少
ข้อดีของการใช้ตัวแปรท้องถิ่นคืออะไร? เลือก ทั้งหมด ที่ถูกต้อง
Local ถูกสร้างใหม่ทุกครั้งที่ถูกเรียกดังนั้นจึงไม่สามารถรักษาค่าระหว่าง calls ได้ นี่คือหน้าที่เดียวที่ global หรือ parameter BYREF แทนที่
เปลี่ยน Procedure เป็น Function
- เปลี่ยน
PROCEDUREเป็นFUNCTIONและเพิ่มRETURNS <type>ลงใน Header - แทนที่
OUTPUT, หรือBYREFParameter ที่นำผลลัพธ์ออก, ด้วยคำสั่งRETURN - เปลี่ยนทุกการเรียกให้ใช้ค่าที่ส่งกลับมา:
Result ← Unpack(Text)แทนCALL Unpack(Text, Result)
เพื่อแปลง procedure เป็น function คุณเปลี่ยน header เป็น FUNCTION with RETURNS, เปลี่ยน OUTPUT เป็น RETURN และเปลี่ยนการเรียกใช้ให้ใช้ค่าที่ส่งกลับมา
สามการเปลี่ยนแปลง สามคะแนน: header, RETURN และการเรียกใช้
ตัวอย่างฝึกหัด: เขียน Module สำหรับ Paper 2
- Global Array
Score : ARRAY[1:50] OF INTEGERเก็บคะแนนสอบ เขียน FunctionCountAbove(Limit : INTEGER)ที่ส่งกลับจำนวนคะแนนที่มากกว่าLimit
FUNCTION CountAbove(BYVAL Limit : INTEGER) RETURNS INTEGER
DECLARE Index, Count : INTEGER
Count <- 0
FOR Index <- 1 TO 50
IF Score[Index] > Limit THEN
Count <- Count + 1
ENDIF
NEXT Index
RETURN Count
ENDFUNCTION
- ระบบให้คะแนนตามคุณสมบัติ: Header พร้อม Parameter และชนิดที่ส่ง回来, การประกาศ Local, Counter เริ่มต้นก่อน Loop, Loop วนรอบทุก Element, เงื่อนไขกับการเปรียบเทียบที่ถูกต้อง, การอัปเดตภายใน, โครงสร้างปิด, และ ONE
RETURNหลัง Loop - Module ที่ยังไม่เสร็จสิ้นก็ได้รับคะแนนสำหรับแต่ละส่วนที่ถูกต้อง ดังนั้นให้เขียนทั้งหมด

แต่ละส่วนของคำตอบโมดูลมีคะแนนของตนเอง
วางองค์ประกอบของคำตอบ Paper 2 module ตามลำดับการเขียน
Header, declare, loop, condition, update, return แต่ละส่วนคือคะแนน แม้ส่วนอื่นจะผิดก็ตาม
คะแนนที่หลุดหายไป
- Routine ที่ต้องเปลี่ยนตัวแปรของผู้เรียกต้องใช้
BYREFใน Header; หากไม่มีBYVALจะถูกสมมติไว้และการเปลี่ยนแปลงจะหายไป - ฟังก์ชันมี
RETURNหนึ่งตัว, หลังลูป, และENDFUNCTION. การมีRETURNภายในลูปจะทำให้ลูปสิ้นสุดที่องค์ประกอบแรกทันที DECLAREทุก Local, และเริ่มต้น Counter หรือ Total เป็น 0 ก่อน Loop- "Efficient pseudocode" หมายความว่าย้ายงานที่ไม่เปลี่ยนออกจาก Loop, หยุดการค้นหาที่การจับคู่ครั้งแรก, และไม่เรียกซ้ำ whose result could be stored
เพื่อให้ loop มีประสิทธิภาพมากขึ้น ค่าที่ไม่เปลี่ยนตามตัวนับ loop ควร:
การยก loop invariant ออกจะช่วยหลีกเลี่ยงการคำนวณค่าเดิมซ้ำในแต่ละรอบ
คุณเข้าใจแล้ว
- a ** procedure ** ทำการและถูก
CALLed; a ** function ** ส่งค่ากลับมาใช้ในนิพจน์ - the ** header ** ชื่อ Routine, ** parameters ** พร้อมชนิดและชนิดที่ส่งกลับมา; ** arguments ** คือค่าที่ส่งเข้ามา
- ** BYVAL ** ส่ง Copy (ค่าเริ่มต้น); ** BYREF ** ให้ Routine เปลี่ยนตัวแปรของผู้เรียกได้
- prefer ** local ** variables; a local ถูกสร้างใหม่ทุกครั้งที่เรียก, a ** global ** รักษาค่าของมัน
- โมดูล Paper 2: หัวข้อ, การประกาศ, การเริ่มต้น, ลูป, เงื่อนไข, การอัปเดต, โครงสร้างปิด, และ
RETURNหนึ่งตัว