Scala w Trójmieście

Opublikowane przez Jarosław Zabiełło Mon, 09 Nov 2009 20:29:00 GMT

19 listopada 2009 w auli Uniwersytetu Gdańskiego odbędzie się spotkanie poświęcone językowi Scala. Organizuje je Trójmiasto Java User Group. Szczegóły tutaj: http://it.wtrojmiescie.org/spotkania/scala.

Tagi , , ,  | brak comments

Problem znaków UTF-8 w Scali i Javie pod Mac OS-X

Opublikowane przez Jarosław Zabiełło Tue, 08 Sep 2009 01:15:00 GMT

Wczoraj, na polskim kanale IRC #scala.pl mieliśmy małą dyskusję na temat złego wyświetlania polskich znaków w Scali pod systemem Mac OS-X Leopard (pod Linuksem jest OK). Co dziwne, mimo, że zmienna środowiska LANG pokazuje wartość pl_PL.UTF-8, próby wyświetlenia polskich napisów nie działają. Więcej, w konsoli Scali w ogóle nie ma możliwości wprowadzenia polskich znaków, ani bezpośrednio z klawiatury, ani poprzez wklejenie ze schowka. Wszystkie polskie ogonki zamieniane są na znak zapytania.

     
println(java.nio.charset.Charset.defaultCharset) 
// x-MacCentralEurope

println("Zażółć gęślą jaźń")
// Za???? g??l? ja??
Czytaj dalej...

Tagi , , , , ,  | 9 comments

Atak na zabezpieczenia protected/private Javy/Scali i PHP5

Opublikowane przez Jarosław Zabiełło Sun, 21 Jun 2009 03:45:00 GMT

Być może jednym z argumentów, dla których Ruby znajduje więcej zainteresowania ze strony programistów Javy niż Python, jest podobny mechanizm zabezpieczenia dostępu do atrybutów klasy za pomocą słów kluczowych private i protected. W języku PHP 5 wprowadzono podobny mechanizm. Ci jednak, którzy myślą, że te kwalifikatory zakresu dostępu stanowią jakiekolwiek poważne zabezpieczenie, są w głębokim błędzie. W banalny sposób można to obejść zarówno w w PHP 5, jak i w Javie. Przy czym o ile, w Javie wynika to raczej z celowego projektu w mechanizmie refleksji, w PHP 5 wygląda to na dziurę bezpieczeństwa…

Czytaj dalej...

Tagi , , , , , , ,  | 27 comments

Scala i Lift on GAE

Opublikowane przez Jarosław Zabiełło Mon, 13 Apr 2009 04:32:00 GMT

Google opublikowało listę języków i javowych frameworków dostępnych dla swojego GAE (Google App Engine). Na liście jest m.in. JRuby, Jython, Groovy i Scala. Z powodu ograniczeń dostępności do wątków i JDBC napisano, że Lift nie jest wspierany. Jak jednak można wyczytać z jednym ostatnich wątków na liście dyskusyjnej Scali, trochę pośpieszono się z tą informacją. Lift daje się odpalić na GAE (to jest fork do kodu źródłowego). Scala posiada dwa rodzaje Aktorów – opartych na natywnych wątkach OS oraz opartych na asynchronicznej pętli, bez użycia wątków. Lift nie potrzebuje więc obsługi wątków do swej pracy. Co do JDBC, to sprawa jest trochę mętna, bo udało się uruchomić Lifta na pamięciowej bazie H2. Być może ograniczenie Google’a dotyczy tylko baz relacyjnych?

Updated: Frameworks and libraries supported by Google App Engine Java : List

Tagi , , , , , , , , , ,  | 3 comments

Scala - język przyszłości

Opublikowane przez Jarosław Zabiełło Sat, 28 Mar 2009 06:17:00 GMT

Kiedy zapytano Jamesa Goslinga (twórcę Javy) o to, który z języków programowania współpracujących z JVM (wirtualną maszyną Javy) by użył teraz, pomijając samą Javę, odpowiedź była zaskakująco szybka i bardzo jasna – Scala.

Nazwa “Scala” pochodzi od “Scalable language” (język skalowalny). Język ten nadaje się równie dobrze do krótkich, zwartych skryptów jak i do tworzenia wydajnych, ogromnych, bezpiecznych systemów sieciowych. W swych założeniach Scala nawiązuje do minimalizmu składni Lispa i Smalltalka (większość rzeczy oparta jest na bibliotekach a nie na składni) dzięki czemu język ten praktycznie nie ma ograniczeń rozwoju i doskonale się skaluje (w miarę potrzeb można tworzyć nowe typy i całe nowe struktury wyglądające jak nowa składnia języka).

Scala jest językiem kompilowanym do bytecodu JVM dzięki czemu potrafi się integrować w sposób praktycznie przezroczysty, z całą platformą Javy (istnieje co prawda implementacja Scali dla platformy .NET ale jest jeszcze niedojrzała). W Scali mamy też wygodną konsolę do interaktywnego testowania kodu (tak jak to jest w Pythonie i Ruby). W Scali każda wartość jest obiektem, każda funkcja zwraca wartość. Zatem każda funkcja też jest obiektem (first class object). Funkcje są też obiektami wyższego rzędu (higher order kinds), można je zagnieżdżać, przekazywać w parametrach, a nawet stosować mechanizm dziedziczenia.

