core: Add binary_search and binary_search_elem methods to slices.

These are like the existing bsearch methods but if the search fails,
it returns the next insertion point.

The new `binary_search` returns a `BinarySearchResult` that is either
`Found` or `NotFound`. For convenience, the `found` and `not_found`
methods convert to `Option`, ala `Result`.

Deprecate bsearch and bsearch_elem.
This commit is contained in:
Brian Anderson 2014-08-06 20:48:25 -07:00
parent 76d46af6d4
commit a4b354ca02
8 changed files with 186 additions and 47 deletions

View File

@ -293,13 +293,12 @@ def emit_bsearch_range_table(f):
f.write(""" f.write("""
fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool { fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool {
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
use core::slice::ImmutableVector; use core::slice::ImmutableSlice;
use core::option::None; r.binary_search(|&(lo,hi)| {
r.bsearch(|&(lo,hi)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) != None }).found().is_some()
}\n }\n
""") """)
@ -352,9 +351,10 @@ def emit_conversions_module(f, lowerupper, upperlower):
f.write("pub mod conversions {") f.write("pub mod conversions {")
f.write(""" f.write("""
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
use core::slice::ImmutableVector; use core::slice::ImmutableSlice;
use core::tuple::Tuple2; use core::tuple::Tuple2;
use core::option::{Option, Some, None}; use core::option::{Option, Some, None};
use core::slice;
pub fn to_lower(c: char) -> char { pub fn to_lower(c: char) -> char {
match bsearch_case_table(c, LuLl_table) { match bsearch_case_table(c, LuLl_table) {
@ -371,11 +371,14 @@ def emit_conversions_module(f, lowerupper, upperlower):
} }
fn bsearch_case_table(c: char, table: &'static [(char, char)]) -> Option<uint> { fn bsearch_case_table(c: char, table: &'static [(char, char)]) -> Option<uint> {
table.bsearch(|&(key, _)| { match table.binary_search(|&(key, _)| {
if c == key { Equal } if c == key { Equal }
else if key < c { Less } else if key < c { Less }
else { Greater } else { Greater }
}) }) {
slice::Found(i) => Some(i),
slice::NotFound(_) => None,
}
} }
""") """)
@ -387,8 +390,8 @@ def emit_conversions_module(f, lowerupper, upperlower):
def emit_grapheme_module(f, grapheme_table, grapheme_cats): def emit_grapheme_module(f, grapheme_table, grapheme_cats):
f.write("""pub mod grapheme { f.write("""pub mod grapheme {
use core::option::{Some, None}; use core::slice::ImmutableSlice;
use core::slice::ImmutableVector; use core::slice;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[deriving(Clone)] #[deriving(Clone)]
@ -400,16 +403,16 @@ def emit_grapheme_module(f, grapheme_table, grapheme_cats):
fn bsearch_range_value_table(c: char, r: &'static [(char, char, GraphemeCat)]) -> GraphemeCat { fn bsearch_range_value_table(c: char, r: &'static [(char, char, GraphemeCat)]) -> GraphemeCat {
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
match r.bsearch(|&(lo, hi, _)| { match r.binary_search(|&(lo, hi, _)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, _, cat) = r[idx]; let (_, _, cat) = r[idx];
cat cat
} }
None => GC_Any slice::NotFound(_) => GC_Any
} }
} }
@ -427,20 +430,21 @@ def emit_grapheme_module(f, grapheme_table, grapheme_cats):
def emit_charwidth_module(f, width_table): def emit_charwidth_module(f, width_table):
f.write("pub mod charwidth {\n") f.write("pub mod charwidth {\n")
f.write(" use core::option::{Option, Some, None};\n") f.write(" use core::option::{Option, Some, None};\n")
f.write(" use core::slice::ImmutableVector;\n") f.write(" use core::slice::ImmutableSlice;\n")
f.write(" use core::slice;\n")
f.write(""" f.write("""
fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 { fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 {
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
match r.bsearch(|&(lo, hi, _, _)| { match r.binary_search(|&(lo, hi, _, _)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, _, r_ncjk, r_cjk) = r[idx]; let (_, _, r_ncjk, r_cjk) = r[idx];
if is_cjk { r_cjk } else { r_ncjk } if is_cjk { r_cjk } else { r_ncjk }
} }
None => 1 slice::NotFound(_) => 1
} }
} }
""") """)
@ -525,19 +529,19 @@ def emit_norm_module(f, canon, compat, combine, norm_props):
f.write(""" f.write("""
fn bsearch_range_value_table(c: char, r: &'static [(char, char, u8)]) -> u8 { fn bsearch_range_value_table(c: char, r: &'static [(char, char, u8)]) -> u8 {
use core::option::{Some, None};
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
use core::slice::ImmutableVector; use core::slice::ImmutableSlice;
match r.bsearch(|&(lo, hi, _)| { use core::slice;
match r.binary_search(|&(lo, hi, _)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, _, result) = r[idx]; let (_, _, result) = r[idx];
result result
} }
None => 0 slice::NotFound(_) => 0
} }
}\n }\n
""") """)

