วิธีคิดแบบนักวิทยาการคอมพิวเตอร์/Conditionals and recursion

<ns>0</ns>
 <revision>
 <parentid>57824</parentid>
 <timestamp>2020-12-17T01:40:43Z</timestamp>
 <contributor>
 <username>Geonuch</username>
 </contributor>
 <minor />
 <comment>ย้อนการแก้ไขโดย  ไปยังรุ่นแก้ไขล่าสุดโดย </comment>
 <origin>52667</origin>
 <model>wikitext</model>
 <format>text/x-wiki</format>
บทที่ 4 Conditionals and recursion.
4.1 The modulus operator.
เป็นสัญลักษณ์ซึ่งทำหน้าที่แทนการคำนวณ ซึ่งทำงานร่วมกับ integer (จำนวนเต็ม) และ integer expressions ใน Python สัญลักษณ์ตัวนี้คือ เครื่องหมาย % โครงสร้างของประโยคก็เหมือนกับ operator ตัวอื่นๆ เช่น
 »> quotient = 7 / 3
 »> print quotient
 2
 »> remainder = 7 % 3
 »> print remainder
 1
 7 เมื่อหารด้วย 3 จะเท่ากับ 2 และเหลือเศษ 1
 modulus operator ให้ผลลัพธ์ที่มีประโยชน์อย่างน่าประหลาดใจ ตัวอย่างเช่นคุณสามารถที่ตะตรวจสอบตัวเลขว่าตัวเลขหนึ่งๆหารลงตัวด้วยตัวเลขอื่นหรือไม่ - ถ้า x % y แล้วเท่ากับ 0 ดังนั้น x หารด้วย y ลงตัว
4.2 Boolean expressions.
เป็น expression ที่ประกอบด้วย true หรือ false ในการเขียน expression นี้ ทำโดยการใช้ operator == สำหรับเปรีบยเทียบค่า2ค่า และสร้างค่าทางตรรกะ
 »> 5 == 5
 True
 »> 5 == 6
 False
 ใน statement แรก ค่าทั้งสองมีค่าเท่ากัน ดังนั้นผลลัพธ์ที่ออกมาคือ True ใน statement ที่สอง 5 ไม่เท่ากับ 6 ดังนั้นเราจะได้ค่า False
 เครื่องหมาย == เป็นหนึ่งในเครื่องหมายที่ใช้ในการเปรียบเทียบ และนอกจากนี้ได้แก่:
 x != y # x ไม่เท่ากับ y
 x > y # x มากกว่า y
 x < y # x น้อยกว่า y
 x >= y # x มากกว่าหรือเท่ากับ y
 x <= y # x น้อยกว่าหรือเท่ากับ y 
 สัญลักษณ์ใน Python นั้นจะแตกต่างกัยสัญลักษณ์ทางคณิตศาสตร์ ข้อผิดพลาดที่เกิดขึ้นโดยทั่วไปคือการใช้ = แทน == ให้จำไว้ว่า = เป็นเครื่องหมายที่สำหรับใช้ในการกำหนดค่าให้กับตัวแปร และ == เป็นเครื่องหมายที่ใช้สำหรับการเปรียบเทียบค่า 
4.3 Logical operators.
มีด้วยกันอยู่ 3 ตัวคือ and or and not ความหมายของ operator เหล่านี้มีความหมายคล้ายคลึงกับความหมายของตัวมันเองในภาษาอังกฤษ ตัวอย่างเช่น x > 0 and x < 10 เป็นจริงในกรณีเดียวเมื่อ x มากกว่า 0 และน้อยกว่า 10
 n%2 == 0 or n%3 == 0 เป็นจริงถ้า ตัวใดตัวหนึ่งของเงื่อนไขเป็นจริง นั่นก็คือ เมื่อตัวเลขใดๆหารด้วย 2 หรือ 3 ได้ลงตัว
 สุดท้าย not ปฏิเสธ boolean expression ดังนั้น not(x > y) เป็น True ถ้า (x > y) เป็น False นั่นคือ ถ้า x น้อยกว่าหรือเท่ากับ y
 พูดอย่างเคร่งครัด ค่าที่ใช้ร่วมกับตัว operator ของ logical operator ควรจะเป็น boolean expressions(expression ที่ประกอบด้วย True และ False) แต่ใน Python ไม่ได้ยึดติดกับตรงนี้ ตัวเลขใดก็ได้ที่ไม่ใช่ 0 จะถูกแปลความให้เป็น "true"
 »> x = 5
 »> x and 1
 1
 »> y = 0
 »> y and 1
 0
 โดยทั่วไป รูปแบบชนิดนี้ไม่ได้เป็นรูปแบบที่ดี ถ้าคุณต้องการที่จะเปรียบเทียบค่ากับ 0 คุณควรที่จะทำให้เห็นอย่างชัดเจน
