คราวที่แล้วพูดถึงโปรแกรมย่อยไปแล้ว คราวนี้จะมาต่อที่ฟังก์ชันอีกแบบนึงนะครับ
 
2. ฟังก์ชันแบบฟังก์ชัน(??)
 
อย่าเพิ่งสับสนกับเรื่องคำเรียกนะครับ เพราะเอาเข้าจริงๆ อีกหน่อยเราก็จะใช้ปนๆกันไปเอง 
 
ฟังก์ชันต่างกับโปรแกรมย่อยตรงที่มีการคืนค่าครับ
 
อย่างที่เราเปรียบเทียบไปคราวก่อน โปรแกรมย่อยเป็นเหมือนเครื่องจักรสำหรับ "ทำงาน" ครับ คือสร้างไป พอจะใช้ ก็เหมือนเรียกให้มันทำนู่นทำนี่ มีข้อดีคือสร้างครั้งเดียว ใช้งานได้หลายครั้ง
 
ฟังก์ชันต่างกันครับ มันเป็นเหมือนเครื่องจักรสำหรับ "ผลิต" หรือ "คำนวณ" มากกว่า หน้าที่ของมันคือรับ input เข้าไป ประมวณผล แล้วตอบ output ออกมา เหมือนกับเครื่องจักรหรือโรงงานผลิต ที่เราโยนวัตถุดิบเข้าไป มันจะให้ผลิตภัณฑ์ออกมาครับ
 
แยกระหว่างฟังก์ชันกับโปรแกรมย่อยไม่ออกก็ไม่เป็นไรนะครับ
 
ถ้าในภาษาซี ก็คือเปรียบเหมือนการสร้างโปรแกรมย่อย มีการส่งค่าบางอย่างให้ (เรียกค่าที่ส่งให้พารามิเตอร์ ในโปรแกรมแฟคทอเรียล พารามิเตอร์ก็คือค่า n นั่นเอง) แต่ให้โปรแกรมคืนผลลัพธ์ออกมา สังเกตว่าในโปรแกรมแฟคทอเรียล มันไม่ได้ "คืน" อะไรออกมาเลยนะครับ รับค่าเข้าไปประมวลผล แล้วก็ printf ออกไปเลย
 
การทำโปรแกรมย่อยให้เป็นฟังก์ชันทำได้โดยการให้มัน "return" ค่าครับ ดูตัวอย่างนะครับ
 
   1:  int factorial(int n)
   2:  {
   3:      int i;
   4:      int fact;
   5:      for(i = 1, fact = 1; i <= n; i++) fact = fact * i;
   6:      return fact;
   7:  }

สังเกตว่าโปรแกรมนี้ทำงานเหมือนโปรแกรม factorial แบบโปรแกรมย่อยทุกอย่าง ต่างกันที่บรรทัดที่ 1 กับ 6 ครับ บรรทัดที่ 1 เราเปลี่ยนคำว่า "void" เป็น "int" ซึ่ง void มีความหมายว่า "ไม่มีการคืนค่า" ครับ ก็คือทำงานเป็นโปรแกรมย่อยปกติ แต่ถ้าตรงนี้เขียนว่า int หมายถึงมีการคืนค่าเป็น int ออกไปครับ
 
ให้มาดูต่อที่บรรทัดที่ 6 จากเดิมที่ให้ printf ออกไปเลย เราเปลี่ยนเป็นการคืนค่าครับ 
 
ความแตกต่างก็คือ โปรแกรมย่อยเราให้มันไป "ทำงาน" ครับ ซึ่งงานที่เราให้มันทำตอนแรก ก็คือบอกผู้ใช้ว่า factorial ของค่านั้นๆเป็นเท่าไหร่ เวลาเรียกใช้แบบเดิม เราก็แค่เรียก factorial(...); มันก็จะแสดงข้อความออกไปให้เรียบร้อย 
 
แต่ฟังก์ชันไม่ใช่ครับ มันทำการ "คืนค่า" ออกไป เวลาเรียกเราต้องเรียกแบบนี้ครับ
 
x = factorial(...);
 
โดย x เป็นตัวแปรประเภท int ที่เราประกาศขึ้นใหม่
 
