Bardzo przydatną cechą operatorów logicznych w wielu językach programowania jest leniwa ewaluacja (lazy evaluation). Polega ona na pominięciu obliczania tych argumentów operatorów &&
(and
) i ||
(or
), które i tak nie mają szans wpłynąć na ostateczny wynik. To pozwala na tworzenie warunków podobnych do poniższego:
Drugi człon nie wykona się tutaj w ogóle, jeśli pierwszy okaże się fałszywy, więc zmienna obj
ustawiona na null
nie spowoduje błędu wykonania.
Oczywiście technika ta jest doskonale znana każdemu przynajmniej średnio zaawansowanemu programiście. Okazuje się jednak, że przynajmniej jeden język idzie dalej i uogólnia ją w sposób pozwalający na stosowanie operatorów and
i or
do argumentów niebędących wartościami logicznymi. Jaki to język?… Python, rzecz jasna :)
W Pythonie dwa standardowe operatory ‘logiczne’ działają w oparciu o możliwość określenia specyficznie pojmowanej prawdziwości wyrażenia, którego typem niekoniecznie jest bool
. Mówiąc w skrócie, każde wyrażenie niepuste i różne od zera – łącznie z odwołaniami do obiektów, liczbami, tablicami, słownikami i innymi kolekcjami – jest uważane za prawdziwe, gdy wystąpi w kontekście wymagającym rozróżnienia prawdy i fałszu.
Takim kontekstem jest chociażby warunek instrukcji if
lub while
– ale nie tylko. Możliwość “rzutowania na bool
” (fachowo nazywanego koercją) jest też wykorzystywana w definicji operatorów and
i or
, które są z grubsza następujące:
A and B
jest równe:
A
, jeśli A
jest wyrażeniem fałszywymB
– w przeciwnym wypadkuA or B
jest równe:
A
, jeśli A
jest wyrażeniem prawdziwymB
– w przeciwnym wypadkuW pierwszej chwili mogą one wydawać się dość skomplikowane, ale nietrudno jest zauważyć, że “działają” one zgodnie z oczekiwaniami wobec argumentów typu bool
i mogą być obliczane leniwie. Ponieważ jednak dzięki nim rezultatem operatora nie jest po prostu True
lub False
, lecz jeden z argumentów, możliwe jest stosowanie and
i or
także wtedy, gdy wynikiem nie ma być wcale wartość logiczna.
Wbrew pozorom ma to czasem wielki sens. Oto bardzo typowy przykład kodu, który korzysta z tej sztuczki:
Co tu się dzieje?… Jeśli wywołanie funkcji zwróci prawdziwą (czyli niepustą i niezerową, więc zapewne sensowną) wartość, jest ona wyświetlona. W przeciwnym razie korzysta się z napisu zastępczego. Dzięki elastycznemu operatorowi or
łatwo więc można określić pewnego rodzaju wartość domyślną (czyli fallback) dla wyrażenia.
Z kolei operator and
jest często wykorzystywany do warunkowego odwoływania się do “głęboko ukrytej” wartości, wymagającej przejścia przez ciąg kilku potencjalnie pustych odwołań:
Jeśli któreś z nich jest równe None
, to taki będzie rezultat całej konstrukcji. W przeciwnym razie wynikiem będzie ostatni argument.
Istnieje szansa, że przynajmniej jeden z powyższych mechanizmów wygląda znajomo, jeśli ma się doświadczenie w językach Java lub C#. Sztuczka z operatorem or
odpowiada bowiem podwójnemu znakowi zapytania (??
) z C#, zaś przykład z and
wprowadzonemu w Javie 7 operatorowi ?.
(znak zapytania i kropka).
Zapewne też w tym momencie wszyscy przypomną sobie o starym dobrym operatorze trójargumentowym, występującym we wspomnianych dwóch językach i jeszcze wielu innych. Okazuje się, że w Pythonie jego działanie też można symulować przy użyciu operatorów and
i or
:
Nie trzeba jednak tego robić, bowiem od wersji 2.5 istnieje nieco inny składniowo odpowiednik takiej konstrukcji:
Warto zwrócić uwagę na inną niż typowa kolejność wyrażeń w tej konstrukcji.
Tak samo jest przynajmniej językach: Perl. Bash, Lua, Lisp. Przy czym w tym ostatnim jest dużo lepiej niż w pozostałych, bo składnia prefiksowa pozwala przekazać więcej argumentów do and czy or.
Sam staram się takich konstrukcji unikać w kontekstach, w których to nie są idiomem.
Np. jest to od dawna stosowna “sztuczka” w perlu. Często można spotkać kod w stylu:
my $number = shift || 5;
który pobierze argument (np. przekazany w linii poleceń) i przypisze go do $number. Jeżeli nie przekazano argumentu to przypisze domyślną wartość 5. Można też tak:
my $number = shift or die "I need more arguments!\n";
|| i or różnią się w Perlu jedynie priorytetem.