środa, 7 listopada 2012

Iterables i reduce

Ostatnio uczestnicząc w kursie "JavaScript dla Javowców" doznałem pewnego olśnienia. Otóż JavaScript, którego nadal nie lubię (choć już trochę bardziej szanuje), ma kilka dość dobrych pomysłów, które warto przenieść do świata Javy.

Konkretnie mówię tu o funkcyjnym podejściu do operacji na tablicach/hashmapach w JS. Ktoś na szczęście już dawno wpadł na ten pomysł i mamy bibliotekę Google Guava, która jest dość dobrze poważana w świecie Javy.

Klika analogii JS => Java:

array.map(...) => Iterables.transform(...)
array.filter(...) => Iterables.filter(...)

W Guavie brakuje mi tylko 1 funkcji do pełni szczęścia, tj. odpowiednika funkcji array.reduce(...). Teoretycznie jest w planach jej prowadzenie, ale póki co jej nie ma, więc można szybko stworzyć własną implementacje.

import com.google.common.base.Preconditions;

public class Iterables {

 /**
  * redukuje przekazany {@link Iterable} do obiektu A
  * @param iterable co redukujemy
  * @param initialValue wartosc inicjalna, najlepiej neutralna dla danej                 operacji 0 dla dodawania, 1 dla mnożenia, itd 
  * @param reducer funkcja redukująca
  * @return wartosc zredukowana
  */
 public static <A, B> A reduce(Iterable<B> iterable, A initialValue, Reducer<A, B> reducer) {

  Preconditions.checkNotNull(initialValue);
  A result = initialValue;
  for (B b : iterable) {
   result = reducer.reduce(result, b);
  }
  return result;
 }
}

/**
 * funkcja redukująca
 * @param <A> klasa obiektu docelowego
 * @param <B> klasa obiektu wejsciowego
 */
public interface Reducer<A, B> {

 /**
  * redukuje parametr wejsciowy do docelowego
  *
  * @param to aktualny obiekt
  * @param from obiekt wejsciowy
  * @return zredukowany obiekt docelowy
  */
 A reduce(A current, B from);
}

I samo wykorzystanie:
List<Integer> someNumbers = Lists.newArrayList(1, 2, 3);
Reducer<Integer, Integer> sum = new Reducer<Integer, Integer>() {

 @Override
 public Integer reduce(Integer current, Integer from) {
  return current + from;
 }
};
Integer sumValue = Iterables.reduce(someNumbers, 0, sum);
assertThat(sumValue).isEqualTo(6);

Niby nic wielce odkrywczego, ale taka jest cała Guava, która proste rzeczy zmienia w jeszcze prostsze lub też "standaryzuje" pewne operacje.

Jeśli komuś takie (funkcyjne) podejście nie bardzo pasuje, to radziłbym się zacząć przyzwyczajać. W kolejnej wersji Javy prawdopodobnie wejdą wyrażenia lambda, więc funkcyjność wkroczy do "czystej" Javy pełną parą.

7 komentarzy:

  1. Co ciekawe, dość dobra znajomość JavaScriptu (syndrom Windows: prawie każdy używa, mało kto lubi) bardzo przydała mi się przy uczeniu Scali. Takie łagodne wprowadzenie do programowania funkcyjnego.

    OdpowiedzUsuń
  2. U mnie odwrotnie. Nauka Scali pomogła mi lepiej zrozumieć JavaScript.
    Btw. byłeś we Wrocławiu na tym szkoleniu, czy gdzieś indziej?

    OdpowiedzUsuń
  3. Tak we Wrocku, jedno z lepszych szkoleń, w których miałem okazję uczestniczyć.

    OdpowiedzUsuń
  4. Kurde, też tam byłem :) Też uważam to za jedno z lepszych szkoleń (jak nie najlepsze) w jakich brałem udział. Muszę jeszcze dokończyć zadania z kursu Scali i coś pewnie o tym szkoleniu więcej napiszę...

    OdpowiedzUsuń
    Odpowiedzi
    1. Chyba Cię kojarzę, miałeś podstawkę pod laptopa? Niestety nie miałem do tej pory żadnej styczności ze Scalą, mam nadzieje, że kiedyś będę miał okazję...

      Usuń
    2. Zgadza się :D Dodatkowo miałem/mam kolorowe naklejki na klawiaturze i siedziałem blisko prowadzącego. Zagadaj następnym razem, jak gdzieś mnie zobaczysz.
      Pozdro.

      Usuń