Słyszeliście historyjkę, jak to kot wskoczył na klawiaturę, wklepując losowe znaki, a Larry stwierdził że to musi dać “Hello world” ? Znalazł się spec który odtworzył ten ciąg znaków:

perl -e 's^^i6(!@*^+s;\*; Wo\$_\;~;.s![(_\!]!l!g+y"i\$@"Hro"+tr-6;~-ed\012-;print'

Ja leżę i kwiczę :)

Źródło: goldenline.pl/forum/perl/127239


Jako, że zbliża się koniec ważności mojego konta na DreamHost, a przedłużać go zamiaru nawet namniejszego nie mam, zaczęłem się przygotowywać do migracji. Jeszcze nie wiem gdzie pójdę sobie, ale pójdę na pewno :)

Jedną z rzeczy z tym związanych jest usunięcie mojego repo (repo.urzenia.net). Cała jego zawartość znajdzie się niedługo jako konkretne projekty na code.google.com. W sumie już wszystko przeniosłem, poza BlipApi.php, które też się tam niedługo znajdzie. Nowe adresy projektów to:

mPack
zestaw bibliotek pomocnych przy tworzeniu serwisów w PHP: m-pack.googlecode.com.
useless-scripts
kilka skryptów które wykorzystuje w mojej codziennej pracy na komputerze: useless-scripts.googlecode.com. Tutaj też dostał się mój ostatni “produkt” killer.pl.
WP Blip!
Plugin do WordPressa, wyświetlający na nim ostatnie wpisy z serwisu Blip!: wp-blip.googlecode.com.

Zrobiłem Killera ;)

Killer to skrypt w perlu który wyświetla tabelę dostępnych procesów, i pozwala wysłać dowolny sygnał (komenda kill w linuksie) do wybranych procesów. Działanie jest bardzo proste, wygląd jeszcze bardziej… Jak ktoś ma ochotę dorobić ładne cssy to jestem za tym żeby je tam dorzucić ;)

Skrypt jest do pobrania z jego strony domowej: repo.urzenia.net/Perl:Killer.


Przez ostatnie cztery dni robocze walczyliśmy z jedną zagwozdką. Spory kawałek kodu, w teorii działa nieźle, w praktyce troszkę gorzej. Objawy:

  • pierwsze odpalenie procedury testowej - pomyślne
  • drugie odpalenie procedury testowej - failed (w tym samym requeście!)
  • ciekawostka: wystarczy gdzieś po drodze w testowanej funkcji odpalić funkcję zrzucającą zawartość struktury (w tym wypadku ref. do hasza), żeby oba testy zaczęły działać poprawnie
  • ciekawostka2: wystarczy dodać tą samą funkcję w innym miejscu, żeby np. pierwsze odpalenie testu wypadało nieprawidłowe, a drugie już działało OK

W sumie, dla nas to przez jakiś czas wyglądało na magię :( kombinowaliśmy na różne sposoby, już zacząłem prosić o włożenie cgi_dump()‘a (naszej funkcji dumpującej zawartość zmiennej) i przekierowanie jej wyjścia do /dev/null ;) W końcu wczoraj doprowadziliśmy do sytuacji, kiedy funkcja zaczęła się zachowywać prawidłowo, ale jeszcze nie znaleźliśmy konkretnej odpowiedzi: CZEMU?

Dziś, po długich bojach, połapaliśmy się w (chyba) wszystkim. Pomijając burdel w kodzie, tworzenia zmiennych do niczego nie potrzebnych, lub też deklarowaniu tych samych zmiennych po kilka razy w kodzie, i nazwy zmiennych typu: $data, @data, %data, sprawcą okazywała się konstrukcja while (($key, $value) = each %hash). Ta niewinna z pozoru konstrukcja posiada jednego ‘fiuczera’, o którym nie doczytaliśmy w dokumentacji, mianowicie:

There is a single iterator for each hash, shared by all each, keys, and values function calls in the program; it can be reset by reading all the elements from the hash, or by evaluating keys HASH or values HASH .

Czyli, przekładając na nieco bardziej ludzki opis: jeśli iterowałeś po haszu za pomocą each()‘a, w międzyczasie tą iterację przerwałeś, to wewnętrzny wskaźnik hasza znajdował się w miejscu w którym została przerwana iteracja. Następne wywołanie each()‘a zacznie się nie od początku hasza, tylko od miejsca w którym zostało przerwane. Oczywiście w niektórych przyapadkach może to być dobre, w innych - w tym naszym - niespecjalnie. Położenie tego wewnętrznego wskaźnika można zresetować np. poprzez użycie funkcji keys() lub values() na haszu, ale czyste (samodzielne) wywołanie tej funkcji jest jednak mało czytelne (a “samowyjaśniający” się kod jest lepszy od dodatkowych komentarzy). Jest jednak czytelniejszy sposób aby osiągnąć nasz cel, a którego nie omieszkaliśmy wykorzystać: zastąpiliśmy wszystkie wywołania konstrukcji while-each, konstrukcją foreach-keys, która według przeprowadzonych przeze mnie jakiś czas temu tesów, jest nawet nieznacznie szybsza! Co prawda traci się tutaj odrobinę na czytelności kodu, jednak przeskoczenie pewnych problemów, których rozwiązanie zajęło nam 4 dni, jest zdecydowanie tego warte.


