Sztuczki z adresem e-mailChcę dzisiaj wspomnieć o kilku ciekawostkach dotyczących tego, co każdy posiada - czyli adresu e-mail. Wydawałoby się, że nie ma tutaj niczego do odkrycia, jako że wszyscy już dawno zdążyliśmy się już przyzwyczaić do poczty elektronicznej i formatu adresów, które ona używa. Jak to jednak okazuje się zwykle w takich sytuacjach, w rzeczywistości przynajmniej kilka trików nie musi być wcale powszechnie znanych :)
I tak pierwszym z nich jest fakt, że część adresu specyficzna dla użytkownika (ta przed znakiem @) może być łańcuchem znaków zawartych w cudzysłowach. Między nimi ulegają wtedy pewnemu poluzowaniu reguł odnośnie dopuszczalnych znaków i dozwolone stają się chociażby spacje. Reasumując, zupełnie poprawny jest adres w rodzaju:
Jest tu jednak pewien haczyk. Otóż serwery pocztowe niekoniecznie muszą takie adresy akceptować. Samo RFC (nr 5321) przestrzega przed potencjalnymi problemami, zalecając nieużywanie cudzysłowów, jeśli... chcemy otrzymywać jakąś pocztę :)
Do czego więc mogą się one przydać? Chociażby do adresów, które wykorzystuje się wyłącznie jako nadawcze, wysyłając np. zautomatyzowane wiadomości do użytkowników jakiejś sieciowej usługi, na które ci nie powinni odpowiadać.
Drugą rzeczą wartą odnotowania jest to, że część adresu następująca po znaku @ niekoniecznie musi być nazwą domeny. Dopuszczalne są mianowicie adresy IP w zwyczajowej postaci kropkowej. Aby je odróżnić przyjęto konwencję, że powinny być zawarte w nawiasach kwadratowych:
Pozwala to chociażby - jak sugeruje powyższy przykład - na testowanie serwerów pocztowych bez konieczności posiadania domeny.
Wreszcie, trzeci trik różni się od dwóch pozostałych tym, że może być rzeczywiście przydatny w prawdziwym świecie ;D Chodzi mianowicie o to, że część adresu przed znakiem @ (specyficzna dla użytkownika) może być interpretowana przez jego serwer pocztowy w dowolny sposób. To sprawia, że ów serwer może udostępniać dodatkowe feature'y z nią związane.
Typowym i częstym przykładem jest możliwość tworzenia tagów adresowych, wspomagających sortowanie wiadomości. Zwykle dzieje się to za pomocą plusa (+) lub myślnika (-), co w sumie skutkuje adresem w rodzaju:
Jeśli server.com obsługuje tagi w takim właśnie formacie, to wiadomości trafiające na powyższy adres zostaną umieszczone w skrzynce użytkownika user, ale pod etykietą stuff. Co to dokładnie znaczy, zależy od serwera; najpewniej będzie to przydzielenie wiadomości do osobnego folderu lub dodatkowe kryterium pomocne przy wyszukiwaniu.
Z popularnych serwisów hostujących pocztę tagi oferuje Google Mail, właśnie w powyższym formacie z plusem.
Przekierowania do innych URL-i
Potrzeba automatycznego przekierowania strony WWW do innego adresu URL pojawia się całkiem często. Dość typowe są na przykład serwisy typu download, gdzie faktyczne ściągnięcie pliku poprzedzone jest krótkim oczekiwaniem i odpowiednim komunikatem. Służy to głównie temu, by użytkownik omyłkowo nie rozpoczął procesu ściągania więcej niż raz.
Przyjrzyjmy się dzisiaj sposobom na dokonywanie takich przekierowań. Jeśli bowiem chwilę się nad tym zastanowić, to okazuje się, że można je przeprowadzać na różnych poziomach protokołów HTTP i nie tylko. Wśród możliwych metod można wyróżnić choćby takie:
window.location. Ustawienie tej właściwości na adres URL powoduje automatyczne wykonanie przekierowania. Do działania wymaga to naturalnie obsługi Javascriptu po stronie klienta, więc może służyć do odfiltrowania tych klientów, którzy nie spełniają tego warunku. Dotyczy to na przykład przeglądarek tekstowych, takich jak Lynx.<meta>. HTML przewiduje możliwość odświeżenia strony po określonym czasie, z opcjonalnym przekierowaniem do innego URL-a. Korzysta się w tym celu ze znacznika <meta>:
Właśnie ta metoda jest zwykle wykorzystywana do implementacji wspomnianego na początku opóźnionego ściągania plików.
Location). Istnieje cała grupa takich kodów o numerach zaczynających się od 300, jak na przykład 301 (Moved Permanently), będący trwałym przekierowaniem, czy 302/307 (Found/Temporary Redirect), wskazujące na mniej lub bardziej tymczasowe odesłanie do innego adresu.Jeśli przypadkiem zastanawiamy się, który rodzaj przekierowania powinniśmy zastosować, to spieszę z odpowiedzią, że prawie na pewno dobrym wyborem będzie któryś z dwóch środkowych elementów listy. Uzasadnieniem jest tu głównie chęć współpracy z robotami wyszukiwarek internetowych. Nie powinniśmy mianowicie spodziewać się, by uruchamiały one kod Javascript na przeglądanych przez siebie stronach, bo kosztowałoby to je zbyt dużo zasobów. Z drugiej strony istnieje też duża szansa, że nie będą one "świadome" "przekierowań" dokonywanych na poziomie systemu DNS, przez co mogą one potraktować dostępność tych samych treści z dwóch różnych adresów jako świadome duplikowanie contentu i próbę ich oszukania.
W grę wchodzą naturalnie jeszcze kwestie praktyczne. Nie każdy dysponuje własną domeną lub możliwością zmiany jej rekordów DNS. Nie każdy może też zmieniać ustawienia serwera HTTP, na którym funkcjonuje jego serwis. Wtedy przydatne są przekierowania dokonywane po stronie klienta np. za pomocą tagów HTML.
lonu pilno be loi sucta bei tu’a la lojban.Używanie abstrakcji w lojbanie.
W jednej z wcześniejszych notek na temat lojbanu wspominałem o tym, że często posługujemy się w nim konstrukcjami, które określa się jako abstrakcje. Pojawiały się one kilka razy nawet tutaj. Osobiście uważam je za jedną z ciekawszych cech języka, dlatego też chciałbym dzisiaj opisać ją nieco dokładniej.
Przypomnijmy, że "zdanie" w lojbanie - czyli bridi - to pewna relacja określona na swoich argumentach, zwanych sumti:
lo bolci cu gunro fa'a le vorme
Piłka toczy się w kierunku bramki.
Ta relacja może opisywać jakieś zdarzenie, fakt, właściwość, itp. Dopóki jej argumentami są proste pojęcia (takie jak piłka czy bramka), do jej wyrażenia wystarczają równie nieskomplikowane konstrukcje gramatyczne. Jednak nie zawsze tak jest. Nierzadko chcemy mówić nie tyle o konkretnych obiektach, co właśnie o zdarzeniach, faktach lub właściwościach, które w lojbanie należą do grupy pojęć zwanych abstrakcjami.
Aby to zrobić, należy jedno bridi uczynić argumentem innego. Jest to możliwe przy pomocy tzw. abstraktorów, z których najczęściej używanym jest nu. Zaaplikowanie go do bridi daje w wyniku zdarzenie, które owo bridi opisuje. Zapewne najłatwiej zobaczyć to na przykładach takich, jak poniższe:
mi klama lo zarci -- Idę to sklepu.
ti nu mi klama lo zarci -- To jest zdarzenie mojego pójścia do sklepu.
lo nu mi klama lo zarci cu pluka mi -- Pójście do sklepu sprawiło mi przyjemność.
Ostatnie wyrażenie prawdopodobnie przetłumaczylibyśmy raczej jako: "Cieszę się, że poszedłem do sklepu", czyli używając pewnego rodzaju zdania złożonego. W lojbanie to samo wyrażamy nieco inaczej, odnosząc się do 'pójścia do sklepu' (lonu mi klama lo zarci) w ten sam sposób, w jaki wcześniej odwoływaliśmy się do bardziej konkretnych obiektów. Ten trochę inny schemat myślenia jest według mnie całkiem interesujący.
Oczywiście przy bardziej skomplikowanych wypowiedziach konieczne może się okazać wielokrotne zagnieżdżanie - tak jak w poniższym przykładzie potencjalnej dyskusji po meczu pewnej znanej i lubianej gry strategicznej:
lonu mi fliba cu jalge lonu lerci fa lonu mi zbasu le .ekspo
Moja przegrana była wynikiem tego, że spóźniłem się z budową expa.
Jak widać to, co lojban wyraża jednolicie za pomocą abstraktora nu, w języku polskim wymaga fraz typu 'to, że' albo rzeczowników pochodzących od czasowników - jak 'przegrana' czy 'budowa'.
Jak można się domyślić, nu nie jest jedynym abstraktorem, a zdarzenia nie są jedynym rodzajem abstrakcji w lojbanie. Innym często spotykanym słówkiem tego typu jest du'u, którego używamy, dyskutując o faktach, opiniach czy wiedzy, gdyż jest to abstraktor stwierdzenia:
mi djuno lodu'u la berlin. raltca lo dotco -- Wiem, że Berlin jest stolicą Niemiec.
la robert jdice lodu'u litru lo fraso -- Robert zdecydował się na podróż do Francji.
W końcu jest też abstraktor ka, wskazujący na właściwości lub cechy. Jego użycie jest już trochę bardziej wyrafinowane, ale typowym przykładem są porównania analogiczne do stopniowania przymiotników. Wówczas właściwość jest "skalą" lub kryterium porównawczym:
do zmadu mi loka nelci lo grute -- Lubisz owoce bardziej niż ja.
mi sisku loka xunre lo'i karce -- Szukam czerwonego samochodu (wśród innych).
Jak widać skalą może być "czerwoność" (loka xunre), "lubienie owoców" (loka nelci lo grute) i w zasadzie cokolwiek, co tylko możemy sobie wymyślić i jest poprawnym bridi. Możliwości są tu więc bardzo, bardzo duże.
Istnieją też oczywiście inne, rzadziej używane abstraktory, jak ni czy si'o. Zainteresowanych odsyłam do odpowiednich źródeł :)
Maksimum, minimumProgramiści grafiki czasu rzeczywistego wiedzą, że gdy w obliczeniach potrzebne są wartości sinusa i cosinusa dla tego samego kąta, należy obliczać je razem. (O ile oczywiście ich nie tablicujemy). To, o czym chciałbym napisać dzisiaj, jest kwestią bardzo podobną, ale możliwą do stosowania w sytuacji spotykanej przez właściwie każdego programistę.
Chodzi o znajdowanie największego i najmniejszego elementu w dowolnej kolekcji. Uzyskanie każdej z tych wartości osobno jest proste niczym Hello world, nawet jeśli nasz język nie ma od tego żadnej wbudowanej funkcji (są jeszcze takie?). Okazuje się jednak, że oddzielne szukanie minimum i maksimum jest nieefektywne. Jeśli bowiem potrzebujemy obu wartości, to należy szukać ich jednocześnie.
Jak? Całkiem prosto. Przechodząc po zawartości pojemnika, trzeba zajmować się naraz nie jednym, lecz dwoma elementami. Zaczynamy jednak od porównania ich ze sobą, a dopiero potem z aktualnym maksimum (większy z dwóch) i aktualnym minimum (mniejszy). Implementacja tego pomysłu dla pojemników STL może wyglądać na przykład tak:
// inicjalizacja wartości początkowych min i max
T m, M;
typename C::size_type i;
if (s & 1u) { m = M = c[0]; i = 1; }
else
{
if (c[0] <= c[1]) { m = c[0]; M = c[1]; }
else { m = c[1]; M = c[0]; }
i = 2;
}
T x, y; /* x>= y */
for ( ; i <s; i += 2)
{
if (c[i] <= c[i + 1]) { y = c[i]; x = c[i + 1]; }
else { x = c[i]; y = c[i + 1]; }
if (x> M) M = x; if (y <m) m = y;
}
*min = m; *max = M;
}
Ponieważ liczba elementów może być nieparzysta lub parzysta, konieczna jest odpowiednia inicjalizacja. W pierwszym przypadku bierzemy pierwszy element jako początkowe maksimum i minimum, zaś w drugim dokonujemy porównania dwóch pierwszych elementów. Resztę kolekcji możemy już przetwarzać dwójkami.
Zysk wydajnościowy z takiego podejścia to teoretycznie około 25%. Wynika to z faktu, iż na dwa elementy "zużywamy" tutaj tylko trzy porównania, czyli o jedno mniej niż dla oddzielnych wywołań min i max. W praktyce osiągi te psuje bardziej czasochłonny start funkcji, więc opłaca się ją stosować głównie dla większych zbiorów elementów.
position w CSSNiniejsza notka ma specyficzne przeznaczenie. Chociaż nie zdziwiłbym się, gdyby dla wielu osób okazała się interesująca i przydatna, to jej głównym celem jest przechowanie dla mnie pewnej wiedzy, której ogarnięcie zajęło mi całkiem sporo czasu. Tak, chodzi właśnie o tytułowy atrybut position z CSS i jego działanie.
A zatem... Jak nazwa jednoznacznie sugeruje, atrybut ten ma dużo wspólnego z określaniem umiejscowienia elementu wyświetlanego dokumentu. Nie zawiera on jednak pozycji jako takiej, lecz sposób jej wyznaczania. Jest to więc atrybut wyliczeniowy, którego możliwe wartości są następujące:
static - domyślny sposób pozycjonowania. Element zostanie umieszczony i będzie wyświetlany w miejscu, które wynika z położenia jego znacznika w źródle dokumentu, czyli w ciągu otaczającego go tekstu. (Jeśli oczywiście sam ten tekst nie został jakoś fikuśnie wypozycjonowany). Atrybuty określające współrzędne (left, top, itd.) są ignorowane.relative - bardzo podobne do powyższego. Różnica polega na tym, że tutaj wspomniane atrybuty są wykorzystywane i mają wpływ na to, gdzie element jest wyświetlany. Ich punktem odniesienia jest "normalna" pozycja elementu w ciągu tekstu. Znanym przykładem są "wciskane" linki, polegające na przesunięciu łącza odrobinę w prawo i w dół, by dać wrażenie jego wciśnięcia:
To, czego position:relative nie robi, to rzeczywista zmiana położenia elementu. Nawet jeśli jest on wyświetlany gdzie indziej, tekst będzie nadal łamany według jego normalnego położenia - tak samo jak przy position:static.
absolute - tzw. pozycjonowanie absolutne, które w rzeczywistości jest... względne. (Ah, ta logika webdesignu ;>). Przy tym ustawieniu element jest mianowicie wyjęty z normalnego ciągu tekstu, co czasem określa się jako stworzenie nowej warstwy (layer). Jego pozycja jest wówczas określana (atrybutami left itd.) względem elementu go zawierającego, którym to jest najbliższy element nadrzędny o pozycjonowaniu innym niż static... Proste i nieskomplikowane, prawda? ;-)fixed - stała pozycja. Stałość polega na tym, że element nie zmienia swojego położenia podczas przewijania strony, bo punktem odniesienia jest dla niego okno przeglądarki. Tak jak w przypadku absolute, tutaj również tworzy on warstwę wyjęta z normalnego ciągu tekstu.Tyle teoria. W praktyce mamy kilka typowych przypadków użycia. Prawdopodobnie najbardziej skomplikowanym z nich jest zastosowanie position:absolute ze względu na jego dziwne wymaganie odnośnie elementu zawierającego. Jeśli go nie uszanujemy, wówczas nasz 'absolutnie' wypozycjonowany element jako punkt odniesienia przyjmie cały obszar dokumentu, czyli element <body>. Aby temu zapobiec, należy otaczającemu go pojemnikowi ustawić position na relative:
Wizualnie nic on się wtedy nie zmieni (jeśli sam nie zawiera atrybutów w stylu left, itp.), natomiast będzie on mógł być kontenerem dla elementów z position:absolute.
Udekorowane funkcjeKilkanaście dni temu opisywałem błąd, który - jak się okazało - był efektem ubocznym pewnej cechy języka Python, uznanej przeze mnie za niemal zupełnie niepotrzebną. Dla równowagi więc dzisiaj przedstawię feature, który wydaje się być bardzo przydatny - żeby nie było, że potrafię tylko krytykować ;-)
Prawdopodobnie najlepiej jest pokazać go na prostym, acz obrazowym przykładzie. Miejmy pewną funkcję na tyle dla nas istotną, że chcemy logować wszystkie jej wywołania. Wydaje się, że nic prostszego:
def vif(): # Very Important Function :)
logging.debug("Called function vif()")
# ...
Ciężko jednak nazwać takie rozwiązanie uniwersalnym. Nie chodzi tu jedynie o jawnie wpisaną nazwę funkcji, ale raczej o konieczność dodawania logging.debug(...) na początku każdej funkcji, jaką chcemy monitorować. Sprawa komplikuje się jeszcze bardziej, gdy interesują nas też wyjścia z funkcji; wówczas jedynym wyjściem jest chyba opakowanie całej treści w jeden wielki blok try-finally. Rezultat na pewno nie będzie piękny :)
I tutaj właśnie z pomocą przychodzą dekoratory - ciekawa opcja języka Python, na pierwszy rzut oka przypominająca adnotacje z Javy. Podobieństwo jest jednak głównie składniowe. Udekorowana wersja naszej ważnej funkcji wygląda bowiem tak:
Znak @ poprzedza tutaj nazwę dekoratora, czyli trace (ang. śledź - i bynajmniej nie chodzi o rybę ;]). Czym jednak jest ów dekorator? Otóż on sam również jest funkcją, mogącą wyglądać choćby tak:
def trace(func):
def _func(*args, **kwargs):
logging.debug("Calling function %s()", func.__name__)
return func(*args, **kwargs)
return _func
Jej jedynym argumentem jest w założeniu funkcja, zaś rezultatem wywołania jest... również funkcja :) A zatem nasz dekorator potrafi przekształcić jedną funkcję w drugą, a dokładniej w jej udekorowaną, "opakowaną" wersję. To opakowanie definiowane jest wewnątrz dekoratora i polega, jak widać, na poprzedzeniu wywołania oryginalnej funkcji zapisem do loga.
Oczywiście za to, aby wywołanie funkcji opatrzonej dekoratorem było tak naprawdę odwołaniem do jej udekorowanej wersji odpowiada już sam język. Nie jest to zresztą skomplikowane, bo w istocie cały mechanizm jest tylko cukierkiem składniowym. Jest to jednak bardzo smaczny cukierek, który potrafi wydatnie podnieść czytelność kodu - jeśli stosuje się go właściwie.
Do czego jednak - oprócz logowania wywołań - dekoratory mogą się przydać? Ano przede wszystkim do upewniania się, że pewne konieczne warunki wstępne dla funkcji są spełnione na jej wejściu (i ewentualnie wyjściu). Może to być na przykład:
@connected - sprawdzenie połączenia z serwerem zanim spróbujemy wymieniać z nim dane i nawiązanie go w razie potrzeby@authorized - określenie uprawnień wymaganych u aktualnie zalogowanego użytkownika przed wywołaniem funkcji wykonującej potencjalnie niebezpieczną operację@synchronized - zabezpieczenie wywołania funkcji semaforem lub sekcją krytycznąWspólną cechą takich dekoratorów jest to, że są one swoistymi pseudodeklaracjami, nieodległymi koncepcyjnie zbyt daleko od komentarzy w rodzaju:
Ich przewagą jest jednak rzeczywiste sprawdzanie, czy wymagania zostały spełnione - i to w sposób automatyczny i przezroczysty. Według mnie to właśnie stanowi o ich sporej przydatności.
Zabezpieczenie przed NULL-emDefensywne programowanie wymaga, by zabezpieczać się przed różnymi niepożądanymi sytuacjami. Jedną z częstszych jest próba odwołania się do obiektu czy wartości, która nie istnieje - czyli np. dereferencja wskaźnika pustego czy użycie odwołania zawierającego null. Stąd bardzo częste ify w rodzaju:
Kiedy jednak sprawdzenie nullowatości jest tylko częścią warunku, wtedy w ruch idzie zwykle "sztuczka" z leniwą ewaluacją (lazy evaluation):
Większość języków ją dopuszcza, jako że jest ona przy okazji pewną formą optymalizacji. Jeśli bowiem pierwszy argument operatora logicznego daje informację o prawdziwości/fałszywości całego wyrażenia, nie trzeba już wyliczać drugiego.
Inną typową sytuacją jest zamiana nulla na jakąś inną wartość domyślną:
Tutaj C# oferuje specjalny operator ??, pomyślany właśnie na tego typu okazje:
Działa on dobrze z typami Nullable, czyli specyficznym rodzajem typów pochodnych, które dopuszczają wartość null tam, gdzie typ macierzysty jej nie przewiduje:
Operator ?? przydaje się wtedy do konwersji na typ bazowy z określoną wartością domyślną.