ผลลัพธ์ก็คือ เราจะได้ค่าของ factorial ของเลขนั้นๆไปเก็บไว้ในตัวแปร x ครับ มันทำหน้าที่เหมือนเครื่องคำนวณ ที่คืนตัวเลขผลลัพธ์ออกมา ไม่ได้มีการ printf ออกไปแต่อย่างใด 
 
ข้อดีของมันก็คือ สิ่งที่มันทำมีแค่คำนวณตามที่เราสั่ง แล้วเราสามารถเอาค่านั้นไปใช้ต่อได้ เช่นเมื่อเราเก็บค่าลงไปในตัวแปร เราอาจจะเอาตัวแปรไปคำนวณอะไรอย่างอื่นได้ตามใจเรา ต่างกับโปรแกรมย่อย ที่ไป printf ให้เสร็จสรรพ แต่เราเอาค่าอะไรไปใช้ต่อไม่ได้ 
 
ตัวอย่างเช่น เราสามารถทำอะไรแบบนี้ได้ใน main
 
x = factorial(5);
y = factorial(7);
printf("Factorial of 5 times factorial of 7 is %d\n", x * y);
 
หรืออาจเขียนแบบรวดรัดแบบนี้
 
printf("Factorial of 5 times factorial of 7 is %d\n", factorial(5) * factorial(7));
 
ซึ่งถ้าใช้โปรแกรมย่อย (เป็น void = ไม่มีการคืนค่า) เราจะทำไม่ได้ครับ แต่ถ้าเราทำเป็นฟังก์ชัน (ประกาศแบบ int) พอมันคำนวณเสร็จแล้ว มันก็จะคืนค่าออกมา เอาไปคำนวณต่อได้ครับ
 
ไม่จำเป็นต้องเป็น int เท่านั้นนะครับ ตัวแปรประเภทอื่นได้หมด อยากให้คืนค่าเป็นตัวอักศร ก็ประกาศเป็น char ก็ได้ 
 
ใช้ปน ๆ กันได้มั้ย แบบว่า ทั้ง printf ออกไปด้วย คืนค่าด้วย
 
คำตอบคือ ได้ครับ! อย่างที่บอก พอเราใช้ๆไป เราก็จะเริ่มไม่สนใจว่ามันเปฯโปรแกรมย่อย หรือฟังก์ชัน มันแล้วแต่งานว่าเราอยากเขียนมันขึ้นมาทำอะไรบ้าง แล้วอยาก return อะไรออกไปมั้ย บางทีเราอาจจะให้มันทำงานเหมือนโปรแกรมย่อยทุกอย่าง เพียงแต่ช่วย return เป็นเลข 1 หรือ 0 แค่เพื่อบอกว่าทำงานสำเร็จมั้ย อะไรทำนองนั้น อย่าง factorial เราอาจจะให้มันทั้ง printf ออกมาแล้วก็ return ออกมาด้วยเลยก็ได้ แล้วแต่เรา ที่อธิบายแยกเป็นสองอย่างเพื่อให้เข้าใจไปเป็นลำดับเท่านั้นเองครับ (หรือทำให้งงมากขึ้นหว่า 55555) 
 
เวลาเขียนโปรแกรมก็ไม่มีอะไรแตกต่างครับ จำแค่ว่าไม่อยาก return อะไร ก็เขียนเป็น void อยาก return ก็เขียนตาม type ที่อยาก return
 
ถึงตรงนี้ มีใครสังเกตมั้ยครับว่า main ก็เป็นฟังก์ชันหนึ่งเหมือนกัน!!!
 
มันเป็นฟังก์ชันเหมือนฟังก์ชันอื่นๆทุกประการครับ โดยเมื่อเรารันโปรแกรม มันจะทำงานเป็นฟังก์ชันแรกเท่านั้นเอง 
 
ส่วนผู้ที่เรียก main ก็คือระบบปฏิบัติการครับ
 
บางคนที่ใช้คอมไพเลอร์เก่าๆ อาจจะเขียน main เป็น void เพราะมันไม่ได้ต้อง return ค่าให้ใคร แต่คอมไพเลอร์ใหม่ๆจะบังคับให้เขียนเป็น int ครับ (ทำให้เราต้อง return 0; ตอนท้ายโปรแกรม) โดยค่าที่ return จะถูกส่งให้ระบบปฏิบัติการ
 
