Więcej niż operatory logiczne

2011-02-11 20:44

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:

  1. if (obj != null && obj.Valid) { obj.DoSomething(); }

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łszywym
    • B – w przeciwnym wypadku
  • A or B jest równe:
    • A, jeśli A jest wyrażeniem prawdziwym
    • B – w przeciwnym wypadku

W 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:

  1. s = get_a_string_from_somewhere() or "N/A"
  2. print s

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ń:

  1. return obj and obj.wrapper and obj.wrapper.result

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:

  1. res = (condition and if_true) or if_false

Nie trzeba jednak tego robić, bowiem od wersji 2.5 istnieje nieco inny składniowo odpowiednik takiej konstrukcji:

  1. res = if_true if condition else if_false

Warto zwrócić uwagę na inną niż typowa kolejność wyrażeń w tej konstrukcji.

Be Sociable, Share!
Be Sociable, Share!
Tags: ,
Author: Xion, posted under Programming »


One comment for post “Więcej niż operatory logiczne”.
  1. lmmilewski:
    February 12th, 2011 o 15:17

    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.

Comments are disabled.
 


© 2023 Karol Kuczmarski "Xion". Layout by Urszulka. Powered by WordPress with QuickLaTeX.com.