Mariusz Prowaźnik

o programowaniu w Javie, Scali i Clojure.


Co lubię w Clojure, cz. 1

Zachęcony zwiększającą się popularnością języka Clojure, postanowiłem zapoznać się z jego możliwościami. Przy okazji, opublikuję serię postów, w których pokażę co ciekawego oferuje ten język.

Clojure działa na JVM, jest dialektem Lisp'a i językiem funkcyjnym. Różni się od Javy o wiele bardziej niż Java od C++. O technicznych cechach języka można poczytać na [clojure.pl], polecam również zajrzeć na [hammerprinciple.com], gdzie zobrazowane są opinie internautów na temat Clojure w porównaniu z Javą. Rzuca się w oczy to, że kod w Clojure uważany jest za bardziej zwięzły - ja mam podobne odczucia. Przejdźmy teraz do praktyki.

Szybki start

By zacząć pisać w Clojure, wystarczy ściągnąć blibliotekę z [clojure.org] i wykonać:

java -jar clojure-1.4.0.jar

To uruchomi REPL, w którym można pisać kod i zostanie on od razu wykonany. Inna możliwość to wypróbować REPL online na stronie [tryclj.com].

Funkcje jako argumenty innych funkcji

Zatem, co jest ciekawego w Clojure, czego nie ma w Javie? Np to, że funkcje mogą być przekazywane jako argumenty do innej funkcji. W rezultacie, coś co w Javie uzyskujemy poprzez wzorzec strategii, w Clojure jest czymś zupełnie naturalnym i prostym. Prosty przykład:

public class StrategySnippet {
 public static void main(String[] args) {
  new StrategySnippet().go();
 }

 public void go() {
  useStrategy(new AddStrategy(), 2, 3);
  useStrategy(new MultiplyStrategy(), 2, 3);
 }

 public void useStrategy(Strategy s, int a, int b) {
  System.out.println(s.execute(a, b));
 }

 interface Strategy {
  int execute(int a, int b);
 }

 class AddStrategy implements Strategy {

  @Override
  public int execute(int a, int b) {
   return a + b;
  }

 }

 class MultiplyStrategy implements Strategy {

  @Override
  public int execute(int a, int b) {
   return a * b;
  }

 }
}

W Clojure analogiczny kod to:

(defn use-strategy[strategy a b]
  (println (strategy a b)))

(defn add-strategy [a b]
  (+ a b))

(defn multiply-strategy [a b]
  (* a b))

(defn go []
  (use-strategy add-strategy 2 3)
  (use-strategy multiply-strategy 2 3)
 )
(go)

W dodatku + i * same w sobie są funkcjami, zatem taki sam efekt uzyskamy poprzez:

(defn go []
  (use-strategy + 2 3)
  (use-strategy * 2 3)
 )
(go)
W kolejnym przykładzie kod sortujący listę String'ów po ich długości. W Javie użyjemy metodę Collection.sort i anonimową klasę implementującą Comparator:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class AnonymousClassSnippet {

 public final static List<String> SOME_LIST = Arrays.asList(new String[] { 
"Jan", "Zbych", "Boromir", "Urlich","Rupert","Jaro" });

 public void sortAndPrint(List<String> list) {
  ArrayList<String> l = new ArrayList<String>(list);
  Collections.sort(l, new Comparator<String>() {

   @Override
   public int compare(String arg0, String arg1) {
    if (arg0.length() > arg1.length())
     return 1;
    else if (arg0.length() < arg1.length())
     return -1;
    else
     return 0;
   }

  });
  System.out.println(l);
 }
 public static void main(String []args){
  new AnonymousClassSnippet().sortAndPrint(SOME_LIST);
 }
}
W Clojure można zapisać to tak:
(def some-vector ["Jan" "Zbych" "Boromir" "Urlich" "Rupert" "Jaro"])
(defn sort-and-print [vec]
  (println 
    (sort #(compare (.length %1) (.length %2)) vec)
  ))
(sort-and-print some-vector)
Użyta tu została funkcja anonimowa - zapisujemy ją:
#( ciało_funkcji)

przy czym %1 i %2 to parametry. Prawda, że krócej?

Brak komentarzy :

Prześlij komentarz