W trakcie lektury A monad tutorial for Clojure programmers (part 3) o monadach trafiłem na przykład, w którym zamiast idiomatycznego ('ma' w tym słowie ma niebagatelne znaczenie! :)) pobrania wartości dla danego klucza w mapie (klucz mapa) zastosowano (prawie) równoważną funkcję (get mapa klucz).
user=> (doc get) ------------------------- clojure.core/get ([map key] [map key not-found]) Returns the value mapped to key, not-found or nil if key not present. nil user=> (def m {:a \a}) #'user/m user=> (class m) clojure.lang.PersistentArrayMap user=> (:a m) \a user=> (get m :a) \a user=> (= (get m :a) (:a m)) trueRóżnica jest niewielka, acz istotna - obsługa sytuacji braku istnienia klucza w mapie. Warto pamiętać, że różnica istnieje, ale w przywołanym przykładzie użycie funkcji (get) sprowadza się do idiomatycznego użycia klucza do poszukiwania jego wartości w mapie. Innymi słowy, klucz w Clojure jest jednocześnie funkcją, która wyszukuje wartość dla niego samego. W przypadku (get) jest możliwość podania trzeciego parametru, który zostanie zwrócony, jeśli nie zostanie odszukany klucz.
user=> m {:a \a} user=> (:b m) nil user=> (get m :b \b) \bJasne? Na pewno! I nie przyjmuję odpowiedzi, że nie :]
Tak przy okazji, właśnie znalazłem przykład do zobrazowania monady, która zareaguje na sytuację braku klucza! Poszukiwałem go przez dobrych kilka dni i właśnie znalazłem. Kolejny istotny powód dla publikowania swoich doświadczeń na blogu. Właśnie, gdyby ktoś rozważał bloga, ale brakowało mu inspiracji lub przewodnika, zgłaszam się na ochotnika, aby wskazać zainteresowanemu tematy. Najlepiej niech to będzie zapaleniec programowania funkcyjnego w dowolnej postaci - Haskell, Scala, Python, Lisp, Ruby, itp.
Wracając do tematu dzisiejszego wpisu, mam dla Ciebie zagadkę związaną z Clojure, aczkolwiek więcej tu logicznego myślenia niż samej znajomości języka. Warto jednak pamiętać, że wyróżnikiem programowania funkcyjnego jest przede wszystkim czystość funkcji - im większa tym lepiej. Zero skutków ubocznych i funkcja jedynie operuje na danych wejściowych zawsze zwracając pewną wartość, w szczególności nil. Pamiętaj, że do rozwiązania zagadki potrzebne jest zapamiętanie, że...patrz na tytuł wpisu. Pora na zagadkę, która mnie osobiście na zauważalny moment "zawiesiła": Dlaczego ostatnia linia zwróci "Nie ma elementu?!"? Nagrodą jest doświadczenie błogiego stanu nirwany zrozumienia podstawowej cechy programowania funkcyjnego.
user=> (def m {:a \a}) #'user/m user=> m {:a \a} user=> (get :a a) nil user=> (get a :a) \a user=> (:b m) nil user=> (get m :b) nil user=> (get m :b "Nie ma elementu?!") "Nie ma elementu?!" user=> (doc assoc) ------------------------- clojure.core/assoc ([map key val] [map key val & kvs]) assoc[iate]. When applied to a map, returns a new map of the same (hashed/sorted) type, that contains the mapping of key(s) to val(s). When applied to a vector, returns a new vector that contains val at index. Note - index must be <= (count vector). nil user=> (assoc m :b \b) {:b \b, :a \a} user=> (get m :b "Nie ma elementu?!") "Nie ma elementu?!"Dlaczego ostatnia linia zwróci "Nie ma elementu?!"? Odpowiedzi dozwolone w komentarzach. Kto pierwszy, ten lepszy.
p.s. Jeśli komuś przypadła zagadka do gustu, proszony jest o wyrażenie swojego zachwytu w komentarzu, abym wiedział, czy kontynuować tego typu wpisy.
Bo mapy są immutable. (assoc m :b \b) nie zmieniło mapy m, tylko zwróciło nową mapę, więc w m dalej nie ma elementu.
OdpowiedzUsuńBtw, obsługa domyślnej wartości zwracanej to wcale nie jest ficzer get. Klucze i mapy używane jako funkcje biorą opcjonalny drugi argument:
user> ({} :foo "Nie ma")
"Nie ma"
user> (:foo {} "Nie ma")
"Nie ma"
Zagadki są jak najbardziej interesujące. Liczę na więcej :)
OdpowiedzUsuńZagadka prościutka, rozwiązanie już w tytule, czekamy na więcej :-)
OdpowiedzUsuńTo chyba nie jest jednak cecha lisp, że można zlekceważyć zwracaną wartość :P
OdpowiedzUsuńNie znam Lispa, więc nie potrafię na to odpowiedzieć. Tutaj liczyłbym na znawców tematu.
OdpowiedzUsuńZwróciłeś mi Twoim komentarzem uwagę na ciekawe wyjaśnienie lekceważenia zwracanej wartości - jest wskazaniem, że zależy nam na skutku ubocznym, albo coś bardzo zbliżonego do niego (np. w monadach).