你這裏的問題是Java流沒有真正list fold operation。通過真正的摺疊操作,將功能編寫爲左摺疊並不困難。例如,在Haskell:
import Data.List (foldl')
-- A custom struct to represent the state of the fold.
data MooreState a = MooreState { candidate :: a, count :: !Int }
findCandidate :: Eq a => [a] -> Maybe a
findCandidate (first:rest) = Just result
where
Moore result _ = foldl' combiner (MooreState first 1) rest
combiner :: Eq a => MooreState a -> a -> MooreState a
combiner (Moore candidate count) current
| count == 0 = MooreState current 1
| candidate == current = MooreState candidate (count + 1)
| otherwise = MooreState candidate (count - 1)
-- The empty list has no candidates.
findCandidate [] = Nothing
Java的reduce()
方法是它有一個真正的左摺疊最接近的,但如果你看看the Javadoc for the reduce()
method that you're using,你會注意到它說:
- 它「不限制順序執行」;
- 它需要積累功能爲聯想。
這個文檔很難解釋,但我讀到它的方式是這樣的。儘管它可以處理要素按順序:
- 如果您累積功能是聯想然後可以保證,如果你處理的流依次產生相同的結果;
- 如果您的累積函數不是關聯的,那麼它可能會產生與簡單順序過程不同的結果。
爲什麼這很重要?那麼,首先,你正在改變一個外部變量的事實意味着你使用count
的方式被破壞了。爲了您所知,可以在元素#5之前處理元素#7。
更多不知不覺,在Haskell的版本combine
操作上述結合不同類型(Moore a
和a
)的輸入,但你使用Java reduce
方法基於一個BinaryOperator<T>
,它結合了兩個相同類型的對象。有another overload of reduce
that uses a BiFunction<U, T, U>
,但這需要您提供BinaryOperator<U> combiner
及其U identity
以及。這是因爲Java的reduce
方法被設計爲可以:
- 將輸入流拆分爲連續的塊;
- 並行處理多個塊;
- 將相鄰塊的結果按順序組合起來。
因此,關聯性和身份要求可以保證這種並行處理產生與順序執行相同的結果。但是這意味着雖然算法有一個直接的功能實現,但是用Java的Stream
類型編寫它卻沒有直接的方法。 (這有一種非直接的方式,但是這會陷入一些巫術,這將會(一)真的很複雜,(b)在Java中非常緩慢。)
所以我個人只會接受Java不是很棒函數式編程語言,單獨留下足夠的好處並按原樣使用命令式的版本。但如果出於某種奇怪的原因,我真的堅持在功能上做,我會去像jOOλ這樣的圖書館,它提供true left folds in Java。然後,你可以做同樣的哈斯克爾溶液(未經測試的僞代碼):
import org.jooq.lambda.Seq;
import org.jooq.lambda.tuple.Tuple2;
class MooreState<A> {
private final A candidate;
private final int count;
// ...constructors and getters...
}
public static Optional<A> findCandidate(Seq<A> elements) {
Tuple2<Optional<A>, Seq<A>> split = elements.splitAtHead();
return split.v1().map(first -> {
Seq<A> rest = split.v2();
return rest.foldLeft(
new MooreState<>(first, 1),
(state, current) -> {
if (state.getCount() == 0) {
return new MooreState<>(current, 1);
} else if (state.getCandidate().equals(current) {
return new MooreState<>(state.getCandidate(),
state.getCount() + 1);
} else {
return new MooreState<>(state.getCandidate(),
state.getCount() - 1);
}
}
);
});
}
...這可能是可怕緩慢。
您可能想使用可變對象而不是原語,但這並不是使用lambda表達式的目標 –