From 20b4168074eb4f399e8a23e8b10083ae71b25c7f Mon Sep 17 00:00:00 2001 From: Pavel Aslanov Date: Thu, 16 Aug 2018 11:55:17 +0100 Subject: [PATCH] More docs and examples - added more docs - example for defining custom `Refs` - macro for defining custom `Refs` --- benches/criterion.rs | 6 +-- src/lib.rs | 22 +++++----- src/measure.rs | 5 ++- src/monoid.rs | 26 +++++------- src/node.rs | 4 ++ src/reference.rs | 91 +++++++++++++++++++++++++++++++++++------- src/test/quickcheck.rs | 2 +- src/test/simple.rs | 2 +- src/tree.rs | 10 ++++- 9 files changed, 118 insertions(+), 50 deletions(-) diff --git a/benches/criterion.rs b/benches/criterion.rs index c9aa567..453e0ae 100644 --- a/benches/criterion.rs +++ b/benches/criterion.rs @@ -22,7 +22,7 @@ fn bench_split(c: &mut Criterion) { let ft: rc::FingerTree<_> = (0..1024).map(Size).collect(); c.bench_function_over_inputs( "split", - move |b, &size| b.iter(|| ft.split(|m| m.value > *size)), + move |b, &size| b.iter(|| ft.split(|m| **m > *size)), SPLIT_1024, ); } @@ -31,7 +31,7 @@ fn bench_concat(c: &mut Criterion) { let ft: rc::FingerTree<_> = (0..1024).map(Size).collect(); let ft_split: HashMap<_, _> = SPLIT_1024 .iter() - .map(|size| (size, ft.split(|m| m.value > *size))) + .map(|size| (size, ft.split(|m| **m > *size))) .collect(); c.bench_function_over_inputs( @@ -52,7 +52,7 @@ fn bench_arc_vs_rc(c: &mut Criterion) { let (size, split) = i; let ft: FingerTree = (0..*size).map(Size).collect(); b.iter(|| { - let (left, right) = ft.split(|m| m.value > *split); + let (left, right) = ft.split(|m| **m > *split); left.concat(&right) }) } diff --git a/src/lib.rs b/src/lib.rs index 4d4e003..bc48749 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,9 +5,9 @@ //! Finger trees is a functional representation of persistent sequences //! supporting access to the ends in amortized constant time, and concatenation //! and splitting in time logarithmic in the size of the smaller piece. It also -//! has `split` operation defined in general form, which can be used to implement -//! sequence, priority queue, search tree, priority search queue and more -//! datastructures. +//! has [`split`](struct.FingerTree.html#method.split) operation defined in general +//! form, which can be used to implement sequence, priority queue, search tree, +//! priority search queue and more datastructures. //! //! ## Links: //! - Original paper: [Finger Trees: A Simple General-purpose Data Structure](http://www.staff.city.ac.uk/~ross/papers/FingerTree.html) @@ -15,7 +15,7 @@ //! //! ## Notes: //! - This implementation does not use non-regular recursive types as implementation -//! described in a paper. As rust's monomorphization does not play well with such types. +//! described in the paper. As rust's monomorphization does not play well with such types. //! - Implmentation abstracts over reference counted types `Rc/Arc`. Using type family trick. //! - Uses strict spine in implementation. //! - Iterator returns cloned value, and in general this implementation assumes that value @@ -24,22 +24,24 @@ //! //! ## Examples: //! ``` +//! # extern crate fingertree; //! # use std::iter::FromIterator; //! use fingertree::measure::Size; //! use fingertree::monoid::Sum; //! use fingertree::{FingerTree, Measured, RcRefs}; //! -//! // construct finger tree with `Size` measure +//! // construct `Rc` based finger tree with `Size` measure //! let ft: FingerTree = vec!["one", "two", "three", "four", "five"] //! .into_iter() //! .map(Size) //! .collect(); +//! assert_eq!(ft.measure(), Sum(5)); //! //! // split with predicate -//! let (left, right) = ft.split(|m| m.value > 2); -//! assert_eq!(left.measure(), Sum::new(2)); +//! let (left, right) = ft.split(|measure| *measure > Sum(2)); +//! assert_eq!(left.measure(), Sum(2)); //! assert_eq!(Vec::from_iter(&left), vec![Size("one"), Size("two")]); -//! assert_eq!(right.measure(), Sum::new(3)); +//! assert_eq!(right.measure(), Sum(3)); //! assert_eq!(Vec::from_iter(&right), vec![Size("three"), Size("four"), Size("five")]); //! //! // concatinate @@ -54,6 +56,7 @@ //! .collect(), //! ); //! ``` +#![doc(test(no_crate_inject))] #![deny(missing_docs)] #![deny(warnings)] @@ -73,8 +76,9 @@ mod test; pub use measure::Measured; pub use monoid::Monoid; +pub use node::NodeInner; pub use reference::{ArcRefs, RcRefs, Ref, Refs}; -pub use tree::FingerTree; +pub use tree::{FingerTree, FingerTreeInner}; pub mod rc { //! `Rc` based implementation of `FingerTree` diff --git a/src/measure.rs b/src/measure.rs index 7b8d459..7d87460 100644 --- a/src/measure.rs +++ b/src/measure.rs @@ -1,4 +1,4 @@ -//! `Measured` trait and definations +//! [`Measured`](measure/trait.Measured.html) trait and implementations use std::fmt; use std::ops::Deref; @@ -49,12 +49,13 @@ where type Measure = Sum; fn measure(&self) -> Self::Measure { - Sum::new(1) + Sum(1) } } impl Deref for Size { type Target = T; + fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/monoid.rs b/src/monoid.rs index 45f0289..44699f6 100644 --- a/src/monoid.rs +++ b/src/monoid.rs @@ -1,4 +1,4 @@ -//! `Monoid` trait and implementations +//! [`Monoid`](monoid/trait.Monoid.html) trait and implementations use std::ops::{Add, Deref}; /// Monoid definition @@ -12,7 +12,7 @@ use std::ops::{Add, Deref}; /// - **associativity**: `a + (b + c) == (a + b) + c` /// - **identity element**: `zero + a == a + zero == a` pub trait Monoid { - /// `zero` or `identity` elemnt of monoid + /// `zero` or `identity` element of monoid fn zero() -> Self; /// `plus` operation of `Monoid` fn plus(&self, other: &Self) -> Self; @@ -33,18 +33,8 @@ pub trait Monoid { // } /// Monoid formed by `Add::add` operation and `Default::default()` identity element -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct Sum { - /// New type wrapped value - pub value: T, -} - -impl Sum { - /// Create `Sum` monoid from any value - pub fn new(value: T) -> Self { - Sum { value } - } -} +#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct Sum(pub T); impl Monoid for Sum where @@ -52,17 +42,19 @@ where T: Default, { fn zero() -> Self { - Sum::new(T::default()) + Sum(T::default()) } fn plus(&self, other: &Self) -> Self { - Sum::new(&self.value + &other.value) + Sum(&self.0 + &other.0) } } impl Deref for Sum { type Target = T; + + #[inline] fn deref(&self) -> &Self::Target { - &self.value + &self.0 } } diff --git a/src/node.rs b/src/node.rs index 7b45e5a..5f16cd2 100644 --- a/src/node.rs +++ b/src/node.rs @@ -2,17 +2,21 @@ use measure::Measured; use monoid::Monoid; use reference::{Ref, Refs}; +/// Only visible to defne custom [`Refs`](trait.Refs.html) pub enum NodeInner where R: Refs, V: Measured, { + #[doc(hidden)] Leaf(V), + #[doc(hidden)] Node2 { measure: V::Measure, left: Node, right: Node, }, + #[doc(hidden)] Node3 { measure: V::Measure, left: Node, diff --git a/src/reference.rs b/src/reference.rs index 88d430e..c3761be 100644 --- a/src/reference.rs +++ b/src/reference.rs @@ -15,8 +15,64 @@ where fn new(value: Self::Target) -> Self; } -/// Interface which defines all reference types needed by finger tree -/// implementation. +impl Ref for Rc { + fn new(value: Self::Target) -> Self { + Rc::new(value) + } +} + +impl Ref for Arc { + fn new(value: Self::Target) -> Self { + Arc::new(value) + } +} + +/// Interface which defines all reference types needed by finger tree implementation. +/// +/// By implementing this interface for your reference type you can use finger treee +/// with your reference type. +/// +/// # Example: +/// ``` +/// #[macro_use] +/// extern crate fingertree; +/// +/// use std::rc::Rc; +/// use std::ops::Deref; +/// use fingertree::measure::Size; +/// use fingertree::{FingerTree, Measured, Ref}; +/// +/// // your custom reference type +/// struct MyRef(Rc); +/// +/// impl Clone for MyRef { +/// fn clone(&self) -> Self { +/// MyRef(self.0.clone()) +/// } +/// } +/// +/// impl Deref for MyRef { +/// type Target = T; +/// fn deref(&self) -> &T { +/// &*self.0 +/// } +/// } +/// +/// impl Ref for MyRef +/// { +/// fn new(value: T) -> Self { +/// MyRef(Rc::new(value)) +/// } +/// } +/// +/// // define type family for your reference +/// fingertree_define_refs!(MyRefs, MyRef); +/// +/// # fn main() { +/// // now you can construct fingertree using your reference type +/// let _: FingerTree = (0..128).map(Size).collect(); +/// # } +/// ``` pub trait Refs: Sized where V: Measured, @@ -27,26 +83,31 @@ where type Tree: Ref>; } -macro_rules! define_refs { - ($ref:ident, $refs:ident) => { - impl $crate::reference::Ref for $ref { - fn new(value: T) -> Self { - $ref::new(value) - } - } - +/// Helper macro to define custom [`Refs`](trait.Refs.html) for `FingerTree` +#[macro_export] +macro_rules! fingertree_define_refs { + (pub $refs:ident, $ref:ident) => { /// References type family pub enum $refs {} + fingertree_define_refs!(@refs_impl $refs, $ref); + }; + + ($refs:ident, $ref:ident) => { + /// References type family + enum $refs {} + fingertree_define_refs!(@refs_impl $refs, $ref); + }; - impl $crate::reference::Refs for $refs + (@refs_impl $refs:ident, $ref:ident) => { + impl $crate::Refs for $refs where V: $crate::measure::Measured, { - type Node = $ref<$crate::node::NodeInner>; - type Tree = $ref<$crate::tree::FingerTreeInner>; + type Node = $ref<$crate::NodeInner>; + type Tree = $ref<$crate::FingerTreeInner>; } }; } -define_refs!(Rc, RcRefs); -define_refs!(Arc, ArcRefs); +fingertree_define_refs!(pub RcRefs, Rc); +fingertree_define_refs!(pub ArcRefs, Arc); diff --git a/src/test/quickcheck.rs b/src/test/quickcheck.rs index 326e32c..7af6e20 100644 --- a/src/test/quickcheck.rs +++ b/src/test/quickcheck.rs @@ -36,7 +36,7 @@ quickcheck! { fn split_and_concat(ft: FingerTree>, index: usize) -> bool { let len = *ft.measure(); let index = if len != 0 { index % len } else { 0 }; - let (left, right) = ft.split(|m| m.value > index); + let (left, right) = ft.split(|m| **m > index); validate(&left); validate(&right); true diff --git a/src/test/simple.rs b/src/test/simple.rs index 4af3ffe..ed3c2e2 100644 --- a/src/test/simple.rs +++ b/src/test/simple.rs @@ -42,7 +42,7 @@ fn concat() { fn split() { let ft: RcFingerTree<_> = (0..TEST_SIZE).map(Size).collect(); for split in 0..TEST_SIZE { - let (left, right) = ft.split(|m| m.value > split); + let (left, right) = ft.split(|m| **m > split); validate(&left); validate(&right); assert_eq!(*left.measure(), split); diff --git a/src/tree.rs b/src/tree.rs index 6b02408..72de334 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -9,13 +9,17 @@ use monoid::Monoid; use node::{Node, NodeInner}; use reference::{Ref, Refs}; +/// Only visible to defne custom [`Refs`](trait.Refs.html) pub enum FingerTreeInner where R: Refs, V: Measured, { + #[doc(hidden)] Empty, + #[doc(hidden)] Single(Node), + #[doc(hidden)] Deep { measure: V::Measure, left: Digit>, @@ -348,10 +352,12 @@ where } } +/// FingerTree implemenetation +/// /// FingerTree is parametrized by two type parpameters /// - `R` - type family trick which determines type of references used in -/// implementation. This crate implementes [`ArcRefs`](::reference::ArcRefs) which is based -/// on `Arc` atomic reference counter, and [`RcRefs`] which is based +/// implementation. This crate implementes [`ArcRefs`](enum.ArcRefs.html) which is based +/// on `Arc` atomic reference counter, and [`RcRefs`](enum.RcRefs.html) which is based /// on `Rc`. /// - `V` - value type which must be measurable and cheaply clonable. pub struct FingerTree