4. คำสั่งควบคุม (More Control Flow Tools)

4.1 ประโยค if (if Statements)
4.2 ประโยค for (for Statements)
4.3 ฟังก์ชัน range() (The range() Function)
4.4 คำสั่ง break และ continue และวลี else สำหรับการวนรอบ (break and continue Statements, and else Clauses on Loops)
4.5 คำสั่ง pass (pass Statements)
4.6 นิยามฟังก์ชัน (Defining Functions)
4.7 เพิ่มเติมเรื่องฟังก์ชัน (More on Defining Functions)

เมื่อกี้ได้รู้จักคำสั่ง while แล้ว บทนี้เรามารู้จักคำสั่งควบคุมให้มากขึ้น


4.1 ประโยค if (if Statements)

>>> x = int(raw_input("Please enter an integer: "))
>>> if x < 0:
...      x = 0
...      print 'Negative changed to zero'
... elif x == 0:
...      print 'Zero'
... elif x == 1:
...      print 'Single'
... else:
...      print 'More'
...

มี elif กี่ตัวก็ได้ และมี else หรือไม่มีก็ได้ (ภาษาอื่นอาจมี switch และ case แต่ไพธอนใช้ if อย่างเดียว)


4.2 ประโยค for (for Statements)

for ของไพธอน ต่างจากภาษาอื่นเล็กน้อย ตอนวนรอบ แทนที่จะใช้ตัวนับซึ่งเป็นตัวเลข ไพธอนกลับใช้ลำดับแทน (เช่น สตริง ลิสต์ หรือทูเปิล)

>>> # Measure some strings:
... a = ['cat', 'window', 'defenestrate']
>>> for x in a:
...     print x, len(x)
... 
cat 3
window 6
defenestrate 12


4.3 ฟังก์ชัน range() (The range() Function)

ใช้สร้างลิสต์จากช่วงของตัวเลขจำนวนเต็ม

แบบง่าย

>>> range(10)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

อาจกำหนดเป็นช่วง

>>> range(5, 10)
[5, 6, 7, 8, 9]

หรือแบบกำหนดขนาดขั้นของการเพิ่มด้วย

>>> range(0, 10, 3)
[0, 3, 6, 9]

>>> range(-10, -100, -30)
[-10, -40, -70]

ใช้ร่วมกับ len() กับลิสต์ (การทำงานกับลิสต์แบบอ้างอิงจากดัชนี จะใช้วิธีนี้เป็นปกติ)

>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
...     print i, a[i]
...
0 Mary
1 had
2 a
3 little
4 lamb


4.4 คำสั่ง break และ continue และวลี else สำหรับการวนรอบ (break and continue Statements, and else Clauses on Loops)

คำสั่ง break ส่งผลให้หลุดจากวงรอบที่คำสั่งนี้บรรจุอยู่

ส่วน continue จะมีผลให้หยุดการทำงานที่จุดนั้น แล้วกลับไปเริ่มวนรอบใหม่

วลี else ใช้สำหรับเมื่อหลุดจากการวนแล้ว จะทำภายในบล็อคนี้หนึ่งครั้ง ยกเว้นถ้าพบคำสั่ง break

>>> for n in range(2, 10):
...     for x in range(2, n):
...         if n % x == 0:
...             print n, 'equals', x, '*', n/x
...             break
...     else:
...         # loop fell through without finding a factor
...         print n, 'is a prime number'
... 
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3


4.5 คำสั่ง pass (pass Statements)

คำสั่ง pass ไม่ทำอะไรเลย แต่มีไว้เผื่อเวลาเราวางโครงสร้างโค้ดไว้แล้ว แต่ยังไม่ได้เขียนท่อนนั้น ก็บรรจุคำสั่งนี้ไว้เพื่อให้สามารถทดสอบการรันได้

>>> while True:
...       pass # Busy-wait for keyboard interrupt
...


4.6 นิยามฟังก์ชัน (Defining Functions)

เอาตัวอย่างในการเขียนอนุกรมฟีโบนัชชีมาเขียน

>>> def fib(n):    # write Fibonacci series up to n
...     """Print a Fibonacci series up to n."""
...     a, b = 0, 1
...     while b < n:
...         print b,
...         a, b = b, a+b
... 

