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



ภาษา python เบื้องต้น บทที่ ๒๘: ฟังก์ชันบางตัวที่เกี่ยวข้องกับอิเทอเรเตอร์
เขียนเมื่อ 2016/04/27 09:56
แก้ไขล่าสุด 2021/09/28 16:42
หลังจากที่ในบทที่ ๒๖ และ บทที่ ๒๗ ได้แนะนำถึงสิ่งที่เรียกว่าอิเทอเรเตอร์ไปแล้ว คราวนี้จะแนะนำฟังก์ชันที่ทำหน้าที่สร้างอิเทอเรเตอร์ที่น่าจะมีประโยชน์และได้ใช้บ่อย

มีฟังก์ชันที่ถูกทำไว้สำเร็จรูปเพื่อใช้สร้างอิเทอเรเตอร์ที่จำเป็น ส่วนใหญ่อยู่ในมอดูลชื่อ itertools เวลาใช้จึงต้องเรียกใช้

ก่อนอื่นให้ทำการ import เข้ามาก่อนเลย
import itertools


แต่นอกจากนี้แล้วก็มีบางส่วนที่อยู่ในกลุ่มฟังก์ชันหลักสามารถใช้งานได้ทันที เช่น map, filter และ reversed ซึ่งได้กล่าวถึงไปแล้ว และในบทนี้จะแนะนำเพิ่มอีก ๒ ตัว คือ zip และ enumerate

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

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

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



enumerate
เป็นฟังก์ชันที่สร้างอิเทอเรเตอร์ของทูเพิลที่นำชุดข้อมูลมาผูกเข้ากับตัวเลข

ตัวอย่าง
print(list(enumerate('กขคงจ'))) # ได้ [(0, 'ก'), (1, 'ข'), (2, 'ค'), (3, 'ง'), (4, 'จ')]

จะเห็นว่าสมาชิกจากสายอักขระที่ใส่เป็นอาร์กิวเมนต์ถูกนำมาผูกเข้ากับทูเพิลทีละตัว

ค่าที่ได้จาก enumerate นั้นเป็นอิเทอเรเตอร์ดังนั้นต้องใช้ list เพื่อแปลงเป็นลิสต์อีกทีเพื่อแสดงสมาชิกทั้งหมด

หากใส่คีย์เวิร์ดไปอีกตัวคือ start จะกำหนดจุดเริ่มต้นของการนับได้ ถ้าไม่ใส่จะเริ่มจาก 0
print(list(enumerate('OBAFGKM',start=100))) # ได้ [(100, 'O'), (101, 'B'), (102, 'A'), (103, 'F'), (104, 'G'), (105, 'K'), (106, 'M')]

enumerate มีประโยชน์เวลาที่เราต้องการให้เวลาที่ใช้ for เพื่อวนซ้ำอยู่มีตัวเลขบางอย่างที่ช่วยนับจำนวนรอบ เวลาใช้ for สามารถนำตัวแปรมารับ ๒ ตัวได้ โดยตัวแรกจะรับค่าตัวเลข และตัวหลังจะรับสมาชิกของข้อมูล
for i,e in enumerate(['H','He','Li','Be','B'],start=1):
    print('%d:%s'%(i,e),end=', ')
# ได้ 1:H, 2:He, 3:Li, 4:Be, 5:B,

การทำงานของฟังก์ชันนี้เราสามารถสร้างเจเนอเรเตอร์ที่ทำงานเหมือนกันขึ้นมาได้ง่าย
def genenumerate(ชุดข้อมูล,start=0):
    i = start
    for c in ชุดข้อมูล:
        yield (i,c)
        i += 1



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

ตัวอย่าง
print(list(zip('กขค','abc'))) # ได้ [('ก', 'a'), ('ข', 'b'), ('ค', 'c')]

สามารถใช้ประโยชน์เช่นในเวลาที่เรามีชุดข้อมูลที่ต้องการวนซ้ำใน for ตั้งแต่ ๒ ลิสต์ขึ้นไป
a = ['ไก่','จิก','เด็ก','ตาย','เด็ก','ตาย','บน','ปาก','โอ่ง']
b = 'กจฎฏดตบปอ'
for x,y in zip(a,b):
    print(y+':'+x, end='|')
# ได้ ก:ไก่|จ:จิก|ฎ:เด็ก|ฏ:ตาย|ด:เด็ก|ต:ตาย|บ:บน|ป:ปาก|อ:โอ่ง|

หรือจะนำมาใช้สร้างดิกชันนารีก็ได้
print(dict(zip(b,a))) # ได้ {'จ': 'จิก', 'อ': 'โอ่ง', 'ต': 'ตาย', 'ฏ': 'ตาย', 'ด': 'เด็ก', 'ฎ': 'เด็ก', 'ป': 'ปาก', 'ก': 'ไก่', 'บ': 'บน'}

