φυβλαςのβλογ
บล็อกของ phyblas



ภาษา python เบื้องต้น บทที่ ๒๑: คำสั่งพิเศษบางตัวที่เกี่ยวข้องกับฟังก์ชัน
เขียนเมื่อ 2016/03/06 10:25
แก้ไขล่าสุด 2024/02/22 11:05
 

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

คำสั่งเหล่านั้นได้แก่ lambda, map, filter, any และ all



lambda

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

ลองยกตัวอย่างฟังก์ชันคำนวณง่ายๆ
def f(x):
    return x**3+4*x**2+5**x+1 

หากนิยามโดยใช้ lambda ก็จะเป็น
f = lambda x:x**3+4*x**2+5**x+1 


รูปแบบการเขียนอาจดูเข้าใจยากสักหน่อย แต่จะเห็นว่าดูแล้วเขียนสั้นลงเล็กน้อย
สรุปรูปแบบการเขียนคือ
<ชื่อตัวแปรที่ต้องการให้เป็นฟังก์ชัน> = lambda <อาร์กิวเมนต์>:<ค่าคืนกลับ>

ผลที่ได้ก็จะมีค่าเท่ากับการใช้ def ลองเทียบกันดู
def <ชื่อตัวแปรที่ต้องการให้เป็นฟังก์ชัน>(<อาร์กิวเมนต์>):
    return <ค่าคืนกลับ>

หากมีอาร์กิวเมนต์หลายตัวก็ใช้จุลภาคคั่นเช่นเดียวกับการนิยามฟังก์ชันทั่วไป เช่น
f = lambda x,y,z:x+y+z
print(f(3,4,5)) # ได้ 12 

จะได้ฟังก์ชันสำหรับบวกค่าตัวเลข ๓ ตัว

ข้อด้อยของการใช้ lambda ก็คือไม่สามารถสร้างฟังก์ชันสำหรับดำเนินการอะไรได้ ทำได้แต่ฟังก์ชันสำหรับคืนค่า

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

สามารถสร้างฟังก์ชันพร้อมกันหลายๆตัวได้อย่างรวดเร็ว เช่นตัวอย่างนี้ สร้างฟังก์ชันบวกลบคูณหารพร้อมกัน
f4 = [lambda x,y:x+y,lambda x,y:x-y,lambda x,y:x*y,lambda x,y:x/y]
print(f4[0](4,5)) # 9
print(f4[1](4,5)) # -1
print(f4[2](4,5)) # 20
print(f4[3](4,5)) # 0.8

การทำแบบนี้ถ้าใช้ def ก็ต้องประกาศฟังก์ชันทีละตัว แล้วค่อยเอาชื่อฟังก์ชันมาเก็บรวมกันในลิสต์ ไม่สามารถทำทุกอย่างในบรรทัดเดียวได้แบบที่ใช้ lambda

นอกจากนี้ lambda ยังมีข้อดีตรงที่สามารถสร้างแล้วใช้งานได้ทันทีโดยที่อาจไม่ต้องตั้งชื่อ ฟังก์ชัน (ไม่ต้องสร้างตัวแปรออบเจ็กต์ฟังก์ชัน) ซึ่งทำให้สะดวก ใช้งานได้รวดเร็ว เช่น
 print((lambda x:x**2)(10)) # ได้ 100
print([(lambda x:x+10)(x) for x in range(10,20)]) # ได้ [20, 21, 22, 23, 24, 25, 26, 27, 28, 29]

ด้านหลังโคลอน : จะต้องเป็นค่าอะไรสักอย่างที่ต้องการให้คืนกลับ แต่จะใส่เป็นฟังก์ชันที่สั่งให้ทำงานอะไรบางอย่างก็ได้ แต่ในกรณีแบบนั้นหากฟังก์ชันนั้นไม่ได้ส่งค่าคืนกลับมาก็จะได้ None
 print((lambda x:print(x*2))(7)) # จะมีเลข 14 ถูก print ออกมาก่อน จากนั้นจะได้ None

ดังนั้นเราอาจใช้สร้างฟังก์ชันธรรมดาที่ไม่มีการคืนค่าก็ได้ เช่นฟังก์ชันที่จะพิมพ์เลข ๒ เท่าของที่ใส่เข้าไป
printx2 = lambda x:print(x*2)
printx2(12) # ได้ 24

