วิธีทำงานโดยเปิดไฟล์แล้วอ่านทีละบรรทัด (ตอน 2)

เราสามารถใช้วิธี:

inputfile = open(filename)
    for line in inputfile:
        work_on_the_line

เช่นโค้ดข้างล่างเปิดไฟล์ /usr/share/dict/words แล้วอ่านทีละบรรทัด โดยที่แต่ละบรรทัดอยู่ในตัวแปร word ด้วยคำสั่ง for word in inputfile:

ตัวอย่าง: หาคำที่เป็น Palindrome (คำที่กลับหน้าหลังแล้วเหมือนเดิม)

In [1]:
#หาคำที่เป็น palindrome (คำที่กลับหน้าหลังแล้วเหมือนเดิม)
inputfile = open('/usr/share/dict/words') #เปิดไฟล์ที่มีคำภาษาอังกฤษบรรทัดละหนึ่งคำ
for word in inputfile: #อ่านเข้ามาทีละบรรทัด เก็บบรรทัดไว้ในตัวแปร word
    word = word.strip() #strip() จะลบตัวอักษรที่มองไม่เห็น (white spaces) ด้านหน้าและด้านหลังของคำ รวมถึงตัวอักษรขึ้นบรรทัดใหม่ (newline, \n)
    if len(word) <= 2: #ถ้าคำยาวไม่เกิน 2 ตัวอักษร เราจะไม่นับมันเป็น palindrome
        continue
    if word == word[::-1]: #word[::-1] หมายถึงการกลับหลังมาหน้า ดูส่วน "slices" ที่ https://snakify.org/en/lessons/lists/
        print(word)
aba
acca
adda
affa
aga
aha
ajaja
aka
ala
alala
alula
ama
amma
ana
anana
anna
apa
ara
arara
atta
ava
awa
bib
bob
boob
bub
civic
dad
deed
deedeed
degged
did
dod
dud
eke
elle
eme
ere
eve
ewe
eye
gag
gig
gog
hah
hallah
huh
ihi
imi
immi
kakkak
kayak
keek
kelek
lemel
level
maam
madam
mem
mesem
mim
minim
mum
murdrum
nan
non
noon
nun
oho
otto
pap
peep
pep
pip
poop
pop
pup
radar
redder
refer
repaper
retter
rever
reviver
rotator
rotor
siris
sis
sooloos
tat
tebbet
teet
tenet
terret
tit
toot
tot
tst
tut
tyt
ulu
ululu
umu
utu
waw
wow
yaray
yoy

ตัวอย่าง: หาคำที่ยาวที่สุด

In [2]:
inputfile = open('/usr/share/dict/words')
max_word = ""  #คำที่ยาวที่สุดที่เราเคยเห็น เนื่องจากตอนนี้ยังไม่เห็นสักคำ มันจึงเป็นคำว่างๆไม่มีตัวอักษรอะไร
for word in inputfile: 
    word = word.strip()
    if len(word) > len(max_word):  #ถ้าคำที่พึ่งอ่านเข้ามายาวกว่าคำที่ยาวที่สุดที่เราเคยเห็น...
        max_word = word      #...เราก็เปลี่ยนคำที่ยาวทีสุดที่เราเคยเห็นเป็นคำนี้  
print(max_word, len(max_word)) #len(x) คือความยาวของ x ว่ามีกี่ตัวอักษร นอกจากนี้ len ยังใช้บอกจำนวนของใน list, dictionary, ฯลฯ ด้วย
formaldehydesulphoxylate 24

เนื่องจากในโค้ดข้างบน เราเปรียบเทียบแบบ if len(word) > len(max_word): คือถ้าคำมีความยาวเท่าๆกัน เราจะจำเฉพาะคำแรกเท่านั้น เพราะเราจะเปลี่ยนคำที่จำก็ต่อเมื่อคำใหม่มีความยาวมากกว่าคำเก่า ถ้าจะหาคำทุกคำที่มีความยาว 24 ตัวอักษรเราก็สามารถหาแบบนี้ตรงๆได้:

In [3]:
inputfile = open('/usr/share/dict/words')
for word in inputfile:
    word = word.strip()
    if len(word) ==  24:  #ดูเฉพาะคำที่มี 24 ตัวอักษร
        print(word, len(word))
formaldehydesulphoxylate 24
pathologicopsychological 24
scientificophilosophical 24
tetraiodophenolphthalein 24
thyroparathyroidectomize 24