ลองเขียนเจเนอเรเตอร์ที่ทำงานเหมือนกับ enumerate ขึ้นมาดูได้
def genzip(*กลุ่มชุดข้อมูล):
    s = tuple(map(iter,กลุ่มชุดข้อมูล))
    while(1):
        y = tuple(map(next,s))
        if(len(y)<len(s)): break
        yield y

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



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

อาร์กิวเมนต์ที่ต้องใส่มี ๒ ตัวคือ เลขเริ่มต้น และระยะห่าง จำนวนที่ใส่อาจไม่ต้องเป็นจำนวนเต็มก็ได้
for i in itertools.count(10,10):
    if(i>=100): break
    print(i,end='.')
# ได้ 10.20.30.40.50.60.70.80.90.

หากไม่ใส่อาร์กิวเมนต์การนับจะเริ่มจาก 0 และไล่ไปทีละ 1

ลองสร้างเจเนอเรเตอร์เลียนแบบได้ดังนี้
def count(เริ่มต้น=0,ระยะห่าง=1):
    n = เริ่มต้น
    while(1):
        yield n
        n += ระยะห่าง



itertools.cycle
เป็นอิเทอเรเตอร์ที่นำข้อมูลจำนวนหนึ่งมาวนซ้ำไปเรื่อยๆอย่างไม่มีที่สิ้นสุด
cyc = itertools.cycle(range(2,7,2))
i = 0
while(i<30):
    print(next(cyc),end=';')
    i += 1
# ได้ 2;4;6;2;4;6;2;4;6;2;4;6;2;4;6;2;4;6;2;4;6;2;4;6;2;4;6;2;4;6;

สร้างเจเนอเรเตอร์ที่ทำงานในลักษณะเดียวกันได้ตามนี้
def cycle(ชุดข้อมูล):
    s = tuple(ชุดข้อมูล)
    while(1):
        for x in s:
            yield x



itertools.repeat
เป็นอิเทอเรเตอร์ที่เอาข้อมูลตัวเดียวมาปล่อยซ้ำไปเรื่อยๆ

อาร์กิวเมนต์มี ๒ ตัว ตัวแรกคือออบเจ็กต์ที่ต้องการซ้ำ ส่วนตัวหลังคือจำนวนครั้งที่ต้องการซ้ำ แต่ถ้าไม่ใส่อาร์กิวเมนต์ตัวหลังจะเป็นการวนซ้ำไม่มีที่สิ้นสุด
print([list(itertools.repeat('o',7))]) # ได้ [['o', 'o', 'o', 'o', 'o', 'o', 'o']]

ลองสร้างเจเนอเรเตอร์จำลองการทำงานดูได้
def repeat(ออบเจ็กต์,จำนวนครั้ง='อนันต์'):
    if(จำนวนครั้ง != 'อนันต์'): # กรณีที่ใส่จำนวนครั้ง
        for i in range(จำนวนครั้ง):
            yield ออบเจ็กต์
    else: # กรณีที่ไม่ใส่จำนวนครั้ง ให้วนอนันต์ครั้ง
        while(1):
            yield ออบเจ็กต์



itertools.chain
เป็นอิเทอเรเตอร์ที่เอาชุดข้อมูลหลายๆชุดมาเชื่อมเรียงต่อกัน

อาร์กิวเมนต์ที่ต้องใส่คือข้อมูลชนิดกลุ่มหรืออิเทอเรเตอร์ จะใส่กี่ตัวก็ได้ ทั้งหมดจะถูกนำมาเรียงต่อกัน
print(list(itertools.chain([1,2],range(3,7),(7,8),{9,10}))) # ได้ [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

การสร้างเจเนอเรเตอร์แสดงการทำงานต้องใช้ดอกจันในการรับอาร์กิวเมนต์ที่ไม่รู้ว่าจะมีกี่ตัว
def chain(*ชุดข้อมูลทั้งหมด):
    for ชุดข้อมูลแต่ละชุด in ชุดข้อมูลทั้งหมด:
        for ข้อมูลในชุด in ชุดข้อมูลแต่ละชุด:
            yield ข้อมูลในชุด



itertools.product
เป็นอิเทอเรเตอร์ที่เอาไว้สร้างทูเพิลที่เกิดจากการนำชุดข้อมูลหลายชุดมาแจกแจง

อาร์กิวเมนต์ที่ต้องใส่คือชุดข้อมูลทั้งหมดที่จะนำมาใช้แจกแจง และจำนวนทูเพิลที่ได้จะเท่ากับจำนวนสมาชิกของแต่ละชุดคูณกันทั้งหมด
x = ['x=1','x=2','x=3']
y = ['y=1','y=2']
print(list(itertools.product(x,y))) # [('x=1', 'y=1'), ('x=1', 'y=2'), ('x=2', 'y=1'), ('x=2', 'y=2'), ('x=3', 'y=1'), ('x=3', 'y=2')]
print(len(list(itertools.product(range(7),range(6),range(2))))) # ได้ 84