>>> # Now call the function we just defined:
... fib(2000)
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597

ฟังก์ชันขึ้นต้นด้วย def ตามด้วยชื่อฟังก์ชันและวงเล็บซึ่งบรรจุอาร์กิวเมนต์ หลังจากนี้จะเป็นบล็อคที่ต้องเยื้องย่อหน้า

บรรทัดพิเศษต่อจากชื่อฟังก์ชัน อาจใส่คำอธิบายการทำงานของฟังก์ชันได้เลย ซึ่งไพธอนจะไม่ตีความเป็นโค้ดที่จะรัน บรรทัดนี้เรียกว่า docstring

ตัวแปรในฟังก์ชันจะถือเป็นตัวแปรท้องถิ่นทั้งหมด เว้นแต่เรากำหนดให้เป็นตัวแปรส่วนรวม ซึ่งต้องกำหนดด้วยคำสั่ง global

การส่งผ่านค่าตัวแปร จะเป็นการส่งผ่านโดยค่าทั้งหมด (pass by value)

ชื่อฟังก์ชันสามารถถูกกำหนดค่าให้กับตัวแปรได้

>>> fib
<function fib at 10042ed0>

>>> f = fib
>>> f(100)
1 1 2 3 5 8 13 21 34 55 89

>>> f
<function fib at 10042ed0>

ฟังก์ชันในไพธอนจะคืนค่ากลับมาเสมอ ซึ่งปกติจะใช้คำสั่ง return VALUE แต่ในตัวอย่างข้างต้นไม่มีการคืนค่าด้วยคำสั่ง return กรณีนี้ไพธอนจะคืนค่าเป็นค่าพิเศษคือ None

>>> print fib(0)
None

จากตัวอย่างข้างต้น สามารถเขียนในรูปฟังก์ชันที่ส่งคืนค่าดังนี้