Czytaj dalej...

Tagi , , , , , , , , , , ,  | 108 comments

Merb 1.0, JRuby docelową platformą?

Opublikowane przez Jarosław Zabiełło Mon, 10 Nov 2008 01:13:00 GMT

Ezra Zygmuntowicz oficjalnie w swoim blogu obwieścił wydanie wersji 1.0 dla frameworka Merb. Mimo, że formalnie Merb nie ma zamiaru walczyć z Rails, na pewno taka konfrontacja nastąpi. Tym bardziej ciekawa, że wkrótce ma wyjść Rails 2.2, który wprowadził trochę znaczących zmian w stosunku do wersji poprzednich, m.in. wielowątkowość oraz wsparcie dla wersji międzynarodowych.

Czytaj dalej...

Tagi , , , , , ,  | 32 comments

Szybsze uruchamianie JRuby

Opublikowane przez Jarosław Zabiełło Wed, 15 Oct 2008 01:14:00 GMT

Osoby często uruchamiające w konsoli JRuby’ego pewnie się trochę denerwują opoźnieniami związanymi z koniecznością załadowania za każdym razem JVM (wirtualnej maszyny Javy). Dzięki serwerowi Nailgun, JVM jest trzymana w pamięci i JRuby jest uruchamiany natychmiast, bez opóźnień. Instalacja jest prosta i opisana na Wiki.

Kompilację Nailguna dla JRuby najwygodniej zrobić za pomocą ant'a:

ant jruby-nailgun

Następnie należy uruchomić serwer

jruby --ng-server

(Serwer domyślnie nasłuchuje na wszystkich interfejsach i na porcie 2113, i jest odpalany na prawach usera który, wykonał tą komendę.)

Od tego momentu wirtualna maszyna Javy ładowana jest do pamięci i aby JRuby z tego korzystał należy uruchamiać go z opcją --ng. Rozwiązanie nadaje się świetnie do częstego uruchamiania skryptów. Znika zupełnie efekt opóźnienia związany z koniecznością ładowania JVM przy każdym uruchomieniu skryptu JRuby.

Małe porównanie:

$ time jruby --ng -e "puts 1"
1
real    0m0.049s
user    0m0.010s
sys     0m0.000s

$ time jruby  -e "puts 1"
1
real    0m0.549s
user    0m0.530s
sys     0m0.060s

Tagi , ,  | 8 comments

JRuby - metoda initialize i Java

Opublikowane przez Jarosław Zabiełło Thu, 14 Aug 2008 02:38:00 GMT

Ostatnio, podczas próby użycia javowej biblioteki w JRuby spotkałem problem z kolizją nazw metod Javy z metodami Rubiego. Mianowicie chodzi o metodę o nazwie initialize jaka była zdefiniowana w bibliotece Javy. W JRuby (i Ruby) nazwa ta jest zarezerwowana dla konstruktora. Dzięki temu, że miałem dostęp do źródeł w Javie, mógłbym po prostu je zmodyfikować. Jednakże, takie podejście nie za bardzo mi się podobało. Co w wypadku kiedy miałbym bibliotekę dostępną tylko w postaci skompilowanych klas?

Czytaj dalej...

Tagi ,  | 3 comments

Ruby, Python i natywne wątki systemu operacyjnego

Opublikowane przez Jarosław Zabiełło Sat, 26 Jul 2008 11:22:00 GMT

Wiele się mówi o tym, że Ruby jak i Python nie posiadają obsługi natywnych wątków systemu operacyjnego. Wbudowane, tzw. green threads, nie są w stanie wykorzystać zalet maszyn wyposażonych w procesory wielordzeniowe. Istnieją jednak implementacje obu języków w czystej Javie. Czy ich użycie daje jakieś znaczące przyśpieszenie?

Czytaj dalej...

Tagi , , , ,  | 33 comments

JRuby 1.1 final

Opublikowane przez Jarosław Zabiełło Sun, 30 Mar 2008 22:51:00 GMT

Jest w końcu oczekiwana wersja stabilna JRuby 1.1. W chwili pisania tej notki, nie ma o tym jeszcze informacji na stronie głównej, ale jest już do pobrania. To bardzo dobra wiadomość, bo JRuby 1.1 już w wersjach rozwojowych był znacznie szybszy od poprzedniej 1.0, a w wielu testach bił wydajnością nie tylko Ruby 1.8.6 i 1.9, ale także Pythona 2.5.1. Dzięki JRuby webowe frameworki takie jak Rails czy Merb uzyskują pełny dostęp do bibliotek Javy i tym samym jakiekolwiek uwagi co do (nie)dojrzałości bibliotek Rubiego stają się nieistotne.

Tagi , ,  | 1 comment

Django on Jython

Opublikowane przez Jarosław Zabiełło Tue, 08 Jan 2008 03:34:00 GMT

Pythonistas chyba pozazdrościli możliwości odpalenia Railsów w JRuby. Trwają prace nad uruchomieniem frameworka Django w Jythonie, implementacji Pythona w czystej Javie.