แต่นอกจากอาร์กิวเมนต์แล้วยังมีคีย์เวิร์ดตัวหนึ่งที่สามารถใช้ได้ คือ repeat ซึ่งใช้ระบุว่าต้องการให้ข้อมูลชุดหนึ่งถูกหยิบมาแจกแจงซ้ำกี่ครั้ง ถ้าไม่ใส่จะมีค่าเป็น 1 หมายความว่าข้อมูลแต่ละชุดถูกหยิบมาครั้งเดียว
print(list(itertools.product('+-',repeat=3))) # ได้ [('+', '+', '+'), ('+', '+', '-'), ('+', '-', '+'), ('+', '-', '-'), ('-', '+', '+'), ('-', '+', '-'), ('-', '-', '+'), ('-', '-', '-')]
print(len(list(itertools.product('+-',repeat=10)))) # ได้ 1024
print(' + '.join(map(''.join,itertools.product('xyz',repeat=3)))) # ได้ yzz + zxx + zxy + zxz + zyx + zyy + zyz + zzx + zzy + zzz

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

เริ่มจากถ้าหากมีข้อมูลอยู่แค่ ๒ ชุดแล้วต้องการมาแจกแจงก็แค่นำมาเข้าวังวน for ๒ ชั้น
def product2(ชุดข้อมูล1,ชุดข้อมูล2):
    s1 = tuple(ชุดข้อมูล1)
    s2 = tuple(ชุดข้อมูล2)
    for c1 in s1:
        for c2 in s2:
            yield (c1,c2)

แต่ว่าในความเป็นจริงแล้วอาจจะมีชุดข้อมูลกี่ชุดก็ได้ ไม่จำกัดแค่ ๒ ถ้าหาก ๓ ชั้นก็ใช้ for ซ้อนกัน ๓ ชั้น แต่ปัญหาคือเราต้องทำให้รองรับได้หมดไม่ว่าจะมีข้อมูลกี่ชุด

ในสถานการณ์ที่ไม่อาจรู้ได้ว่าจะมีวังวนซ้อนกันกี่ชั้นแบบนี้ สิ่งที่ใช้การได้ดีก็คือการเขียนฟังก์ชันเวียนเกิด (กล่าวถึงไปในบทที่ ๒๐)

ลองเขียนฟังก์ชันขึ้นมาใหม่โดยใช้ดอกจันแทนอาร์กิวเมนต์ไม่จำกัดจำนวน และมีการวัดจำนวนอาร์กิวเมนต์ ถ้าหากยาว ๒ ก็ทำการวนซ้ำด้วยข้อมูล ๒ ชุดนั้น แต่ถ้ายาวกว่าก็ให้ทำการเรียกตัวฟังก์ชันนี้เอง
def productn(*กลุ่มชุดข้อมูล):
    s = tuple(map(tuple,กลุ่มชุดข้อมูล))
    if(len(s)==2):
        for c1 in s[0]:
            for c2 in s[1]:
                yield (c1,c2)
    else:
        for c1 in productn(*s[:-1]): # เรียกใช้ตัวเอง
            for c2 in s[-1]:
                yield c1 + (c2,)

แต่ในตัวฟังก์ชันจริงๆต้องเพิ่มคีย์เวิร์ด repeat เข้าไปด้วย สุดท้ายแล้วก็เลยเขียนเป็นแบบนี้
def product(*กลุ่มชุดข้อมูล,repeat=1):
    s = tuple(map(tuple,กลุ่มชุดข้อมูล))*repeat
    if(len(s)==2):
        for c1 in s[0]:
            for c2 in s[1]:
                yield (c1,c2)
    else:
        for c1 in product(*s[:-1]):
            for c2 in s[-1]:
                yield c1 + (c2,)



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

อาร์กิวเมนต์มี ๒ ตัว ตัวแรกคือชุดข้อมูลที่ต้องการใช้ ส่วนตัวหลังคือจำนวนที่ต้องการเลือกจากในนั้น หากไม่ใส่ก็จะถือว่าเป็นการเลือกทั้งหมด ก็คือเลือกเป็นจำนวนเท่ากับจำนวนข้อมูลในชุด
print(list(itertools.permutations('xy'))) # ได้ [('x', 'y'), ('y', 'x')]
print(' + '.join(map(''.join,itertools.permutations('xyz')))) # ได้ xyz + xzy + yxz + yzx + zxy + zyx
print(' + '.join(map(''.join,itertools.permutations(['x','y','z'],2)))) # ได้ xy + xz + yx + yz + zx + zy

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

