Python Vadovėlis/Loginiai reiškiniai

Iš wiki.angis.net.
Jump to navigation Jump to search
 ← Ciklai For Turinys Žodynai → 

Šiame skyriuje padėsiu tau išsiaiškinti, kas yra loginiai reiškiniai. Sugalvojau nedidelį programos pavyzdį, kurio tau įvedinėti nebūtina:

a = 6
b = 7
c = 42
print(1, a == 6)
print(2, a == 7)
print(3, a == 6 and b == 7)
print(4, a == 7 and b == 7)
print(5, not a == 7 and b == 7)
print(6, a == 7 or b == 7)
print(7, a == 7 or b == 6)
print(8, not (a == 7 and b == 6))
print(9, not a == 7 and b == 6)

Šios programos išvestis yra:

1 True
2 False
3 True
4 False
5 True
6 True
7 False
8 True
9 False

Galvoje, turbūt, jau sukasi mintis: kas čia ką tik įvyko? Paaiškinsiu, kaip veikia kiekviena eilutė, kad mintys susidėliotų į savas vietas. Mano programoje gausu print sakinių, iš kurių kiekvienas išspausdina skaičių ir loginę reikšmę. Skaičius man padeda suprasti, kurio print sakinio rezultatas tai yra. Atkreipk dėmesį, kad kiekvienoje išvesties eilutėje yra žodis False (Klaidinga) arba True (Teisinga), o Python'e kiekviena loginė reikšmė False gali būti užrašoma skaičiumi 0, o True 1.


Eilučių:

print(1, a == 6)
print(2, a == 7)

rezultatas atitinkamai yra True ir False, kadangi pirmasis reiškinys yra teisingas, o antrasis yra klaidingas. Trečioji eilutė: print(3, a == 6 and b == 7) - šiek tiek skiriasi nuo prieš tai buvusių - joje yra naudojamas operatorius and. Loginis operatorius and grąžina rezultatą True tik tada, kai reiškiniai iš abiejų and pusių yra teisingi, kitu atveju visas reiškinys bus klaidingas ir grąžins False. Ketvirtoje eilutėje: print(4, a == 7 and b == 7) tik vienas iš reiškinių yra teisingas, dėl to eilutės rezultatas yra False. Operatoriaus and galimi rezultatai gali būti apibrėžti tokia lentele:

Reiškinys Rezultatas
True and True True
True and False False
False and True False
False and False False

Atkreipk dėmesį, jei pirmas reiškinys yra klaidingas, tai Python’as nebetikrins antrojo, kadangi jis žino, jog visas reiškinys yra klaidingas. Parašyk šias eilutes ir paleisk programą: False and print("Labas") ir palygink rezultatą su šios eilutės True and print("Labas") rezultatu. Techniškai tai yra vadinama sutrumpintos grandies įvertinimas (short-circuit evaluation). Penktoje eilutėje, print(5, not a == 7 and b == 7), yra naudojamas not operatorius. not suteikia priešingą reikšmę reiškiniui (reiškinys taip pat galėtų būti užrašomas taip: print(5, a != 7 and b == 7)). Štai lentelė apibrėžianti operatoriaus not rezultatus:

Reiškinys Rezultatas
not True False
not False True

Dvi toliau pateiktos eilutės: print(6, a == 7 or b == 7) ir print(7, a == 7 or b == 6) naudoja operatorių or. Pastarasis operatorius grąžina True jei pirmasis reiškinys yra teisingas arba, jei antrasis reiškinys yra teisingas, arba, jei abu reiškiniai yra teisingi. Jei nei vienas iš reiškinių nėra teisingas, tuomet yra grąžinamas False. Štai lentelė apibrėžianti operatoriaus or rezultatus:

Reiškinys Rezultatas
True or True True
True or False True
False or True True
False or False False

Turėk omeny, jei pirmasis reiškinys yra teisingas Python’as nebetikrins antrojo, kadangi jis žino, jog visas reiškinys bus teisingas. Kaip jau minėjau anksčiau, naudojant operatorių or, jei bent vienas iš reiškinių yra teisingas, tuomet viso reiškinio galutinis rezultatas bus teisingas. Pirmoji dalis yra teisinga, todėl antroji dalis gali būti klaidinga arba teisinga, tačiau visa išraiška vis dar teisinga.