บางทีเราอาจจะสนใจคำที่ยาวๆโดยไม่จำเป็นต้องยาวที่สุด เช่นถ้าอยากเห็นคำที่ยาวตั้งแต่ 20 ตัวอักษรขึ้นไปเราก็อาจทำอย่างนี้ได้:

In [4]:
inputfile = open('/usr/share/dict/words')
long_words = []  #สร้าง list ชื่อ long_words เพื่อเก็บคำยาวๆหลายๆคำ ถ้าไม่คุ้นเคยให้อ่านที่ https://snakify.org/en/lessons/lists/
for word in inputfile:
    word = word.strip()
    if len(word) >= 20:          #ถ้าคำมี 20 ตัวอักษรขึ้นไป...
        long_words.append(word)  #...ให้เอาคำนั้นไปใส่เพิ่มใน list ที่ชื่อ long_words
print(long_words)
['abdominohysterectomy', 'acetylmethylcarbinol', 'acetylphenylhydrazine', 'aminoacetophenetidine', 'anarchoindividualist', 'anatomicochirurgical', 'anatomicopathological', 'anatomicophysiologic', 'anatomicophysiological', 'anemometrographically', 'anthrohopobiological', 'anthropoclimatologist', 'anthropogeographical', 'anthropomorphization', 'anthropomorphological', 'anthropomorphologically', 'anthropomorphotheist', 'anthropophysiography', 'anthropoteleological', 'antianthropomorphism', 'anticonfederationist', 'anticonstitutionalist', 'anticonstitutionally', 'antiprestidigitation', 'antitintinnabularian', 'appendorontgenography', 'aquopentamminecobaltic', 'Archaeopterygiformes', 'arteriosympathectomy', 'autodepolymerization', 'barothermohygrograph', 'benzalphenylhydrazone', 'benzofuroquinoxaline', 'Biblicopsychological', 'blepharochromidrosis', 'blepharoconjunctivitis', 'blepharohematidrosis', 'blepharosphincterectomy', 'bronchoaspergillosis', 'bronchoesophagoscopy', 'calcareoargillaceous', 'characteristicalness', 'chemicomineralogical', 'chemicopharmaceutical', 'chemicophysiological', 'Chlamydobacteriaceae', 'chlamydobacteriaceous', 'cholecystenterorrhaphy', 'cholecystenterostomy', 'cholecystgastrostomy', 'cholecystnephrostomy', 'cholecystoduodenostomy', 'cholecystogastrostomy', 'cholecystojejunostomy', 'cholecystolithotripsy', 'cholecystonephrostomy', 'choledochoduodenostomy', 'choledochoenterostomy', 'choledocholithotripsy', 'chromophotolithograph', 'chronocinematography', 'cinephotomicrography', 'compartmentalization', 'constitutionalization', 'consubstantiationist', 'counterdemonstration', 'counterdisengagement', 'counterestablishment', 'counterexcommunication', 'counterexpostulation', 'counterinterpretation', 'counterpronunciamento', 'counterreconnaissance', 'counterrevolutionary', 'counterrevolutionist', 'counterrevolutionize', 'cryptocrystallization', 'crystallographically', 'crystalloluminescence', 'dacryocystoblennorrhea', 'dacryocystorhinostomy', 'dacryocystosyringotomy', 'deanthropomorphization', 'decahydronaphthalene', 'deintellectualization', 'desoxycorticosterone', 'diphenylchloroarsine', 'diphenylquinomethane', 'disdenominationalize', 'disestablishmentarian', 'disproportionableness', 'disproportionateness', 'duodenocholecystostomy', 'duodenocholedochotomy', 'duodenopancreatectomy', 'electrocardiographic', 'electrocauterization', 'electrochronographic', 'electrocontractility', 'electroencephalogram', 'electroencephalograph', 'electroencephalography', 'electrometallurgical', 'electrophysiological', 'electropneumatically', 'electrosynthetically', 'electrotelethermometer', 'electrotherapeutical', 'encephalomeningocele', 'enterocholecystostomy', 'epididymodeferentectomy', 'epididymodeferential', 'establishmentarianism', 'formaldehydesulphoxylate', 'formaldehydesulphoxylic', 'galvanocauterization', 'galvanocontractility', 'gastroenteroanastomosis', 'gastroenterocolostomy', 'gastrohysterorrhaphy', 'glossolabiolaryngeal', 'glossolabiopharyngeal', 'hematospectrophotometer', 'hepaticoduodenostomy', 'heterochromatization', 'heterotransplantation', 'hexachlorocyclohexane', 'hexamethylenetetramine', 'hexanitrodiphenylamine', 'hexosemonophosphoric', 'histomorphologically', 'historicocabbalistical', 'historicogeographical', 'historicophilosophica', 'homeotransplantation', 'hydrometallurgically', 'hydropneumopericardium', 'hydroxyanthraquinone', 'hypercholesterinemia', 'hypercholesterolemia', 'hyperconscientiousness', 'hyperdolichocephalic', 'hyperphosphorescence', 'hypsibrachycephalism', 'hypsidolichocephalic', 'hypsidolichocephalism', 'incomprehensibleness', 'incontrovertibleness', 'indigotindisulphonic', 'indistinguishability', 'indistinguishableness', 'institutionalization', 'intellectualistically', 'intercommunicability', 'intercrystallization', 'interdestructiveness', 'interdifferentiation', 'internationalization', 'interparenthetically', 'intertransformability', 'keratoconjunctivitis', 'labioglossolaryngeal', 'labioglossopharyngeal', 'labyrinthibranchiate', 'laparocholecystotomy', 'laparocolpohysterotomy', 'lithochromatographic', 'lymphangioendothelioma', 'macracanthrorhynchiasis', 'mandibulosuspensorial', 'mechanicocorpuscular', 'mechanicointellectual', 'Mediterraneanization', 'membranocartilaginous', 'meningoencephalocele', 'metaphenylenediamine', 'microcinematographic', 'microcolorimetrically', 'microcryptocrystalline', 'microcrystallography', 'microseismometrograph', 'monobromoacetanilide', 'naphthalenesulphonic', 'naphthylaminesulphonic', 'neurochorioretinitis', 'nondenominationalism', 'noninterchangeability', 'noninterventionalist', 'nonrepresentationalism', 'omnirepresentativeness', 'oophorosalpingectomy', 'ophthalmoblennorrhea', 'ophthalmodiastimeter', 'ophthalmodynamometer', 'ophthalmothermometer', 'orbiculatoelliptical', 'orthodolichocephalic', 'otorhinolaryngologic', 'otorhinolaryngologist', 'overindustrialization', 'palaeoanthropography', 'palaeodendrologically', 'palaeometeorological', 'paleoanthropological', 'paleodendrologically', 'pancreaticoduodenostomy', 'pancreaticogastrostomy', 'pancreatoduodenectomy', 'pancreatoenterostomy', 'parallelogrammatical', 'paraphenylenediamine', 'pathologicoanatomical', 'pathologicohistological', 'pathologicopsychological', 'pectinatodenticulate', 'pentamethylenediamine', 'pericardiomediastinitis', 'peritoneopericardial', 'pharmacoendocrinology', 'pharyngoepiglottidean', 'phenolsulphonephthalein', 'philodestructiveness', 'philoprogenitiveness', 'philosophicohistorical', 'philosophicojuristic', 'philosophicoreligious', 'philosophicotheological', 'phlebarteriodialysis', 'phoneticogrammatical', 'phoneticohieroglyphic', 'photochromolithograph', 'photochronographical', 'photochronographically', 'photospectroheliograph', 'photospectroscopical', 'phototelegraphically', 'physicophilosophical', 'physicophysiological', 'physiologicoanatomic', 'phytopaleontological', 'piezocrystallization', 'platybrachycephalous', 'platydolichocephalic', 'platydolichocephalous', 'plethysmographically', 'pneumatotherapeutics', 'pneumohydropericardium', 'pneumoventriculography', 'poliencephalomyelitis', 'polioencephalomyelitis', 'poluphloisboiotatotic', 'predisadvantageously', 'premisrepresentation', 'preterdiplomatically', 'proconstitutionalism', 'Prorhipidoglossomorpha', 'prostatovesiculectomy', 'protobasidiomycetous', 'protocatechualdehyde', 'protransubstantiation', 'pseudoanthropological', 'pseudoconglomeration', 'pseudohermaphroditic', 'pseudohermaphroditism', 'Pseudolamellibranchia', 'Pseudolamellibranchiata', 'pseudolamellibranchiate', 'pseudomonocotyledonous', 'pseudoparenchymatous', 'pseudoparthenogenesis', 'pseudophenanthroline', 'psychophysiologically', 'pylethrombophlebitis', 'pyopneumocholecystitis', 'pyopneumopericardium', 'pyopneumoperitonitis', 'reticulatocoalescent', 'roentgenographically', 'saccharogalactorrhea', 'saccharomucilaginous', 'scientificogeographical', 'scientificohistorical', 'scientificophilosophical', 'scientificoreligious', 'scleroconjunctivitis', 'scleroticochorioiditis', 'scleroticochoroiditis', 'semiprofessionalized', 'spectromicroscopical', 'spectrophotoelectric', 'spectropyrheliometer', 'spinulosodenticulate', 'spondylotherapeutics', 'stereophotogrammetry', 'stereophotomicrograph', 'stereophotomicrography', 'stereoroentgenography', 'subjectivoidealistic', 'superincomprehensible', 'superphlogistication', 'superserviceableness', 'supersuperabundantly', 'superultrafrostified', 'syncategorematically', 'teleoroentgenography', 'tessarescaedecahedron', 'tetraiodophenolphthalein', 'theoanthropomorphism', 'theologicoastronomical', 'theologicohistorical', 'theologicometaphysical', 'thermophosphorescence', 'thermopolymerization', 'thoracogastroschisis', 'thymolsulphonephthalein', 'thyroparathyroidectomize', 'thyroparathyroidectomy', 'transubstantiationalist', 'transubstantiationite', 'transubstantiatively', 'tribophosphorescence', 'trihemitetartemorion', 'turbinatocylindrical', 'tychoparthenogenesis', 'ultradolichocephalic', 'ultraphotomicrograph', 'ultrastandardization', 'uncharacteristically', 'uncontradictableness', 'uncontrovertableness', 'uncontrovertibleness', 'undiscriminatingness', 'undistinguishableness', 'unextinguishableness', 'unproportionableness', 'uranostaphylorrhaphy', 'ureterocystanastomosis', 'ureteropyelonephritis', 'ureterosalpingostomy', 'ureterosigmoidostomy', 'vagoglossopharyngeal', 'zoologicoarchaeologist', 'zygomaticoauricularis']