4.4 Conditional execution.
เพื่อทีจะเขียนโปรแกรมให้ดีนั้น เราต้องการความสามารถที่จะตรวจเช็คเงื่อนไขและเปลี่ยนแปลงการทำงานของโปรแกรมอย่างสอดคล้องอยู่เสมอ Conditional statements ให้ความสามารถนี้กับเรา รูปแบบอย่างง่ายที่สุดของ if statement:
 if x > 0:
 print "x is positive"
 boolean expression หลังจาก if statement เรียกว่า condition(เงื่อนไข) ถ้าหากว่ามันเป็น True หลังจากนั้น statement ที่ย่อหน้าก็จะถูกดำเนินการ แต่ถ้าไม่ก็จะไม่มีอะไรเกิดขึ้น
อย่าง compound statement อื่นๆ if statement สร้างขึ้นด้วย header และ block ของstatement:
HEADER:
 FIRST STATEMENT
 LAST STATEMENT
 header จะเริ่มบนบรรทัดใหม่และจบด้วยเครื่องหมาย (:) ย่อหน้าที่ตามมาจะเรียกว่า block และ statement ที่อยู่ใน compound statement จะเรียกว่า body ของstatement
 statement ใน body ของ if statement นั้นสามารถที่จะมีเท่าไหร่ก็ได้ไม่จำกัด แต่ต้องมีอย่างน้อย 1 statement ในบางครั้ง body ที่ไม่มี statementนั้นก็มีประโยชน์ กล่าวคือ เสมือนกับการจองพื้นที่สำหรับโค้ดที่เรายังไม่ได้เขียนลงไป ในกรณีนี้ คุณสามารถที่จะใช้ pass statement ซึ่งจะไม่ทำอะไรเลย
4.5 Alternative execution.
รูปแบบที่สองของ if statement คือ ทางเลือกของการดำเนินการ ที่ซึ่งมีความเป็นไปได้อยู่ 2 และตัดสินใจเลือกเงื่อนไขที่จะทำการดำเนินการ ไวยกรณ์มีลักษณะเช่นนี้:
 if x%2 == 0:
 print x, "is even"
 else:
 print x, "is odd"
 ถ้าส่วนที่เหลืออย เมื่อ x ถูกหารด้วย 2 ลงตัว เราก็จะรู้ว่า x เป็นจำนวนคู่ แล้วโปรแกรมก็จะทำการแสดงข้อความของผลนั้น ถ้าเงื่อนไขไม่ถูกต้อง statement อีกชุดหนึ่งก็จะถูกดำเนินการแทนตั้งแต่ที่เงื่อนไขจะต้องเป็น True และ False แน่นอนว่าจะต้องมี 1ทางเลือกที่จะต้องถูกดำเนินการ ทางเลือกดังกล่าวนั้นถูกเรียกว่า branches เพราะว่าทางเลือกเหล่านั้นเป็นแขนงหรือทางแยกในการไหลของการดำเนินการ
 เช่นด้านล่าง ถ้าคุณต้องการเช็ค parity ของตัวเลขบ่อยครั้ง คุณควรตัด code นี้ไปเป็น function:
 def printParity(x):
 if x%2 == 0:
 print x, "is even"
 else:
 print x, "is odd"
 สำหรับทุกๆค่าของ x printParityแสดงข้อความที่ได้เตรียมไว้ เมื่อคุณทำการเรียก คุณสามารถให้ตัวเลขใดๆก็ได้แทน argument
 »> printParity(17)
 17 is odd
 »> printParity(y+1)
 18 is even