หลัง lambda อาจไม่จำเป็นจะต้องใส่ตัวแปรเลยก็ได้ กรณีแบบนี้ก็จะได้ฟังก์ชันที่ไม่ต้องการอาร์กิวเมนต์
 print((lambda:1)()) # ได้ 1



map

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

โดยทั่วไปก็จะต้องใช้ for เพื่อวนทำซ้ำให้ครบทุกตัว
def f(x):
    return x**3
xxxx = [2,7,11,16]
x3 = []
for x in xxxx:
    x3 += [f(x)]
print(x3) # ได้ [8, 343, 1331, 4096]

แต่ว่ามีวิธีที่จะเขียนให้ง่ายและสั้นขึ้นมาก คือการใช้คำสั่ง map
def f(x):
    return x**3
xxxx = [2,7,11,16]
x3 = list(map(f,xxxx))
print(x3) # ได้ [8, 343, 1331, 4096]

map นั้นเป็นคำสั่งสำหรับให้คืนค่าที่ฟังก์ชันหนึ่งทำกับกลุ่มข้อมูลหนึ่ง

การใช้ map นั้นเป็นการสร้างออบเจ็กต์ชนิดหนึ่งคือชนิด map ซึ่งเป็นอิเทอเรเตอร์ตัวหนึ่ง ถ้าต้องการให้เป็นลิสต์ก็ต้องคร่อมด้วย list() ไปอีกทีดังตัวอย่าง

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

สรุปโครงสร้างของการใช้ map
<ตัวแปรที่รับค่าออบเจ็กต์ map> = map(<ฟังก์ชัน>,<ลิสต์>)

หรือถ้าต้องการเปลี่ยนเป็นลิสต์ทันที
<ตัวแปรที่รับค่าออบเจ็กต์ map> = list(map(<ฟังก์ชัน>,<ลิสต์>))

อนึ่ง ที่จริงแล้ววิธีที่เขียนสั้นๆได้อีกวิธีคือใช้ for สร้างลิสต์
x3 = [f(x) for x in xxxx]

ผลที่ได้ก็เหมือนกัน จะใช้แบบไหนก็แล้วแต่กรณี แล้วแต่ความถนัด

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

บางครั้ง map ยังใช้คู่กับ lambda เพื่อจะได้ไม่ต้องนิยามฟังก์ชันขึ้นมาก่อน ทำให้การเขียนยิ่งสั้นกะทัดรัด
xxxx = [2,7,11,16]
x3 = list(map(lambda x:x**3,xxxx))
print(x3) # ได้ [8, 343, 1331, 4096]

ลองประยุกต์ใช้กับอย่างอื่นอีก เช่น เปลี่ยนตัวเลขเป็นสายอักขระ
print(list(map(str,range(10)))) # ได้ ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
print(''.join(map(str,range(10)))) # ได้ 0123456789

จะเห็นว่าเมธอด .join สามารถใช้กับออบเจ็กต์ map ได้โดยตรงโดยไม่ต้องแปลงเป็นลิสต์ก่อน

***map คืนค่าเป็นอิเทอเรเตอร์แบบนี้ในไพธอน 3 แต่ในไพธอน 2 นั้น map จะคืนค่าเป็นลิสต์ไม่ใช่อิเทอเรเตอร์ ดังนั้นไม่ต้องมาแปลงเป็นลิสต์อีกทีเพื่อแสดงผล
filter ก็เช่นเดียวกัน
>>> รายละเอียด



filter

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

ตัวอย่าง มีลิสต์อยู่อันหนึ่งที่มีสายอักขระอยู่หลายตัว จะกรองเอาเฉพาะสายอักขระที่สั้นกว่า 6 ตัวอักษร
k = ['egao','kibou','yuuki','jishin','kagayaki','ai','yasashisa']
f = []
for s in k:
    if(len(s)<6):
        f += [s]
print(f) # ได้ ['egao', 'kibou', 'yuuki', 'ai'] 

หรืออาจเขียนสั้นๆด้วยวิธีการสร้างลิสต์ใหม่จาก for เป็น
f = [s for s in k if(len(s)<6)]
print(f) # ได้ ['egao', 'kibou', 'yuuki', 'ai']

แต่ก็มีอีกวิธีหนึ่งที่สามารถใช้ได้ คือใช้ filter ซึ่งมีวิธีการเขียนดังนี้
def filt(s): # นิยามฟังก์ชันสำหรับคัดกรองขึ้นมาก่อน
    return len(s)<6
f = list(filter(filt,k))
print(f) # ได้ ['egao', 'kibou', 'yuuki', 'ai']