Aštuntoje: print(8, not (a == 7 and b == 6)) ir devintoje: print(9, not a == 7 and b == 6) eilutėse yra atvaizduojamas reiškinių grupavimo pavyzdys. Reiškinius Python’e galima sugrupuoti naudojant skliaustelius. Sugrupavę reiškinius, mes priverčiame Python’ą iš pradžių įvertinti reiškinius skliaustuose ir tik tada likusias reiškinio dalis. Atkreipk dėmesį, kad skliausteliai pakeitė reiškinį iš neteisingo į teisingą. Taip nutiko dėl to, kad operatorius not buvo pritaikytas reiškiniui skliausteliuose, vietoj to, kad būtų pritaikytas tik a == 7 daliai. Parodysiu pavyzdį, kaip rašant kodą galima pritaikyti loginius operatorius:

sąrašas = ["Gyvenimas", "Visata", "Viskas", "Benas", "Liepa", "Gyvenimas", "Liepa"]

# kuriama sąrašo kopija. Peržiūrėk skyrių Daugiau apie sąrašus, kuriame sužinosi ką reiškia [:].
kopija = sąrašas[:]
# surūšiuoti sąrašą
kopija.sort()
ankstesnis = kopija[0]
del kopija[0]

kiekis = 0

# sąraše surask vienodus elementus
while kiekis < len(kopija) and kopija[kiekis] != ankstesnis:
    ankstesnis = kopija[kiekis]
    kiekis = kiekis + 1

# Jei nėra atitikmens tuomet kiekis negali būti < len
# kadangi ciklas kol bus vykdomas tol, kol kiekis < len
# ir nėra rastas atitikmuo

if kiekis < len(kopija):
    print("Pirmasis atitikmuo:", ankstesnis)

Šios programos rezultatas: Pirmasis atitikmuo: Gyvenimas

Programa ieško vienodų elementų, tikrindama šią sąlygą: while kiekis < len(kopija) and kopija[kiekis] nėra lygus ankstesnis . Kai kiekis yra didesnis už paskutinio kopija sąrašo elemento indeksą arba, kai atsiranda sutampantis elementas, operatorius and nebėra tiesa, todėl ciklas pasibaigia. if sakinys patikrina ar ciklas while pasibaigė dėl to, kad buvo rastas sutampantis elementas.

Šioje programoje yra panaudotas dar vienas and operatoriaus „triukas“. Jei dar kartą pažiūrėsi į operatoriaus and lentelę pamatysi, jog trečioje eilutėje yra netiesa ir netiesa. Jei kiekis >= len(kopija) (kitais žodžiais, jei kiekis < len(kopija) yra False), tada kodo dalis kopija[kiekis] nebus tikrinama. Taip yra todėl, kad Python’as žino, jei pirmasis teiginys yra klaidingas, tai abu jie nebegali būti teisingi. Šis „short circuit“ triukas yra naudingas tuomet, kai antrasis and operatoriaus reiškinys gali sukelti klaidą. Aš panaudojau pirmąjį reiškinį (kiekis < len(kopija)), kad patikrinčiau ar kintamasis kiekis yra validus sąrašo kopija indeksas. Jei manimi netiki, tuomet ištrink iš sąrašo elementus „Liepa“ ir „Gyvenimas“, sukeisk vietomis sąlygas kiekis < len(kopija) and kopija[kiekis] != ankstesnis (rašyk: kopija[kiekis] != ankstesnis and kiekis < len(kopija)) ir patikrink ar programa vis dar veikia, kaip turėtų.

Loginiai operatoriai gali būti naudojami, kai mes norime patikrinti dvi arba daugiau skirtingų sąlygų vienu metu.

Pastaba apie loginius operatorius

Dažna pradedančiųjų programuotojų klaida yra neteisingas supratimas, kaip veikia loginiai operatoriai ir kaip juos „skaito“ Python'o interpretatorius. Pavyzdžiui: vos tik sužinojus apie operatorius and ir or, galima pagalvoti, kad išraiška x == ('a' or 'b') patikrins, ar kintamasis x yra lygus a arba kintamasis x lygus b, tačiau taip nėra. Norint suprasti, apie ką kalbu, pradėk interaktyvią sesiją su interpretatoriumi ir įvesk šias išraiškas:

>>> 'a' == ('a' or 'b')
>>> 'b' == ('a' or 'b')
>>> 'a' == ('a' and 'b')
>>> 'b' == ('a' and 'b')

Štai yra rezultatai, kurie nėra intuityvūs:

>>> 'a' == ('a' or 'b')
True
>>> 'b' == ('a' or 'b')
False
>>> 'a' == ('a' and 'b')
False 
>>> 'b' == ('a' and 'b')
True

Šiuo momentu gali pasirodyti, jog operatoriai and ir or veikia klaidingai. Neatrodo logiška, jog pirmuose reiškiniuose 'a' yra lygu 'a' arba 'b', tačiau 'b' nėra lygu 'a' arba 'b'. Taip pat yra ir su paskutiniu reiškiniu - jis neatrodo teisingas, nes 'b' yra lygus 'a' ir 'b'. Išsiaiškinus, kaip veikia Python’o interpretatorius pasidaro aišku, kodėl gavome būtent tokius rezultatus.

Kai Python’o interpretatorius „pamato“ reiškinį, kuriame yra naudojamas or, jis paima pirmąjį teiginį ir patikrina ar jo rezultatas yra True. Jei pirmasis teiginys yra True, tuomet Python’as grąžina to teiginio rezultatą, netikrindamas antrojo. Kaip jau minėjau anksčiau, naudojant operatorių or užtenka patikrinti ar pirmasis teiginys yra teisingas, nes tada mes jau žinome, kad visas reiškinys yra teisingas ir programai nebereikia papildomai tikrinti antrojo. Iš kitos pusės, jei pirmasis teiginys grąžina False, tada Python’as turi patikrinti ir antrojo teiginio rezultatą bei grąžinti jo reikšmę. Antrasis teiginys nusako visos išraiškos rezultatą, kadangi pirmoji pusė buvo klaidinga. Šis interpretatoriaus „tingus“ įvertinimas vadinamas "short circuiting" ir yra įprastas būdas įvertinti logines išraiškas daugelyje programavimo kalbų.

Panašiai veikia ir operatorius and: Python’as naudoja, "short circuit" techniką, kad pagreitintų išraiškos įvertinimą. Jei pirmasis teiginys yra klaidingas, tada visas reiškinys yra klaidingas. Kitu atveju, jei pirmasis teiginys yra teisingas, tada Python’as patikrina antrąjį ir grąžina galutinę reikšmę.

Reiktų atkreiptį dėmesį į vieną niuansą: ne tik loginių išraiškų rezultatai gali būti apibrėžiami True arba False reikšmėmis. Norėdami patikrinti bet kurio objekto x loginę reikšmę, galite naudoti funkciją bool(x). Žemiau yra pateikiama lentelė, kurioje yra pavyzdžiai, kokiems objektams Python’as priskiria True arba False:

True False
True False
1 0
Skaičiai kitokie nei nulis Tekstinė eilutė 'None'
Ne tuščios tekstinės eilutės Tuščios tekstinės eilutės
Sąrašai (lists) turintys elementų Sąrašai (lists) neturintys elementų
Žodynai (dictionaries) turintys elementų Žodynai (dictionaries) neturintys elementų

Dabar galime suprasti gluminančius rezultatus, kuriuos gavome, kai anksčiau išbandėme logines išraiškas. Pažiūrėkime, ką Python'o interpretatorius „mato“, eidamas per šį kodą:

Pirmasis atvejis:

>>> 'a' == ('a' or 'b')  # Iš pradžių žiūrėk į reiškinį skliaustuose: "('a' or 'b')"
                           # 'a' yra netuščia teksto eilutė, todėl reiškinio rezultatas yra True
                           # Grąžink tą pirmą reikšmę: 'a'
>>> 'a' == 'a'           # teksto eilutė 'a' yra lygi teksto eilutei 'a', todėl reiškinio rezultatas yra True
True

Antrasis atvejis:

>>> 'b' == ('a' or 'b')  # Iš pradžių žiūrėk į reiškinį skliaustuose: "('a' or 'b')"
                           # 'a' yra netuščia teksto eilutė, todėl reiškinio rezultatas yra True
                           # Grąžink tą pirmą reikšmę: 'a'