4.6 Chained conditionals.
บางครั้งมีความเป็นไปได้ที่มากกว่า 2 และเราก็ต้องการทางแยกที่มากกว่า 2 เช่นกัน ทางเดียวที่จะแสดงการคำนวณเช่นนั้นคือ chained conditional:
 if x < y:
 print x, "is less than", y
 elif x > y:
 print x, "is greater than", y
 else:
 print x, "and" , y, "are equal"
 elif คือ การย่อ ของ "else if" Again แน่นอนว่าเพียงทางเลือกเดียวเท่านั้นที่จะถูกดำเนินการ สำหรับ elif statements นั้นสามารถจะมีได้เท่าไหรก็ได้ไม่จำกัด แต่ทางเลือกสุดท้ายจะต้องเป็น else statements:
 if choice == 'A' :
 functionA
 elif choice == 'B' :
 functionB
 elif choice == 'C' :
 functionC
 else:
 print "Invalid choice"
 แต่ละเงื่อนไขจะถูกตรวจสอบในคำสั่ง ถ้าเงื่อนไขแรกเป็น false เงื่อนไขต่อไปก็จะถูกตรวจสอบ เป็นต้น ถ้าหนึ่งในเงื่อนไขนั้นๆเป็น true ทางเลือกนั้นๆก็จะถูกดำเนินการ และก็จะจบ statement แม้ว่าจะมีมากกว่า 1 เงื่อนไขที่เป็น true เงื่อนไขแรกที่เป็น true เท่านั้นที่จะถูกดำเนินการ
4.7 Nested conditionals.
เงื่อนไขหนึ่งๆสามารถที่จะใส่อยู่ในเงื่อนไขอื่นๆได้เช่นเดียวกัน เราสามารถที่จะเขียนตัวอย่างของการแบ่งแยกออกเป็นสามส่วนได้ตามตัวอย่างต่อไปนี้:
 if x == y:
 print x, "and", y, "are equal"
 else:
 if x < y:
 print x, "is less than", y
 else:
 print x, "is greater than", y
 เงื่อนไขที่อยู่ข้างนอกประกอบด้วยสองทางเลือก ทางเลือกแรกมี statement ซึ่งเป็นผลลัพธ์ง่ายๆ ทางเลือกที่สองประกอบด้วย if statement ซึ่งมีทางเลือกอีก 2 ที่เป็นของตัวมันเอง
 ถึงแม้ว่าการย่อหน้าของ statement จะทำให้เกิดเป็นโครงร่างเกิดขึ้น nested conditionals ก็ยังคงยากต่อการอ่านโดยเร็ว โดยทั่วไปมันเป็นความคิดที่ดีที่จะหลีกเลี่ยงมันเมื่อคุณทำได้
 Logical operators เป็นทางหนึ่งที่จะช่วยทำให้ nested conditional statements ดูง่ายขึ้น สำหรับตัวอย่าง เราจะทำการเขียนโค๊ดต่อไปนี้ใหม่โดยใช้เพียงเงื่อนไขเดียว
 if 0 < x:
 x < 10:
 print "x is a positive single digit"
 print statement จะถูกดำเนินการเพียงกรณีเดียวถ้าเราทำให้เงื่อนไขทั้งสองผ่าน ดังนั้นเราสามรถใช้ and operator เข้ามาช่วยได้
 if 0 < x and x < 10:
 print "x is a positive single digit"
 เงื่อนไขเหล่านี้เป็นชนิดที่ธรรมดา ดังนั้น Python จึงมีการจัดเตรียมตัวเลือกไวยกรณ์ที่เหมือนกับเครื่องหมายการคำนวณทางคณิตศาสตร:
 if 0 < x < 10:
 print "x is a positive single digit"
 เงื่อนไขนี้เป็นความหมายเดียวกับ compound boolean expression และ nested conditional 
