Browse Source

Implemented 'min_by_key' and 'max_by_key' operation

master
Bergmann89 5 years ago
parent
commit
0d84424d68
7 changed files with 229 additions and 42 deletions
  1. +6
    -4
      asparit/src/core/driver.rs
  2. +30
    -18
      asparit/src/core/executor.rs
  3. +52
    -2
      asparit/src/core/iterator.rs
  4. +26
    -9
      asparit/src/executor/sequential.rs
  5. +31
    -9
      asparit/src/executor/tokio.rs
  6. +42
    -0
      asparit/src/inner/max.rs
  7. +42
    -0
      asparit/src/inner/min.rs

+ 6
- 4
asparit/src/core/driver.rs View File

@@ -1,14 +1,16 @@
use crate::{DefaultExecutor, Executor};

pub trait Driver<'a, D>: Sized
pub trait Driver<'a, T1, T2 = (), T3 = ()>: Sized
where
D: Send + 'a,
T1: Send + 'a,
T2: Send + 'a,
T3: Send + 'a,
{
fn exec_with<E>(self, executor: E) -> E::Result
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())
}
}

+ 30
- 18
asparit/src/core/executor.rs View File

@@ -2,30 +2,42 @@ use super::{
Consumer, IndexedProducer, IndexedProducerCallback, Producer, ProducerCallback, Reducer,
};

pub trait Executor<'a, D>: Sized
pub trait Executor<'a, T1, T2 = (), T3 = ()>: Sized
where
D: Send + 'a,
T1: Send + 'a,
T2: Send + 'a,
T3: Send + 'a,
{
type Result: Send;
type Inner: Executor<'a, T2, T3, ()>;

fn exec<P, C, R>(self, producer: P, consumer: C) -> Self::Result
where
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
where
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 join<R>(left: Self::Result, right: Self::Result, reducer: R) -> Self::Result
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> {
@@ -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
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;

@@ -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
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;



+ 52
- 2
asparit/src/core/iterator.rs View File

@@ -22,8 +22,8 @@ use crate::{
map::Map,
map_init::MapInit,
map_with::MapWith,
max::{Max, MaxBy},
min::{Min, MinBy},
max::{Max, MaxBy, MaxByKey},
min::{Min, MinBy, MinByKey},
product::Product,
reduce::{Reduce, ReduceWith},
sum::Sum,
@@ -1142,6 +1142,31 @@ pub trait ParallelIterator<'a>: Sized + Send {
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
/// iterator is empty, `None` is returned; otherwise, `Some(max)`
/// is returned.
@@ -1196,6 +1221,31 @@ pub trait ParallelIterator<'a>: Sized + Send {
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.
///
/// # Examples


+ 26
- 9
asparit/src/executor/sequential.rs View File

@@ -3,17 +3,20 @@ use crate::core::{Consumer, Executor, Folder, IndexedProducer, Producer, Reducer
#[derive(Default)]
pub struct Sequential;

impl<'a, D> Executor<'a, D> for Sequential
impl<'a, T1, T2, T3> Executor<'a, T1, T2, T3> for Sequential
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
where
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() {
consumer.into_folder().complete()
@@ -25,8 +28,8 @@ where
fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result
where
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() {
consumer.into_folder().complete()
@@ -39,10 +42,24 @@ where
(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
R: Reducer<D> + Send,
R: Reducer<T1> + Send,
{
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)
}
}

+ 31
- 9
asparit/src/executor/tokio.rs View File

@@ -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
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
where
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 splitter = Splitter::new(splits);
@@ -48,8 +51,8 @@ where
fn exec_indexed<P, C, R>(self, producer: P, consumer: C) -> Self::Result
where
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 splitter = IndexedSplitter::new(
@@ -75,8 +78,8 @@ where

fn join<R>(left: Self::Result, right: Self::Result, reducer: R) -> Self::Result
where
R: Reducer<D> + Send + 'a,
D: 'a,
R: Reducer<T1> + Send + 'a,
T1: 'a,
{
async move {
let left = left.await;
@@ -86,6 +89,25 @@ where
}
.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>


+ 42
- 0
asparit/src/inner/max.rs View File

@@ -63,3 +63,45 @@ where
.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))
}
}

+ 42
- 0
asparit/src/inner/min.rs View File

@@ -63,3 +63,45 @@ where
.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))
}
}

Loading…
Cancel
Save