View File

@ -102,6 +102,7 @@ pub use core::slice::{Chunks, Slice, ImmutableSlice, ImmutablePartialEqSlice};
pub use core::slice::{ImmutableOrdSlice, MutableSlice, Items, MutItems}; pub use core::slice::{ImmutableOrdSlice, MutableSlice, Items, MutItems};
pub use core::slice::{MutSplits, MutChunks}; pub use core::slice::{MutSplits, MutChunks};
pub use core::slice::{bytes, MutableCloneableSlice}; pub use core::slice::{bytes, MutableCloneableSlice};
pub use core::slice::{BinarySearchResult, Found, NotFound};
// Functional utilities // Functional utilities

View File

@ -51,6 +51,7 @@ use raw::{Repr};
// Avoid conflicts with *both* the Slice trait (buggy) and the `slice::raw` module. // Avoid conflicts with *both* the Slice trait (buggy) and the `slice::raw` module.
use RawSlice = raw::Slice; use RawSlice = raw::Slice;
// //
// Extension traits // Extension traits
// //
@ -205,8 +206,25 @@ pub trait ImmutableSlice<'a, T> {
* Returns the index where the comparator returned `Equal`, or `None` if * Returns the index where the comparator returned `Equal`, or `None` if
* not found. * not found.
*/ */
#[deprecated = "use binary_search"]
fn bsearch(&self, f: |&T| -> Ordering) -> Option<uint>; fn bsearch(&self, f: |&T| -> Ordering) -> Option<uint>;
/**
* Binary search a sorted vector with a comparator function.
*
* The comparator function should implement an order consistent
* with the sort order of the underlying vector, returning an
* order code that indicates whether its argument is `Less`,
* `Equal` or `Greater` the desired target.
*
* If the value is found then `Found` is returned, containing the
* index of the matching element; if the value is not found then
* `NotFound` is returned, containing the index where a matching
* element could be inserted while maintaining sorted order.
*/
#[unstable]
fn binary_search(&self, f: |&T| -> Ordering) -> BinarySearchResult;
/** /**
* Returns an immutable reference to the first element in this slice * Returns an immutable reference to the first element in this slice
* and adjusts the slice in place so that it no longer contains * and adjusts the slice in place so that it no longer contains
@ -377,6 +395,7 @@ impl<'a,T> ImmutableSlice<'a, T> for &'a [T] {
} }
#[deprecated = "use binary_search"]
fn bsearch(&self, f: |&T| -> Ordering) -> Option<uint> { fn bsearch(&self, f: |&T| -> Ordering) -> Option<uint> {
let mut base : uint = 0; let mut base : uint = 0;
let mut lim : uint = self.len(); let mut lim : uint = self.len();
@ -396,6 +415,26 @@ impl<'a,T> ImmutableSlice<'a, T> for &'a [T] {
return None; return None;
} }
#[unstable]
fn binary_search(&self, f: |&T| -> Ordering) -> BinarySearchResult {
let mut base : uint = 0;
let mut lim : uint = self.len();
while lim != 0 {
let ix = base + (lim >> 1);
match f(&self[ix]) {
Equal => return Found(ix),
Less => {
base = ix + 1;
lim -= 1;
}
Greater => ()
}
lim >>= 1;
}
return NotFound(base);
}
fn shift_ref(&mut self) -> Option<&'a T> { fn shift_ref(&mut self) -> Option<&'a T> {
unsafe { unsafe {
let s: &mut RawSlice<T> = transmute(self); let s: &mut RawSlice<T> = transmute(self);
@ -826,13 +865,31 @@ pub trait ImmutableOrdSlice<T: Ord> {
* *
* Returns the index of the element or None if not found. * Returns the index of the element or None if not found.
*/ */
#[deprecated = "use binary_search_elem"]
fn bsearch_elem(&self, x: &T) -> Option<uint>; fn bsearch_elem(&self, x: &T) -> Option<uint>;
/**
* Binary search a sorted vector for a given element.
*
* If the value is found then `Found` is returned, containing the
* index of the matching element; if the value is not found then
* `NotFound` is returned, containing the index where a matching
* element could be inserted while maintaining sorted order.
*/
#[unstable]
fn binary_search_elem(&self, x: &T) -> BinarySearchResult;
} }
impl<'a, T: Ord> ImmutableOrdSlice<T> for &'a [T] { impl<'a, T: Ord> ImmutableOrdSlice<T> for &'a [T] {
#[deprecated = "use binary_search_elem"]
fn bsearch_elem(&self, x: &T) -> Option<uint> { fn bsearch_elem(&self, x: &T) -> Option<uint> {
self.bsearch(|p| p.cmp(x)) self.bsearch(|p| p.cmp(x))
} }
#[unstable]
fn binary_search_elem(&self, x: &T) -> BinarySearchResult {
self.binary_search(|p| p.cmp(x))
}
} }
/// Trait for &[T] where T is Cloneable /// Trait for &[T] where T is Cloneable
@ -1337,6 +1394,41 @@ impl<'a, T> DoubleEndedIterator<&'a mut [T]> for MutChunks<'a, T> {
/// The result of calling `binary_search`.
///
/// `Found` means the search succeeded, and the contained value is the
/// index of the matching element. `NotFound` means the search
/// succeeded, and the contained value is an index where a matching
/// value could be inserted while maintaining sort order.
#[deriving(PartialEq, Show)]
pub enum BinarySearchResult {
/// The index of the found value.
Found(uint),
/// The index where the value should have been found.
NotFound(uint)
}
impl BinarySearchResult {
/// Converts a `Found` to `Some`, `NotFound` to `None`.
/// Similar to `Result::ok`.
pub fn found(&self) -> Option<uint> {
match *self {
Found(i) => Some(i),
NotFound(_) => None
}
}
/// Convert a `Found` to `None`, `NotFound` to `Some`.
/// Similar to `Result::err`.
pub fn not_found(&self) -> Option<uint> {
match *self {
Found(_) => None,
NotFound(i) => Some(i)
}
}
}
// //
// Free functions // Free functions