อาจจะเขียนงงๆนะครับ ไม่ค่อยมีเวลาปรับแต่งภาษาซักเท่าไหร่ 555+ ใครสงสัย ไม่เข้าใจ หรือเข้าใจแต่อยากแนะนำอะไร ส่งอีเมล์มาได้ที่ molev.mt@gmail.com นะครับ ยินดีตอบทุกคำถามและรับฟังทุกคำติชม : ))))
 

edit @ 25 Jan 2012 19:00:22 by Mistertun

หลังจากที่ทิ้งบล็อกไปนานเหลือเกิน ไม่ได้เข้ามาดูเลย กลับมาอีกที เห็นเพจวิวขึ้นหลักแสน ดีใจและขอบคุณมากๆครับ ขอโทษทุกคนด้วยที่ไม่ได้มีโอกาสตอบคำถามในคอมเมนต์เลยเพราะอย่างที่บอกคือไม่ได้เข้ามาดูจริงๆ เอาเป็นว่าวันนี้ขอกลับมาเขียนเรื่องฟังก์ชันเป็นการแก้ตัวละกันนะครับ วิธีการเขียนอาจจะปลี่ยนไปบ้างนะครับ เพราะตัวผู้เขียนก็เปลี่ยนไปเยอะเหมือนกันในช่วงสองสามปีมานี้ 555+
 
ฟังก์ชันคืออะไร
 
อันที่จริงเรียกว่า "ฟังก์ชัน" ก็ไม่แน่ใจครับว่าถูกต้องมั้ย เอาเป็นว่าใช้ตามความเคยชินของผู้เขียนแล้วกันครับ ฟังก์ชันจะแบ่งออกเป็นสองประเภทครับ
 
1. แบบโปรแกรมย่อยหรือ Procedure
 
ให้นึกถึงว่ามันเป็นเหมือนเครื่องจักรทำงานครับ เวลาเราต้องทำอะไรซ้ำๆหลายๆครั้ง เราก็จะรู้สึกว่ามันน่าเบื่อจริงมั้ยครับ เช่นต้องประชาสัมพันธ์งานวันปีใหม่ของบริษัท ได้ข้อความมาข้อความนึง ประมาณ "ขอเชิญทุกท่านร่วมงานปีใหม่วันที่ 31 ธันวาคมนี้ บลาๆๆๆ" เราต้องเดินถือโทรโข่งไปประกาศข้อความนี้ซ้ำๆทั้งบริษัท น่าเบื่อใช่มั้ยครับ
 
แต่แล้ววันนึงก็มีคนเอาระบบอัดเสียงไปใส่โทรโข่ง สิ่งที่เราต้องทำ ก็คือพูดบอกมันไปครั้งนึงว่าอยากพูดว่าอะไร จากนั้นเราก็แค่กดปุ่มให้มันพูดข้อความที่อัดไว้ ง่ายอะไรอย่างนี้
 
นี่แหละครับ คือไอเดียของโปรแกรมย่อย มาดูตัวอย่างจริงๆกันมั่งดีกว่าครับ
 
เคยมั้ยครับ บางทีเราเขียนโปรแกรมไว้ส่วนนึง พอเขียนต่อไปซักพัก ก็พบว่าอยากเอาโปรแกรมส่วนที่เขียนไปแล้วกลับมาใช้อีก
 
เช่น จะเขียนโปรแกรมใหญ่ๆขึ้นมาซักโปรแกรม แล้วในส่วนนึงของโปรแกรมนั้นต้องมีการคำนวณแฟคทอเรียล (คือการคำนวณค่าของ 1 x 2 x 3 x 4 x ..... x N อย่างแฟคทอเรียลของ 5 ก็คือ 120 มาจาก 1x2x3x4x5  แฟคทอเรียลของ 7 ก็คือ 5040 มาจาก 1x2x3x4x5x6x7 เป็นต้น) ถ้าเขียนแบบเดิมๆเราก็คงจะเขียนลูป for คูณเอา ดูไม่น่ามีปัญหาใช่มั้ยครับ
 
แต่หากว่าเขียนโปรแกรมดังกล่าวต่อไปสักพัก แล้วพบว่าต้องคำนวณแฟคทอเรียลอีก วิธีที่ง่ายที่สุดที่เราทำได้ตอนนี้คือกลับไปหาโค้ดที่คำนวนแฟคทอเรียล ก๊อปปี้ แล้วมาเพสลงในที่ที่เราต้องการใช้อีกครั้ง การเขียนแบบนี้ถ้าเราต้องคำนวณบ่อย  ๆ โปรแกรมเราก็จะมีแต่โค้ดหาแฟคทอเรียลรกหูรกตาเต็มไปหมดเลย จริงมั้ยครับ
 