Czytaj dalej...

Tagi , , ,  | 8 comments

Jest w końcu Java 6 dla Leoparda

Opublikowane przez Jarosław Zabiełło Fri, 30 Nov 2007 11:34:00 GMT

Zarówno Apple jak i Sun się ociągają z wypuszczeniem Javy 6 dla Leoparda (tj. Mac OS-X 10.5). W internecie można znaleźć trochę wkurzonych osób. Niektórzy chcą nawet słać petycje do Apple aby coś z tym zrobili.

Czytaj dalej...

Tagi , , ,  | 3 comments

JRuby 1.0

Opublikowane przez Jarosław Zabiełło Wed, 13 Jun 2007 09:24:00 GMT

Ukazała się finalna wersja JRuby 1.0 – implementacji języka Ruby w czystej Javie. Zespół JRuby zachęca do testowania Railsów na JRuby. Z tego co pamiętam, to wersji finalnej JRuby zapowiadano włączenie optymalizacji kodu. Zanim zatem wyjdzie w końcu Ruby 2.0, JRuby on Rails ma już teraz szansę przełamać stereotyp o słabszej wydajności Rubiego. Szczególnie może to mieć duże znaczenie dla platformy Windows, bo Ruby na systemach POSIX (Linux, BSD, Mac OS X) pracuje znacznie wydajniej.

Dla miłośników Mac OS X jest dostępny artykuł pokazujący jak skonfigurować całe środowisko Javy dla Rail. Ale do pracy z Rails, najprościej użyć Netbeans 6 który od jakiegoś czasu pozwala1 na uruchamianie Railsów na JRuby (można wybrać sobie opcję JRuby lub klasyczny CRuby).

Zobacz też artykuł “JRuby and the Java Platform” jaki ukazał się wczoraj na stronie firmy Sun.


1 Szkoda, że NB6 nie ma jeszcze zaimplementowanego kolorowania i podpowiadania helperów dla Haml. Jak ktoś ma siły i czas to tu jest opis jak dodać kolorowanie dla jakiegoś innego języka.

Tagi , , ,  | 14 comments

Jython 2.2beta1

Opublikowane przez Jarosław Zabiełło Fri, 09 Feb 2007 21:51:00 GMT

Pogłoski o przedwczesnej śmierci Jythona (javowej implementacji Pythona), są chyba przesadzone. Właśnie wyszła kolejna wersja 2.2 beta1. Instalacja:

java -jar jython_installer-2.2b1.jar

Tagi , ,  | 4 comments

Serwery kontynuacyjne - przyszłość webu

Opublikowane przez Jarosław Zabiełło Wed, 22 Nov 2006 01:26:00 GMT

Każdy, kto zajmował się kiedykolwiek programowaniem stron internetowych wie, że protokół HTTP jest bezstanowy. Innymi słowy, każde przeładowanie strony internetowej generuje kompletnie niezależne wywołanie serwera i nie ma on możliwości aby powiązać wywołanie z konkretnym klientem. Aby temu zaradzić, wprowadzono prostą sztuczkę. Wprowadzono tzw. mechanizm cookies (dosł. ciasteczek), malutkich plików które serwer może wysyłać do klienta (tam i z powrotem). Ten unikalny identyfikator (sesji) można też trzymać w adresie URL, ale ta metoda jest zdecydowanie odradzana z powodu łatwości nadużyć (łatwo wykraść taki numer i podszyć się pod autora) Od strony klienta jedynym wymaganiem jest aby nie blokował sobie w przeglądarce mechanizmu cookies.

Serwer znając identyfikator sesji, może związać z nim szereg danych, np. aktualną listę zamawianych produktów włożonych do koszyka w sklepie internetowym. Dane te zwykle trzyma się w bazie lub w pamięci operacyjnej. Oczywiście czas życia danych jest ustalany przez serwet i w normalnym wypadku są one cyklicznie kasowane. (Z tym “normalnym” wypadkiem to różnie bywa, bo wiele frameworków tego nie zapewnia i trzeba samemu o to zadbać)

Mechanizm sesji jest dosyć wydajny, bo zapytania wysyłane do serwera mogą być obsługiwane asynchronicznie, czyli bez żadnych blokad w oczekiwaniu na ukończenie obsługi zapytania. Przez lata “stanowość” protokołu HTTP zapewniało się właśnie za pomocą sesji.

Ten mechanizm ma jednak swoje wady. Największą jest złożoność jaką wprowadza po stronie skryptów na serwerze. (I to nie jest wcale taka trywialna sprawa jak to można było zaobserwować u developerów frameworka CherryPy którzy miesiącami nie potrafili napisać stabilnie działającego mechanizmu obsługi sesji.)

Kontynuacje

Kompletnie innym podejściem jest skorzystanie z tzw. mechanizmu kontynuacji. Mechanizm ten podobny jest do gracza, który w dowolnym momencie może zapisać i wczytać stan gry. Kontynuacje zapewniają taki mechanizm zapisu stanu programu aby w dowolnym momencie później go przywrócić. Niektórzy porównują to do maszyny czasu, bo można w dowolnym momencie przywrócić wcześniejszy stan aplikacji.