4.8 The return statement.
 เป็น statement ที่ยินยอมให้เรายุติการดำเนินการของ function ก่อนที่จะจบ เหตุผลหนึ่งที่ใช้มันก็คือถ้าคุณตรวจเจอเงื่อนไขที่ผิดพลาด
 import math
 def printLogarithm(x):
 if x <= 0:
 print "Positive numbers only, please"
 return
 result = mathlog(x)
 print "The log of x is", result
 function printLogarithm มีพารามิเตอร์หรือตัวแปรที่ชื่อ x อย่างแรกที่ทำก็คือ ตรวจสอบว่าไม่ว่า x จะน้อยกว่าหรือเท่ากับ 0 กรณีนี้มันจะแสดงข้อความ error และใช้ return เพื่อออกจาก function นั้น
 ให้จำไว้ว่าถ้าจะมีการใช้ฟังก์ชั่น math จะต้องมีการ import มันก่อนเสมอ
4.9 Recursion.
เราก็ได้กล่าวถึงกันไปแล้วสำหรับ function หนึ่ง เรียกไปยัง function อื่น และคุณก็ได้เห็นตัวอย่างเหล่านั้นไปพอสมควร เราจะไม่จะกล่าวถึงว่ามันถูกต้องสำหรับ function ที่เรียกตัวมันเอง มันอาจไม่แน่ชัดว่าทำไมมันถึงเป็นสิ่งที่ดี แต่มันกลับกลายมาเป็นสิ่งหนึ่งที่มหัศจรรย์และน่าสนใจที่โปรแกรมสามารถทำได้ ตัวอย่าง ดูจาก function ต่อไปนี้:
 def countdown(n):
 if n == 0:
 print "Blastoff!"
 else:
 print n
 countdown(n-1)
 countdown คาดหวังกับตัวแปร n ว่าจะต้องเป็นจำนวนเต็มบวก ถ้า n เป็น 0 ผลลัพธ์ที่จะออกมาคือ "Blastoff!" มิฉะนั้น ก็จะแสดงค่า n ออกมาและทำการเรียก function ที่ชื่อ countdown ตัวมันเอง โดยมี (n-1)เป็นตัวแปรที่ใช้รับค่าเข้าฟังก์ชั่น
 จะเกิดอะไรขึ้นหากเราเรียก function เช่นนี้:
 »> countdown(3)
 การดำเนินการของ countdown จะเริ่มด้วย n=3 และเมื่อ n ไม่เท่ากับ 0 มันจะออกผลลัพธ์เป็นค่าเท่ากับ 3, และเรียกฟังก์ชั่นตัวมันเอง
 การดำเนินการของ countdown เริ่มด้วย n=2 และเมื่อ n ไม่เท่ากับ 0 มันจะออกผลลัพธ์เป็นค่าเท่ากับ 2, และเรียกฟังก์ชั่นตัวมันเอง
 การดำเนินการของ countdown เริ่มด้วย n=1 และเมื่อ n ไม่เท่ากับ 0 มันจะออกผลลัพธ์เป็นค่าเท่ากับ 1, และเรียกฟังก์ชั่นตัวมันเอง
 การดำเนินการของ countdown เริ่มด้วย n=0 และเมื่อ n เป็น 0 มันจะออกผลลัพธ์เป็นคำว่า, "Blastoff!" จากนั้นก็จะ return
 countdown ทำการคืนค่า n=1
 countdown ทำการคืนค่า n=2
 countdown ทำการคืนค่า n=3
 ซึ่งผลทั้งหมดจะออกมาในลักษณะนี้:
 3
 2
 1
 Blastoff!
 ตัวอย่างถัดมา ให้ดูเช่นเดิมที่ function newLine และ threeLines
 def newLine:
 print
 def threeLines:
 newLine:
 newLine:
 newLine:
 แม้ว่ามันจะสามารถใช้งานได้มันก็ไม่ได้ช่วยอะไรได้มากถ้าเราต้องการผลลัพธ์เป็น 2บรรทัดใหม่ หรือจะ 106 ทางเลือกที่ดีกว่าน่าจะเป็นเช่นนี้:
 def nLines(n)
 if n > 0:
 print
 nLines(n-1)
 โปรแกรมนี้เหมือนกับ countdown; ตราบเท่าที่ n มากกว่า 0 มันจะออกผลลัพธ์เป็นบรรทัดใหม่แล้วก็ทำการเรียกตัวเองซ้ำโดยผ่านตัวแปร(n-1) เพิ่มบรรทัดใหม่ 