เขียนแบบนี้อาจดูเข้าใจยากสักหน่อย อาร์กิวเมนต์ตัวแรกของ filter คือฟังก์ชันซึ่งจะถูกพิจารณาค่าความจริงเท็จ ในที่นี้ชื่อ filt ส่วนตัวที่สองคือลิสต์ที่จะนำมาคัดกรอง คือลิสต์ k

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

ผลที่ได้จะอยู่ในรูปของออบเจ็กต์ชนิด filter ซึ่งก็เป็นอิเทอเรเตอร์เช่นเดียวกับ map

filter มักใช้คู่กับ lambda เพื่อจะทำให้ไม่จำเป็นต้องนิยามฟังก์ชันขึ้นมาก่อน ดูกะทัดรัดขึ้นมาก
f = list(filter(lambda s:len(s)<6,k))
print(f) # ได้ ['egao', 'kibou', 'yuuki', 'ai']



ตัวอย่างการใช้ การหาเลขจำนวนเฉพาะโดยวิธีการตะแกรงของเอราโตสเทแนส

ตะแกรงของเอราโตสเทแนส (Ἐρατοσθένης) มีที่มาจากนักคณิตศาสตร์กรีกโบราณ เป็นการคัดกรองหาเลขที่เป็นจำนวนเฉพาะโดยการไล่ตัดตัวเลขที่หารจำนวนเฉพาะ ลงตัวไปทีละนิด โดยเริ่มดูจาก 2 ตามด้วย 3 แล้วก็ 5 ไปเรื่อยๆ

กล่าวคือ เริ่มไล่ดูว่าตัวไหนหาร 2 ลงตัวก็ตัดจำนวนนั้นออก จากนั้นทำซ้ำกับ 3 ส่วนเลข 4 ถูกตัดไปแล้วจึงข้ามไปทำ 5 แล้วก็ข้ามไป 7 และ 11 ต่อไปเรื่อยๆ
n = 121 # จำนวนตัวเลขที่จะพิจารณา
ch = range(2,n+1)
i = 0 # ตำแหน่งของสมาชิกในลิสต์ที่จะใช้เป็นตัวกรอง เริ่มจากตัวแรก
while(ch[i]<=n**0.5): # ให้วนกรองไปเรื่อยๆจนกว่าจะถึงตัวเลขที่เท่ากับรากที่สองของ n
    ch = list(filter(lambda x:x%ch[i]!=0 or x==ch[i],ch)) # คัดกรองเอาไว้เฉพาะตัวที่หารตัวที่เป็นตัวกรองอยู่ไม่ลงตัว และตัวกรองเอง
    print('รอบที่ %d: '%(i+1)+','.join(map(str,ch))) # แสดงผลเลขที่เหลืออยู่ในแต่ละรอบ
    i += 1 # พิจารณาตัวถัดไป 

ผลลัพธ์
รอบที่ 1: 2,3,5,7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43,45,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,77,79,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,115,117,119,121
รอบที่ 2: 2,3,5,7,11,13,17,19,23,25,29,31,35,37,41,43,47,49,53,55,59,61,65,67,71,73,77,79,83,85,89,91,95,97,101,103,107,109,113,115,119,121
รอบที่ 3: 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,49,53,59,61,67,71,73,77,79,83,89,91,97,101,103,107,109,113,119,121
รอบที่ 4: 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,121
รอบที่ 5: 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113



any กับ all

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

any(ลิสต์) เทียบเท่ากับ (1 in ลิสต์)
print(1 in [1,0,1]) # ได้ True
print(any([1,0,1])) # ได้ True
print(any([0,0,0])) # ได้ False

ส่วน all นั้นจะตรวจว่าข้อมูลในกลุ่มเป็นจริงทั้งหมดหรือเปล่า ถ้ามี False แม้แต่ตัวเดียวจะเป็น False ทันที

all(ลิสต์) เทียบเท่ากับ (1 in ลิสต์)
print(0 not in [1,0,1]) # ได้ False
print(all([1,0,1])) # ได้ False
print(all([1,1,1])) # ได้ True