เพียงแต่ว่าจะต้องตัดกรณีที่เลือกอันซ้ำ กันทิ้งไป ซึ่งอาจใช้เงื่อนไขที่ว่าหากกรณีไหนที่มีเลขลำดับซ้ำกันก็ตัดกรณีนั้นทิ้ง การทำแบบนี้สามารถใช้ set ช่วยได้ เพราะเซ็ตมีคุณสมบัติที่ว่าสมาชิกห้ามซ้ำ หากตรวจสอบความยาวหลังแปลงเป็นเซ็ตแล้วพบว่าลดลงก็แสดงว่ามีตัวซ้ำ ก็ตัดกรณีนั้นทิ้ง
def permutations(ชุดข้อมูล,r='จำนวนข้อมูลทั้งหมด'):
    s = tuple(ชุดข้อมูล)    
    n = len(s)
    if(r=='จำนวนข้อมูลทั้งหมด'): r = len(n)
    for ii in product(range(n),repeat=r):
        if(len(set(ii)) == len(ii)):
            yield tuple(s[i] for i in ii)



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

combinations ต่างจาก permutations ตรงที่ไม่สนใจลำดับของการหยิบ ดังนั้นผลที่ได้จะมีจำนวนน้อยกว่า โดยตัดตัวที่มีสมาชิกซ้ำกันออกไป

อาร์กิวเมนต์มีสองตัวเช่นเดียวกับ permutations แต่อาร์กิวเมนต์ตัวหลังคือจำนวนที่เลือกนั้นจำเป็นต้องใส่ จะละไว้ไม่ได้
print(list(itertools.combinations('xyzt',4))) # ได้ [('x', 'y', 'z', 't')]
print(' '.join(map(''.join,itertools.combinations('xyzt',3)))) # ได้ xyz xyt xzt yzt
print(' '.join(map(''.join,itertools.combinations(['x','y','z','t'],2)))) # ได้ xy xz xt yz yt zt

การสร้างเจเนอเรเตอร์นั้นอาจใช้วิธีในทำนองเดียวกับ permutations แต่เพิ่มเงื่อนไขว่าจะต้องมีการจัดเรียงรูปแบบเดียว คือตามลำดับเดิมที่ถูกจัดวางในกลุ่ม ดังนั้นหมายความว่าหากใช้ฟังก์ชัน sorted เพื่อจัดเรียงแล้วจะต้องได้เท่าเดิม ตัวที่ไม่เหมือนเดิมแสดงว่าเรียงลำดับไม่ถูกต้องอยู่ก็คัดออกไป
def combinations(ชุดข้อมูล,r):
    s = tuple(ชุดข้อมูล)
    n = len(s)
    for ii in product(range(n),repeat=r):
        if(sorted(ii) == list(ii) and len(set(ii)) == len(ii)):
            yield tuple(s[i] for i in ii)



itertools.combinations_with_replacement
คล้ายกับ combinations แต่ว่าพิจารณาในกรณีที่สามารถเลือกซ้ำได้

ลองเปรียบเทียบระหว่าง product, permutations, combinations และ combinations_with_replacement ดูได้
print(list(itertools.product('กขค',repeat=2))) # ได้ [('ก', 'ก'), ('ก', 'ข'), ('ก', 'ค'), ('ข', 'ก'), ('ข', 'ข'), ('ข', 'ค'), ('ค', 'ก'), ('ค', 'ข'), ('ค', 'ค')]
print(list(itertools.permutations('กขค',2))) # ได้ [('ก', 'ข'), ('ก', 'ค'), ('ข', 'ก'), ('ข', 'ค'), ('ค', 'ก'), ('ค', 'ข')]
print(list(itertools.combinations('กขค',2))) # ได้ [('ก', 'ข'), ('ก', 'ค'), ('ข', 'ค')]
print(list(itertools.combinations_with_replacement(' กขค',2))) # ได้ [('ก', 'ก'), ('ก', 'ข'), ('ก', 'ค'), ('ข', 'ข'), ('ข', 'ค'), ('ค', 'ค')]

การสร้างก็คล้ายกับ combinations แต่ตัดเงื่อนไขที่ว่าต้องไม่เลือกซ้ำทิ้งไป
def combinations_with_replacement(ชุดข้อมูล,r):
    s = tuple(ชุดข้อมูล)
    n = len(s)
    for ii in product(range(n),repeat=r):
        if(sorted(ii) == list(ii)):
            yield tuple(s[i] for i in ii)



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



อ้างอิง


<< บทที่แล้ว      บทถัดไป >>
หน้าสารบัญ


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

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

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

หมวดหมู่

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

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

สารบัญ

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

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

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



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

  ค้นหาบทความ

  บทความแนะนำ

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

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

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月

2019年

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

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

ไทย

日本語

中文