ถ้าเราอยากรู้ว่าคำที่มีตัวอักษร 1, 2, 3,..., 24 ตัว มีอย่างละกี่คำเราก็อาจทำตรงๆอย่างนี้ได้ แบบนี้จะมีการอ่านไฟล์ 24 ครั้ง:

In [5]:
print("#letters\t#words") #พิมพ์่ชื่อคอลัมน์ให้คนอ่าน
for letters in range(1,25): #letters คือจำนวนตัวอักษร เริ่มจาก 1 ไปถึง 25-1 = 24 เพราะเรารู้ว่าคำยาวสุดมี 24 อักษร
    count = 0  #จำนวนคำที่มี "letters" ตัวอักษร
    inputfile = open('/usr/share/dict/words') #อ่านรายการคำ หนึ่งบรรทัดมีหนึ่งคำ
    for word in inputfile:  #อ่านไปทีละบรรทัด
        word = word.strip()  #ลบตัวขึ้นบรรทัดใหม่ (\n หรือ new line)
        if word[0] in "abcdefghijklmnopqrstuvwxyz":  #ดูเฉพาะคำที่ขึ้นต้นด้วยอักษรตัวเล็กเท่านั้น ไม่ดูพวกชื่อเฉพาะ
            if len(word) ==  letters:  #ดูเฉพาะคำที่มีตัวอักษร letters ตัว แล้วเพิ่มจำนวนใน count
                count = count + 1
    print(f"{letters}\t\t{count}")  #แสดงผลด้วย f-string (ดู #3 ที่ https://realpython.com/python-string-formatting/) 
