วาดรูปด้วยภาษาโลโก้ : ตอนที่ 11 – เริ่มใหม่อีกครั้ง
จากตอนที่แล้ว เราใช้ฟังก์ชันกันมาก็หลายครั้ง ถ้าเราเรียกใช้ฟังก์ชัน แล้วฟังก์ชันนั้นเรียกใช้งานฟังก์ชันอื่น ก็ถือเป็นเรื่องปกติ แต่น้องๆ เคยคิดไหมครับว่า ถ้าเราเรียกใช้ฟังก์ชัน แล้วฟังก์ชันนั้นเรียกใช้งานฟังก์ชันตัวเอง จะเกิดอะไรขึ้น?
(เนื้อหาตอนนี้ค่อนข้างยาก ถ้าน้องๆ อ่านแล้วยังไม่เข้าใจ สามารถข้ามไปได้โดยไม่ต้องกังวลครับ เพราะเรื่องนี้ไม่มีผลกับเรื่องอื่นๆ แต่ถ้าน้องๆ อยากทำความเข้าใจ ลองเขียนทบทวนทำความเข้าใจเองก่อน แล้วค่อยกลับมาอ่านใหม่นะครับ)
คำสั่งที่น้องๆ จะเจอในบทนี้
คำสั่ง | ความสามารถ | ตัวอย่าง |
fd ระยะทาง bk ระยะทาง |
forward = เดินหน้า backward = ถอยหลัง |
fd 100 = เดินหน้า 100 pixel bk 50 = ถอยหลัง 50 pixel |
rt องศา lt องศา |
right turn = หมุนขวา left turn = หมุนซ้าย |
rt 90 = หมุนไปทางขวา 90 องศา lt 45 = หมุนไปทางซ้าย 45 องศา |
repeat จำนวนครั้ง [ คำสั่ง ] | repeat = ทำคำสั่งต่อไปนี้ ซ้ำ n ครั้ง | repeat 3 [ fd 100 rt 120 ] repeat 4 [ fd 100 rt 90 ] repeat 6 [ fd 100 rt 60 ] |
TO ชื่อฟังก์ชัน :ตัวแปร คำสั่ง END |
function = สร้างชุดคำสั่ง | TO square :size repeat 4 [ fd :size rt 90 ] END |
cs clean |
clear screen = ล้างหน้าจอ clean = ล้างหน้าจอ |
|
pu pd |
pen up = ยกปากกา pen down = วางปากกา |
|
ht st |
hide turtle = ซ่อนเต่าโลโก้ show turtle = แสดงเต่าโลโก้ |
|
setxy พิกัดx พิกัดy | set (x,y) = กำหนดตำแหน่งเต่าโลโก้ | setxy 200 100 |
setpensize ขนาด setpencolor รหัสสี fill setscreencolor รหัสสี |
set pen size = กำหนดขนาดปากกา set pen color = กำหนดสีปากกา fill = ระบายสี set screen color = กำหนดสีพื้นหลัง |
setpensize 5 setpencolor 10 fill setscreencolor 10 |
น้องๆ น่าจะเคยเห็น รูปถ่ายที่มีคนถ่ายรูปกับรูปถ่ายของตัวเอง ซึ่งในนั้นก็จะมี รูปถ่ายที่มีคนถ่ายรูปกับรูปถ่ายของตัวเอง ซึ่งในนั้นก็จะมี รูปถ่ายที่มีคนถ่ายรูปกับรูปถ่ายของตัวเอง ….. โอ๊ย! พูดง่ายๆ คือ รูปแบบด้านล่างนี้ละครับ
ซึ่งเราสามารถเทียบเหตุการณ์นี้กับโปรแกรมวาดรูปดังต่อไปนี้ครับ
TO square_r :s repeat 4 [ fd :s rt 90 ] square_r :s/2 END square_r 200 |
โปรแกรมที่ 11-1
การทำงานของโปรแกรมนี้ นอกจากจะวาดรูปสี่เหลี่ยมแล้ว มันจะวาดรูปสี่เหลี่ยมที่เล็กกว่า โดยการเรียกใช้ฟังก์ชันตัวเองครับ อธิบายโดยละเอียดคือ
- คำสั่ง square_r 200 – จะวาดรูปสี่เหลี่ยมจัตุรัสขนาด 200 pixel – จากนั้นเรียกใช้ square_r 100
- คำสั่ง square_r 100 – จะวาดรูปสี่เหลี่ยมจัตุรัสขนาด 100 pixel – จากนั้นเรียกใช้ square_r 50
- คำสั่ง square_r 50 – จะวาดรูปสี่เหลี่ยมจัตุรัสขนาด 50 pixel – จากนั้นเรียกใช้ square_r 25
- คำสั่ง square_r 25 – จะวาดรูปสี่เหลี่ยมจัตุรัสขนาด 25 pixel – จากนั้นเรียกใช้ square_r 5
- คำสั่ง square_r 5 – จะวาดรูปสี่เหลี่ยมจัตุรัสขนาด 12.5 pixel – จากนั้นเรียกใช้ square_r 6.25
- คำสั่ง square_r 25 – จะวาดรูปสี่เหลี่ยมจัตุรัสขนาด 6.25 pixel – จากนั้นเรียกใช้ square_r 3.125
- ( เป็นเช่นนี้ไปเรื่อยๆ )
เราเรียกฟังก์ชันที่เรียกใช้ฟังก์ชันตัวเองว่า Recursive Function ครับ (Recursive แปลว่า ซ้ำๆ)
แต่ปัญหาของโปรแกรมข้างต้น คือ มันจะทำงานไปเรื่อยๆ โดยไม่จบการทำงานครับ !!!
ถ้าเราเรียกใช้งานฟังก์ชันที่มีการเรียกใช้ฟังก์ชันตัวเอง โดยไม่มีเงื่อนไขจบการทำงาน ฟังก์ชันนั้นก็จะทำงานไปเรื่อยๆ โดยไม่มีวันสิ้นสุด
ดังนั้น สิ่งที่สำคัญมากสำหรับฟังก์ชันที่เรียกใช้งานตัวเองก็คือ เงื่อนไขจบการทำงานครับ (Exit Condition)
เราจะมาปรับปรุงโปรแกรม 11-1 โดยเพิ่มเงื่อนไขจบการทำงานเข้าไปครับ
TO square_r :s if ( :s < 10 ) [ stop ] repeat 4 [ fd :s rt 90 ] square_r :s/2 END square_r 200 |
โปรแกรมที่ 11-2
อธิบายการทำงานของโปรแกรม
- คำสั่ง square_r 200 – พบว่าขนาด 200 มากกว่า 10 – วาดรูปสี่เหลี่ยมจัตุรัสขนาด 200 pixel – จากนั้นเรียกใช้ square_r 100
- คำสั่ง square_r 100 – พบว่าขนาด 100 มากกว่า 10 – วาดรูปสี่เหลี่ยมจัตุรัสขนาด 100 pixel – จากนั้นเรียกใช้ square_r 50
- คำสั่ง square_r 50 – พบว่าขนาด 50 มากกว่า 10 – วาดรูปสี่เหลี่ยมจัตุรัสขนาด 50 pixel – จากนั้นเรียกใช้ square_r 25
- คำสั่ง square_r 25 – พบว่าขนาด 25 มากกว่า 10 – วาดรูปสี่เหลี่ยมจัตุรัสขนาด 25 pixel – จากนั้นเรียกใช้ square_r 5
- คำสั่ง square_r 5 – พบว่าขนาด 12.5 มากกว่า 10 – วาดรูปสี่เหลี่ยมจัตุรัสขนาด 12.5 pixel – จากนั้นเรียกใช้ square_r 6.25
- คำสั่ง square_r 25 – พบว่าขนาด 6.25 น้อยกว่า 10 – จึงจบการทำงาน
ต่อไปเราจะมาดูตัวอย่างโปรแกรมที่ประยุกต์ใช้การทำงานของฟังก์ชัน Recursive กันครับ
TO helix_1 :s if :s < 10 [ stop ] fd :s rt 90 helix_1 :s – 10 END cs setxy 0 -100 helix_1 200 |
โปรแกรมที่ 11-3
อธิบายการทำงานของโปรแกรม
- คำสั่ง helix_1 200 – พบว่า :s มีค่า 200 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 200 pixel หันขวา 90 องศา – เรียกใช้ helix_1 190
- คำสั่ง helix_1 190 – พบว่า :s มีค่า 190 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 190 pixel หันขวา 90 องศา – เรียกใช้ helix_1 180
- คำสั่ง helix_1 180 – พบว่า :s มีค่า 180 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 180 pixel หันขวา 90 องศา – เรียกใช้ helix_1 170
- ( ทำเช่นนี้ไปเรื่อยๆ )
- คำสั่ง helix_1 30 – พบว่า :s มีค่า 30 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 30 pixel หันขวา 90 องศา – เรียกใช้ helix_1 20
- คำสั่ง helix_1 20 – พบว่า :s มีค่า 20 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 20 pixel หันขวา 90 องศา – เรียกใช้ helix_1 10
- คำสั่ง helix_1 10 – พบว่า :s มีค่า 10 ซึ่งไม่มากกว่า 10 – จบการทำงาน
TO helix_2 :s if :s < 10 [ stop ] fd :s rt 30 helix_2 :s * 0.95 END cs setxy 0 -100 helix_2 100 |
โปรแกรมที่ 11-4
อธิบายการทำงานของโปรแกรม
- คำสั่ง helix_2 100 – พบว่า :s มีค่า 100 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 100 pixel หันขวา 30 องศา – เรียกใช้ helix_2 95
- คำสั่ง helix_2 95 – พบว่า :s มีค่า 95 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 95 pixel หันขวา 30 องศา – เรียกใช้ helix_2 90.25
- คำสั่ง helix_2 90.25 – พบว่า :s มีค่า 25 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 90.25 pixel หันขวา 30 องศา – เรียกใช้ helix_2 85.74
- ( ทำเช่นนี้ไปเรื่อยๆ )
- คำสั่ง helix_2 11.02 – พบว่า :s มีค่า 30 ซึ่งมากกว่า 02 – วาดเส้นตรง ยาว 11.02 pixel หันขวา 30 องศา – เรียกใช้ helix_2 10.47
- คำสั่ง helix_2 10.47 – พบว่า :s มีค่า 20 ซึ่งมากกว่า 47 – วาดเส้นตรง ยาว 10.47 pixel หันขวา 30 องศา – เรียกใช้ helix_2 9.94
- คำสั่ง helix_2 9.94 – พบว่า :s มีค่า 94 ซึ่งไม่มากกว่า 10 – จบการทำงาน
ต่อไปเราจะมาวาดรูปใยแมงมุมกันครับ
TO spiderweb :s if :s < 10 [ stop ] fd :s rt 45 spiderweb :s * 0.98 END cs spiderweb 100 |
โปรแกรมที่ 11-5
อธิบายการทำงานของโปรแกรม
- คำสั่ง spiderweb 100 – พบว่า :s มีค่า 100 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 100 pixel หันขวา 45 องศา – เรียกใช้ spiderweb 98
- คำสั่ง spiderweb 98 – พบว่า :s มีค่า 98 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 98 pixel หันขวา 45 องศา – เรียกใช้ spiderweb 96
- คำสั่ง spiderweb 96 – พบว่า :s มีค่า 96 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 96 pixel หันขวา 45 องศา – เรียกใช้ spiderweb 94
- ( ทำเช่นนี้ไปเรื่อยๆ )
- คำสั่ง spiderweb 10.4 – พบว่า :s มีค่า 4 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 10.4 pixel หันขวา 45 องศา – เรียกใช้ spiderweb 10.2
- คำสั่ง spiderweb 10.2 – พบว่า :s มีค่า 2 ซึ่งมากกว่า 10 – วาดเส้นตรง ยาว 10.2 pixel หันขวา 45 องศา – เรียกใช้ spiderweb 9.99
- คำสั่ง spiderweb 9.99 – พบว่า :s มีค่า 10 ซึ่งไม่มากกว่า 10 – จบการทำงาน
สุดท้ายเราจะประยุกต์ใช้เทคนิค Recursive ในการวาดรูปต้นไม้กันครับ
TO my_tree :s if :s < 10 [ fd :s bk :s stop ] fd :s / 2 lt 30 my_tree :s /2 rt 30 rt 30 my_tree :s /2 lt 30 fd :s / 2 bk :s END cs my_tree 200 |
โปรแกรมที่ 11-6
อธิบายการทำงานของโปรแกรม
- คำสั่ง my_tree 200 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 100 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 100 – หันขวา 30 องศา
- หันขวา 30 องศา – เรียกใช้ my_tree 100 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 100 pixel
- ถอยหลัง 200 pixel
- คำสั่ง my_tree 100 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 50 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 50 – หันขวา 30 องศา
- หันขวา 30 องศา – เรียกใช้ my_tree 50 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 50 pixel
- ถอยหลัง 100 pixel
- คำสั่ง my_tree 50 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 25 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 25 – หันขวา 30 องศา
- หันขวา 30 องศา – เรียกใช้ my_tree 25 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 25 pixel
- ถอยหลัง 50 pixel
- คำสั่ง my_tree 25 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 12 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 12 – หันขวา 30 องศา
- หันขวา 30 องศา – เรียกใช้ my_tree 12 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 12 pixel
- ถอยหลัง 25 pixel
- คำสั่ง my_tree 12 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 6 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 6 – หันขวา 30 องศา
- หันขวา 30 องศา – เรียกใช้ my_tree 6 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 6 pixel
- ถอยหลัง 12 pixel
- คำสั่ง my_tree 6 – พบว่า :s มีค่า 6 ซึ่งน้อยกว่า 10 – จบการทำงาน
เราสามารถวาดแผนภาพแสดงการทำงานได้ดังนี้ครับ (รูปด้านล่างเป็นแค่การทำงานส่วนหนึ่งของโปรแกรมเท่านั้น)
น้องๆ อาจจะสงสัยว่าทำไมต้นไม้รูปด้านบนดูแปลกๆ เรามาดูต้นไม้รูปด้านล่างกันบ้างครับ
TO my_tree :s if :s < 10 [ fd :s bk :s stop ] fd :s / 4 lt 30 my_tree :s *2/3 rt 30 fd :s / 4 rt 30 my_tree :s *1/2 lt 30 fd :s / 4 lt 30 my_tree :s *1/3 rt 30 fd :s / 4 bk :s END cs my_tree 200 |
โปรแกรมที่ 11-7
อธิบายการทำงานของโปรแกรม
- คำสั่ง my_tree 200 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 50 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 133 – หันขวา 30 องศา
- วาดเส้นตรง ยาว 50 pixel
- หันขวา 30 องศา – เรียกใช้ my_tree 100 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 50 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 67 – หันขวา 30 องศา
- วาดเส้นตรง ยาว 50 pixel
- ถอยหลัง 200 pixel
- คำสั่ง my_tree 100 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 25 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 66 – หันขวา 30 องศา
- วาดเส้นตรง ยาว 25 pixel
- หันขวา 30 องศา – เรียกใช้ my_tree 50 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 25 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 33 – หันขวา 30 องศา
- วาดเส้นตรง ยาว 25 pixel
- ถอยหลัง 100 pixel
- คำสั่ง my_tree 50 – พบว่า :s มีค่ามากกว่า 10
- วาดเส้นตรง ยาว 12.5 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 33 – หันขวา 30 องศา
- วาดเส้นตรง ยาว 12.5 pixel
- หันขวา 30 องศา – เรียกใช้ my_tree 25 – หันซ้าย 30 องศา
- วาดเส้นตรง ยาว 12.5 pixel
- หันซ้าย 30 องศา – เรียกใช้ my_tree 17 – หันขวา 30 องศา
- วาดเส้นตรง ยาว 12.5 pixel
- ถอยหลัง 50 pixel
- ( เป็นเช่นนี้ไปเรื่อยๆ )
เราสามารถวาดแผนภาพแสดงการทำงานได้ดังนี้ครับ (รูปด้านล่างเป็นแค่การทำงานส่วนหนึ่งของโปรแกรมเท่านั้น)
และนี่คือเรื่องราวของ Recursive Function ทั้งหมดที่นำมาฝากกันในวันนี้ครับ สามารถติดตามตอนต่อไปได้ที่นี่ครับ https://karnlab.com/tag/ภาษาโลโก้