การประยุกต์ใช้นั้นเช่นเดียวกับ filter คือสามารถใช้คู่กับ map และ lambda ได้ โดยใช้ lambda สร้างฟังก์ชันที่ตรวจสอบเงื่อนไขที่ต้องการ จากนั้นใช้ map เพื่อให้ฟังก์ชันนั้นทำกับทุกตัวในลิสต์ ผลที่ได้ก็คือจะได้ลิสต์ที่มีค่า True หรือ False (1 หรือ 0)
print(any(map(lambda x:x>0,[-7,-1,2,6]))) # ได้ True
print(all(map(lambda x:x>0,(-5,0,3,9)))) # ได้ False
print(all(map(lambda x:x%2,range(-7,9,2)))) # ได้ True
print(any(map(lambda x:x%2==0,range(-5,13,4)))) # ได้ False



สรุปเนื้อหา

ทั้ง lambda, map และ filter นั้นลักษณะการเขียนอาจดูแล้วเข้าใจยากในช่วงแรกๆ แต่หากใช้เป็นแล้วในบางกรณีจะช่วยให้การเขียนง่ายขึ้นมาก
แต่ก็ไม่ใช่คำสั่งที่ขาดไม่ได้ ดังนั้นบางคนอาจไม่เคยต้องใช้มันเลย แต่ก็อาจควรเรียนรู้ไว้สักหน่อยเผื่อว่าไปอ่านโค้ดที่คนอื่นเขียนแล้วเขาใช้ จะได้เข้าใจได้



อ้างอิง




-----------------------------------------

囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧囧

ดูสถิติของหน้านี้

หมวดหมู่

-- คอมพิวเตอร์ >> เขียนโปรแกรม >> python

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

สารบัญ

รวมคำแปลวลีเด็ดจากญี่ปุ่น
มอดูลต่างๆ
-- numpy
-- matplotlib

-- pandas
-- manim
-- opencv
-- pyqt
-- pytorch
การเรียนรู้ของเครื่อง
-- โครงข่าย
     ประสาทเทียม
ภาษา javascript
ภาษา mongol
ภาษาศาสตร์
maya
ความน่าจะเป็น
บันทึกในญี่ปุ่น
บันทึกในจีน
-- บันทึกในปักกิ่ง
-- บันทึกในฮ่องกง
-- บันทึกในมาเก๊า
บันทึกในไต้หวัน
บันทึกในยุโรปเหนือ
บันทึกในประเทศอื่นๆ
qiita
บทความอื่นๆ

บทความแบ่งตามหมวด



ติดตามอัปเดตของบล็อกได้ที่แฟนเพจ

  ค้นหาบทความ

  บทความแนะนำ

ตัวอักษรกรีกและเปรียบเทียบการใช้งานในภาษากรีกโบราณและกรีกสมัยใหม่
ที่มาของอักษรไทยและความเกี่ยวพันกับอักษรอื่นๆในตระกูลอักษรพราหมี
การสร้างแบบจำลองสามมิติเป็นไฟล์ .obj วิธีการอย่างง่ายที่ไม่ว่าใครก็ลองทำได้ทันที
รวมรายชื่อนักร้องเพลงกวางตุ้ง
ภาษาจีนแบ่งเป็นสำเนียงอะไรบ้าง มีความแตกต่างกันมากแค่ไหน
ทำความเข้าใจระบอบประชาธิปไตยจากประวัติศาสตร์ความเป็นมา
เรียนรู้วิธีการใช้ regular expression (regex)
การใช้ unix shell เบื้องต้น ใน linux และ mac
g ในภาษาญี่ปุ่นออกเสียง "ก" หรือ "ง" กันแน่
ทำความรู้จักกับปัญญาประดิษฐ์และการเรียนรู้ของเครื่อง
ค้นพบระบบดาวเคราะห์ ๘ ดวง เบื้องหลังความสำเร็จคือปัญญาประดิษฐ์ (AI)
หอดูดาวโบราณปักกิ่ง ตอนที่ ๑: แท่นสังเกตการณ์และสวนดอกไม้
พิพิธภัณฑ์สถาปัตยกรรมโบราณปักกิ่ง
เที่ยวเมืองตานตง ล่องเรือในน่านน้ำเกาหลีเหนือ
ตระเวนเที่ยวตามรอยฉากของอนิเมะในญี่ปุ่น
เที่ยวชมหอดูดาวที่ฐานสังเกตการณ์ซิงหลง
ทำไมจึงไม่ควรเขียนวรรณยุกต์เวลาทับศัพท์ภาษาต่างประเทศ

บทความแต่ละเดือน

2024年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2023年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2022年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2021年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

2020年

1月 2月 3月 4月
5月 6月 7月 8月
9月 10月 11月 12月

ค้นบทความเก่ากว่านั้น

ไทย

日本語

中文