View File

@ -28,4 +28,5 @@ mod option;
mod ptr; mod ptr;
mod raw; mod raw;
mod result; mod result;
mod slice;
mod tuple; mod tuple;

35
src/libcoretest/slice.rs Normal file
View File

@ -0,0 +1,35 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::slice::{Found, NotFound};
#[test]
fn binary_search_not_found() {
let b = [1i, 2, 4, 6, 8, 9];
assert!(b.binary_search(|v| v.cmp(&6)) == Found(3));
let b = [1i, 2, 4, 6, 8, 9];
assert!(b.binary_search(|v| v.cmp(&5)) == NotFound(3));
let b = [1i, 2, 4, 6, 7, 8, 9];
assert!(b.binary_search(|v| v.cmp(&6)) == Found(3));
let b = [1i, 2, 4, 6, 7, 8, 9];
assert!(b.binary_search(|v| v.cmp(&5)) == NotFound(3));
let b = [1i, 2, 4, 6, 8, 9];
assert!(b.binary_search(|v| v.cmp(&8)) == Found(4));
let b = [1i, 2, 4, 6, 8, 9];
assert!(b.binary_search(|v| v.cmp(&7)) == NotFound(4));
let b = [1i, 2, 4, 6, 7, 8, 9];
assert!(b.binary_search(|v| v.cmp(&8)) == Found(5));
let b = [1i, 2, 4, 5, 6, 8, 9];
assert!(b.binary_search(|v| v.cmp(&7)) == NotFound(5));
let b = [1i, 2, 4, 5, 6, 8, 9];
assert!(b.binary_search(|v| v.cmp(&0)) == NotFound(0));
let b = [1i, 2, 4, 5, 6, 8];
assert!(b.binary_search(|v| v.cmp(&9)) == NotFound(6));
}

