φυβλαςのβλογ
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
蒙古语
语言学
maya
概率论
与日本相关的日记
与中国相关的日记
-- 与北京相关的日记
-- 与香港相关的日记
-- 与澳门相关的日记
与台湾相关的日记
与北欧相关的日记
与其他国家相关的日记
qiita
其他日志

按类别分日志



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

  查看日志

  推荐日志

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