Zalet z takiego podejścia jest wiele. Weźmy np. sprawę obsługi “nieśmiertelnego” przycisku Back w przeglądarce. Obsługa tego przycisku sprawia spore problemy. Programiści najchętniej w ogóle by go zablokowali. W wypadku kontynuacji ten problem w ogóle nie istnieje. Kliknął ktoś Back aby cofnąć się do poprzedniej strony? Żaden problem. Serwer przywróci cały wcześniejszy stan aplikacji bez żadnego problemu. I to w sposób przezroczysty dla programisty! Programowanie stron webowych staje się znacznie prostsze, niczym tworzenie aplikacji desktopowej włącznie z debugowaniem krok po kroku. Wg różnych prognoz, tak będziemy programować serwisy internetowe za kilka lat!

Sama idea nie jest najwyraźniej jeszcze powszechnie znana, skoro w rozmowie z developerami irlandzkiego Google, zaskoczyłem ich informacją na ten temat. Kto wie, czy Google mocniej nie zainwestuje w Rubiego (aktualnie mocno korzysta z Pythona, który jest podstawą większości ich skryptów).

Smalltalk i Seaside

Smalltalk jest bardzo starym językiem bo jego początki sięgają lat 70-tych. Od zawsze był językiem w pełni obiektowym i dynamicznym. To m.in. Smalltalk pierwszy wymyślił wirtualną maszynę której to ideę później skopiowali twórcy Javy. W Smalltalku napisano najlepszy (w chwili obecnej) na świecie serwer kontynuacyjny – Seaside.

Czy to spowoduje renesans na Smalltalka? Osobiście w to wątpię. Główną wadą Smalltalka jest jego monolityczność i hermentyczność. Uruchamiając środowisko Smalltalak uruchamia się cały, zamknięty świat Smalltalka. Np. nie ma tam dostępu do plików z zewnątrz tak, jak w innych językach. To spowodowało, że język ten, mimo pewnych sukcesów, nigdy nie zdobył sobie powszechnego uznania. I moim zdanie, nigdy już nie zdobędzie, bo w międzyczasie wyrosła mu młoda, obiecująca konkurencja.

Ruby

Ruby jest językiem relatywnie młodym. Tzn. został po raz pierwszy udostępniony publicznie w tym samym roku co Java – 1995. Ale przez większość lat był mało znany poza Japonią (skąd pochodzi jego twórca). Ruby jest także jednym z tych nielicznych języków, które (podobnie jak Smalltalk) posiadają wbudowaną obsługę kontynuacji. Istnieją co prawda różne próby implementacji tego mechanizmu w Javie1, ale tylko Ruby posiada ten mechanizm jako coś naturalnego i nie trzeba stosować żadnych specjalnych sztuczek aby go emulować. Z tego co pamiętam, Seaside pierwotnie był tworzony w Ruby, ale w tamtym okresie, kontynuacje w Ruby były jeszcze niedojrzałe, więc twórcy przerzucili się na Smalltalka. Ruby jednak szybko się rozwija, nabiera dojrzałości i moim zdaniem, ma bardzo duże szanse, aby wyprzedzić pozostałe języki przy tworzeniu serwisów internetowych nowej generacji, serwisów opartych na kontynuacjach.

Przykład kontynuacji.

Poniższy przykład2 pokazuje implementację generatora za pomocą kontynuacji.

class Generator

  def initialize
    do_generation
  end

  def next
    callcc do |here|
      @main_context = here;
       @generator_context.call
    end
  end

   private

   def do_generation
     callcc do |context|
       @generator_context = context;
       return
     end
     generating_loop
   end

   def generate(value)
     callcc do |context|
       @generator_context = context;
       @main_context.call(value)
     end
   end
end

A oto oparty na nim generator ciągu liczb Fibbonacciego:

class FibGenerator < Generator
  def generating_loop
    generate(1)
    a, b = 1, 1
    loop do
      generate(b)
      a, b = b, a+b
    end
  end
end

fib = FibGenerator.new
15.times { print "#{fib.next} " }
#wynik: 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 

1 Np. Riff lub Jetty 6.

2 Przykład z książki “The Ruby Way”, 1ed.

Posted in , ,  | Tagi , , ,  | 23 comments

Sun zatrudnił programistów JRuby

Opublikowane przez Jarosław Zabiełło Fri, 08 Sep 2006 08:33:00 GMT

W środowisku javowców Ruby jest językiem który robi trochę zamieszania (jakoś tak się składa, że Ruby bardziej przemawia do programistów Javy niż Python). Cieszy zatem, że firma Sun podjęła decyzję o wsparciu projektu JRuby zatrudniając jego czołowych developerów. Powinno to znacznie przyśpieszyć prace nad tym projektem podobnie jak stało się z IronPythonem, gdy Microsoft zatrudnił jego twórcę.

Jestem ciekaw jak długo programiści zachowają entuzjazm dla swego języka, gdy będzie można generować taki sam bytecode Javy lecz w niezrównanie prostszy sposób. :)

Przykładowy kod Javy:

public class Filter {
  public static void main(String[] args) {
    List list = new java.util.ArrayList();
    list.add("Tim"); list.add("Ike"); list.add("Tina");
    Filter filter = new Filter();
    for (String item : filter.filterLongerThan(list, 3)) { 
      System.out.println( item ); 
    }
  }
  public List filterLongerThan(List list, int length) {
    List result = new ArrayList();
    for (String item : list) {
      if (item.length() <= length) { result.add( item ); }
    }
    return result;
  }
}

A oto odpowiadający mu kod w Ruby:

list = ['Tim', 'Ike', 'Tina']
list.select {|n| n.length > 3}.each {|n| puts n}

Oczywiście to nie wszystko. Dzięki JRuby można uzyskać efekty kompletnie nieosiągalne w standardowej Javie – np. można pracować z biblioteką Swing w sposób interaktywny, z poziomu interpretera zmieniając na żywo jej obiekty.

Wkrótce ma także być gotowa wersja Railsów działająca z JRuby (Zobacz prezentację w PowerPoint). Tym samym odeszłyby wszelkie uwagi co do wydajności Railsów, bo współczesna wirtualna maszyna Javy jest tak silnie zoptymalizowna że dorównuje językowi C++. Oczywiście model wątkowy JRuby jest zgodny z wydajnym i dojrzałym modelem wątkowym Javy – po prostu z niego korzysta.

Posted in ,  | Tagi , ,  | 32 comments

Akcesory w Javie, Pythonie, Ruby i PHP5

Opublikowane przez Jarosław Zabiełło Tue, 07 Feb 2006 22:25:00 GMT

Java, język który dobył sobie silną pozycję na rynku korporacyjnym, jest od jakiegoś czasu pod obstrzałem krytyki z różnych stron. Wyszła cała seria książek krytykujących model obiektowy oraz metodologie promowane przez Javę (od najsłynniejszej Beyond Java po Bitter Java, Bitter EJB, czy inne)

Nie umniejszając zalet Javy, jej krytycy wytykają jej niepotrzebną nadmiarowość kodu, ociężałość i małą zwrotność, która powoduje że język ten słabo nadaje się do modnej ostatnio metodologii _agile programming. _ Aby lepiej ten problem zobaczyć, przyjrzyjmy się typowej praktyce programistów Javy. Tekst ten zainspirowany jest niedawną dyskusją jaka miała miejsce w jednym z blogów mojego kolegi.

Czym są akcesory?

Akcesory (lub inaczej: gettery i settery) to slangowe okreslenie metod jakie używa obiekt do odczytu i modyfikacji swoich atrybutów. Są one tak nagminnie używane przez programistów Javy, że niektóre edytory (np. Eclipse) zostały nawet wyposażone w makra do automatycznego ich generowania.

Dorzucanie nieużywanego kodu “na wszelki wypadek”...

Nagminna (wśród programistów Javy) praktyka dodawania akcesorów do atrybutów klasy pachnie jakimś antipatternem i są głosy, które używanie akcesorów nazywają wprost złą praktyką . Jest w tym trochę racji. Ale, jak to poniżej wykażę, jest to pewnego rodzaju kompromis w związku ze słabym modelem obiektowy Javy.

Dlaczego programiści Javy generują akcesory dla każdego atrybutu nawet, jak nie przewidują potrzeby ich używania? Otóż czynią to “na wszelki wypadek” bo nie wiedzą, czy w przyszłości nie będzie im to potrzebne…

Weźmy np. taki kod (z powodu kolejnego ograniczenia Javy nie można w jednym pliku trzymać więcej, niż jednej klasy; tu dla krótkości umieszczam kod z obu plików razem)

public class First {
  public String msg = "hello";
}

public class Second extends First {
  public static void main(String[] args) {
    First obj = new First();        
    System.out.println(obj.msg);
  }
}

Jak widać, w Javie można sięgać do atrybutu w sposób bezpośredni.

Wyobraźmy sobie jednak, że projekt nam się rozrósł i mamy już całkiem sporo kodu oraz sporo plików (pomnażanych wydatnie przez javowe ograniczenia co do ilości klas mogących wystąpić w pliku). Teraz przychodzi polecenie aby w momencie odczytu i zapisu atrybutu coś dodatkowego wykonać. (Np. niech to będzie logowanie informacji o takim zdarzeniu, albo zablokowanie możliwości modyfikacji treści atrybutu. Wszystko jedno co)

Mamy zatem pierwszy problem. Trzeba w tych wszystkich milionach miejsc, gdzie odwoływaliśmy się bezpośrednio do atrybutów, wymienić kod…

Właśnie dlatego, aby takich niespodzianek uniknąć w przyszłości, programiści Javy dorzucają dodatkowe metody opakowujące odczyt i zapis atrybutów. Eclipse upraszcza ten proces zwalniając programistę od ręcznego wpisywania tego kodu.

Klasa First po zmianie:

public class First {
  public String msg = "hello";
  public String getMsg() {
    return msg;
  }
  public void setMsg(String msg) {
    this.msg = msg;
  }
}

Wydaje się, że problem jest rozwiązany. Ale to dopiero początek innych problemów.