กระบวนการที่ function เรียกตัวเองนั้นคือ recursion
4.10 Stack diagrams for recursive function.
ในหมวดที่ 3.11 เราคุ้นเคยกับ stack diagram ซึ่งอธิบายถึงสภาพของโปรแกรม ใรระหว่างที่มีการเรียกฟังก์ชั่น (function call: statement ที่ทำการดำเนินการฟังก์ชั่น ประกอบด้วยชื่อของฟังก์ชั่นและตามด้วย argument หรือตัวแปรที่ใช้รับค่าเข้าฟังก์ชั่นที่อยู่ภายในวงเล็บ)
 ซึ่ง diagram ชนิดเดียวกันนี้สามารถที่จะช่วยอธิบาย recursive function
ทุกครั้งที่มีการเรียกฟังก์ชั่น Python จะทำการสร้าง frame (โครงสร้างฟังก์ชั่นขึ้นมา), ซึ่งประกอบด้วย ตัวแปรของฟังก์ชั่นนั้นและ parameters (ชื่อซึ่งใช้ในฟังก์ชั่นเพื่ออ้างถึงค่าที่ผ่านด้วย argument หรือตัวแปรที่ใช้รับค่าเข้าฟังก์ชั่น)
 สำหรับ recursive function จะมี frame มากกว่า 1 บน stack ในเวลาเดียวกัน
นี่เป็นรูปภาพของ stack diagram ของฟังก์ชั่น countdown ซึ่งถูกเรียกด้วยมีค่า n=3
โดยปรกติ บนสุดของ stack จะเป็น frame สำหรับ ฟังก์ชั่น main มันจะว่างเปล่าเนื่องจากไม่ได้มีการสร้างตัวแปรใดๆใน main หรือผ่านค่า parameters ใดๆไปที่มัน
 frame ของ ฟังก์ชั่น countdown มีค่า parameter n ที่ต่างกัน ด้านล่างสุดของ stack ที่ซึ่ง n=0 เรียกว่า base case (ทางแยกของเงื่อนไขใน recursive function ซึ่งจะไม่เกิดผลในการเรียกซ้ำ)
มันจะไม่ทำการเรียกซ้ำ และจะไม่มี frame เพิ่มขึ้นอีก
4.11 Infinite recursion.
ถ้าหากการเรียกซ้ำไม่เคยไปถึง base case มันก็จะทำการเรียกซ้ำต่ออย่างต่อเนื่อง และโปรแกรมจะไม่มีการยุติ นี่เป็นที่รู้จักกันคือ infinite recursion และโดยทั่วไปมันไม่ได้ถูกพิจารณาว่าเป็นความคิดที่ดี 
 นี่เป็นตัวอย่างโปรแกรมอย่างเล็กที่สุดที่เป็น infinite recursion:
def recurse:
 recure:
 ในทั่วไปกระบวนการของการเขียนและทดสอบโปรแกรม โปรแกรมที่เป็น infinite recursion จะไม่สามารถรันอย่างไม่สิ้นสุดได้ 
 Python จะรายงานข้อความ error เมื่อมีการเรียกซ้ำจนถึงที่สุด:
 File "<stdin>", line 2, in recurse
 (98 repetitions omitted)
 File "<stdin>", line 2, in recurse
RuntimeError: Maximum recursion depth exceeded
 traceback (list ของฟังก์ชั่นที่กำลังทำการดำเนินการ จะถูกปริ้นเมื่อมี runtime error เกิดขึ้น) นี้มีขนาดใหญ่กว่าเล็กน้อยกว่าตัวอย่างที่เราได้เห็นกันในบทที่แล้ว
