!"
แผนการสอนประจําบทเรียน รายชือ่ อาจารยผจู ดั ทํา สุณี รักษาเกียรติศักดิ์ หัวขอของเนื้อหา ตอนที่ 5.1 ชนิดขอมูลแบบสแตก (2 คาบ) เรือ่ งที่ 5.1.1
การกําหนดคุณลักษณะเฉพาะของสแตก
เรือ่ งที่ 5.1.2
การสรางสแตกดวยอะเรย
เรือ่ งที่ 5.1.3
การสรางสแตกดวยลิงคลสิ ต
เรือ่ งที่ 5.1.4
การใชสแตก
เรือ่ งที่ 5.1.5
การเปรียบเทียบประสิทธิภาพของการสรางสแตกดวยอะเรยและลิงคลสิ ต
ตอนที่ 5.2 การประยุกตใชสแตก (1 คาบ) เรือ่ งที่ 5.2.1
การจัดการหนวยความจํา
เรือ่ งที่ 5.2.2
การใชสแตกในกระบวนการเรียกใชโพรซีเจอรหรือฟงชันก
เรือ่ งที่ 5.2.3
การตรวจสอบอักขระสมดุล
เรือ่ งที่ 5.2.4
การคํานวณคานิพจนเลขคณิต
แนวคิด 1. ชนิดขอมูลแบบสแตกมีการประยุกตใชมากในวิทยาการคอมพิวเตอร 2. ชนิดขอมูลแบบสแตกมีโครงสรางขอมูลแบบเชิงเสน (linear) 3. ชนิดขอมูลแบบสแตกมีคุณสมบัติเฉพาะคือ สมาชิกขอมูลที่เขาไปในสแตกทีหลังจะออกกอน (Last In First Out หรือ LIFO) 4. การดําเนินงานกับสแตกไดแก การสรางสแตก (create) การนําสมาชิกลงสแตก (push) การ นําสมาชิกออกจากสแตก (pop) การทดสอบวาสแตกวางหรือไม (empty) การทดสอบวาส แตกเต็มหรือไม (full) การทําใหสแตกเปนสแตกวาง (clear) 5. การที่จะใหผูใชเขาใจคุณสมบัติของสแตกที่ตรงกัน จะตองมีออกแบบคุณสมบัตขิ องสแตก โดยการเขียนคุณลักษณะเฉพาะ (specification) ของสแตกที่เราตองการอยางชัดเจน
#$ 6. เมือ่ มีการเขียนคุณลักษณะเฉพาะทีช่ ดั เจนแลว ผูส รางก็สามารถจะสรางสแตกตามทีอ่ อก แบบไวได โดยรายละเอียดของการสรางจะซอนจากผูใช 7. ในการสรางสแตกผูส รางตองดําเนินการสองสิง่ ใหญ ๆ คือ เลือกการแทนที่ขอมูลของสแตก และสรางการดําเนินงานโดยใชการแทนทีข่ อ มูลทีเ่ ลือกแลว 8. ในการสรางสแตกผูสรางสามารถที่จะเลือกใชการแทนที่ขอมูลของสแตกแบบอะเรยหรือลิงค ลิสตกไ็ ด 9. ในการใชชนิดขอมูลแบบสแตก ผูใชเพียงแตใชตามคุณลักษณะเฉพาะที่ออกแบบไวเทานั้น ไมตองสนใจวาผูสรางจะเลือกการแทนที่ขอมูลของสแตกอยางไร และสรางมาดวยวิธีการ อยางไร วัตถุประสงค หลังจากศึกษาบทเรียนที่ 5 แลว นักศึกษาสามารถ 1. บอกถึงคุณลักษณะเฉพาะของสแตกได วาสแตกมีสมาชิกเปนอยางไร มีโครงสรางขอมูล อยางไร และสามารถดําเนินงานอะไรกับสแตกไดบา ง 2. สรางสแตกโดยใชการแทนที่ขอมูลแบบอะเรยได 3. สรางสแตกโดยใชการแทนที่ขอมูลแบบลิงคลิสตได 4. ใชสแตกหรือทดสอบการใชงานของสแตกได 5. เปรียบเทียบประสิทธิภาพระหวางการสรางสแตกดวยอะเรยและลิงคลสิ ต ในรูปของฟงชันบิ๊ก โอ (function Oh) ได 6. บอกตัวอยางการประยุกตใชสแตกในสาขาวิชาวิทยาการคอมพิวเตอรได กิจกรรมการเรียนการสอน กิจกรรมทีน่ กั ศึกษาตองทําสําหรับการเรียนการสอน ไดแก 1. ศึกษาเอกสารชุดวิชา/โฮมเพจชุดวิชา ตอนที่ 5.1 และตอนที่ 5.2 2. ทํากิจกรรมของบทเรียนที่ 5 3. ทําแบบประเมินผลของบทเรียนที่ 5 เอกสารประกอบการสอน 1. เอกสารชุดวิชา สื่อการสอน 1. โฮมเพจชุดวิชา 2. สไลดประกอบการบรรยาย (Powerpoint) 3. โปรแกรมคอมพิวเตอร
#% ประเมินผล 1. ประเมินผลจากกิจกรรมที่ทํา 2. ประเมินผลจากคําถามทายบทเรียน
#& ตอนที่ 5.1 สแตก หัวเรื่อง เรือ่ งที่ 5.1.1
การกําหนดคุณลักษณะเฉพาะของสแตก
เรือ่ งที่ 5.1.2
การสรางสแตกดวยอะเรย
เรือ่ งที่ 5.1.3
การสรางสแตกดวยลิงคลสิ ต
เรือ่ งที่ 5.1.4
การทดสอบการใชสแตก
เรือ่ งที่ 5.1.5
การเปรียบเทียบประสิทธิภาพของการสรางสแตกดวยอะเรยและลิงคลสิ ต
แนวคิด 1. การกําหนดคุณลักษณะเฉพาะของสแตกมีความสําคัญมากในการสือ่ สารระหวางผูส รางสแตก และผูใชสแตก โดยที่ผูใช ใชโดยทราบขอกําหนดจากคุณลักษณะเฉพาะที่กําหนด และผูส ราง ก็สรางตามคุณลักษณะเฉพาะทีก่ าํ หนด 2. ผูใชไมตองสนใจในรายละเอียดของการสราง ผูใชเพียงแตทราบวาสแตกมีคุณสมบัติอยางไร และสามารถดําเนินงานอะไรกับสแตกไดบา ง ในการดําเนินงานแตละอยางผูใชตองใหขอมูล เขาอยางไร และผูใชจะไดผลลัพธอะไรหลังจากใชการดําเนินงานนั้น ๆ 3. ผูสรางสามารถที่จะสรางสแตกโดยใชการแทนที่ขอมูลแบบอะเรยหรือลิงคลิสตก็ได โดยไมมี ผลตอการเรียกใชของผูใช วัตถุประสงค หลังจากที่ศึกษาตอนที่ 5.1 แลว นักศึกษาสามารถ 1. 2. 3. 4. 5. เรื่องที่ 5.1.1
เขียนคุณลักษณะเฉพาะของชนิดขอมูลแบบสแตก และบอกการดําเนินงานทีส่ าํ คัญของสแตก สรางสแตกดวยอะเรยได สรางสแตกดวยลิงคลสิ ตได ทดสอบการใชสแตกเพือ่ ตรวจสอบวาสแตกทีผ่ สู ราง สรางมา สรางไดถกู ตอง เปรียบเทียบประสิทธิภาพของการสรางสแตกดวยอะเรยและลิงคลสิ ตได การกําหนดคุณลักษณะเฉพาะของสแตก
ในการกําหนดคุณลักษณะเฉพาะของสแตก จะประกอบดวย 3 สวน ใหญ ๆ คือ • การกําหนดวาสมาชิกของสแตกที่ใชมีชนิดขอมูลเปนอะไร • การกําหนดวาสมาชิกของสแตกมีโครงสราง หรือความสัมพันธกันอยางไร • การกําหนดวาเราสามารถดําเนินงานอะไรกับสแตกไดบา ง
#' โดยรายละเอียดของคุณลักษณะเฉพาะของสแตกจะเปนดังนี้ คุณลักษณะเฉพาะ 5.1 คุณลักษณะเฉพาะของสแตก สมาชิก:
สมาชิกของสแตกจะมีชนิดเปนอยางไรก็ได แตในที่นี้สมมติใหมีชนิดเปน StdElement
โครงสราง:
สแตกจะมีโครงสรางแบบเชิงเสน และอันดับของการนําสมาชิกเขาและออกจากสแตกมี ความสําคัญ คือ สมาชิกที่เขาไปอยูในสแตกกอนจะออกจากสแตกหลังสมาชิกที่เขาไปใน สแตกทีหลัง นัน่ คือ การเขาทีหลังออกกอน (Last ln First Out หรือ LIFO)
การดําเนินงาน: มีการดําเนินงานทั้งหมด 6 การดําเนินงาน สัญลักษณ : ให S – pre
เปน สถานะของสแตกกอนการดําเนินงาน
S – post
เปน สถานะของสแตกหลังการดําเนินงาน
Pre
เปนเงือ่ นไขเริม่ ตนทีต่ อ งเปนจริงกอนการดําเนินงาน
Post
เปนผลลัพธจากการดําเนินงาน
Create Pre:
ไมมี
Post:
มีสแตกวางเกิดขึ้น
Push ( E : StdElement ) Pre:
สแตกยังไมเต็ม (ยังมีที่วาง)
Post:
สแตกจะมี E เปนสมาชิกลาสุดของสแตก หรือสมาชิกบนสุดของสแตก
Pop ( VAR E : StdElement ) Pre:
สแตกตองไมวาง
Post:
E เปนสมาชิกลาสุดของ S – pre และ S – post จะไมมี E เปนสมาชิกอีกตอไป
Empty : boolean Pre:
ไมมี
Post:
ถา สแตกไมมีสมาชิกอยูเลย แลว Empty จะเปนจริง ไมเชนนั้น Empty จะเปนเท็จ
Full : boolean Pre:
ไมมี
Post:
ถา สแตกมีสมาชิกเต็ม แลว Full จะเปนจริง ไมเชนนั้น Full จะเปนเท็จ
Pre:
ไมมี
Post:
สแตกจะเปนสแตกวาง
Clear
#( หมายเหตุ ในบทเรียนที่ 3 เราไดกลาวถึงชนิดขอมูล StdElement เปนดังนี้ TYPE
StdElement = RECORD Key : Keytype; Data : Datatype END;
ตัวอยาง 5.1 ตัวอยางสมาชิกขอมูลชนิดตาง ๆ ตัวอยางของชนิดขอมูล StdElement เชน Student หรือ Book ซึง่ มีชนิดเปนดังนี้ TYPE Student = RECORD Id : integer; {Key part} Name : string[20]; {Data part: Name,Math,Stat,Comp} Math, Stat, Comp : integer END;
TYPE Book = RECORD Call_no Author
: integer; {Key part} : string[15]; {Data part: Author, Title, Publisher, Status} Title : string[20]; Publisher : string[15]; Status : char; {I = Checked In, O = Checked Out}; END;
กิจกรรม 5.1 ฝกการเขียนคุณลักษณะเฉพาะของการดําเนินงาน ในคุณลักษณะเฉพาะขางตนของการดําเนินงาน Pop มีเงื่อนไขเริ่มตน (pre) วา สแตกตองไมเปน สแตกวาง หากเราเอาเงือ่ นไขเริม่ ตนนีอ้ อกไป นัน้ คือสแตกจะเปนสแตกวางก็ได ถาสแตกเปนสแตกวาง การ ดําเนินงานของ Pop ก็ไมสาํ เร็จ ซึง่ จะสงผลลัพธออกมาทางพารามิเตอร Fail จงเขียนคุณลักษณะเฉพาะของการดําเนินงาน Pop ใหมตามขอกําหนดที่กลาวขางตน เรื่องที่ 5.1.2 การสรางสแตกดวยอะเรย ในการสรางสแตกดวยอะเรย หมายถึงเราเลือกการแทนที่ขอมูลของสแตกดวยอะเรยซึ่งเปนการจัด สรรเนื้อที่หนวยความจําแบบสแตติก นัน้ คือ จะมีการกําหนดขนาดของสแตกลวงหนาวาจะมีขนาดเทาใดและ จะมีการจัดสรรเนื้อที่หนวยความจําใหเลย การสรางสแตกนีจ้ ะสรางในยูนติ ชือ่ StackUA ดังนี้ UNIT StackUA; INTERFACE CONST Maxsize = 100 {User supplied}; TYPE
KeyType = 1..100 {ID}; DataType = RECORD Name : string[20]; Math, Stat, Comp : integer; END; StdElement = RECORD
1 2 3 4 5 6 7 8 9 10 11
#) VAR
Key : KeyType; {User supplied} Data : DataType; {User supplied} END; S : ARRAY[1..Maxsize] OF StdElement; Top : 0..Maxsize;
PROCEDURE PROCEDURE PROCEDURE FUNCTION FUNCTION PROCEDURE
Create; Push(E : StdElement); Pop(VAR E : StdElement); Empty : boolean; Full : boolean; Clear;
IMPLEMENTATION PROCEDURE Create; BEGIN Top := 0 END; PROCEDURE Push(E : StdElement); BEGIN Top := Top + 1; S[Top] := E; END; PROCEDURE Pop(VAR E : StdElement); BEGIN
END; FUNCTION Empty : boolean; BEGIN IF Top = 0 THEN Empty := true ELSE Empty := false; END; FUNCTION Full : boolean; BEGIN IF Top = Maxsize THEN Full := true ELSE Full := false END; PROCEDURE Clear; BEGIN Top := 0 END; END.
โปรแกรม 5.1 การสรางสแตกดวยอะเรย ขอสังเกต ในการนิยาม VAR ขางตน ตัวแปร S และ Top จะเปนตัวแปรที่บงถึงสแตก
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
#* กิจกรรม 5.2 ทดสอบการสรางชนิดขอมูลสแตกดวยอะเรย จงเขียนโคดของการดําเนินงาน Push ใหสมบูรณ พรอมทดสอบ UNIT StackUA ของโปรแกรม 5.1 โดยใหสมาชิกของสแตกมีชนิดเปน Student และใหชื่อไฟล source code เปน StackUA.pas และจง สังเกตวาเมือ่ คอมไพลผา นแลวจะเกิด object code ชื่อ StackUA.TPU
#! เรื่องที่ 5.1.3 การสรางสแตกดวยลิงคลิสต ในการสรางสแตกดวยลิงคลสิ ต หมายถึงเราเลือกการแทนทีข่ อ มูลของสแตกดวยลิงคลสิ ตซง่ึ เปนการ จัดสรรเนื้อที่หนวยความจําแบบไดนามิก นัน้ คือ หนวยความจําจะถูกจัดสรรเมื่อมีการขอใชจริง ๆ ระหวางการ ประมวลผลโปรแกรมผานตัวแปรชนิด pointer การสรางสแตกนีจ้ ะสรางในยูนติ ชือ่ StackUL ดังแสดงใน โปรแกรม 5.2
## UNIT StackUL; INTERFACE CONST Maxsize TYPE
= 100 { User supplied };
KeyType DataType
= 1..100 {ID}; = RECORD Name : string[20]; Math, Stat, Comp : integer; END; StdElement = RECORD Key : Keytype ; Data : Datatype ; END; pointer = ^Node; Node = RECORD El : StdElement; Next : pointer; END; VAR Top : pointer; {Top is stack} PROCEDURE Create; PROCEDURE Push (E : StdElement); PROCEDURE Pop (VAR E : StdElement); FUNCTION Empty : boolean; FUNCTION Full : boolean; PROCEDURE Clear; IMPLEMENTATION PROCEDURE Create; BEGIN Top := nil; END; PROCEDURE Push(E : StdElement); VAR P : pointer ; BEGIN new(P); P^.El := E; P^.Next := Top; Top := P; END; PROCEDURE Pop(VAR E : StdElement); VAR P : pointer; BEGIN P := Top; E := P^.EL; Top := Top^.Next; dispose(P); END; FUNCTION Empty : boolean ; BEGIN IF Top = NIL THEN Empty := true ELSE Empty := false; END; FUNCTION Full : boolean ; BEGIN Full := false END; PROCEDURE Clear; VAR P : pointer; BEGIN WHILE Top <> NIL DO BEGIN P := Top; Top := Top^.Next; dispose(P); END; END; END.
โปรแกรม 5.2 การสรางสแตกดวยลิงคลิสต
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
#" หมายเหตุ 1. ถาเลือกใชการสรางสแตกดวยลิงคลสิ ต สแตกจะไมมีวันเต็มคราบใดที่ยังมีเนื้อที่ในหนวยความ จํา (ในฮีป) 2. เราสามารถจะใชการดําเนินงานทีม่ อี ยูแ ลว เชน Empty และ Pop ใน Clear ได ดังนี้ PROCEDURE Clear; VAR E : StdElement ; BEGIN WHILE NOT Empty DO Pop(E); END;
กิจกรรม 5.3 ทดสอบการสรางชนิดขอมูลสแตกดวยอะเรย จงทดสอบ UNIT StackUL ตามตัวอยางที่กลาวขางตน โดยใหสมาชิกของสแตกมีชนิดเปน Student และใหชื่อไฟล source code เปน StackUL.pas และจงสังเกตวาเมื่อคอมไพลผานแลวจะเกิด object code ชื่อ StackUL.TPU เรื่องที่ 5.1.4 การใชและการทดสอบสแตก เมือ่ เราสรางสแตกขึน้ มาแลว ไมวาจะเปนการแทนที่ขอมูลสแตกดวยอะเรย (StackUA.TPU) หรือ การแทนทีข่ อ มูลสแตกดวยลิงคลสิ ต (StackUL.TPU) ผูใชสามารถจะเรียกใชการดําเนินงานของสแตกไดใน รูปแบบเดียวกัน ไมขึ้นกับการแทนที่ขอมูลที่ใช ตัวอยางการทดสอบการใชงานของสแตกที่มีชนิดขอมูลสมาชิกเปน Book เปนดังนี้ PROGRAM StackT; USES wincrt, StackUA; VAR Choice : char; Data : Book; PROCEDURE MainMenu; BEGIN writeln(' ----------------------------------------'); writeln(' | MAIN MENU |'); writeln(' ----------------------------------------'); writeln(' | 1. Push data into Stack |'); writeln(' | 2. Pop data out of Stack |'); writeln(' | 3. Test whether Stack is empty |'); writeln(' | 4. Clear Stack |'); writeln(' | 9. Exit |'); writeln(' ----------------------------------------'); END; PROCEDURE GetElement (VAR E : Book); BEGIN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
"$ writeln('Please input data '); write('Call_no : '); readln(E.Call_no); write('Author : '); readln(E.Author); write('Title : '); readln(E.Title); write('Publisher : '); readln(E.Publisher); write('Status : '); readln(E.Status); write('Price : '); readln(E.Price); END; PROCEDURE PrintElement (E : Book); BEGIN WITH E DO writeln (Call_no:7, Author:15, Title:20, Publisher:15, Status:9, Price:6); END; BEGIN { Main } clrscr; create; MainMenu; writeln; write('Please enter your selection : '); readln(Choice); WHILE Choice <> '9' DO BEGIN CASE Choice OF '1' : BEGIN GetElement(Data); Push(Data); writeln ('One element is pushed on top of Stack'); END; '2' : BEGIN Pop(Data, Fail); IF Fail THEN writeln('Stack is empty, cannot pop') ELSE BEGIN writeln ('The top element of the stack before popping is:'); writeln ('Call_no':7, 'Author':15, 'Title':20, 'Publisher':15, 'Status':9, 'Price':6); PrintElement(Data); END; END; '3' : BEGIN IF Empty THEN writeln('Stack is empty') ELSE writeln('Stack is not empty'); END; '4' : BEGIN Clear; writeln ('Stack is clear'); END;
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
"% ELSE
MainMenu;
END {CASE}; writeln; write('Please enter your selection : '); readln(Choice); END {WHILE}; writeln ('$$$ END TESTING GOOD BYE $$$'); readln; END.
69 70 71 72 73 74 75 76 77
โปรแกรม 5.3 การทดสอบการใชงานสแตก กิจกรรม 5.4 ทดสอบการใชงานสแตก จงทดสอบการใชงานสแตกโดยคอมไพลและรันโปรแกรมขางตน โดยใหชื่อเปน StackT.pas โดยทํา 2 ครัง้ ครั้งแรกเรียกใช USES StackUA และครั้งที่ 2 เรียกใช USES StackUL ซึ่งจะเห็นไดวาผูใชไมตอง เปลีย่ นแปลงโปรแกรมการเรียกใชเลย ไมวา ผูส รางจะสรางสแตกดวยอะเรยหรือลิงคลสิ ตกต็ าม เรื่องที่ 5.1.5 เปรียบเทียบประสิทธิภาพของการสรางสแตกดวยอะเรยและลิงคลิสต ในการเปรียบเทียบประสิทธิภาพของการสรางสแตกดวยอะเรยและลิงคลสิ ตนน้ั เราจะเปรียบเทียบ ใน 2 ดาน คือ 1. การเลือกการแทนทีข่ อ มูล 2. การสรางการดําเนินงาน ขอเปรียบเทียบของการเลือกการแทนที่ขอมูลของสแตก การเลือกการแทนทีข่ อ มูลสแตกดวยอะเรย มีขอจํากัดสําหรับขนาดของสแตกและจะตองจองเนื้อที่ เทากับขนาดที่ใหญที่สุดของสแตกไวเลย เพราะเปนการจัดสรรเนือ้ ทีใ่ นหนวยความจําแบบสแตติก สวนการ เลือกการแทนทีข่ อ มูลสแตกดวยลิงคลสิ ต ไมมีขอจํากัดของขนาดของสแตกและหนวยความจําจะถูกใชก็ตอ เมื่อมีขอมูลจริงๆ แลวเทานัน้ เพราะเปนการจัดสรรเนื้อที่หนวยความจําแบบไดนามิก ซึ่งทําใหประหยัดเนื้อ ที่ในหนวยความจํามากกวา แตการเขียนโคตสําหรับการแทนที่ขอมูลสแตกดวยอะเรยงายกวาการแทนที่ขอ มูลดวยลิงคลสิ ต ขอเปรียบเทียบของประสิทธิภาพของการดําเนินงาน ขอเปรียบเทียบของการดําเนินงานทัง้ 6 การดําเนินงานของสแตกทีส่ รางดวยอะเรยและสแตกทีส่ ราง ดวยลิงคลิสตดังแสดงในตาราง 5.1
"& ตาราง 5.1 เปรียบเทียบประสิทธิภาพของการดําเนินงานของสแตกที่สรางดวยอะเรยและลิงคลิสต การดําเนินงาน
สรางดวยอะเรย
สรางดวยลิงคลสิ ต
Create
O(1)
O(1)
Push
O(1)
O(1)
Pop
O(1)
O(1)
Full
O(1)
-
Empty
O(1)
O(1)
Clear
O(1)
O(n)
จะเห็นไดวาทุกการดําเนินงานของสแตกไมวาจะใชอะเรยหรือลิงคลิสตจะใชเวลาคงที่คือ 0(1) ไม ขึน้ อยูก บั จํานวนของขอมูลในสแตกยกเวนการดําเนินงานของ Clear สําหรับสแตกทีส่ รางดวยลิงคลสิ ตซง่ึ เวลาที่ใช Clear สแตกขึ้นอยูกับจํานวนขอมูลที่อยูในสแตกคือ 0(n) เมื่อ n คือจํานวนขอมูลที่อยูในสแตก
"' ตอนที่ 5.2 การประยุกตใชสแตก หัวเรื่อง เรือ่ งที่ 5.2.1
การจัดการหนวยความจํา
เรือ่ งที่ 5.2.2
การสรางสแตกในกระบวนการเรียกใชโพรซีเจอรหรือฟงชันก
เรือ่ งที่ 5.2.3
การตรวจสอบอักขระสมดุล
เรือ่ งที่ 5.2.4
การคํานวณคานิพจนเลขคณิต
แนวคิด ชนิดขอมูลแบบสแตกมีการประยุกตใชมากในการเขียนโปรแกรมของสาขาวิชาทางวิทยาการ คอมพิวเตอร เชน การจัดสรรหนวยความจําในการประมวลผลโปรแกรม และการคํานวณคานิพจนเลขคณิต เปนตน วัตถุประสงค หลังจากที่ศึกษาตอนที่ 5.2 แลว นักศึกษาสามารถ 1. บอกถึงการประยุกตใชชนิดขอมูลแบบสแตกในการโปรแกรมของสาขาวิชาวิทยาการ คอมพิวเตอร เรื่องที่ 5.2.1 การจัดการหนวยความจํา ในการจัดการหนวยความจําของคอมพิวเตอร โครงสรางขอมูลแบบสแตกคู (double stack) ไดนาํ มาประยุกตใช หนวยความจําสามารถแทนดวยโครงสรางขอมูลแบบอะเรยดังนี้ CONST M : Number of memory bytes VAR Ram : ARRAY [0..M-1] OF byte;
โดยทั่วไปการจัดการหนวยความจําจะเปนดังนี้ Reserved By System Low Memory
Programs And procedures
Dynamic variable heap !
Local
Available space
Operating Variable system stack " High Memory
ลองมาดูตัวอยางของการจัดเก็บคาของตัวแปรในหนวยความจําในตัวอยางโปรแกรม 5.4
"(
2 3 6
3 4
5
1 2 7 8
PROGRAM MemAllocation; VAR P, Q, R : { User defined } ; PROCEDUDE A; VAR S : { User defined } ; BEGIN { A } ----> ----> B ; ----> END ; PROCEDURE B; TYPE pointer = ^Node; Node = RECORD El : StdElement ; Next : pointer ; END ; VAR D : pointer ; X, Y : {User defined}; BEGIN { B } ----> new(D); ----> new(D); new (D); ----> END; BEGIN {Main} ----> ----> A; ----> END. ---->
โปรแกรม 5.4 การประยุกตใชสแตกในการจัดการหนวยความจํา ภาพของคาของตัวแปรในหนวยความจําเมื่อกําลังประมวลผลโปรแกรม ณ จุดตาง ๆ เปน ดังนี้ คําสัง่ แรกของ BEGIN {Main} เนื้อที่หนวยความจําในสแตกจะถูกใชสําหรับตัวแปร P, Q, R Reserved By System
Programs And procedures
P Operating Q system R ↑ Top of heap
2. เมื่อเรียกใชโพรซีเจอร A ณ คําสัง่ แรกของ BEGIN {A}
↑ Top of stack
")
Reserved By System
Programs And procedures
S
↑ Top of heap
P Operating Q system R
↑ Top of stack
3. เมื่อเรียกใชโพรซีเจอร B ณ คําสัง่ แรกของ BEGIN {B} Reserved By System
Programs And procedures
X S Y
P Operating Q system R
↑ Top of heap
↑ Top of stack
D
X S Y
↑ Top of heap
↑ Top of stack
D
D
X S Y
↑ Top of heap
↑ Top of stack
4. หลังคําสัง่ new (D) Reserved By System
Programs And procedures
P Operating Q system R
5. หลังคําสัง่ new (D) Reserved By System
Programs And procedures
D
P Operating Q system R
"* 6. หลังจากเสร็จสิน้ การประมวลผลของโพรซีเจอร B (ตัวแปร X, Y จะถูก Pop ออกจาก สแตก ) Reserved By System
Programs And procedures
S D
D
D ↑ Top of heap
P Operating Q system R
↑ Top of stack
ขอสังเกต ถาเราเรียกใชตัวแปรแบบไดนามิกและเราไมไดทําการ dispose เมื่อไมตองการใชแลว (เชน เราออกจากโพรซีเจอร B แลว ) เนื้อที่ใน heap ก็จะเสียไปไมสามารถนํามาใชไดอีก 7. หลังจากเสร็จสิน้ การประมวลผลของโพรซีเจอร A (ตัวแปร S จะถูก pop ออกจากสแตก) Reserved By System
Programs And procedures
D
D
D
P Operating Q system R
↑ Top of heap
↑ Top of stack
8. หลังจากเสร็จสิน้ การประมวลผลของโปรแกรม Main (ตัวแปร P, Q, R จะถูก pop ออกจากสแตก) Reserved By System
Programs And procedures
D
D
D ↑ Top of heap
Operating system ↑ Top of stack
กิจกรรม 5.5 การสรางสแตกคู จงสรางสแตกคูดวยอะเรย โดยใหมีขอ กําหนดของสแตกเหมือนเดิม แตพารามิเตอรของการดําเนิน งานทั้ง 6 จะเปลีย่ นไปดังนี้ TYPE
StackNo = 1..2;
PROCEDURE
Create;
PROCEDURE
Push (E : StdElement; S : StackNo);
PROCEDURE
Pop (Var E : StdElement; S : StackNo);
"! FUNCTION
Empty (S : StackNo) : boolean;
PROCEDURE
Clear (S : StackNo);
เรื่องที่ 5.2.2 การใชสแตกในกระบวนการเรียกใชโพรซีเจอรหรือฟงกชัน (และรีเคอรชน่ั ) จากตัวอยางในเรื่องที่ 5.2.1 กอนหนานีเ้ ราจะเห็นสถานะของสแตกในการเรียกใชโพรซีเจอรเปนดังนี้ (M คือ Main, A คือ โพรซีเจอร A, B คือ โพรซีเจอร B) B M ณ เวลาที่ตําแหนง
1
A
A
A
M
M
M
3
6
2
M 7
8
ณ เวลาที่ตําแหนง 2 เมือ่ มีการเรียกใช A จะมีการ push คาตางๆ ทีจ่ าํ เปนสําหรับ A ซึง่ เราเรียกวา activation record ของ A ไดแก ตําแหนงของคําสั่งใน M ที่เรียกใช A เพื่อที่จะกลับไปทําคําสั่งตอไปใน M ไดเมื่อทําคําสั่งใน A เสร็จแลว และคาของตัวแปรใน A เปนตน ในระหวางที่ประมวลผล A อาจจะมีการเรียกใช B เชน ณ เวลาที่ตําแหนง 3 ก็จะมีการ push activation record ของ B ลงในสแตกซึง่ activation aecord ของ B ก็จะมีคาตางๆ ที่จําเปน ไดแก ตําแหนงของคําสั่งใน A ที่เรียกใช B และคาของตัวแปรใน B เปนตน เมือ่ เสร็จสิน้ การประมวลผลของ A ณ เวลาที่ตําหนง 7 จะมีการ pop ขอมูลของ M (activation record ของ M ) ออกจากสแตกเปนอันเสร็จสิน้ ตัวอยางของ activation record ของโพรซีเจอรหรือฟงกชันแบบรีเคอชัน (recursion) ลองพิจารณาฟงชันเพือ่ คํานวณคาแฟกตอเรียล (Factorial) ดังนี้ FUNCTION Factorial (N : integer) : integer; BEGIN IF N = 0 THEN Factorial := 1 ELSE Factorial := N * Factorial(N - 1) END; BEGIN {Main} P := Factorial (3); END;
ให F แทน Factorial, M แทน Main สถานะของสแตกในการประมวลผลดังแสดลในภาพประกอบ 5.1
"# F(0) F=1 F(1)
F(1)
F(1)
F=1*F(0)
F=1*F(0)
F=1*F(0)
F(2)
F(2)
F(2)
F(2)
F(2)
F=2*F(1)
F=2*F(1)
F=2*F(1)
F=2*F(1)
F=2*F(1)
F(3)
F(3)
F(3)
F(3)
F(3)
F(3)
F(3)
F=3*F(2)
F=3*F(2)
F=3*F(2)
F=3*F(2)
F=3*F(2)
F=3*F(2)
F=3*F(2)
M
M
M
M
M
M
M
M
M
P=F(3)
P=F(3)
P=F(3)
P=F(3)
P=F(3)
P=F(3)
P=F(3)
P=F(3)
p=6
ภาพประกอบ 5.1 การประยุกตใชสแตกสําหรับรีเคอรชั่น เรื่องที่ 5.2.3 การตรวจสอบอักขระสมดุล (Balancing Symbol) ผูที่มีประสบการณในการเขียนโปรแกรมมาแลว จะพบวาสิ่งที่เรามักจะหลงลืมเมื่อเขียนโปรแกรม และทําใหเกิดขอผิดพลาดอยูบ อ ย ๆ คือ การหลงลืมอักขระสมดุล เชน { คูก บั }, [ คูก บั ], ( คูก บั ) เปนตน ซึง่ ในการตรวจสอบอักขระสมดุลนัน้ คอมไพเลอรนําชนิดขอมูลแบบสแตกมาประยุกตใชได โดยมีวิธีการดังนี้ ใหอานอักขระทีละตัว 1. ถา อักขระเปนอักขระเปด เชน {, [, (, เปนตน ให Push ลงสแตก 2. ถา อักขระเปนอักขระปด เชน }, ], ), เปนตน ใหตรวจสอบวาอักขระบน Top ของสแตกเปน อักขระเปดทีค่ กู นั หรือไม ถาใช ให Pop อักขระนัน้ ออกจากสแตก แตถาไมใช แสดงผล error 3. เมือ่ อานอักขระหมดแลว แตสแตกไมเปนสแตกวางใหแสดงผล error เรื่องที่ 5.2.4 การคํานวณคาของนิพจนเลขคณิต (Arithmetic Expression ) โดยทั่วๆ ไปทางคณิตศาสตร เรามักนิยมเขียนนิพจนเลขคณิตในลักษณะที่เรียกวา สัญกรณเติม กลาง (infix notation) คือตัวดําเนินการ (operator) จะอยูตรงกลางของตัวถูกดําเนินการ (operand) ดังรูป operand
operator
operand
ตัวดําเนินการ ก็คอื เครื่องหมายทางคณิตศาสตร สําหรับการคํานวณตางๆ เรียงตามลําดับการ ดําเนินการกอน-หลัง (precedence) ไดแก ยกกําลัง
^
คูณ หาร
*,/
บวก ลบ
+,-
"" ถาเครือ่ งหมายมีลาํ ดับการดําเนินการเดียวกัน จะเลือกดําเนินงานของเครื่องหมายจากซายไปขวา และถามีวงเล็บจะดําเนินงานสิง่ ทีอ่ ยูใ นวงเล็บกอน ในการคํานวณนิพจนเลขคณิตดวยคอมพิวเตอร คอมพิวเตอรจะทําการแปลงนิพจนที่เขียนแบบสัญ กรณเติมกลาง ใหเปนแบบสัญกรณเติมหลัง (postfix notation) กอน คือ จะอยูในรูปแบบที่ตัวดําเนินการ (operator) อยูหลัง เชน A+B
---->
AB+
A-B
---->
AB-
A*B
---->
AB*
A/B
---->
AB/
A^B
---->
AB^
ชนิดขอมูลแบบสแตกไดนํามาใชในการเปลี่ยนนิพจนในรูปแบบของสัญกรณเติมกลาง ใหเปนสัญ กรณเติมหลัง และนํามาใชในการหาคาของนิพจนนั้นๆ ดวย ดังรายละเอียดในวิธกี าร (algorithm) ดังตอไป นี้ วิธีการเปลี่ยนนิพจนแบบเติมกลางใหเปนแบบเติมหลัง 1. อานคาตัวอักขระของนิพจน ซึง่ อักขระนีจ้ ะเปนไดดงั กรณีตอ ไปนี้ - เปนตัวถูกดําเนินการ (operand), - เปนวงเล็บเปด ”(“, - เปนวงเล็บปด”)”, 2. เปนตัวดําเนินการ (operator) 3. IF ตัวอักขระเปน operand THEN ใหแสดงผลลัพธตัวอักขระนั้น ELSE ตัวอักขระอาจจะเปนวงเล็บหรือ operator 4. IF สแตกวาง THEN Push ตัวอักขระลงบนสแตก ELSE สแตกไมวาง IF ตัวอักขระเปน “(“ THEN Push ตัวอักขระลงบนสแตก ELSE ตัวอักขระเปน “)” หรือ operator 5. IF ตัวอักขระ เปน “)” THEN PoP สิ่งที่อยูบนสแตกจนกระทั่งพบ “(“ และ pop ออกมาดวย ELSE ตัวอักขระเปน operator (สิ่งที่อยูบนสแตก “(“ มี priority ต่าํ สุด) 6. IF priority (ตัวอักขระ) > priority (สิ่งที่อยูบนสแตก) THEN Push ตัวอักขระลงบนสแตก ELSE Pop สิง่ ทีอ่ ยูบ นสแตกออกจนกระทัง่ Priority (ตัวอักขระ) > priority (สิ่งที่อยูบนสแตก) Push ตัวอักขระลงบนสแตก
%$$ 7. กลับไปที่ 1 และทําจนกระทั่งหมดขอมูล 8. Pop สิ่งที่อยูในสแตกออกมา หมายเหตุ 1. สิ่งที่อยูในสแตกจะมีเฉพาะเครื่องหมายการกระทํา ( + , - , * , / ,^) และวงเล็บเปด “(“ เทานัน้ 2. ถาตัวอักขระเปน operand จะเขียน ตัวอักขระนัน้ ออกมาทันที 3. ถา Pop operator ออกจากสแตกใหเขียน operator นัน้ ดวย แตถา Pop วงเล็บเปด “(“ ไมตอง เขียน ถาเราใชวิธีดังกลาวขางตนกับนิพจน ((A+B)*C/D+E^F)/G จะเปนดังนี้ 1
1
1
1
1
1
1
1
0
1
2
3
4
5
6
7
8
)
/
G
/
/
ลําดับ
1
2
3
4
5
6
7
8
ขอมูล
(
(
A
+
B
)
*
C /
D +
E
^
F
Top
(
(
(
+
+
(
*
*
/
/
+
+
^
^
(
(
(
(
(
(
(
(
(
(
+
+
(
(
(
(
สแตก ผลลัพธ
A
B
+
9
1
C *
D /
E
F
^ +
ขั้นตอนการคํานวณคานิพจนแบบสัญกรณเติมหลัง 1. อานคาตัวอักขระของนิพจนจากซายไปขวา 2. IF ตัวอักขระเปน operand THEN Push ตัวอักขระลงบนสแตก ELSE ตัวอักขระเปน operator Pop สิ่งที่อยูบนสแตก 2 ตัว มาดําเนินการตาม opeartor ของตัวอักขระนั้น ๆ Push ผลลัพธทไ่ี ดลงบนสแตก
G /
%$% 3. กลับไปที่ 1 และทําจนกระทั่งหมดขอมูล 4. Pop สิง่ ทีอ่ ยูใ นสแตกออกมาจะเปนผลลัพธ
%$&
1
ลําดับ A
ขอมูล A
2
B
สแตก B A
3
+
A+B
4
C
C A+B
5
*
(A+B)*C
6
D
D (A+B)*C
7
/
((A+B)*C)/D
8
E
E ((A*B)*C)/D
9
F
F E ((A+B)*C)/D
10
^
(E^F) ((A+B)*C)/D
11
+
(((A+B)*C)/D)+(E^F)
12
G
G (((A+B*C)/D)+(E^F)
13
/
((((A+B)*C)/D)+(E^F))/G