Jeśli bowiem, automatycznie dorzucamy te metody do wszystkich tysięcy atrybutów nowo tworzonych klas, to pierwszą rzeczą którą zauważamy, jest nagłe “spuchnięcie kodu”. Mamy kupę nieużywanych linii kodu, z których prawdopodobnie większość nie będzie nigdy używana!

Ale to nie wszystko. Załóżmy, że stajemy przed potrzebą zmiany nazwy atrybutu. Oczywiście, możemy to zmienić w jednym miejscu (w ciele akcesora), ale wtedy wprowadzamy chaos w pozostałej części odnośnie… nazw (atrybut “msg” zmieniony na “title” trochę głupio wygląda z akcesorami o nazwie “getMsg” i “setMsg”) Musimy przekopać się przez miliony miejsc w kodzie i pozmieniać nazwy starym wywołaniom.

Zobaczmy jak ta sytuacja wygląda w językach dynamicznych.

Python

Python (podobnie jak Java) pozwala na bezpośredni dostęp do atrybutów klasy. A co w sytuacji kiedy chcemy opakować atrybut akcesorami? Nic prostszego. Python pozwala na dodanie akcesorów wtedy, i tylko wtedy, kiedy są potrzebne. Na dodatek czyni to w sposób całkowicie przezroczysty dla pozostałej części kodu. Programista Javy może sobie o tym tylko pomarzyć.

class X(object):
    def get_msg(self):
        return self.__msg
    def set_msg(self, val):
        self.__msg = val
    msg = property(get_msg, set_msg)

obj = X()
obj.msg = "hello" 
print obj.msg

Python pozwala także związać z dowolną metodą, atrybutem, klasą czy modułem docstring, czyli tekst z dokumentacją, objaśnieniem itp. To jedna z genialnych cech Pythona specjalnie pomyślana dla leniwych programistów, którym nie chce się pisać dokumentacji. ;)

Ruby

W języku Ruby z definicji nie ma żadnej możliwości dostępu do atrubutów klasy inaczej jak przez akcesory. Odpada więc problem zapominania aby je dodać. Ruby jednak narzuca nazwy dla akcesorów (mają nazwę taką jak atrybut!) i całość wygląda tak, jakby operowano bezpośrednio na atrybucie.

class X
  def msg
    @msg
  end
  def msg=(val)
    @msg = val
  end
end

obj = X.new
obj.msg = "hello" 
puts obj.msg

Dla osób, które nie lubią za dużo pisać, Ruby ma wygodne skróty. Powyższą definicję klasy można zapisać także w ten sposób:

class X
  attr_accessor :msg
end

Istnieją także oddzielne skróty dla getterów i setterów. No i można po przecinku dodać akcesory dla całej grupy atrybutów.

PHP5

PHP w wersji 5 ma przebudowany model obiektowy od podstaw. Twórcy języka PHP nie starali się poprawiać modelu obiektowego PHP4 (był on tak zły, że prościej było im napisać go od nowa). W nowym PHP5 mamy już możliwość przezroczystego dodania akcesora.

<?php
class X {
  private $attributes = array('msg'=>null);
  private function __get($attrname) {
    return $this->attributes[$attrname];
  }
  private function __set($attrname, $val) {
    $this->attributes[$attrname] = $val;
  }
}

$obj = new X();
$obj->msg = "hello";
print $obj->msg;
?>

Składnia może nie jest tak prosta jak w Ruby, ale (przynajmniej w tym miejscu), PHP5 zachowuje się tu sensownie i unika dylematów Javy.

Posted in , ,  | Tagi , , ,  | 25 comments

Ruby, Python vs Java,C++

Opublikowane przez Jarosław Zabiełło Mon, 14 Nov 2005 20:41:00 GMT

Od czasu do czasu na grupach dyskusyjnych mają miejsce przepychanki na temat wyższości jednych języków nad drugimi. Czasami takie dyskusje wywołują sporo emocji. Zmęczony ciągłymi ogólnikowymi sloganami o tym jak to w C++ lub Javie pisze się lepiej i szybciej (dobry dowcip) programy niż w językach dynamicznych takich jak Python czy Ruby, postanowiłem rzucić małe wyzwanie miłośnikom tych języków.

Zadanie polegało na napisaniu wyszukiwarki do polskiego tekstu Koranu (w sumie nie widziałem nic takiego, stąd pomysł). Aby nie było za banalnie, trzeba było napisać taki kod który by automatycznie wyssał przekład Koranu ze strony internetowej http://www.planetaislam.com/koran.html następnie przetworzył go i wstawił do bazy mysql oraz przełączył się w tryb wyszukiwania fraz.

Wyzwanie zostało przyjęte i… mijają tygodnie za tygodniami a moi dyskutanci nie są w stanie przedstawić żadnego działającego kodu w Javie ani w C++. Tak to teoria zderza się z praktyką. Oto naprędce stworzona wersja w Ruby:

require 'mysql'
require 'open-uri'

@przeklad = 'bielawski'
@conn = Mysql.new('localhost', 'login', 'hasło', 'koran', 3308)

