Poznajemy nowe języki: Scala, czyli jak wypisać elementy z listy

napisane przez wiktor, 17:39 12-12-2007

Scala. Co to za język?

  • Scala została sklasyfikowana jako język funkcyjny, obiektowy, wielo-paradygmatyczny (mam nadzieję, że dobrze tłumaczę),
  • statycznie i silnie typowany,
  • obecna implementacja bazuje na JVM, niedługo można się spodziwać implementacji na .NET,
  • tak!, Scala działa na platformie Java, więc ma dostęp do całego ekosystemu bibliotek Javy,
  • nietuzinkową postacią jest sam twórca języka Martin Odersky, który jest współautorem obecnego kompilatora Javy oraz jest współprojektantem obecnych typów generycznych w Javie,
  • jest to młody język programowania i posiada wiele ciekawych własności, o których to będę pisał w kolejnych postach,
  • więcej: Scala na Wikipedii, strona domowa Scali, wywiad z twórcą języka.

OK, wezmę na warsztat prostą czynność: wypisanie elementów z listy.
Kod w Javie byłby następujący:

JAVA:
  1. List<Integer> lista = new ArrayList<Integer>() {{
  2.    add(1); add(2); add(3); add(4);
  3. }};
  4.  
  5.  
  6. for (Integer x : lista)
  7.     System.out.println(x);
  8.  
  9. // Można też tak, ale raczej nie bym nie polegał na metodzie toString()
  10. System.out.println(lista.toString());
  11.  
  12. // lub po prostu:
  13. System.out.println(lista);

Dla odmiany w Ruby'im zrobilibyśmy to tak:

RUBY:
  1. # tutaj akurat mamy tablicę
  2. lista = %w(1, 2, 3, 4)
  3.  
  4. # żeby ładnie było to definiujmy println
  5. println = lambda { |x| puts x }
  6.  
  7. lista.each &println

Zobaczmy teraz, jak zrobilibyśmy to w Scali.

SCALA:
  1. val lista = List(1, 2, 3, 4)
  2.  
  3. lista.foreach((x: Int) => println(x))

Brzydko... idziemy dalej. Scala jest językiem funkcyjnym, który ma mechanizm currying. Opuśćmy deklarację typu i nawiasy.

SCALA:
  1. lista.foreach(x => println(x))

Nadal brzydko... idziemy dalej. Co jest argumentem metody foreach? Sygnatura metody jest następująca:

SCALA:
  1. def foreach(f: A => Unit): Unit

Na potrzebę chwili traktujmy Unit tak samo jak void. Zatem argumentem foreach jest funkcja, która przyjmuje argument A (jakiś nieustalony typ, patrz: currying) i zwraca Unit. Natomiast println jest zdefiniowany tak:

SCALA:
  1. def println(x: Any) = Console.println(x)

czyli bierze jakiś argument (nadal patrz: currying, funkcje wyższego rzędu) i zwraca Unit. Zatem typy się zgadzają. Zapiszmy to krócej:

SCALA:
  1. lista.foreach(println)

Już lepiej, ale można jeszcze lepiej :). Scala ma bardzo fajną własność, którą nigdzie wcześniej nie spotkałem. Mianowicie w Scali technicznie nie ma operatorów. Wyrażenie 1 + 2 jest tożsame z wywołaniem metody 1.+(2). Jeśli metoda przyjmuję tylko jeden parametr to można pominąć nawiasy oraz kropkę. Zatem:

SCALA:
  1. lista foreach println

Piękny zapis, nieprawdaż? :) A czy twój ulubiony język zrobi to lepiej?