>>> 'b' == 'a'           # teksto eilutė 'b' nėra lygi teksto eilutei 'a', todėl reiškinio rezultatas yra False
False 

Trečiasis atvejis:

>>> 'a' == ('a' and 'b') # Iš pradžių žiūrėk į reiškinį skliaustuose: "('a' and 'b')"
                           # 'a' yra netuščia teksto eilutė, todėl reiškinio rezultatas yra True, patikrinkime antrąją reikšmę
                           # 'b' yra netuščia teksto eilutė, todėl antrojo reiškinio rezultatas yra True
                           # Grąžink antrąją reikšmę kaip viso reiškinio rezultatą: 'b'
>>> 'a' == 'b'           # teksto eilutė 'a' nėra lygi teksto eilutei 'b', todėl reiškinio rezultatas yra False
False

Ketvirtasis atvejis:

>>> 'b' == ('a' and 'b') # Iš pradžių žiūrėk į reiškinį skliaustuose:
                           # 'a' yra netuščia teksto eilutė, todėl reiškinio rezultatas yra True, patikrinkime antrąją reikšmę
                           # 'b' yra netuščia teksto eilutė, todėl antrojo reiškinio rezultatas yra True
                           # Grąžink antrąją reikšmę kaip viso reiškinio rezultatą: 'b'
>>> 'b' == 'b'           # teksto eilutė 'b' yra lygi teksto eilutei 'b', todėl reiškinio rezultatas yra True
True 

Taigi, Python’as tikrai teisingai įvertino duotuosius reiškinius, kurių rezultatai iš pradžių neatrodė akivaizdūs. Kaip minėjau anksčiau, svarbu atpažinti, kokį rezultatą tavo loginė išraiška grąžins ir kodėl.

Grįžtant prie pradinių reiškinių, štai kaip juos turėtum aprašyti, kad jie grąžintų reikšmes, kurių tikiesi:

>>> 'a' == 'a' or 'a' == 'b' 
True
>>> 'b' == 'a' or 'b' == 'b' 
True
>>> 'a' == 'a' and 'a' == 'b' 
False
>>> 'b' == 'a' and 'b' == 'b' 
False

Įvertinus šiuos reiškinius yra gaunamos logines reikšmes True arba False, o ne teksto eilutės, kad galėtume gauti teisingus palyginimo rezultatus.

Pavyzdžiai

slaptazodis1.py

## Ši programa prašo vartotojo prisijungimo vardo ir slaptažodžio.
# Kai vartotojas juos įveda, programa įvertina ar jis gali prisijungti.

vardas = input("Koks tavo vardas? ")
slaptažodis = input("Koks tavo slaptažodis? ")
if vardas == "Benas" and slaptažodis == "Penktadienis":
    print("Sveikas, Benai!")
elif vardas == "Jonas" and slaptažodis == "Kietas":
    print("Sveikas, Jonai!")
else:
    print("Aš tavęs nepažįstu.")

Pavyzdiniai rezultatai:

Koks tavo vardas? Benas

Koks tavo slaptažodis? Penktadienis

Sveikas, Benai!


Koks tavo vardas? Andrius

Koks tavo slaptažodis? Pinigai

Aš tavęs nepažįstu.

Pratimai

Parašyk programą, kurioje vartotojas turi atspėti tavo vardą, tačiau jie turės tik 3 spėjimus tai padaryti iki kol programa bus uždaryta.

Sprendimas  
print("Pabandyk atspėti mano vardą!")
kiekis = 1
vardas = "gilbertas"
spėjimas = input("Koks mano vardas? ")
while kiekis < 3 and spėjimas.lower() != vardas:    # .lower visas didžiąsias raides teksto eilutėje pakeičia į mažąsias, todėl net jei vartotojas įves Gilbertas, tai vis tiek bus teisingas atsakymas
    print("Tu neteisus!")
    spėjimas = input("Koks mano vardas? ")
    kiekis = kiekis + 1

if spėjimas.lower() != vardas:
    print("Tu neteisus!") # ši žinutė nėra atspausdinama trečiąjį bandymą, todėl mes tai padarome dabar
    print("Tu nebegali spėti.")
else:
    print("Taip! Mano vardas yra", vardas + "!")
 ← Ciklai For Turinys Žodynai →