เมื่อ error เกิดขึ้น ก็มีถึง 100 recurse frame บน stack!
4.12 Keyboard input.
โปรแกรมที่เราได้เขียนจนเดี๋ยวนี้นั้นมีความหยาบอยู่เล็กน้อยคือมันไม่ได้มีการรับค่ามาจากผู้ใช้งานเลย โปรแกรมเหล่านั้นแค่ทำงานเหมือนๆเดิมอยู่ตลอดเวลา
 Python จึงได้มีการจัด built-in functions ที่สามารถทำาการรับค่าจากคีย์บอร์ด อย่างง่ายที่สุดเรียกว่า raw_input เมื่อฟังก์ชั่นนี้ถูกเรียกใช้งาน โปรแกรมก็จะหยุดและรอให้ผู้ใช้งาน พิมพ์บางอย่าง 
 เมื่อผู้ใช้งาน กด Return หรือ ปุ่ม Enter โปรแกรมก็จะดำเนินต่อไปและ raw_input จะทำการคืนค่าที่ผู้ใช้งานป้อนเข้ามาเป็น string:
 »> input = raw_input 
 What are you waiting for?
 »> print input
 What are you waiting for?
 ก่อนที่จะทำการเรียก raw_input เป็นความคิดที่ดีที่จะแสดงข้อความเพื่อบอกให้ผู้ใช้งานรู้ว่าต้องป้อนข้อมูลหรือพิมอะไร ข้อความนี้เรียกว่า Prompt
 เราสามารถที่จะให้ prompt นั้นทำหน้าที่แทน argument:
 »> name = raw_input ("Whatis your name? ")
 Whatis your name? Arthur, King of the Brintons!
 »> print name
 Arthur, King of the Brintons!
 หากเราคาดหวังที่จะต้องการให้เป็น integer เราสามารถที่จะใช้ input function:
 prompt = "Whatis the airspeed velocity of an unladen swallow?\n"
 speed = input(prompt)
 ถ้าผู้ใช้พิมพ์อักษรที่เป็นตัวเลขเข้ามา มันจะถูกแปลงให้เป็น integer และทำการกำหนดค่าให้กับ speed โชคร้าย ถ้าหากผู้ใช้งานพิมพ์ตัวอักษรที่ไม่ได้เป็นตัวเลขเข้ามา โปรแกรมก็จะล้ม:
 »> speed = input (prompt)
 Whatis the airspeed velocity of an unladen swallow?
 What do you mean, an African or a European swallow?
 SyntaxError: invalid syntax
 เพื่อที่จะหลีกเลี่ยง error ชนิดนี้ เป็นความคิดที่ดีที่จะใช้ raw_input เพื่อรับค่าที่เป็น string หลังจากนั้นก็ใช้ฟังก์ชั่นสำหรับการแปลง เพื่อแปลงค่าที่รับเป็นชนิดอื่น
4.13 Glossary.
modulus operator: An operator, denoted with a percent sign (%), that works on integers and yields the remainder when one number is divided by another.
 boolean expression: An expression that is either true or false.
 comparison operator: One of the operators that compares two values: ==, !=, >, <, >=, and <=.
 logical operator: One of the operators that combines boolean expressions: and, or, and not.
 conditional statement: A statement that controls the flow of execution depending on some condition.
 condition: The boolean expression in a conditional statement that determines which branch is executed.
 compound statement: A statement that consists of a header and a body. The header ends with a colon (:). The body is indented relative to the header.
 block: A group of consecutive statements with the same indentation.
 body: The block in a compound statement that follows the header.
 nesting: One program structure within another, such as a conditional statement inside a branch of another conditional statement.
 recursion: The process of calling the function that is currently executing.
base case: A branch of the conditional statement in a recursive function that does not result in a recursive call.
 infinite recursion: A function that calls itself recursively without ever reaching the base case. Eventually, an infinite recursion causes a runtime error.
 prompt: A visual cue that tells the user to input data.