Komentarzy (26)

  1. adriahu napisał(a):
    PERL:
    1. @lista = (1, 2, 3, 4);
    2. $\ = "\n";
    3. print for @lista;

    Nie wiem czy lepiej, ale też ładnie ;)

  2. adriahu napisał(a):

    No i python, też ładnie :)

    PYTHON:
    1. lista = [1, 2, 3, 4]
    2. for element in lista: print element

  3. Wiktor napisał(a):

    @adriahu

    Perl? Hmmmm... ciekawe, ale widać jaką magię Perla w drugiej linijce :). Gdybym to gdzie indziej zobaczył to pewnie bym się nie domyślił, o co chodzi :). Użyteczna jest konstrukcja print for.

    Python? Też ładnie, ale print element powinno się znajdować w następnej linijce ;).

  4. adriahu napisał(a):

    W perlu to nie jest magia, tych parę użytecznych zmiennych można łatwo zapamiętać :) A w pythonie nie trzeba nowej linii :P

  5. Wiktor napisał(a):

    Dobry komentarz napisał także Adrian Olek, że w Ruby'im wystarcza po prostu:

    RUBY:
    1. puts [1, 2, 3, 4]

    Przy założeniu, że wypisujemy na standardowe wyjście, gdybyśmy chcieli wypisywać gdzieś do logów to wygodniejsze byłoby zastosowanie bloku.

    Podejście w Javie, Ruby'im (tym w komentarzu), Pythonie, Perlu jest czysto imperatywne, natomiast w Ruby'im (tym w poście) oraz w Scali jest bardziej funkcyjne.

    Kwestia gustu, a o gustach się nie dyskutuje :).

  6. Wiktor napisał(a):

    @adriahu

    Perl. Każdy język ma parę takich "tajemnic" :).

    Python. Jasne, że nie trzeba, ale wtedy w Javie bym zapisał to tak:

    JAVA:
    1. for(Integer x : lista) System.out.println(x);

    ;)

  7. adriahu napisał(a):

    Faktycznie o gustach nie powinno się dyskutować, ale gdy myślę o podejściu funkcyjnym (funkcjonalnym) :D od razu przypomina mi się świetny dialekt lispa o nazwie scheme. Czy to nie w tym języku "scałkowano pierwszy raz ruch układu słonecznego"? :P

  8. Poznajemy nowe języki programowania: Scala, czyli jak wypisać elementy z listy // w pigułce net napisał(a):

    [...] Poznajemy nowe języki programowania: Scala, czyli jak wypisać elementy z listyblog.mocna-kawa.com/2007/12/12/poznajemy-nowe-jezyki-scala-c... dodane przez wwiktorr od paru sekund [...]

  9. adriahu napisał(a):

    Już jest 20:33 :P hmmm

  10. Koziołek napisał(a):

    Rzeczywiście ładne. A teraz prosimy o jakieś bardziej praktyczne zastosowanie. Wypisać listę (10 elementów) można i w whitespace:
    http://compsoc.dur.ac.uk/whitespace/count.ws
    (jak nie widać nacisnąć ctrl+a)
    Na razie widzę ładne rozwiązanie problemów osób, które nie lubią perla/basha i potrzebują śmigać w konsoli, a na pisanie w "zwykłej" javie nie mają czasu. Dobra alternatywa dla Rubiego. Co jeszcze? Silna kontrola typów, plus, statyczne typowanie minus, ale mały.

  11. Wiktor napisał(a):

    @Koziołek

    Whitespace jest świetny :). Dodajmy jeszcze brainfucka i będziemy w siódmym niebie :D

  12. lopex napisał(a):

    Największe możliwości fajnego zapisu w scali dają call by name i implicits, dzięki nim można bez problemu pisać potem:

    1.foo

    czy

    myif (cond) { foo } myelse { bar }

  13. Pitorek napisał(a):

    A ja tam wolę Coca Colę - parafrazując "wielkiego poĘtę" :-) tzn. nie chciałbym kiedyś przeglądać kodu o tak srkóconej postaci, bo - jeśli się dobrze nie znasz - to ciężko coś takiego przeczytać, do czego się odnosi i wogóle... Kurczę, a może jestem zbyt konserwatywny? ;)

    Pozdrawiam

  14. jau napisał(a):

    pitorek: to daj poczytac kod javy osobie "swieckiej" ;). Wiadomo, ze zeby czytac kod trzeba znac (chocby troche) jezyk programowania.

    Z innej beczki:
    Siedze w jezykach kilka lat, ruby, python, java, c# itd. Niedawno tez odkrylem scale i jak na razie wglebiam sie .. cos czuje, ze znalazlem swoj nowy ulubiony jezyk :).

  15. Wiktor napisał(a):

    @jau

    Nieprawdaż? Jak ja się wgłębiam w Scalę to mam takie miłe uczucie, że to jest to :). Tak samo, jak było z Javą (myślałem: ale ona fajne ;)).

  16. jau napisał(a):

    JAVA: for (int x : Arrays.asList(1, 2, 4) System.out.println(x);

  17. jau napisał(a):

    poza tym scala pozwala stworzyc nowy typ danych - stos, gdzie dodawanie elementow bedzie wygladac tak

    lista :-) element

    a usuwanie

    lista :-P element

    Pokazcie mi drugi jezyk, ktory tak potrafi :-D

  18. Roman napisał(a):

    Z tego co rozumiem (i co chyba jest napisane na Wikipedii) to currying nie polega na ominięciu typu w deklaracji, tylko stworzeniu nowej funkcji, ktora przyjmuje mniej argumentow...

  19. MySZ napisał(a):

    Perl:
    print join ("\n", @lista);

    lub używając 'zmiennej magicznej':
    $, = "\n";
    print @lista;

    Python:
    print "\n".join (lista)

  20. Mardok napisał(a):

    A mi sie marzy LISP z dobrymi bibliotekami, a tak jestem zdany na rubiego. Ech co by nie mowic o javie to biblioteki ma swietne.

  21. Tomek napisał(a):

    Scala ma bardzo fajną własność, którą nigdzie wcześniej nie spotkałem. Mianowicie w Scali technicznie nie ma operatorów. Wyrażenie 1 + 2 jest tożsame z wywołaniem metody 1.+(2)

    Podstawa to podstawy. O ile pamiętam w takim języku jak Smalltalk operatory były tak naprawdę metodami, więc nie jest to nic nowego.

    Kolejne języki nie wprowadzają tak naprawdę nic nowego poza kompilacjami istniejących już i stosowanych paradygmatów programowania w różnych proporcjach.

    Warto byłoby się zastanowić raczej nad pytaniem "Dlaczego scala może ułatwić tworzenie oprogramowania ? Dlaczego miałaby się dobrze przyjąć?" Moim zdaniem heterogeniczne środowiska (powiedzmy Java + Scala) znakomicie podnoszą kłopotliwość tworzenia (złożonych) systemów informatycznych, zwłaszcza w kontekście zespołowej pracy i późniejszego utrzymania.

  22. Wiktor napisał(a):

    @Tomek

    Smalltalk to było dopiero szaleństwo. Metody np:

    labirynt go: there :with light :and

    Piękne :).

    Muszę się zgodzić, że nieheterogeniczne środowiska są trudniejsze w utrzymaniu. Jest także to kwestia ludzi, którzy żeby poznać działanie aplikacji muszą znać kilka języków.

    Mnie bardzo podoba się idea (nie weryfikowana jeszcze w praktyce) polyglot programming, gdzie używa się odpowiednich języków/technologii do pracy, np. Ruby on Rails do frontendu, Java jako backend. Czas wszystko zweryfikuje. Ja wierzę w tą ideę.

  23. Tomek napisał(a):

    Ja w tę ideę wierzyłem (i nadal trochę wierzę).

    Ideę zweryfikowałem na 2 sporych projektach ( 4 m-ce x 4 os), miksując JEE + Adobe Flex 2. Generalnie ujdzie, ale:

    w krótkim czasie programista nie jest w stanie być dobry w wiecej niż 1 technologii, mam na myśli bycie rzeczywiście świadomym języka, jego charakterystyki, dobryk praktyk etc.
    konieczność przełączania się z języka do języka wybija z rytmu pracy
    mimo prób rozdzielenia warstw oprogramowania niestety niektóre rzeczy musisz powielać
    integracja bywa trudna, czasochłonna i nie działa zbyt wydajnie
    proces budowania lub CI jest zdecydowanie trudniejszy

  24. br napisał(a):

    To chyba nie jest currying. Tylko po prostu funkcja println jest takiego typu jakiego jest parametr for'a.

    Currying to taki bajer że napisa f x y => x + y montuje funkcję typu int => (int => int). Zamiast int*int => int jak w pascalu, po bożemu.

  25. Wiktor napisał(a):

    @br

    Tak, z rozpędu napisałem, że to currying... co nie zmienia postaci rzeczy, ze currying w Scali występuje ;).

    Wyjaśnię przykład br do końca:
    f x y => x + y (funkcja bierze 2 argumenty i zwraca ich sume)

    succ = f 1 (ukonkretnienie pierwszego argumentu, typ succ to (int => int)

    write succ 4 (wypisze 5)

  26. jau napisał(a):

    @Mardok
    http://clojure.org/

Zostaw komentarz

Możesz używać znaczników do formatowania kodu takich jak: <b>...</b>, <code>...</code> lub dla konkretnych języków programowania: [java]...[/java], [ruby]...[/ruby] itd.


Wiktor Gworek Nazywam się Wiktor Gworek i jestem gospodarzem tego bloga.
Przeczytaj więcej o mnie »