🧠 Mantıksal Operatörler
11-1.24.15
Mantıksal işlemleri yapan operatörlerdir. Genelde programlama dillerinde
3 adet mantıksal işlem olur: and, or ve not işlemi. Python’da bu
operatörler and
, or
ve not
anahtar sözcüğü ile gösterilir. Diğer dillerde
ise şöyledir (tabloda C örnek olarak verilmiştir):
İşlem |
C |
Python |
---|---|---|
AND, ve |
|
|
OR, veya |
|
|
NOT, değil |
|
|
and
ve or
operatörleri binary infix operatörler iken not
operatörü
unary prefix operatördür.
Bu temel mantıksal opreatörlerin doğruluk tablosunu, truth table, ayrıca vermiyorum, zaten biliyoruz. Bu operatörlerin temelde bool türünden değerler üzerinde işlem yapmasını bekleriz. Python’da ise bu operatörlerin operandları herhangi bir türden olabilir.
>>> 3 and -4.5
-4.5
Java ve C# gibi dillerde ise mantıksal operatörlerin operand’ları bool türden olmak zorundadır.
//C kodu, clang 20.1.0 -O0
#include <stdio.h>
int main(void)
{
// Implicit conversion?
printf("%d\n", 3 && -4.5); //Sonuç 1
}
⚡ Kısa Devre Özelliği, Short Circuit
Diğer programlama dillerinde de olan bir özelliktir. and
ve or
operatörünün
her zaman sol tarafındaki ifade önce değerlendirilir. Bu operatörler bir ifadeyi
değerlendirdiği zaman onları True
ve False
olarak yorumlarlar. int
ve
float
türden operandlar için sıfır değeri False
, sıfır dışı değerler True
anlamına gelmektedir. Eğer and
operatörünün sol operandının değeri False
olarak değerlendirilirse sağ taraftaki ifade hiç yapılmaz, operatör sol
tarafındaki ifadenin değerini üretir. and
operatörünün sol tarafındaki değer
True
ise bu kez sağ tarafındaki ifade yapılır. Bu durumda operatör sağ
taraftaki ifadenin değerini üretir. or
operatörü de benzerdir. Yine önce sol
taraftaki ifade yapılır. Bu ifade True
ise sağ tarafındaki ifade hiç yapılmaz,
operatör sol tarafındaki ifadenin değerini üretir. Eğer sol tarafındaki ifade
False
ise bu sefer sağ tarafındaki ifade değerlendirilir ve operatör sağ
tarafındaki ifadenin değerini üretir.
Kısa devre özelliği yanıltıcı olabilmektedir. Örneğin foo() or bar()
gibi
bir ifade yazarsak burada önce foo()
çalıştırılır ve geri dönüş değeri True
olarak değerlendirilirse bar()
hiç çalıştırılmaz bile.
Yukarıda verdiğimiz 3 and -4.5
örneğinde sol taraf 3
, True
olarak
yorumlandığı için sağ taraf da değerlendirilir ve and
operatörünün oluşturduğu
değer bu durumda -4.5
olur ve ifade de float
türden olur.
x = 3 and -4.5
print(x, type(x)) # -4.5 <class 'float'>
x = 0 and -4.5
print(x, type(x)) # 0 <class 'int'>
x = 0 or 0
print(x, type(x)) # 0 <class 'int'>
x = False and -10
print(x, type(x)) # False <class 'bool'>
Dikkat
Burada dikkat ederseniz operandların True
ya da False
olarak
değerlendirilmesi başka bir şeydir ama and
ve or
operatörleri True
veya
False
vermez, ya sağdaki ya soldaki değeri olduğu gibi verir. C ve C++’ta
böyle değildir, Swift ve Ruby’de böyledir.
11-1.37.24
Mantıksal operatörlerin önceliği karşılaştırma operatörlerinden küçüktür.
Bknz: Operatör Öncelikleri
Bu durumda a > b and c > d
dediğimiz zaman gerçekten de a, b’den büyük mü
ve aynı zamanda c de d’den büyük mü diye bir işlem yapmış oluruz. Muhtemelen
algımız da bu şekilde olacaktır. Karşılaştırma operatörlerinin her zaman
bool
türden değer oluşturduğunuz anımsayalım.
not
Operatörü
11-1.45.20
and
ve or
operatörlerinin sağ veya sol operandlarının değerlerini verdiğini
söylemiştik. not
operatörü böyle değildir, her zaman True
ya da False
verir.
Örneğin:
x = not False
print(x, type(x)) # True <class 'bool'>
x = not True
print(x, type(x)) # False <class 'bool'>
x = not 4.5
print(x, type(x)) # False <class 'bool'>
x = not -3.5
print(x, type(x)) # False <class 'bool'>
x = not 0
print(x, type(x)) # True <class 'bool'>
x = not 0.0
print(x, type(x)) # True <class 'bool'>
and
ve or
Operatörlerinin Birlikte Kullanımı
11-1.55.20
Bu operatörleri beraber kullanırken dikkatli olmaktada fayda vardır. and
operatörü, or
operatöründen yüksek önceliklidir. Ama kısa devre özeliğinden
dolayı beklenti dışı sonuçlar oluşabilmektedir.
ifade1 and ifade2 or ifade3
dediğimiz zaman öncelikle ifade1 and ifade2
işlemi yapılır ve bunun sonucu
ifade3
ile or
işlemine sokulur. Yani (ifade1 and ifade2) or ifade3
ile
eşdeğerdir.
x = 10 and 0 or 5
print(x) # 5
Örneğin burada ilk olarak 10 and 0
yapılır. Bunun değeri 0
dır. Sonra
0 or 5
yapılır ve sonuç 5
olmaktadır.
Ya da ifade1 or ifade2 and ifade3
yazdığımız zaman
ifade1 or (ifade2 and ifade3)
demiş olmaktayız.
Kısa devre özelliği ile de birleştiği zaman şöyle bir durum oluşmaktadır.
Örneğin ifade1 and ifade2 or ifade3
ifadesinin (ifade1 and ifade2) or ifade3
ile eşdeğer olduğunu söylemiştik. Burada or
operatörünün solundaki
(ifade1 and ifade2)
ifadesi önce yapılacaktır. and
operatöründen dolayı da
önce ifade1
yapılır ama eğer False
ise ifade2
yapılmaz, ifade3
yapılır:
def bir():
print("bir")
return 0
def iki():
print("iki")
return 1
def uc():
print("uc")
return 1
bir() and iki() or uc() # ekrana bir ve uc geliyor
Eğer ifade1
, True
ise ifade2
yapılır. ifade2
de True
ise and
işleminin sonucu True
döneceği için or
işleminde olacak kısa devre
özelliğinden dolayı ifade3
yapılmaz. Eğer ifade2
, False
ise ifade3
yapılır.
ifade1 and ifade2 or ifade3
için aşağıdaki tablo geçerlidir:
|
|
|
Sıra |
---|---|---|---|
|
|
|
1, 3 |
|
|
|
1, 3 |
|
|
|
1, 3 |
|
|
|
1, 3 |
|
|
|
1, 2, 3 |
|
|
|
1, 2, 3 |
|
|
|
1, 2 |
|
|
|
1, 2 |
ifade1 or ifade2 and ifade3
şeklinde bir ifade de işlem sırası aslında
şöyledir operatör önceliğinden dolayı ifade1 or (ifade2 and ifade3)
anlamına
gelmektedir. İşte burada bir kafa karışıklığı olabilir. Çünkü ilk izlenim
ifade2 and ifade3
ün önce yapılması gerektiği olmaktadır. Oysa ki or
operatörünün kısa devre özelliğinden dolayı önce ifade1
değerlendirilir.
Aynı tabloyu oluşturalım.
ifade1 or ifade2 and ifade3
için aşağıdaki tablo geçerlidir:
|
|
|
Sıra |
---|---|---|---|
|
|
|
1, 2 |
|
|
|
1, 2 |
|
|
|
1, 2, 3 |
|
|
|
1, 2, 3 |
|
|
|
1 |
|
|
|
1 |
|
|
|
1 |
|
|
|
1 |
Görüldüğü üzere ifade1
, True
olduğu zaman doğrudan kısa devre özelliğini
görmekteyiz. ifade2
ve ifade3
ile de and
operatörünü kısa devre özelliğini
gözlemleyebiliyoruz.
Bu durum C dilinde de geçerlidir. Örneğin:
//C kodu, clang 20.1.0 -O0
#include <stdio.h>
int bir(void) {
puts("bir");
return 1;
}
int iki(void) {
puts("iki");
return 0;
}
int uc(void) {
puts("uc");
return 1;
}
int main(void)
{
printf("%d\n", bir() || iki() && uc()); // "bir"
}
Kodunu ele alalım. C dilinde de &&
operatörünün önceliği ||
operatöründen
yüksektir. Ama burada da önce bir()
fonksiyonu çağrılır ve kısa devre
özelliğinden dolayı diğer fonksiyonlar çağrılmaz.
İpucu
and
ve or
operatörlerinin her zaman sol tarafı önce yapılır. İfadelerin
değerlendirilme sırasını bulurken bu gerçeği göz önünde bulundurabiliriz.
Kendimize şu hatırlatmaları yapabiliriz:
1️⃣ Operatör önceliği, operator precedence, ile çalışma sırası yani
order of evaluation konuları paralel ama aynı konular değildir. Özellikle
kısa devre özelliği olan and
ve or
operatörlerinin kullanıldığı durumda
sonuç aynı olsa da order of evaluation beklentimizden farklı olabilir. Bu
Python implementasyonun yaptığı optimizasyon tekniklerden farklıdır, daha
deterministik bir davranıştır.
2️⃣ Kod yazarken order of evaluation konusunda varsayımlar yapmak ve kodumuzu
buna bağlı olacak şekilde yazmak, özellikle Python gibi yüksek seviyeli dillerde,
iyi bir tercih olmayabilir. Tek and
ve or
da durum net ama and
ve or
tek bir ifadede karışmaya başlayınca bizim de kafamız karışabilir.
Özetle Python’da and
ve or
operatörleri aynı ifadede kullanıldığında
aslında her zaman soldaki operatörün sol tarafı önce yapılmaktadır. Diğer
taraflar sonuca göre yapılır yapılmaz.
Örnek:
>>> 5 and 2.4 or 8 # 5 yapıldı True, 2.4 True. 2.4 or 8 de 2.4 sonucunu verdi
2.4
>>> 2 and 0 or 4 # 2 yapıldı True, 0 false. 0 or 4
4
>>> 2 or 4 or 6 # 2 yapıldı True, 2
2
>>> 0 or 10 and 0 # 0 yapıldı False, 10 yapıldı True sonra 10 and 0 yapıldı 0
0
11-2.09.20
💭 Yorumlar
Yorum altyapısı giscus tarafından (evet tarafından!) sağlanmaktadır. Yorum yazabilmek için GitHub hesabınız üzerinden giriş yapmanız gerekmektedir. Yorumlar, Github Discussions üzerinde saklanmaktadır.
ef3fa31a-be4b-406c-a59b-45fe9ede6cd1