2017-11-13 60 views
7

mój program ma ten wiersz:Podjęcie non-asocjacyjną funkcję, aby zmniejszyć

Function<String, Integer> f = (String s) -> s.chars().reduce(0, (a, b) -> 2 * a + b); 

Funkcja były przekazywane do zmniejszenia nie jest łączne. Dokumentacja Reduce mówi, że funkcja przekazywana musi być skojarzona.

Jak mogę przepisać to jako wyrażenie, które nie powoduje zerwania umowy?

+3

Nie możesz. Po prostu użyj starej pętli goold. –

+0

Czy mówisz o funkcji 'f' lub' (a, b) -> 2 * a + b'? jeśli jest to 'f', myślę, że można go bezpiecznie używać w dowolnym strumieniu równoległym; jeśli jest to '(a, b) -> 2', to też będzie w porządku, ponieważ nie widzę żadnego powodu:' s.chars(). parallel(). reduce (0, (a, b) - > 2 * a + b) ' –

Odpowiedz

4

W obecnym realizacji i IFF nie zamierzasz używać równoległego - jesteś bezpieczny, co masz teraz. Oczywiście, jeśli zgadzasz się z tymi zrzeczeniami.

Albo można oczywiście stworzyć funkcję z pętli for:

Function<String, Integer> f = s -> { 
     int first = s.charAt(0) * 2 + s.charAt(1); 
     int total = first; 

     for (int x = 1; x < s.length() - 1; x++) { 
      total = total * 2 + s.charAt(x + 1); 
     } 

     return total; 

    }; 
+0

To naprawdę" przeszkadza mi "w ramach obecnej implementacji. Programiści Javy zastrzegli sobie w pespekcie prawo do zmiany sposobu działania. –

+0

Pacifically? Masz na myśli w przeciwieństwie do walki o to? –

+0

@NickODell tak, to jest gorzka część. Nadal możesz utworzyć "Funkcję" z prostej pętli for (edytuj post) – Eugene

3

można przekonwertować tej funkcji do asocjacyjną funkcji, jak wyjaśniono w this answer na przykładzie List.hashCode(). Różnica dotyczy tylko współczynnika (2 vs. 31) i wartości początkowej (1 vs. 0).

Może być dostosowany do zadania, które jest szczególnie proste, gdy masz losowy wejście dostępu niczym String:

Function<String, Integer> f = 
    s -> IntStream.range(0, s.length()).map(i -> s.charAt(i)<<(s.length()-i-1)).sum(); 

Byłoby to nawet równolegle, ale jest to mało prawdopodobne, że kiedykolwiek spotkać takie humongous ciągi, które równoległa ocena przynosi korzyści. Więc co pozostaje, jest to, że większość ludzi może uznać to rozwiązanie mniej czytelny niż prosty for pętli ...


Należy zauważyć, że powyższe rozwiązanie wykazuje różne zachowanie przepełnienia, czyli jeśli String ma więcej niż 32 char s, z należytym do korzystania z operatora zmiany zamiast pomnażania z dwoma.
Poprawkę dotyczącą tego problemu sprawia, że ​​rozwiązanie jeszcze bardziej wydajne:

Function<String, Integer> f = s -> 
    IntStream.range(Math.max(0, s.length()-32), s.length()) 
      .map(i -> s.charAt(i)<<(s.length()-i-1)).sum(); 

Jeśli łańcuch posiada więcej niż 32 char s, to tylko przetwarza ostatnich 32 char S, który jest już wystarczające, aby obliczyć ten sam rezultat jak twoja pierwotna funkcja.

+0

Faktycznie piszę funkcję haszującą - próbowałem dostarczyć prosty, samowystarczalny przykład. Dziękuję za link do drugiego pytania - pomyślałem, że szukam wystarczająco dobrze duplikatów, ale najwyraźniej nie. –