#letters	#words
1		26
2		121
3		1135
4		4347
5		8497
6		15066
7		20552
8		26434
9		28833
10		27924
11		23773
12		18837
13		13877
14		9151
15		5585
16		3223
17		1738
18		815
19		417
20		194
21		81
22		40
23		16
24		5

ตัวอย่างฟังก์ชั่น

ฟังก์ชั่นจะทำหน้าที่ทำนองเดียวกับ Blocks ใน Scratch ถ้ายังไม่คุ้นเคยให้ไปดูที่นี่

เราจะป้อนข้อมูล input เข้าไป แล้่วมันจะทำงานหรือสร้าง output ให้เรา

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

ดูตัวอย่างสองสามอันนี้เป็นไอเดีย

In [6]:
def double(x):  
    "รับค่า x เข้าไปแล้วคำนวณค่า 2*x ให้"
    return(2*x)
In [7]:
def is_even(x):
    "ดูว่า x เป็นเลขคู่หรือไม่"
    if x % 2 == 0: #ถ้าเศษจากการหารด้วย 2 เป็น 0 ก็แสดงว่าเป็นเลขคู่
        return(True)
    else:
        return(False)
In [8]:
def starts_with_small_letter(word):
    "ดูว่าตัวอักษรแรกใน word เป็นอักษรตัวเล็กหรือไม่"
    small_letters = "abcdefghijklmnopqrstuvwxyz"
    if word[0] in small_letters:
        return(True)
    else:
        return(False) #ถ้าตัวแรกไม่อยู่ใน a-z เราก็ถือว่าคำนั้นไม่ได้ขึ้นต้นด้วยอักษรตัวเล็ก
    