นี่แหละครับที่ทำให้ "โปรแกรมย่อย" ได้แจ้งเกิดกับเขาเสียที
 
โปรแกรมย่อยคือการที่เราเขียนโปรแกรมหนึ่งไว้ ตั้งชื่อให้กับมัน แล้วเมื่อเราต้องการเรียกใช้โปรแกรมนั้น สิ่งที่ต้องทำก็มีเพียงเรียกชื่อโปรแกรมครับ (ยังไม่ต้องสนวิธีเขียนนะครับ เอาให้เข้าใจไอเดียก่อน) 
 
เช่นในกรณีตัวอย่าง เราก็เขียนโปรแกรมหาแฟคทอเรียล แล้วก็ตั้งชื่อให้มัน เช่น "factorial" เมื่อเราต้องการใช้ เราก็แค่เรียก "factorial" ไม่ต้องเขียนโค้ดทั้งก้อนครับ
 
มันก็เหมือนกับการอัดเสียงใส่โทรโข่งแล้วเก็บไว้ใช้ซ้ำๆนั่นเอง 
 
ถามว่าเราจะสร้างโปรแกรมย่อยนี่ได้ยังไง ให้ดูตัวอย่างนี้ครับ ยังไม่ต้องสนใจว่าจะเขียนไว้ที่ไหน 
 
   1:  void factorial(int n)
   2:  {
   3:      int i;
   4:      int fact;
   5:      for(i = 1, fact = 1; i <= n; i++) fact = fact * i;
   6:      printf("%d factorial is %d\n", n, fact);
   7:  }
 
บรรทัด 1 เป็นการประกาศฟังก์ชันหรือโปรแกรมย่อยครับ void คืออะไรช่างมัน factorial คือชื่อครับ ส่วน int n ในวงเล็บเป็นตัวแปรที่เอาไว้รับค่าว่าอยากให้าแฟคทอเรียลถึงตัวที่เท่าไหร่ (คือหาแค่ 1 x 2 หรือ 1 x 2 x 3 หรือ 1 x 2 x 3 x 4 x ....) 
 
บรรทัด 2 เป็นวงเล็บแสดงการเริ่มต้นขอบเขตของฟังก์ชัน คล้ายกับเวลาเขียน main ต้องมีวงเล็บกำหนดขอบเขตโปรแกรมนั่นแหละครับ
 
บรรทัด 3-6 เป็นการเขียนโปรแกรมหา factorial ตามปกติเลยครับ ประกาศตัวแปน วนลูปคูณไปเรื่อย ๆ printf
 
บรรทัดที่ 7 วงเล็บปิด สิ้นสุดฟังก์ชันครับ
 
จะสังเกตได้ว่าการเขียนจะมีรูปแบบคล้ายๆเวลาเราเขียน main เลยครับ เพียงแต่ไม่มีการ return 0; ตอนท้าย
ในตัวโปรแกรมย่อยเราจะเขียนอะไรลงไปก็ได้ครับ คิดซะว่ากำลังเขียนอยู่ใน main ตามปกติเลย
 
ถามว่าเขียนแล้วให้เอาไว้ตรงไหน คำตอบคือเอาไว้ใต้ include แล้วก็ก่อน main ครับ ดูโปรแกรมเต็มจะหน้าตาประมาณนี้
 
 
   1:  #include 
   2:   
   3:  void factorial(int n)
   4:  {
   5:      int i;
   6:      int fact;
   7:      for(i = 1, fact = 1; i <= n; i++) fact = fact * i;
   8:      printf("%d factorial is %d\n", n, fact);
   9:  }
  10:      
  11:  int main()
  12:  {
  13:      ....
  14:  }
 
main จะเป็นเหมือนตัวโปรแกรมหลักครับ เวลาเรารันโปรแกรมจะเริ่มทำงานใน main ก่อน ส่วน factorial ที่ประกาศไว้ก็เป็นเหมือนเครื่องจักร หรือโทรโข่ง ที่ตั้งรอเอาไว้ให้เราใช้ เวลาจะใช้ก็เรียก factorial(n) โดย n เป็นเลขที่เราต้องการหาแฟคทอเรียลครับ เช่นอยากหาแฟคทอเรียงของ 5 เราก็สั่ง factorial(5); ครับ เวลาสั่งก็สั่งใน main ได้เลยครับ 
 
