2017-06-13 12 views
6

Powiedzmy mam ten kod KONIECZNIE:Jak zastosować listę funkcji do wartości w Java 8?

List<Function<T, T>> functions = ... 
    T value = ... 
    for (Function<T, T> function : functions) { 
     value = function.apply(value); 
    } 

Jak mogę napisać to w stylu funkcjonalnym (jak to, co robi krotnie w Scala)?

+1

możesz wpisać wartość i '# forEach' listę' funkcji', ale to tak naprawdę nie zyskuje niczego (i nie jest tak funkcjonalne). Ogólnie kod wygląda dobrze, co to jest, java jest imperatywny język. – Rogue

+1

[Praca z ArrayList funkcji w Java-8] (https://stackoverflow.com/questions/30274124/working-with-arraylist-of-functions-in-java-8) To pomoże –

+4

Wygląda blisko to [this one] (https://stackoverflow.com/q/32338553/2711488) ... – Holger

Odpowiedz

10

To właśnie został poproszony kilka godzin temu na Consumer ... Można je zmniejszyć do jednej funkcji i zastosować że:

@SafeVarargs 
private static <T> Function<T, T> combineF(Function<T, T>... funcs) { 
    return Arrays.stream(funcs).reduce(Function.identity(), Function::andThen); 
} 
+4

Podczas semantycznego tego samego, '.reduce (Function :: and Then) .orElse (Function.identity())' może wytworzyć nieco bardziej wydajną funkcję. – Holger

+0

@Holder Czy są jakieś problemy z używaniem '.reduce (Function :: and Then) .orElseGet (Function :: identity)', zamiast tego? – srborlongan

+1

@rborlongan, który został omówiony wcześniej, zobacz: https://stackoverflow.com/questions/44261253/when-i-need-to-use-optional-orelseget-over-optional-orelse/44261755#44261755 – Eugene

1

Oto wariant odpowiedzi Eugeniusza, tylko dla zabawy :

public static <T> Function<T, T> combine(List<Function<T, T>> functions) { 
    return new Object() { 
     Function<List<Function<T, T>>, Function<T, T>> combiner = list -> 
      list.size() == 1 ? list.get(0) : 
      list.get(0).andThen(this.combiner.apply(list.subList(1, list.size()))); 
    }.combiner.apply(functions); 
} 

To tworzy anonimową klasę wewnętrzną z atrybutem, który jest rekurencyjną lambda. Ten atrybut ma nazwę combiner i jest to funkcja wyższego rzędu, która pobiera listę funkcji jako argument i zwraca funkcję jako wynik. Ta funkcja wyższego rzędu zwraca pierwszą funkcję listy, jeśli lista zawiera tylko jeden element lub stosuje andThen do pierwszej funkcji listy, z funkcją, która wynika z rekursywnego wywołania funkcji wyższego rzędu z podlistą funkcje zaczynające się od drugiego elementu.

Anonimowa klasa wewnętrzna jest potrzebna, ponieważ rekurencyjne lambdy można zdefiniować tylko jako atrybuty klasy.

Nie trzeba dodawać, że jest to o wiele bardziej skomplikowane niż przesyłanie strumieniowe listy i zmniejszanie za pomocą operatora binarnego Function::andThen. Poza tym rekurencyjne lambdy nie są za darmo: używają stosu do wywołań rekursywnych.

+0

Oprócz tworzenia pól dla lambdas do powtarzania się, inną (mniej intuicyjną) metodą w Javie 8 jest użycie kombinacji [Y combinator] (http://rosettacode.org/wiki/Y_combinator#Java). – srborlongan

+1

@srborlongan Or [trampoliny] (http://raganwald.com/2013/03/28/trampolines-in-javascript.html) ... –

Powiązane problemy