View File

@ -35,6 +35,7 @@
use std::cmp; use std::cmp;
use std::mem; use std::mem;
use std::slice;
use std::slice::MutableSlice; use std::slice::MutableSlice;
use compile::{ use compile::{
Program, Program,
@ -222,8 +223,8 @@ impl<'r, 't> Nfa<'r, 't> {
let negate = flags & FLAG_NEGATED > 0; let negate = flags & FLAG_NEGATED > 0;
let casei = flags & FLAG_NOCASE > 0; let casei = flags & FLAG_NOCASE > 0;
let found = ranges.as_slice(); let found = ranges.as_slice();
let found = found.bsearch(|&rc| class_cmp(casei, c, rc)); let found = found.binary_search(|&rc| class_cmp(casei, c, rc))
let found = found.is_some(); .found().is_some();
if found ^ negate { if found ^ negate {
self.add(nlist, pc+1, caps); self.add(nlist, pc+1, caps);
} }
@ -513,7 +514,7 @@ pub fn is_word(c: Option<char>) -> bool {
// Try the common ASCII case before invoking binary search. // Try the common ASCII case before invoking binary search.
match c { match c {
'_' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' => true, '_' | '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' => true,
_ => PERLW.bsearch(|&(start, end)| { _ => PERLW.binary_search(|&(start, end)| {
if c >= start && c <= end { if c >= start && c <= end {
Equal Equal
} else if start > c { } else if start > c {
@ -521,7 +522,7 @@ pub fn is_word(c: Option<char>) -> bool {
} else { } else {
Less Less
} }
}).is_some() }).found().is_some()
} }
} }

View File

@ -15,20 +15,21 @@
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
use core::option::{Option, Some, None}; use core::option::{Option, Some, None};
use core::slice;
use core::slice::ImmutableSlice; use core::slice::ImmutableSlice;
use tables::normalization::{canonical_table, compatibility_table, composition_table}; use tables::normalization::{canonical_table, compatibility_table, composition_table};
fn bsearch_table<T>(c: char, r: &'static [(char, &'static [T])]) -> Option<&'static [T]> { fn bsearch_table<T>(c: char, r: &'static [(char, &'static [T])]) -> Option<&'static [T]> {
match r.bsearch(|&(val, _)| { match r.binary_search(|&(val, _)| {
if c == val { Equal } if c == val { Equal }
else if val < c { Less } else if val < c { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, result) = r[idx]; let (_, result) = r[idx];
Some(result) Some(result)
} }
None => None slice::NotFound(_) => None
} }
} }
@ -82,16 +83,16 @@ pub fn compose(a: char, b: char) -> Option<char> {
match bsearch_table(a, composition_table) { match bsearch_table(a, composition_table) {
None => None, None => None,
Some(candidates) => { Some(candidates) => {
match candidates.bsearch(|&(val, _)| { match candidates.binary_search(|&(val, _)| {
if b == val { Equal } if b == val { Equal }
else if val < b { Less } else if val < b { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, result) = candidates[idx]; let (_, result) = candidates[idx];
Some(result) Some(result)
} }
None => None slice::NotFound(_) => None
} }
} }
} }

View File

@ -15,12 +15,11 @@
fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool { fn bsearch_range_table(c: char, r: &'static [(char,char)]) -> bool {
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
use core::slice::ImmutableSlice; use core::slice::ImmutableSlice;
use core::option::None; r.binary_search(|&(lo,hi)| {
r.bsearch(|&(lo,hi)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) != None }).found().is_some()
} }
pub mod general_category { pub mod general_category {
@ -6228,19 +6227,19 @@ pub mod normalization {
fn bsearch_range_value_table(c: char, r: &'static [(char, char, u8)]) -> u8 { fn bsearch_range_value_table(c: char, r: &'static [(char, char, u8)]) -> u8 {
use core::option::{Some, None};
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
use core::slice::ImmutableSlice; use core::slice::ImmutableSlice;
match r.bsearch(|&(lo, hi, _)| { use core::slice;
match r.binary_search(|&(lo, hi, _)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, _, result) = r[idx]; let (_, _, result) = r[idx];
result result
} }
None => 0 slice::NotFound(_) => 0
} }
} }
@ -6357,6 +6356,7 @@ pub mod conversions {
use core::slice::ImmutableSlice; use core::slice::ImmutableSlice;
use core::tuple::Tuple2; use core::tuple::Tuple2;
use core::option::{Option, Some, None}; use core::option::{Option, Some, None};
use core::slice;
pub fn to_lower(c: char) -> char { pub fn to_lower(c: char) -> char {
match bsearch_case_table(c, LuLl_table) { match bsearch_case_table(c, LuLl_table) {
@ -6373,11 +6373,14 @@ pub mod conversions {
} }
fn bsearch_case_table(c: char, table: &'static [(char, char)]) -> Option<uint> { fn bsearch_case_table(c: char, table: &'static [(char, char)]) -> Option<uint> {
table.bsearch(|&(key, _)| { match table.binary_search(|&(key, _)| {
if c == key { Equal } if c == key { Equal }
else if key < c { Less } else if key < c { Less }
else { Greater } else { Greater }
}) }) {
slice::Found(i) => Some(i),
slice::NotFound(_) => None,
}
} }
static LuLl_table: &'static [(char, char)] = &[ static LuLl_table: &'static [(char, char)] = &[
@ -6916,19 +6919,20 @@ pub mod conversions {
pub mod charwidth { pub mod charwidth {
use core::option::{Option, Some, None}; use core::option::{Option, Some, None};
use core::slice::ImmutableSlice; use core::slice::ImmutableSlice;
use core::slice;
fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 { fn bsearch_range_value_table(c: char, is_cjk: bool, r: &'static [(char, char, u8, u8)]) -> u8 {
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
match r.bsearch(|&(lo, hi, _, _)| { match r.binary_search(|&(lo, hi, _, _)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, _, r_ncjk, r_cjk) = r[idx]; let (_, _, r_ncjk, r_cjk) = r[idx];
if is_cjk { r_cjk } else { r_ncjk } if is_cjk { r_cjk } else { r_ncjk }
} }
None => 1 slice::NotFound(_) => 1
} }
} }
@ -7112,8 +7116,8 @@ pub mod charwidth {
} }
pub mod grapheme { pub mod grapheme {
use core::option::{Some, None};
use core::slice::ImmutableSlice; use core::slice::ImmutableSlice;
use core::slice;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
#[deriving(Clone)] #[deriving(Clone)]
@ -7132,16 +7136,16 @@ pub mod grapheme {
fn bsearch_range_value_table(c: char, r: &'static [(char, char, GraphemeCat)]) -> GraphemeCat { fn bsearch_range_value_table(c: char, r: &'static [(char, char, GraphemeCat)]) -> GraphemeCat {
use core::cmp::{Equal, Less, Greater}; use core::cmp::{Equal, Less, Greater};
match r.bsearch(|&(lo, hi, _)| { match r.binary_search(|&(lo, hi, _)| {
if lo <= c && c <= hi { Equal } if lo <= c && c <= hi { Equal }
else if hi < c { Less } else if hi < c { Less }
else { Greater } else { Greater }
}) { }) {
Some(idx) => { slice::Found(idx) => {
let (_, _, cat) = r[idx]; let (_, _, cat) = r[idx];
cat cat
} }
None => GC_Any slice::NotFound(_) => GC_Any
} }
} }