| @@ -1,14 +1,16 @@ | |||||
| use crate::{DefaultExecutor, Executor}; | use crate::{DefaultExecutor, Executor}; | ||||
| pub trait Driver<'a, D>: Sized | |||||
| pub trait Driver<'a, T1, T2 = (), T3 = ()>: Sized | |||||
| where | where | ||||
| D: Send + 'a, | |||||
| T1: Send + 'a, | |||||
| T2: Send + 'a, | |||||
| T3: Send + 'a, | |||||
| { | { | ||||
| fn exec_with<E>(self, executor: E) -> E::Result | fn exec_with<E>(self, executor: E) -> E::Result | ||||
| where | where | ||||
| E: Executor<'a, D>; | |||||
| E: Executor<'a, T1, T2, T3>; | |||||
| fn exec(self) -> <DefaultExecutor as Executor<'a, D>>::Result { | |||||
| fn exec(self) -> <DefaultExecutor as Executor<'a, T1, T2, T3>>::Result { | |||||
| self.exec_with(DefaultExecutor::default()) | self.exec_with(DefaultExecutor::default()) | ||||
| } | } | ||||
| } | } | ||||
| @@ -2,30 +2,42 @@ use super::{ | |||||
| Consumer, IndexedProducer, IndexedProducerCallback, Producer, ProducerCallback, Reducer, | Consumer, IndexedProducer, IndexedProducerCallback, Producer, ProducerCallback, Reducer, | ||||
| }; | }; | ||||
| pub trait Executor<'a, D>: Sized | |||||
| pub trait Executor<'a, T1, T2 = (), T3 = ()>: Sized | |||||
| where | where | ||||
| D: Send + 'a, | |||||
| T1: Send + 'a, | |||||
| T2: Send + 'a, | |||||
| T3: Send + 'a, | |||||
| { | { | ||||
| type Result: Send; | type Result: Send; | ||||
| type Inner: Executor<'a, T2, T3, ()>; | |||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: Producer + 'a, | P: Producer + 'a, | ||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send + 'a; | |||||
| C: Consumer<P::Item, Result = T1, Reducer = R> + 'a, | |||||
| R: Reducer<T1> + Send + 'a; | |||||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: IndexedProducer + 'a, | P: IndexedProducer + 'a, | ||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send + 'a; | |||||
| C: Consumer<P::Item, Result = T1, Reducer = R> + 'a, | |||||
| R: Reducer<T1> + Send + 'a; | |||||
| fn split(self) -> (Self, Self); | fn split(self) -> (Self, Self); | ||||
| fn join<R>(left: Self::Result, right: Self::Result, reducer: R) -> Self::Result | fn join<R>(left: Self::Result, right: Self::Result, reducer: R) -> Self::Result | ||||
| where | where | ||||
| R: Reducer<D> + Send + 'a, | |||||
| D: 'a; | |||||
| R: Reducer<T1> + Send + 'a, | |||||
| T1: 'a; | |||||
| fn into_inner(self) -> Self::Inner; | |||||
| fn map<O>( | |||||
| inner: <Self::Inner as Executor<'a, T2, T3, ()>>::Result, | |||||
| operation: O, | |||||
| ) -> Self::Result | |||||
| where | |||||
| O: Fn(T2) -> T1 + Send + 'a; | |||||
| } | } | ||||
| pub struct ExecutorCallback<E, C> { | pub struct ExecutorCallback<E, C> { | ||||
| @@ -39,12 +51,12 @@ impl<E, C> ExecutorCallback<E, C> { | |||||
| } | } | ||||
| } | } | ||||
| impl<'a, E, D, C, I, R> ProducerCallback<'a, I> for ExecutorCallback<E, C> | |||||
| impl<'a, E, T1, C, I, R> ProducerCallback<'a, I> for ExecutorCallback<E, C> | |||||
| where | where | ||||
| E: Executor<'a, D>, | |||||
| D: Send + 'a, | |||||
| C: Consumer<I, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send + 'a, | |||||
| E: Executor<'a, T1>, | |||||
| T1: Send + 'a, | |||||
| C: Consumer<I, Result = T1, Reducer = R> + 'a, | |||||
| R: Reducer<T1> + Send + 'a, | |||||
| { | { | ||||
| type Output = E::Result; | type Output = E::Result; | ||||
| @@ -56,12 +68,12 @@ where | |||||
| } | } | ||||
| } | } | ||||
| impl<'a, E, D, C, I, R> IndexedProducerCallback<'a, I> for ExecutorCallback<E, C> | |||||
| impl<'a, E, T1, C, I, R> IndexedProducerCallback<'a, I> for ExecutorCallback<E, C> | |||||
| where | where | ||||
| E: Executor<'a, D>, | |||||
| D: Send + 'a, | |||||
| C: Consumer<I, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send + 'a, | |||||
| E: Executor<'a, T1>, | |||||
| T1: Send + 'a, | |||||
| C: Consumer<I, Result = T1, Reducer = R> + 'a, | |||||
| R: Reducer<T1> + Send + 'a, | |||||
| { | { | ||||
| type Output = E::Result; | type Output = E::Result; | ||||
| @@ -22,8 +22,8 @@ use crate::{ | |||||
| map::Map, | map::Map, | ||||
| map_init::MapInit, | map_init::MapInit, | ||||
| map_with::MapWith, | map_with::MapWith, | ||||
| max::{Max, MaxBy}, | |||||
| min::{Min, MinBy}, | |||||
| max::{Max, MaxBy, MaxByKey}, | |||||
| min::{Min, MinBy, MinByKey}, | |||||
| product::Product, | product::Product, | ||||
| reduce::{Reduce, ReduceWith}, | reduce::{Reduce, ReduceWith}, | ||||
| sum::Sum, | sum::Sum, | ||||
| @@ -1142,6 +1142,31 @@ pub trait ParallelIterator<'a>: Sized + Send { | |||||
| MinBy::new(self, operation) | MinBy::new(self, operation) | ||||
| } | } | ||||
| /// Computes the item that yields the minimum value for the given | |||||
| /// function. If the iterator is empty, `None` is returned; | |||||
| /// otherwise, `Some(item)` is returned. | |||||
| /// | |||||
| /// Note that the order in which the items will be reduced is not | |||||
| /// specified, so if the `Ord` impl is not truly associative, then | |||||
| /// the results are not deterministic. | |||||
| /// | |||||
| /// # Examples | |||||
| /// | |||||
| /// ``` | |||||
| /// use rayon::prelude::*; | |||||
| /// | |||||
| /// let a = [-3_i32, 34, 2, 5, -10, -3, -23]; | |||||
| /// | |||||
| /// assert_eq!(a.par_iter().min_by_key(|x| x.abs()), Some(&2)); | |||||
| /// ``` | |||||
| fn min_by_key<O, K>(self, operation: O) -> MinByKey<Self, O> | |||||
| where | |||||
| O: Fn(&Self::Item) -> K + Clone + Send + 'a, | |||||
| K: Ord + Send, | |||||
| { | |||||
| MinByKey::new(self, operation) | |||||
| } | |||||
| /// Computes the maximum of all the items in the iterator. If the | /// Computes the maximum of all the items in the iterator. If the | ||||
| /// iterator is empty, `None` is returned; otherwise, `Some(max)` | /// iterator is empty, `None` is returned; otherwise, `Some(max)` | ||||
| /// is returned. | /// is returned. | ||||
| @@ -1196,6 +1221,31 @@ pub trait ParallelIterator<'a>: Sized + Send { | |||||
| MaxBy::new(self, operation) | MaxBy::new(self, operation) | ||||
| } | } | ||||
| /// Computes the item that yields the maximum value for the given | |||||
| /// function. If the iterator is empty, `None` is returned; | |||||
| /// otherwise, `Some(item)` is returned. | |||||
| /// | |||||
| /// Note that the order in which the items will be reduced is not | |||||
| /// specified, so if the `Ord` impl is not truly associative, then | |||||
| /// the results are not deterministic. | |||||
| /// | |||||
| /// # Examples | |||||
| /// | |||||
| /// ``` | |||||
| /// use rayon::prelude::*; | |||||
| /// | |||||
| /// let a = [-3_i32, 34, 2, 5, -10, -3, -23]; | |||||
| /// | |||||
| /// assert_eq!(a.par_iter().max_by_key(|x| x.abs()), Some(&34)); | |||||
| /// ``` | |||||
| fn max_by_key<O, K>(self, operation: O) -> MaxByKey<Self, O> | |||||
| where | |||||
| O: Fn(&Self::Item) -> K + Clone + Send + 'a, | |||||
| K: Ord + Send, | |||||
| { | |||||
| MaxByKey::new(self, operation) | |||||
| } | |||||
| /// Takes two iterators and creates a new iterator over both. | /// Takes two iterators and creates a new iterator over both. | ||||
| /// | /// | ||||
| /// # Examples | /// # Examples | ||||
| @@ -3,17 +3,20 @@ use crate::core::{Consumer, Executor, Folder, IndexedProducer, Producer, Reducer | |||||
| #[derive(Default)] | #[derive(Default)] | ||||
| pub struct Sequential; | pub struct Sequential; | ||||
| impl<'a, D> Executor<'a, D> for Sequential | |||||
| impl<'a, T1, T2, T3> Executor<'a, T1, T2, T3> for Sequential | |||||
| where | where | ||||
| D: Send + 'a, | |||||
| T1: Send + 'a, | |||||
| T2: Send + 'a, | |||||
| T3: Send + 'a, | |||||
| { | { | ||||
| type Result = D; | |||||
| type Result = T1; | |||||
| type Inner = Sequential; | |||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: Producer + 'a, | P: Producer + 'a, | ||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D>, | |||||
| C: Consumer<P::Item, Result = T1, Reducer = R> + 'a, | |||||
| R: Reducer<T1>, | |||||
| { | { | ||||
| if consumer.is_full() { | if consumer.is_full() { | ||||
| consumer.into_folder().complete() | consumer.into_folder().complete() | ||||
| @@ -25,8 +28,8 @@ where | |||||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: IndexedProducer, | P: IndexedProducer, | ||||
| C: Consumer<P::Item, Result = D, Reducer = R>, | |||||
| R: Reducer<D>, | |||||
| C: Consumer<P::Item, Result = T1, Reducer = R>, | |||||
| R: Reducer<T1>, | |||||
| { | { | ||||
| if consumer.is_full() { | if consumer.is_full() { | ||||
| consumer.into_folder().complete() | consumer.into_folder().complete() | ||||
| @@ -39,10 +42,24 @@ where | |||||
| (Self, Self) | (Self, Self) | ||||
| } | } | ||||
| fn join<R>(left: D, right: D, reducer: R) -> Self::Result | |||||
| fn join<R>(left: T1, right: T1, reducer: R) -> Self::Result | |||||
| where | where | ||||
| R: Reducer<D> + Send, | |||||
| R: Reducer<T1> + Send, | |||||
| { | { | ||||
| reducer.reduce(left, right) | reducer.reduce(left, right) | ||||
| } | } | ||||
| fn into_inner(self) -> Self::Inner { | |||||
| self | |||||
| } | |||||
| fn map<O>( | |||||
| inner: <Self::Inner as Executor<'a, T2, T3, ()>>::Result, | |||||
| operation: O, | |||||
| ) -> Self::Result | |||||
| where | |||||
| O: Fn(T2) -> T1, | |||||
| { | |||||
| operation(inner) | |||||
| } | |||||
| } | } | ||||
| @@ -27,17 +27,20 @@ impl Default for Tokio { | |||||
| } | } | ||||
| } | } | ||||
| impl<'a, D> Executor<'a, D> for Tokio | |||||
| impl<'a, T1, T2, T3> Executor<'a, T1, T2, T3> for Tokio | |||||
| where | where | ||||
| D: Send + 'a, | |||||
| T1: Send + 'a, | |||||
| T2: Send + 'a, | |||||
| T3: Send + 'a, | |||||
| { | { | ||||
| type Result = BoxFuture<'a, D>; | |||||
| type Result = BoxFuture<'a, T1>; | |||||
| type Inner = Tokio; | |||||
| fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: Producer + 'a, | P: Producer + 'a, | ||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send + 'a, | |||||
| C: Consumer<P::Item, Result = T1, Reducer = R> + 'a, | |||||
| R: Reducer<T1> + Send + 'a, | |||||
| { | { | ||||
| let splits = producer.splits().unwrap_or(self.splits); | let splits = producer.splits().unwrap_or(self.splits); | ||||
| let splitter = Splitter::new(splits); | let splitter = Splitter::new(splits); | ||||
| @@ -48,8 +51,8 @@ where | |||||
| fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result | ||||
| where | where | ||||
| P: IndexedProducer + 'a, | P: IndexedProducer + 'a, | ||||
| C: Consumer<P::Item, Result = D, Reducer = R> + 'a, | |||||
| R: Reducer<D> + Send + 'a, | |||||
| C: Consumer<P::Item, Result = T1, Reducer = R> + 'a, | |||||
| R: Reducer<T1> + Send + 'a, | |||||
| { | { | ||||
| let splits = producer.splits().unwrap_or(self.splits); | let splits = producer.splits().unwrap_or(self.splits); | ||||
| let splitter = IndexedSplitter::new( | let splitter = IndexedSplitter::new( | ||||
| @@ -75,8 +78,8 @@ where | |||||
| fn join<R>(left: Self::Result, right: Self::Result, reducer: R) -> Self::Result | fn join<R>(left: Self::Result, right: Self::Result, reducer: R) -> Self::Result | ||||
| where | where | ||||
| R: Reducer<D> + Send + 'a, | |||||
| D: 'a, | |||||
| R: Reducer<T1> + Send + 'a, | |||||
| T1: 'a, | |||||
| { | { | ||||
| async move { | async move { | ||||
| let left = left.await; | let left = left.await; | ||||
| @@ -86,6 +89,25 @@ where | |||||
| } | } | ||||
| .boxed() | .boxed() | ||||
| } | } | ||||
| fn into_inner(self) -> Self::Inner { | |||||
| self | |||||
| } | |||||
| fn map<O>( | |||||
| inner: <Self::Inner as Executor<'a, T2, T3, ()>>::Result, | |||||
| operation: O, | |||||
| ) -> Self::Result | |||||
| where | |||||
| O: Fn(T2) -> T1 + Send + 'a, | |||||
| { | |||||
| async move { | |||||
| let value = inner.await; | |||||
| operation(value) | |||||
| } | |||||
| .boxed() | |||||
| } | |||||
| } | } | ||||
| fn exec<'a, P, C>(mut splitter: Splitter, producer: P, consumer: C) -> BoxFuture<'a, C::Result> | fn exec<'a, P, C>(mut splitter: Splitter, producer: P, consumer: C) -> BoxFuture<'a, C::Result> | ||||
| @@ -63,3 +63,45 @@ where | |||||
| .exec_with(executor) | .exec_with(executor) | ||||
| } | } | ||||
| } | } | ||||
| /* MaxByKey */ | |||||
| pub struct MaxByKey<X, O> { | |||||
| iterator: X, | |||||
| operation: O, | |||||
| } | |||||
| impl<X, O> MaxByKey<X, O> { | |||||
| pub fn new(iterator: X, operation: O) -> Self { | |||||
| Self { | |||||
| iterator, | |||||
| operation, | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<'a, X, O, K> Driver<'a, Option<X::Item>, Option<(K, X::Item)>> for MaxByKey<X, O> | |||||
| where | |||||
| X: ParallelIterator<'a>, | |||||
| O: Fn(&X::Item) -> K + Clone + Send + Sync + 'a, | |||||
| K: Send + Ord + 'a, | |||||
| { | |||||
| fn exec_with<E>(self, executor: E) -> E::Result | |||||
| where | |||||
| E: Executor<'a, Option<X::Item>, Option<(K, X::Item)>>, | |||||
| { | |||||
| let operation = self.operation; | |||||
| let executor = executor.into_inner(); | |||||
| let ret = self | |||||
| .iterator | |||||
| .map(move |x| (operation(&x), x)) | |||||
| .reduce_with(|a, b| match (a.0).cmp(&b.0) { | |||||
| Ordering::Greater => a, | |||||
| _ => b, | |||||
| }) | |||||
| .exec_with(executor); | |||||
| E::map(ret, |x| x.map(|x| x.1)) | |||||
| } | |||||
| } | |||||
| @@ -63,3 +63,45 @@ where | |||||
| .exec_with(executor) | .exec_with(executor) | ||||
| } | } | ||||
| } | } | ||||
| /* MinByKey */ | |||||
| pub struct MinByKey<X, O> { | |||||
| iterator: X, | |||||
| operation: O, | |||||
| } | |||||
| impl<X, O> MinByKey<X, O> { | |||||
| pub fn new(iterator: X, operation: O) -> Self { | |||||
| Self { | |||||
| iterator, | |||||
| operation, | |||||
| } | |||||
| } | |||||
| } | |||||
| impl<'a, X, O, K> Driver<'a, Option<X::Item>, Option<(K, X::Item)>> for MinByKey<X, O> | |||||
| where | |||||
| X: ParallelIterator<'a>, | |||||
| O: Fn(&X::Item) -> K + Clone + Send + Sync + 'a, | |||||
| K: Send + Ord + 'a, | |||||
| { | |||||
| fn exec_with<E>(self, executor: E) -> E::Result | |||||
| where | |||||
| E: Executor<'a, Option<X::Item>, Option<(K, X::Item)>>, | |||||
| { | |||||
| let operation = self.operation; | |||||
| let executor = executor.into_inner(); | |||||
| let ret = self | |||||
| .iterator | |||||
| .map(move |x| (operation(&x), x)) | |||||
| .reduce_with(|a, b| match (a.0).cmp(&b.0) { | |||||
| Ordering::Less => a, | |||||
| _ => b, | |||||
| }) | |||||
| .exec_with(executor); | |||||
| E::map(ret, |x| x.map(|x| x.1)) | |||||
| } | |||||
| } | |||||