Jako, że od jakiegoś czasu zajmuję się perlem, to zależy mi na wynajdywaniu różnych użytecznych sztuczek i/lub informacji dotyczących tego języka. Poniżej zbiór kilku różnych porad, które jakoś się w ostatnim czasie uzbierały :)

  1. ciekawostka: używanie konstrukcji while-each do iteracji po elementach hasza jest wolniejsze niż używanie pary for-keys! Zrobiłem sobie jakiś czas temu do tego prostego benchmarka. Różnica prędkości nie jest duża, wacha się w granicach błędu (3-4%), ale zawsze jest to coś, tym bardziej, że for-keys pozwala na przeiterowanie po elementach hasza w jakiejś dowolnej, określonej przez nas kolejności :)
  2. często jest tak, że jakaś funkcja zwraca nam tablicę elementów, a my potrzebujemy tej referencji do tej tablicy. najprostszym, i do niedawna najczęściej przeze mnie używanym sposobem było zrobienie:
    @$ref = funkcja_zwracajaca_tablice();

    Od niedawna, używam konstrukcji:

    $ref = [funkcja_zwracajaca_tablice()];

    wzrost prędkości sięga nawet 40%!

  3. Powyższa reguła dotyczy także haszy, czyli nie:
    %$ref = funkcja_zwracajaca_hasz();

    tylko

    $ref = {funkcja_zwracajaca_hasz()};
  4. tutaj taki ogólny tips: po co duplikować kod, pisząc:
    $var1 = ($var2 eq 'aaa') ? 1 : 0;

    skoro można zapisać to w skrócie:

    $var1 = ($var2 eq 'aaa');

    ? Bardzo często spotykam się z takim bzdurnym zapisem jak ten pierwszy - pamiętaj że narzuca to dodatkowe wykonanie warunku…

  5. Uwielbiam w perlu prawostronne warunki/pętle. Po prostu strasznie upraszczają zapis, zwiększając przy tym czytelność (oczywiście wszystko jest łatwo nadużyć, ale to osobna kwestia). Przy pętlach brakowało mi strasznie możliwości iterowania z jakimś warunkiem, i wykonania jakiejś akcji gdy warunek jest spełniony. Jak zwykle niezastąpiony bochen znalazł i na to radę:
    ($_%2) and $_ foreach (1..6);

    Co prawda powyższy zapis łatwo zastąpić grepem, ale nie zawsze potrzebujemy otrzymać tablicę wartości, czasem trzeba tylko wykonać jakąś akcję :)

  6. Spotykałem się czasem z problemem przekazywania nazwy funkcji, jaką trzeba wykonać, w zmiennej. Nie zawsze można przekazać referencję do tej funkcji, a na pewno nie zawsze jest to najwygodniejsze co możemy użyć. No i oczywiście pomijam tutaj użycie evala. Metody na rozwiązanie tego problemu są dwie, przy czym preferuję drugą, gdyż nie ponosimy przy tym ryzyka wystąpienia jakichś błędów przy wyłączonym “strict refs”.
    sub test { print 'testujemy'; }
    my $t = 'test';
    • {
          no strict "refs";
          &{$test}();
      }
    • &{*fn = $test}();

    Należy jednak pamiętać że metoda “z globem” jest nawet do 30% wolniejsza od wersji z “no strict refs” (20% jeśli włączymy zaraz na początku wywoływanej w ten sposób funkcji “use strict refs”).

Jeśli macie jeszcze jakieś ciekawostki związane z perlem, to poproszę o linki/konkrety w komentarzach :)


Dziś będzie po perlowemu.

Ten kod:

	if (@_) {
		if (@_ == 1) {
			return $$CONFIG{$_[0]};
		} else {
			my $ret;
			push(@$ret, $$CONFIG{$_}) foreach (@_);
			return $ret;
		}
	} else {
		return $CONFIG;
	}

I ten kod:

	return $CONFIG		unless @_;
	return $$CONFIG{$_[0]}	if (@_ == 1);

	my @ret = @$CONFIG{@_};
	return wantarray ? @ret : \@ret;

Robią dokładnie to samo, choć dla niewprawnego oka linijka my @ret = @$CONFIG{@_}; może być nieco myląca :)