ตัวอย่าง 
 
   1:  #include 
   2:   
   3:  void factorial(int n)
   4:  {
   5:      int i;
   6:      int fact;
   7:      for(i = 1, fact = 1; i <= n; i++) fact = fact * i;
   8:      printf("%d factorial is %d\n", n, fact);
   9:  }
  10:      
  11:  int main()
  12:  {
  13:      factorial(4);
  14:      factorial(5);
  15:      factorial(7);
  16:      return 0;
  17:  }

output ก็จะประมาณนี้ครับ
 
4 factorial is 24
5 factorial is 120
7 factorial is 5040
 
สังเกตว่าชื่อตัวแปรที่ใช้ใน main กับที่ใช้ใน factorial อาจจะซ้ำกันได้นะครับ เพราะถือว่าอยู่กันคนละส่วน (Scope) ตัวแปรจะเรียกใช้ข้ามกันไม่ได้
 
ฟังก์ชันแบบนี้บางภาษาโปรแกรมจะไม่เรียกว่าฟังก์ชันครับ อาจจะใช้คำว่า procedure หรือ subprogram แทน เอาเป็นว่าช่างมันก่อนแล้วกันครับ 
 
ต่อตอนที่ 2 ที่นี่ นะครับ
 
หากมีข้อสงสัย รบกวนส่งคำถามทางอีเมล์นะครับ (molev.mt@gmail.com)


edit @ 25 Jan 2012 18:57:58 by Mistertun

edit @ 25 Jan 2012 19:00:09 by Mistertun

[เนื้อหาใช้ได้กับทุกๆภาษาโปรแกรม แต่ผู้เขียนถนัดภาษาซี จึงขอใช้ภาษาซีในการข่วยอธิบาย] 

[เนื้อหา advance เล็กน้อยนะครับ แนะนำให้ค่อยๆอ่านและที่สำคัญคืออ่านให้จบไม่ว่าจะติดตรงไหน]  

[เนื้อหาอาจจะไม่ค่อยถูกต้องตามทฤษฎีเกียวกับ Complexity แต่เน้นเขียนให้เอาไปใช้ได้คร่าวๆในการแข่งขัน เอาไปใช้สอบทฤษฎีอาจจะไม่ได้นะครับ] 

ทีนี้ แล้วถ้าโปรแกรมของเรา ไม่ได้มีลูปแค่ก้อนเดียว เราจะคำนวณยังไงหว่า เช่น สมมติมีลูปอยู่ 3 ลูป อันนึงชั้นเดียว [ O(n) ] อีกสองลูปเป็นลูปสองชั้น [ O(2(n^2)) ] ถ้าตีซะว่าเวลาในการรันแต่ละรอบของแต่ละลูปเท่ากัน เราก็น่าจะเขียนได้ว่าเวลาในการรันทั้งหมดคือ O(2(n^2) + n) ใช่มะ แล้วถ้าเกิด มีอีกลูปนึง เขียนแค่ for( i=0 ; i<5 ; i++).. คือวนลูปแค่ 5 รอบ ไม่ใช่ n รอบ มันก็น่าจะเป็น O(2(n^2) + n + 5) ป่ะ...

คำตอบ...

ถ้าในทางปฏิบัติล่ะก็ ใช่ครับ เพราะในการคำนวณเวลารันเราจะต้องคำนวณแบบเก็บกันทุกรายละเอียดเพื่อควบคุมไม่ให้เวลาเกิน แต่ในทางทฤษฎีมันจะเป็นอย่างงี้ครับ

1. constant ตัดทิ้ง!!!  
หมายความว่าลูปใดๆที่มีเวลาในการรันเป็น constant คือเป็นค่าคงตัว ไม่ได้ขึ้นอยู่กับขนาดของข้อมูล จะไม่ถูกนำไปคูณในฟังก์ชันบิ๊กโอ คือจะถูกจัดซะว่าเป็น O(1) ไปเลย ในที่นี้ก็คือลูป 5 รอบของเรานั่นเอง สรุปก็คือ ถ้าในโปรแกรมของเรามีลูป 5 รอบ 10 รอบอยู่ มันจะถูกจัดว่าเป็น O(1) หรือแม้แต่พันล้านรอบ ถ้าเรารู้ว่ามันจะรันพันล้านรอบทุกครั้ง มันก็จะเป็น O(1) ครับ เพราะถือว่าเป็นค่าคงตัว แต่ถ้าอย่าง O(n) มันยังขึ้นอยู่กับขนาดของข้อมูล (n) ว่าเป็นเท่าไหร่ ไม่ใช่ค่าคงตัว