>>> def fib2(n): # return Fibonacci series up to 
...     """Return a list containing the Fibonacci series up to n.""
...     result = []
...     a, b = 0, 1
...     while b < n:
...         result.append(b)    # see below
...         a, b = b, a+b
...     return result
...
>>> f100 = fib2(100)    # call it
>>> f100                # write the result
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]

ความรู้ใหม่

  • ฟังก์ชันที่ไม่ระบุการคืนค่า จะส่งค่ากลับเป็น None
  • คำสัง result.append(b) เป็นเมธอดของลิสต์ result เมธอดก็คือฟังก์ชันที่เป็นเฉพาะของออบเจกต์นั้น ซึ่งมีรูปแบบการเขียนเป็น obj.methodname จากตัวอย่างการใช้เมธอด result.append(b) มีผลเท่ากับ "result = result + [b]" แต่เขียนได้กระชับและเข้าใจง่ายกว่า


4.7 เพิ่มเติมเรื่องฟังก์ชัน (More on Defining Functions)

มีหลักในการกำหนดค่าอาร์กิวเมนต์คือ

4.7.1 แบบกำหนดค่าปริยาย (Default Argument Values)

เป็นการกำหนดค่าปริยายให้กับอาร์กิวเมนต์ มีรูปแบบว่าอาร์กิวเมนต์ที่จะกำหนดค่าปริยายให้ จะต้องอยู่ทางขวาเสมอ ส่วนตัวที่ไม่กำหนด จะต้องอยู่ทางซ้ายเสมอ

def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
    while True:
        ok = raw_input(prompt)
        if ok in ('y', 'ye', 'yes'): return True
        if ok in ('n', 'no', 'nop', 'nope'): return False
        retries = retries - 1
        if retries < 0: raise IOError, 'refusenik user'
        print complaint

การใช้งานเช่น ask_ok('Do you really want to quit?') หรือ ask_ok('OK to overwrite the file?', 2, 'Please answer y or n')

จากตัวอย่างหลัง retries คือ 2 และ complaint คือ 'Please answer y or n'

ในตัวอย่างนี้ มีคำใหม่คือ in เป็นการดูว่าตัวแปร ok อยู่ภายในรายการที่กำหนดหรือไม่

ข้อควรระวัง

  • การกำหนดค่าให้กับอาร์กิวเมนต์ จะกำหนดในครั้งแรกครั้งเดียว จึงต้องระมัดระวังในการใช้งาน ตามตัวอย่างคือ
    >>> i = 5
    >>> def f(arg=i):
    ...     print arg
    ... 
    >>> f()
    5
    
    >>> i=6
    >>> f()
    5
  • ตัวแปรที่เป็น mutable คือลิสต์ ทูเปิล และอินสแตนซ์ของคลาส ต้องระวังในการใช้งานอย่างยิ่ง เนื่องจากเมื่อมันถูกกำหนดค่าแล้ว ค่าของมันจะยังคงอยู่ภายในฟังก์ชันนั้น เวลาอ้างถึงในรอบหลัง ๆ จะทำให้ผิดพลาดได้
    >>> def f(a, L=[]):
    ...     L.append(a)
    ...     return L
    ... 
    >>> print f(1)
    [1]
    
    >>> print f(2)
    [1, 2]
    
    >>> print f(3)
    [1, 2, 3]

    วิธีแก้คือ ให้หลีกเลี่ยงข้อมูลชนิดนี้ในการกำหนดค่าปริยาย จากตัวอย่างจะดัดแปลงฟังก์ชันเป็น

    def f(a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L
4.7.2 แบบระบุคีย์เวิร์ด (Keyword Arguments)

เหมือนกับหัวข้อก่อนหน้า แต่ในหัวข้อนี้ เจาะจงอธิบายลักษณะที่กำหนดเป็นคีย์เวิร์ด มีรูปแบบคือ "keyword = value"

def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
    print "-- This parrot wouldn't", action,
    print "if you put", voltage, "volts through it."
    print "-- Lovely plumage, the", type
    print "-- It's", state, "!"

การใช้งาน

  • แบบนี้ใช้ได้
    parrot(1000)
    parrot(action = 'VOOOOOM', voltage = 1000000)
    parrot('a thousand', state = 'pushing up the daisies')
    parrot('a million', 'bereft of life', 'jump')
  • แบบนี้ผิด
    parrot()                     # ผิดเพราะขาดค่าที่ไม่มีค่าปริยาย คือ voltage
    parrot(voltage=5.0, 'dead')  # ผิดเพราะค่าที่ไม่ใช่ค่าปริยายอยู่ขวา ที่ถูกต้องอยู่ซ้าย
    parrot(110, voltage=220)     # ผิดเพราะกำหนดค่าซ้อน
    parrot(actor='John Cleese')  # ผิดเพราะชื่อไม่มีชื่อคีย์เวิร์ด actor

ตัวอย่างการรายงานข้อผิดพลาดของการกำหนดค่าซ้อน

>>> def function(a):
...     pass
... 
>>> function(0, a=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'

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

def function_name(normal_parameter, *tuple_parameter, **dictionary_parameter)

ตัวอย่างคือ

def cheeseshop(kind, *arguments, **keywords):
    print "-- Do you have any", kind, '?'
    print "-- I'm sorry, we're all out of", kind
    for arg in arguments: print arg
    print '-'*40
    keys = keywords.keys()
    keys.sort()
    for kw in keys: print kw, ':', keywords[kw]

เรียกใช้ด้วยคำสั่ง

cheeseshop('Limburger', "It's very runny, sir.",
           "It's really very, VERY runny, sir.",
           client='John Cleese',
           shopkeeper='Michael Palin',
           sketch='Cheese Shop Sketch')

ในที่นี้

  • อาร์กิวเมนต์ธรรมดา คือ kind = 'Limburger'
  • อาร์กิวเมนต์ที่เป็นทูเปิล คือ arguments = ("It's very runny, sir.", "It's really very, VERY runny, sir.")
  • อาร์กิวเมนต์ที่เป็นดิกชันนารี คือ keywords = { client:'John Cleese', shopkeeper:'Michael Palin', sketch:'Cheese Shop Sketch' }

ผลลัพธ์คือ

-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch

ความรู้ใหม่

จากตัวอย่าง มีการเรียกใช้เมธอด sort() ในการเรียงข้อมูลดัชนี ซึ่งเป็นเมธอดของลิสต์

ลิสต์นี้ได้มาจากการหาดัชนีของดิกชันนารี keyword ได้ออกมาเป็นลิสต์ชื่อ keys ด้วยเมธอดของดิกชันนารีคือ keys()

4.7.3 การกำหนดจำนวนอาร์กิวเมนต์ที่ยืดหยุ่น (Arbitrary Argument Lists)

หากเราส่งผ่านอาร์กิวเมนต์แบบอ้างอิงซึ่งจะกลายเป็นทูเปิลแล้ว เราจะได้ความยืดหยุ่นในการกำหนดอาร์กิวเมนต์ ตัวอย่างคือ

>>> def testparm(x, *y):
...   print 'x=',x,'y=',y
...
>>> testparm('a',1,2,3)
x= a y= (1, 2, 3)

หรือ

>>> def testparm(x, *y):
...   print 'x=', x, 'y=', 
...   for i in y:
...     print i,
... 

>>> testparm('a',1,2,3)
x= a y= 1 2 3

ตัวอย่างในบทความต้นฉบับคือ

def fprintf(file, format, *args):
    file.write(format % args)
4.7.4 ถอดอาร์กิวเมนต์จากลิสต์หรือดิกชันนารี (Unpacking Argument Lists)

จากตัวอย่างก่อน ๆ ที่เรารู้เรื่องการผ่านค่าแบบอ้างอิงเป็นทูเปิลและดิกชันนารีแล้ว เราสามารถพลิกแพลงได้ เช่น ในตัวอย่างนี้ใช้ลิสต์แทนทูเปิล (ถ้าไม่มีการเปลี่ยนแปลงค่า ลิสต์และทูเปิลสามารถใช้แทนกันได้แบบตรง ๆ)

>>> range(3, 6)             # normal call with separate arguments
[3, 4, 5]

>>> args = [3, 6]
>>> range(*args)            # call with arguments unpacked from a list
[3, 4, 5]

ตัวอย่างนี้เป็นดิกชันนารี

>>> def parrot(voltage, state='a stiff', action='voom'):
...     print "-- This parrot wouldn't", action,
...     print "if you put", voltage, "volts through it.",
...     print "E's", state, "!"
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
4.7.5 แลมบ์ด้า (Lambda Forms)

ยืมความสามารถเรื่อง Functional Programming แบบภาษา Lisp มาใช้

โครงสร้างชวนเวียนหัวหน่อย ดูตัวอย่างดีกว่า

แบบไม่ใช้ lambda

>>> def f(x):
...   return x*2
... 
>>> f(3)
6

ใช้ lambda แบบแรก

>>> g = lambda x: x*2
>>> g(3)
6

ใช้ lambda แบบชั่วคราวจริง ๆ

>>> (lambda x: x*2)(3)
6

อีกตัวอย่างนึง ใช้ผสมกับฟังก์ชัน

>>> def make_incrementor(n):
...     return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42

>>> f(1)
43
4.7.6 ข้อความอธิบายการทำงาน (Documentation Strings)

บรรทัดแรกถัดจากชื่อฟังก์ชัน เป็นบรรทัดพิเศษที่ใส่บรรทัดข้อความอธิบายการทำงานของฟังก์ชัน ถ้าใช้อัญประกาศสามตัว """ หรือ ''' ก็สามารถเขียนได้หลายบรรทัด

สามารถเรียกดูข้อความในบรรทัดนี้ได้จากเมธอด function_name.__doc__

ตัวอย่าง

>>> def my_function():
...     """Do nothing, but document it.
... 
...     No, really, it doesn't do anything.
...     """
...     pass
... 

>>> print my_function.__doc__
Do nothing, but document it.

    No, really, it doesn't do anything.
Taxonomy upgrade extras: 
Creative Commons License ลิขสิทธิ์ของบทความเป็นของเจ้าของบทความแต่ละชิ้น
ผลงานนี้ ใช้สัญญาอนุญาตของครีเอทีฟคอมมอนส์แบบ แสดงที่มา-อนุญาตแบบเดียวกัน 3.0 ที่ยังไม่ได้ปรับแก้