def download(max_chapter=15)
  def clean_html(src) # nie znam biblioteki czyszczacej html wiec robie to regexem
    src.gsub(/&nbsp;/, ' ').gsub(/(<[^>]+>)/, '').gsub(/[ ]{2,}/, ' ').gsub(/&#/, '').gsub(/&quot;/, '"')
  end   
  @lines = []
  1.upto(max_chapter) do |sura|
    url = sprintf('http://www.planetaislam.com/koran/%s/%03d.htm', @przeklad, sura)
    src = clean_html(open(url).read)
    rows = src.scan(/(\d+?)[. ]([^\d]+)/) # wyluskuj wersety i numery rozdzialow
    rows.each do |row|
      @lines << sprintf('%s:%s %s', sura, row[0], row[1].gsub(/[\s]{2,}/, ' ').strip)
    end
    f = File.new("#{@przeklad}.txt", "w") # zapisuje sobie do pliku
    @lines.each { |line| f.write("#{line}\n") }                               
  end
end
def loaded_db
  @conn.list_tables.include?(@przeklad) # Sprawdzam czy juz istnieje tabela z danymi.
end
def load_db
  @conn.query("DROP TABLE IF EXISTS #{@przeklad}")
  @conn.query("CREATE TABLE #{@przeklad} (
    id int(10) unsigned NOT NULL auto_increment,
    sura varchar(255) NOT NULL,
    nr int(10) unsigned NOT NULL,
    verse text NOT NULL,
    PRIMARY KEY (id)
    ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci")
  @conn.query("SET CHARACTER SET latin2")
  @lines.each do |line|
    sura, nr, verse = line.scan(/(\d+?):(\d+?) (.+)/)[0]
    verse.gsub!(/'/,"''") # uzywam topornego sql bo cos mi metoda prepare nie dziala, moze zla biblioteka?
    @conn.query("INSERT INTO #{@przeklad} (sura, nr, verse) VALUES(#{sura}, #{nr}, '#{verse}')")
  end
end  
def search_result(phrase)
  @conn.query("SET CHARACTER SET cp1250")
  sql = "SELECT * FROM " + @przeklad + " WHERE verse LIKE '%" + phrase + "%' "
  c = @conn.query(sql)
  while row = c.fetch_row
    puts sprintf("Sura %s,%s %s\n", row[1], row[2], row[3])
  end
end

if $0 == __FILE__
  if !loaded_db          
    download
    load_db
  end
  search_result('kobieta')
end

A oto ostateczna wersja w Pythonie:

#!python

"""
Wyszukiwarka do Koranu

Przykłady uzycia:
    python koran.py --txt zabijajcie
    python koran.py --sura=ix
    python koran.py --sura=9 --nr=4
    python koran.py --sura=IX --nr=4    

Wymagania:
    interpreter Pythona 2.4 + standardowe biblioteki
    pythonowska biblioteka MySQLdb
    serwer MySQL 4.1

Autor:
    Jarosław Zabiełło http://zabiello.com
"""

import HTMLParser, os, re, urllib2, sys
from optparse import OptionParser
import MySQLdb

class Koran(object):
    dbname ="koran"
    przeklad = 'bielawski'
    lines = []

    def __init__(self):
        self.conn = MySQLdb.connect(
            host='localhost',
            port=3308,
            user='login',
            passwd='hasło',
            db=self.dbname)

    def download(self, max_chapter=15):
        """Sciagam dane z internetu i je troszke oczyszczam z nadmiarowego kodu html"""
        def html_clean(src):
            x = HtmlStripper()
            x.feed(src)
            return src = x.get_fed_data()            
        self.lines = []
        for i in xrange(1,max_chapter):
            url = 'http://www.planetaislam.com/koran/%s/%03d.htm' % (self.przeklad, i)
            src =  html_clean(urllib2.urlopen(url).read()) # pobrany caly plik
            rows = re.findall(r'(\d+?)[. ]([^\d]+)', src) # wyluskuj wersety i numery rozdzialow            
            for row in rows:
                self.lines.append('%s:%s %s' % (i, row[0], re.sub(r'[\s]{2,}', ' ', row[1]).strip()))
        self.lines = [unicode(line, 'iso-8859-2').encode('utf-8') for line in self.lines]
        f = open(self.przeklad+'.txt', 'w')  # zapisuje sobie kopie na dysk
        for line in self.lines:
            f.write('%s\n' % line)
        f.close()

    def loaded_db(self):
        """Sprawdzam czy juz istnieje tabela z danymi."""
        c = self.conn.cursor()
        c.execute("SHOW TABLES")
        rows = c.fetchall()
        if self.przeklad in [row[0] for row in rows]:
            c.execute("SELECT COUNT(*) FROM %s" % self. przeklad)
            return c.fetchone()[0]
        return False

    def load_db(self):
        "Zakladam ze stworzono baze o nazwie self.dbname i laduje dane do tabeli self.przeklad"
        sqls = (
            "DROP TABLE IF EXISTS %s" % self.przeklad,
            '''CREATE TABLE %s (
                id int(10) unsigned NOT NULL auto_increment,
                sura varchar(255) NOT NULL,
                nr int(10) unsigned NOT NULL,
                verse text NOT NULL,
                PRIMARY KEY (id)
            ) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_polish_ci''' % self.przeklad)
        c = self.conn.cursor()
        for sql in sqls: # odtwarzam baze
            c.execute(sql)
        c.execute("SET CHARACTER SET UTF8")
        sql = "INSERT INTO "+self.przeklad+" (sura, nr, verse) VALUES (%s,%s,%s)"
        for line in self.lines:
            found = re.match(r'(\d+?):(\d+?) (.+)', line)
            if found:
                c.execute(sql, found.groups())
        c.close()

    def set_i18n(self, cursor, phrase=None):
        if sys.platform == 'win32': # zakladam ze bede wyswietlal wyniki w okienku konsoli, czyli kodowanie dosowe
            if phrase:
                phrase = unicode(phrase, 'cp1250').encode('cp852')
            cursor.execute("SET CHARACTER SET cp852")
        elif sys.platform == 'linux':
            cursor.execute("SET CHARACTER SET latin2")
        return phrase

    def search_result(self, phrase):
        """Wyszukuje wersetow wg prostej zasady: znadz wszystkie wersety w ktorych gdziekolwiek
        wystepuje fraza phrase. Dla wygody dodalem obsluge polskich znakow spod konsoli dosowej
        i linuksa."""
        c = self.conn.cursor()
        phrase = self.set_i18n(c, phrase)
        sql = "SELECT * FROM " + self.przeklad + " WHERE verse LIKE '%" + phrase + "%' "
        print sql
        c.execute(sql)
        rows = c.fetchall()
        c.close()
        print "Znaleziono %d wersetow:\n" % len(rows)
        for row in rows:
            print "Sura %s,%s %s\n" % (int_to_roman(int(row[1])), row[2], row[3])

    def show_verse(self, sura, nr=0, context=0):
        c = self.conn.cursor()
        self.set_i18n(c)
        if sura.isalpha():
            sura = roman_to_int(sura)
        if sura and nr: # wyswietl pojedyncza sure
            sql = "SELECT verse FROM " + self.przeklad + " WHERE sura=%s AND nr=%s"
            c.execute(sql, (sura, nr))
            verse = c.fetchone()[0]
            print "Sura %s,%s %s\n" % (int_to_roman(int(sura)), nr, verse)
        else: # wyswietl cala sure
            sql = "SELECT nr, verse FROM " + self.przeklad + " WHERE sura=%s ORDER BY nr"
            c.execute(sql, [sura])
            rows = c.fetchall()
            for row in rows:
                print "Sura %s,%s %s\n" % (int_to_roman(int(sura)), row[0], row[1])
        c.close()

class HtmlStripper(HTMLParser.HTMLParser):
    """src: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/440481"""
    def __init__(self):
        self.reset()
        self.fed = []
    def handle_data(self, d):
        self.fed.append(d)
    def get_fed_data(self):
        return ''.join(self.fed)

def int_to_roman(input):
    """src: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81611"""
    if type(input) != type(1):
        raise TypeError, "expected integer, got %s" % type(input)
    if not 0 < input < 4000:
        raise ValueError, "Argument must be between 1 and 3999"   
    ints = (1000, 900,  500, 400, 100,  90, 50,  40, 10,  9,   5,  4,   1)
    nums = ('M',  'CM', 'D', 'CD','C', 'XC','L','XL','X','IX','V','IV','I')
    result = ""
    for i in range(len(ints)):
        count = int(input / ints[i])
        result += nums[i] * count
        input -= ints[i] * count
    return result

def roman_to_int(input):
    """src: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81611"""
    if type(input) != type(""):
        raise TypeError, "expected string, got %s" % type(input)
    input = input.upper()
    nums = ['M', 'D', 'C', 'L', 'X', 'V', 'I']
    ints = [1000, 500, 100, 50,  10,  5,   1]
    places = []
    for c in input:
        if not c in nums:
            raise ValueError, "input is not a valid roman numeral: %s" % input
    for i in range(len(input)):
        c = input[i]
        value = ints[nums.index(c)]
        try:
            nextvalue = ints[nums.index(input[i +1])]
            if nextvalue > value:
                value *= -1
        except IndexError:
            pass
        places.append(value)
    sum = 0
    for n in places:
        sum += n
    return sum

if __name__ == '__main__':
    appl = Koran()
    if not appl.loaded_db():
        print 'Pobieram dane z internetu....'
        appl.download()
        print 'Wkladam dane do bazy...'
        appl.load_db()
    parser = OptionParser()
    parser.add_option('--txt', dest='txt', help='wyszukiwana fraza')
    parser.add_option('--sura', dest='sura', help='numer Sury (liczba dziesietna lub rzymska)')
    parser.add_option('--nr', dest='nr', help='numer wersetu dla Sury')
    (options, args) = parser.parse_args()
    if options.txt:
        appl.search_result(options.txt)
    elif options.sura:
        appl.show_verse(options.sura, options.nr)
    else:
        print "Wywolaj z parametrem --help"

W międzyczasie zdążyłem nawet wykorzystać swój kod Pythona aby dodać go jako moduł do Plone. :)

Posted in ,  | Tagi , ,  | 80 comments