2015-11-13 84 views
7

我們習慣於在Haskell中使用foldr(例如,使用Java語法)a List<T>,並且返回所需的任何類型(<T>,List<T>等)。Haskell在Java中的foldr相當於

例如在Haskell,該函數採用一個List<Integer>和返回另一個List<Integer>和用途的儲液器List<Integer>(僅是一個示例,該函數的objetive無所謂):

evens :: [Integer] -> [Integer] 
evens = foldr (\ x acc -> if mod x 2 == 0 then x : acc else acc) [] 

現在的Java 8是地地道道的具有功能性風格特點,我們要寫功能(不僅是免費複製,相當於一個List<T>的),帶着一種foldr因爲我們這裏使用:

public static Double entropy (List<Double> probs){ 
    return -probs.stream().reduce(0.0, (acc, p) -> acc + p * Math.log(p, 2)); 
} 

使用reduce的問題是,當我們採取List<T>時,我們只能返回<T>,我們希望返回不同的類型或集合。

有沒有在Java中8做foldr的方法嗎?

+2

您能否提供一個示例輸入/輸出來更好地理解需求? – Tunaki

+1

如果我閱讀Haskell的權利(我從來沒有做過Haskell,但我可以嘗試),似乎你只篩選甚至是元素並將它們收集到列表中。這將是:'probs.stream()。filter(i - > i%2 == 0).collect(toList())'。 – Tunaki

+0

@Tunaki問題是我們需要一種類似我們提供的示例中的累加器。 – Nico

回答

4

我對Java 8並不擅長,但我認爲通向解決方案的道路在於Haskell如何根據Foldablefold提供默認foldr

foldr :: (a -> b -> b) -> b -> t a -> b 
foldr f z t = appEndo (foldMap (Endo . f) t) z 

foldMap :: (Foldable t, Functor t, Monoid m) => (a -> m) -> t a -> m 
foldMap f = fold . fmap f 

fold :: (Foldable t, Monoid m) => t m -> m 

其中Endo a只是構成下的同胚的幺半羣。

因此,一個解決方案可以是使用Function<B,B>作爲Endo b, 類似物,並通過Function::composeT reduce(T identity, BinaryOperator<T> accumulator)作爲累加器。

// given 
// BiFunction<A,B,B> step 
// B start 
// Stream<A> stream 
stream           // Stream<A> 
    .map((a) -> (b) -> step.apply(a,b))    // Stream<Function<B,B>> 
    .reduce(Function.identity(), Function::compose) // Function<B,B> 
    .apply(start)         // B 

但我目前沒有Java 8編譯器,所以我無法測試它。

如果我們想要做foldl,解決方案將是類似的,除了我們會使用Function::andThen

// given 
// BiFunction<B,A,B> step 
// B start 
// Stream<A> stream 
stream           // Stream<A> 
    .map((a) -> (b) -> step.apply(b,a))    // Stream<Function<B,B>> 
    .reduce(Function.identity(), Function::andThen) // Function<B,B> 
    .apply(start)         // B 
4

This method似乎是最接近的對手:

interface Stream<T> { 
    // ... 

    <U> U reduce(U identity, 
       BiFunction<U,? super T,U> accumulator, 
       BinaryOperator<U> combiner) 

    // ... 
} 

它更像Haskell的foldMapfoldl',但是,並沒有摺疊的右路組合。對於像Java這樣熱衷評估的語言來說,這可能是更好的選擇 - 通過熱切評估的左側摺疊始終在恆定空間中運行,而右側摺疊以急速評估在線性空間中運行 - 右側摺疊很長的列表可能會導致堆棧崩潰。

在Haskell,因爲它有懶的評價,正確與不嚴格的列表中的脊椎也經常線性空間中運行的功能褶皺。這在下面的實施find,不具有評估折過去,它會返回元素利用,例如:

find :: (a -> Bool) -> [a] -> Maybe a 
find p = foldr go Nothing 
    where go a next 
    | p a = Just a  -- discards `next` without evaluating it 
    | otherwise = next 

但在類似方案,您可避免急於語言來避免這樣的實現使用這樣的功能褶皺,因爲你希望他們提前退出:

(define (find pred? as) 
    (and (not-null? as) 
     (let ((head (car as))) 
     (if (pred? head) 
      head 
      (find pred? (cdr as)))))) 

的另一件事是,Java流也意在支持它的設計,這樣的元素可以被訪問亂序並行,所以操作(和因此Javadoc堅持聯想操作)。