In [9]:
def number_of_words_with_length(n, list_of_words):
    "ดูคำในไฟล์ list_of_words ว่ามีกี่คำที่มี n ตัวอักษร"
    inputfile = open(list_of_words)
    count = 0
    for word in inputfile:
        word = word.strip()
        if starts_with_small_letter(word):
            if len(word) == n:
                count = count + 1
    return(count)
In [10]:
double(100) #ควรคำนวณออกมาเป็น 200
Out[10]:
200
In [11]:
is_even(15) #ควรเป็น False เพราะ 15 ไม่ใช่เลขคู่
Out[11]:
False
In [12]:
starts_with_small_letter("kangaroo") #ควรเป็น True เพราะ "kangaroo" ขึ้นต้นด้วยอักษรตัวเล็ก
Out[12]:
True
In [13]:
number_of_words_with_length(11, "/usr/share/dict/words") #ควรเป็น 23773 เมื่อเทียบกับด้านบน
Out[13]:
23773

ลองนับจำนวนคำที่มีตัวอักษรตั้งแต่ 1, 2, 3, ..., 24 อีกที โดยใช้ฟังก์ชั่น number_of_words_with_length() ด้านบน

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

In [14]:
#โค้ดดูสั้นลง แต่อ่านไฟล์ 24 ครั้งเหมือนเดิม เพราะทุกครั้งที่ฟังก์ชั่น number_of_words_with_length ทำงานจะมีการอ่านไฟล์ 1 ครั้ง
print("#letters\t#words")
for letters in range(1,25):
    count = number_of_words_with_length(letters, "/usr/share/dict/words")
    print(f"{letters}\t\t{count}")
#letters	#words
1		26
2		121
3		1135
4		4347
5		8497
6		15066
7		20552
8		26434
9		28833
10		27924
11		23773
12		18837
13		13877
14		9151
15		5585
16		3223
17		1738
18		815
19		417
20		194
21		81
22		40
23		16
24		5

ตัวอย่างนับคำที่ความยาวต่างๆโดยอ่านไฟล์ครั้งเดียว

In [15]:
""" 
    อ่านไฟล์ครั้งเดียว เก็บจำนวนคำที่มีความยาว n อักษรไว้ใน count[n]
    count ต้องยาว 25 แทนที่จะเป็น 24 เพราะเราต้องการ count[24] ด้วยเพราะคำที่ยาวที่สุดยาว 24 ตัวอักษร
    ทำแบบนี้เร็วกว่าแบบข้างบนที่อ่านไฟล์ 24 ครั้ง
"""
print("#letters\t#words")
count = [0] * 25 #count เป็น list = [0, 0, 0, ..., 0] มี 0 25ตัว
inputfile = open('/usr/share/dict/words')
for word in inputfile:
    word = word.strip()
    if starts_with_small_letter(word):
        count[len(word)] += 1
for length in range(1,25):
    print(f"{length}\t\t{count[length]}")
    
#letters	#words
1		26
2		121
3		1135
4		4347
5		8497
6		15066
7		20552
8		26434
9		28833
10		27924
11		23773
12		18837
13		13877
14		9151
15		5585
16		3223
17		1738
18		815
19		417
20		194
21		81
22		40
23		16
24		5

เราสามารถเอาจำนวนคำใน count[] มาวาดกราฟได้ด้วย matplotlib ดังนี้ จะเห็นว่าคำยาว 9 ตัวอักษรมีจำนวนมากที่สุด:

In [16]:
%matplotlib inline
import matplotlib.pyplot as plt

lengths = range(1,25)  #ความยาวคำตั้งแต่ 1 ถึง 24 ตัวอักษร 
numwords = count[1:]   #count[]เก็บจำนวนคำที่มีความยาว n อักษรไว้ใน count[n]
 
plt.bar(lengths, numwords, align='center', alpha=0.5)
plt.xticks(lengths, lengths)
plt.title('Number of words vs. word length')
plt.xlabel('Word length in characters')
plt.ylabel('Number of words')

plt.show()
In [ ]: