1
Fork 0

Provide an implementation of DoubleEndedIterator for the results of &[T]::split and &[T]::rsplit

This makes the splitting functions in std::slice return DoubleEndedIterators. Unfortunately,
splitn and rsplitn cannot provide such an interface and so must return different types. As a
result, the following changes were made:

* RevSplits was removed in favor of explicitly using Rev
* Splits can no longer bound the number of splits done
* Splits now implements DoubleEndedIterator
* SplitsN was added, taking the role of what both Splits and RevSplits used to be
* rsplit returns Rev<Splits<'a, T>> instead of RevSplits<'a, T>
* splitn returns SplitsN<'a, T> instead of Splits<'a, T>
* rsplitn returns SplitsN<'a, T> instead of RevSplits<'a, T>

All functions that were previously implemented on each return value still are, so outside of changing
of type annotations, existing code should work out of the box. In the rare case that code relied
on the return types of split and splitn or of rsplit and rsplitn being the same, the previous
behavior can be emulated by calling splitn or rsplitn with a bount of uint::MAX.

The value of this change comes in multiple parts:

* Consistency. The splitting code in std::str is structured similarly to the new slice splitting code,
  having separate CharSplits and CharSplitsN types.
* Smaller API. Although this commit doesn't implement it, using a DoubleEndedIterator for splitting
  means that rsplit, path::RevComponents, path::RevStrComponents, Path::rev_components, and
  Path::rev_str_components are no longer needed - they can be emulated simply with .rev().
* Power. DoubleEndedIterators are able to traverse the list from both sides at once instead of only
  forwards or backwards.
* Efficiency. For the common case of using split instead of splitn, the iterator is slightly smaller
  and slightly faster.

[breaking-change]
This commit is contained in:
Jonathan S 2014-04-20 21:28:38 -05:00
parent 23262a8390
commit f58a8c9d76
3 changed files with 64 additions and 84 deletions

View file

@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -16,11 +16,11 @@ use clone::Clone;
use cmp::{Eq, TotalEq}; use cmp::{Eq, TotalEq};
use from_str::FromStr; use from_str::FromStr;
use io::Writer; use io::Writer;
use iter::{AdditiveIterator, Extendable, Iterator, Map}; use iter::{DoubleEndedIterator, Rev, AdditiveIterator, Extendable, Iterator, Map};
use option::{Option, None, Some}; use option::{Option, None, Some};
use str; use str;
use str::Str; use str::Str;
use slice::{CloneableVector, RevSplits, Splits, Vector, VectorVector, use slice::{CloneableVector, Splits, Vector, VectorVector,
ImmutableEqVector, OwnedVector, ImmutableVector}; ImmutableEqVector, OwnedVector, ImmutableVector};
use vec::Vec; use vec::Vec;
@ -29,14 +29,13 @@ use super::{BytesContainer, GenericPath, GenericPathUnsafe};
/// Iterator that yields successive components of a Path as &[u8] /// Iterator that yields successive components of a Path as &[u8]
pub type Components<'a> = Splits<'a, u8>; pub type Components<'a> = Splits<'a, u8>;
/// Iterator that yields components of a Path in reverse as &[u8] /// Iterator that yields components of a Path in reverse as &[u8]
pub type RevComponents<'a> = RevSplits<'a, u8>; pub type RevComponents<'a> = Rev<Components<'a>>;
/// Iterator that yields successive components of a Path as Option<&str> /// Iterator that yields successive components of a Path as Option<&str>
pub type StrComponents<'a> = Map<'a, &'a [u8], Option<&'a str>, pub type StrComponents<'a> = Map<'a, &'a [u8], Option<&'a str>,
Components<'a>>; Components<'a>>;
/// Iterator that yields components of a Path in reverse as Option<&str> /// Iterator that yields components of a Path in reverse as Option<&str>
pub type RevStrComponents<'a> = Map<'a, &'a [u8], Option<&'a str>, pub type RevStrComponents<'a> = Rev<StrComponents<'a>>;
RevComponents<'a>>;
/// Represents a POSIX file path /// Represents a POSIX file path
#[deriving(Clone)] #[deriving(Clone)]
@ -397,15 +396,7 @@ impl Path {
/// Returns an iterator that yields each component of the path in reverse. /// Returns an iterator that yields each component of the path in reverse.
/// See components() for details. /// See components() for details.
pub fn rev_components<'a>(&'a self) -> RevComponents<'a> { pub fn rev_components<'a>(&'a self) -> RevComponents<'a> {
let v = if *self.repr.get(0) == SEP_BYTE { self.components().rev()
self.repr.slice_from(1)
} else { self.repr.as_slice() };
let mut ret = v.rsplit(is_sep_byte);
if v.is_empty() {
// consume the empty "" component
ret.next();
}
ret
} }
/// Returns an iterator that yields each component of the path as Option<&str>. /// Returns an iterator that yields each component of the path as Option<&str>.
@ -417,7 +408,7 @@ impl Path {
/// Returns an iterator that yields each component of the path in reverse as Option<&str>. /// Returns an iterator that yields each component of the path in reverse as Option<&str>.
/// See components() for details. /// See components() for details.
pub fn rev_str_components<'a>(&'a self) -> RevStrComponents<'a> { pub fn rev_str_components<'a>(&'a self) -> RevStrComponents<'a> {
self.rev_components().map(str::from_utf8) self.str_components().rev()
} }
} }

View file

@ -1,4 +1,4 @@
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT // Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at // file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT. // http://rust-lang.org/COPYRIGHT.
// //
@ -37,15 +37,13 @@ pub type StrComponents<'a> = Map<'a, &'a str, Option<&'a str>,
/// ///
/// Each component is yielded as Option<&str> for compatibility with PosixPath, but /// Each component is yielded as Option<&str> for compatibility with PosixPath, but
/// every component in WindowsPath is guaranteed to be Some. /// every component in WindowsPath is guaranteed to be Some.
pub type RevStrComponents<'a> = Rev<Map<'a, &'a str, Option<&'a str>, pub type RevStrComponents<'a> = Rev<StrComponents<'a>>;
CharSplits<'a, char>>>;
/// Iterator that yields successive components of a Path as &[u8] /// Iterator that yields successive components of a Path as &[u8]
pub type Components<'a> = Map<'a, Option<&'a str>, &'a [u8], pub type Components<'a> = Map<'a, Option<&'a str>, &'a [u8],
StrComponents<'a>>; StrComponents<'a>>;
/// Iterator that yields components of a Path in reverse as &[u8] /// Iterator that yields components of a Path in reverse as &[u8]
pub type RevComponents<'a> = Map<'a, Option<&'a str>, &'a [u8], pub type RevComponents<'a> = Rev<Components<'a>>;
RevStrComponents<'a>>;
/// Represents a Windows path /// Represents a Windows path
// Notes for Windows path impl: // Notes for Windows path impl:
@ -650,11 +648,7 @@ impl Path {
/// Returns an iterator that yields each component of the path in reverse as a &[u8]. /// Returns an iterator that yields each component of the path in reverse as a &[u8].
/// See str_components() for details. /// See str_components() for details.
pub fn rev_components<'a>(&'a self) -> RevComponents<'a> { pub fn rev_components<'a>(&'a self) -> RevComponents<'a> {
fn convert<'a>(x: Option<&'a str>) -> &'a [u8] { self.components().rev()
#![inline]
x.unwrap().as_bytes()
}
self.rev_str_components().map(convert)
} }
fn equiv_prefix(&self, other: &Path) -> bool { fn equiv_prefix(&self, other: &Path) -> bool {

View file

@ -119,7 +119,6 @@ use result::{Ok, Err};
use mem; use mem;
use mem::size_of; use mem::size_of;
use kinds::marker; use kinds::marker;
use uint;
use unstable::finally::try_finally; use unstable::finally::try_finally;
use raw::{Repr, Slice}; use raw::{Repr, Slice};
use RawVec = raw::Vec; use RawVec = raw::Vec;
@ -148,7 +147,6 @@ pub fn mut_ref_slice<'a, A>(s: &'a mut A) -> &'a mut [A] {
/// match a predicate function. /// match a predicate function.
pub struct Splits<'a, T> { pub struct Splits<'a, T> {
v: &'a [T], v: &'a [T],
n: uint,
pred: |t: &T|: 'a -> bool, pred: |t: &T|: 'a -> bool,
finished: bool finished: bool
} }
@ -158,11 +156,6 @@ impl<'a, T> Iterator<&'a [T]> for Splits<'a, T> {
fn next(&mut self) -> Option<&'a [T]> { fn next(&mut self) -> Option<&'a [T]> {
if self.finished { return None; } if self.finished { return None; }
if self.n == 0 {
self.finished = true;
return Some(self.v);
}
match self.v.iter().position(|x| (self.pred)(x)) { match self.v.iter().position(|x| (self.pred)(x)) {
None => { None => {
self.finished = true; self.finished = true;
@ -171,7 +164,6 @@ impl<'a, T> Iterator<&'a [T]> for Splits<'a, T> {
Some(idx) => { Some(idx) => {
let ret = Some(self.v.slice(0, idx)); let ret = Some(self.v.slice(0, idx));
self.v = self.v.slice(idx + 1, self.v.len()); self.v = self.v.slice(idx + 1, self.v.len());
self.n -= 1;
ret ret
} }
} }
@ -180,38 +172,18 @@ impl<'a, T> Iterator<&'a [T]> for Splits<'a, T> {
#[inline] #[inline]
fn size_hint(&self) -> (uint, Option<uint>) { fn size_hint(&self) -> (uint, Option<uint>) {
if self.finished { if self.finished {
return (0, Some(0)) (0, Some(0))
} } else {
// if the predicate doesn't match anything, we yield one slice (1, Some(self.v.len() + 1))
// if it matches every element, we yield N+1 empty slices where
// N is either the number of elements or the number of splits.
match (self.v.len(), self.n) {
(0,_) => (1, Some(1)),
(_,0) => (1, Some(1)),
(l,n) => (1, cmp::min(l,n).checked_add(&1u))
} }
} }
} }
/// An iterator over the slices of a vector separated by elements that impl<'a, T> DoubleEndedIterator<&'a [T]> for Splits<'a, T> {
/// match a predicate function, from back to front.
pub struct RevSplits<'a, T> {
v: &'a [T],
n: uint,
pred: |t: &T|: 'a -> bool,
finished: bool
}
impl<'a, T> Iterator<&'a [T]> for RevSplits<'a, T> {
#[inline] #[inline]
fn next(&mut self) -> Option<&'a [T]> { fn next_back(&mut self) -> Option<&'a [T]> {
if self.finished { return None; } if self.finished { return None; }
if self.n == 0 {
self.finished = true;
return Some(self.v);
}
match self.v.iter().rposition(|x| (self.pred)(x)) { match self.v.iter().rposition(|x| (self.pred)(x)) {
None => { None => {
self.finished = true; self.finished = true;
@ -220,21 +192,42 @@ impl<'a, T> Iterator<&'a [T]> for RevSplits<'a, T> {
Some(idx) => { Some(idx) => {
let ret = Some(self.v.slice(idx + 1, self.v.len())); let ret = Some(self.v.slice(idx + 1, self.v.len()));
self.v = self.v.slice(0, idx); self.v = self.v.slice(0, idx);
self.n -= 1;
ret ret
} }
} }
} }
}
/// An iterator over the slices of a vector separated by elements that
/// match a predicate function, splitting at most a fixed number of times.
pub struct SplitsN<'a, T> {
iter: Splits<'a, T>,
count: uint,
invert: bool
}
impl<'a, T> Iterator<&'a [T]> for SplitsN<'a, T> {
#[inline]
fn next(&mut self) -> Option<&'a [T]> {
if self.count == 0 {
if self.iter.finished {
None
} else {
self.iter.finished = true;
Some(self.iter.v)
}
} else {
self.count -= 1;
if self.invert { self.iter.next_back() } else { self.iter.next() }
}
}
#[inline] #[inline]
fn size_hint(&self) -> (uint, Option<uint>) { fn size_hint(&self) -> (uint, Option<uint>) {
if self.finished { if self.iter.finished {
return (0, Some(0)) (0, Some(0))
} } else {
match (self.v.len(), self.n) { (1, Some(cmp::min(self.count, self.iter.v.len()) + 1))
(0,_) => (1, Some(1)),
(_,0) => (1, Some(1)),
(l,n) => (1, cmp::min(l,n).checked_add(&1u))
} }
} }
} }
@ -747,18 +740,18 @@ pub trait ImmutableVector<'a, T> {
/// separated by elements that match `pred`, limited to splitting /// separated by elements that match `pred`, limited to splitting
/// at most `n` times. The matched element is not contained in /// at most `n` times. The matched element is not contained in
/// the subslices. /// the subslices.
fn splitn(self, n: uint, pred: |&T|: 'a -> bool) -> Splits<'a, T>; fn splitn(self, n: uint, pred: |&T|: 'a -> bool) -> SplitsN<'a, T>;
/// Returns an iterator over the subslices of the vector which are /// Returns an iterator over the subslices of the vector which are
/// separated by elements that match `pred`. This starts at the /// separated by elements that match `pred`. This starts at the
/// end of the vector and works backwards. The matched element is /// end of the vector and works backwards. The matched element is
/// not contained in the subslices. /// not contained in the subslices.
fn rsplit(self, pred: |&T|: 'a -> bool) -> RevSplits<'a, T>; fn rsplit(self, pred: |&T|: 'a -> bool) -> Rev<Splits<'a, T>>;
/// Returns an iterator over the subslices of the vector which are /// Returns an iterator over the subslices of the vector which are
/// separated by elements that match `pred` limited to splitting /// separated by elements that match `pred` limited to splitting
/// at most `n` times. This starts at the end of the vector and /// at most `n` times. This starts at the end of the vector and
/// works backwards. The matched element is not contained in the /// works backwards. The matched element is not contained in the
/// subslices. /// subslices.
fn rsplitn(self, n: uint, pred: |&T|: 'a -> bool) -> RevSplits<'a, T>; fn rsplitn(self, n: uint, pred: |&T|: 'a -> bool) -> SplitsN<'a, T>;
/** /**
* Returns an iterator over all contiguous windows of length * Returns an iterator over all contiguous windows of length
@ -936,31 +929,33 @@ impl<'a,T> ImmutableVector<'a, T> for &'a [T] {
#[inline] #[inline]
fn split(self, pred: |&T|: 'a -> bool) -> Splits<'a, T> { fn split(self, pred: |&T|: 'a -> bool) -> Splits<'a, T> {
self.splitn(uint::MAX, pred)
}
#[inline]
fn splitn(self, n: uint, pred: |&T|: 'a -> bool) -> Splits<'a, T> {
Splits { Splits {
v: self, v: self,
n: n,
pred: pred, pred: pred,
finished: false finished: false
} }
} }
#[inline] #[inline]
fn rsplit(self, pred: |&T|: 'a -> bool) -> RevSplits<'a, T> { fn splitn(self, n: uint, pred: |&T|: 'a -> bool) -> SplitsN<'a, T> {
self.rsplitn(uint::MAX, pred) SplitsN {
iter: self.split(pred),
count: n,
invert: false
}
} }
#[inline] #[inline]
fn rsplitn(self, n: uint, pred: |&T|: 'a -> bool) -> RevSplits<'a, T> { fn rsplit(self, pred: |&T|: 'a -> bool) -> Rev<Splits<'a, T>> {
RevSplits { self.split(pred).rev()
v: self, }
n: n,
pred: pred, #[inline]
finished: false fn rsplitn(self, n: uint, pred: |&T|: 'a -> bool) -> SplitsN<'a, T> {
SplitsN {
iter: self.split(pred),
count: n,
invert: true
} }
} }