แปลว่าก็เหลือแค่  O(2(n^2) + n + 1) งั้นสิ?....เปล่าครับ มันยังมีข้อ 2

2. ผู้ยิ่งใหญ่เท่านั้นที่อยู่รอด!!!
หมายถึง เราจะดูพจน์ที่มีดีกรีสูงที่สุดพจน์เดียวเท่านั้นครับ...คือตัวที่ดูมีค่ามากที่สุดอ่ะ เรามี 2(n^2) + n + 1 ก้อนที่ดูจะใหญ่ที่สุดก็คือ 2(n^2) ใช่มะครับ เราก็เก็บไว้แค่นั้น ที่เหลือก็...โยนทิ้ง...เหลือแค่ O (2(n^2))

เสร็จแล้วหรอ...แค่นี้อ่ะนะ?

ยังๆ มีอีกๆ

3.constant อีกแล้ว อย่ามาเจ๋อ!!!
ตอนนี้เรามี 2(n^2) เห็นมั้ยครับว่ามันยังเหลือค่าคงตัวอีกตัวนึง คือ 2 ที่คูณอยู่ข้างหน้านั่นเอง ทำให้เราเหลือผู้ชนะเพียงหนึ่งเดียว คือ n^2 !!!! 

สรุปว่าเปน O(n^2) ใช่มะ?...

อื่ม...ใช่ ...

แล้ว 2 ไม่ใช่ค่าคงตัวเหรอ...

- -"....มันมาจาก nn (n*n) ไงพวก ...

แล้ว 2(n^2) ไม่ใช่ n^2 + n^2 เหรอ ...

ก็เค้าเอาก้อนใหญ่สุด "ก้อนเดียว" n^2 + n^2 เหลือเหลือก้อนเดียว คือ n^2...

อื่ม...แล้ว ถ้า print ตารางขนาดกว้าง n ยาว m อ่ะ...

ก็เขียนไปเลยว่า O(nm)... 

แล้วทำไมต้องตัดนู่นตัดนี่ให้วุ่นวายด้วย...

อันนี้ไม่รู้จริงๆครับ... 

มี O ไรอีกบ้างอ่ะท่าน

O(n^3) O(n^4) O(log n) O(n log n)  O(2^n) ...

อืมม สมมติบางทีมันรันนิดเดียว บางทีมันก็รันเยอะ...
ผมหมายถึง สมมติวนหาข้อมูลตัวนึงในอาเรย์ n ตัวอย่างเงี้ย ทางโชคดี วนไปช่องแรกก็เจอเลย มันก็เป็น O(1) ใช่ป่ะ ถ้าโชคร้าย ข้อมูลที่ต้องการ อยู่ช่องสุดท้าย ก็ O(n) อย่างงี้จะเป็น O ไรอ่ะ

ตอบ: ฟังก์ชัน Big O ดูที่ Worst Case ครับ หมายถึงดูในกรณีที่เลวร้ายที่สุด เพราะเวลาเราไปแข่ง หากเราประเมินจาก Best Case หรือกรณีที่ดีที่สุดแล้วมันทัน บางทีไปเจอข้อมูลทดสอบที่เลวร้ายมากๆ มันก็อาจจะไม่ทัน เราเลยประเมินที่ Worst Case เป็นหลัก

คร่าวๆก็มีประมาณนี้ครับ คิดว่าน่าจะพอสำหรับการเอาไปใช้งาน ที่สำคัญคือเราต้องดูให้ได้ว่าโค้ดของเราวนลูปอย่างไร บางทีวนลูปอาจจะดูง่าย แต่ถ้าเป็นลักษณะของ Recursive อาจจะดูยาด ตรงนี้ต้องใช้ประสบการณ์ครับ ถ้ามีไรโพสถามไว้ได้เลยนะครับ  

หากมีข้อสงสัยอะไร รบกวนส่งคำถามทางอีเมล์นะครับ (molev.mt@gmail.com)