From ab20aeb26b8e1fb008df4e15fa459a79c3f6dcaf Mon Sep 17 00:00:00 2001 From: Peter Minten Date: Sun, 12 Oct 2014 16:57:03 +0200 Subject: [PATCH 1/2] Add implementation of custom-set exercise This may be slightly different from other tracks' custom set exercises as I've adapted the exercise to fit in with how the current sets in the rust base lib work. --- config.json | 3 +- custom-set/custom-set_test.rs | 194 ++++++++++++++++++++++++ custom-set/example.rs | 268 ++++++++++++++++++++++++++++++++++ 3 files changed, 464 insertions(+), 1 deletion(-) create mode 100644 custom-set/custom-set_test.rs create mode 100644 custom-set/example.rs diff --git a/config.json b/config.json index 49888fed4..6bdf3f9fb 100644 --- a/config.json +++ b/config.json @@ -14,7 +14,8 @@ "bob", "grade-school", "phone-number", - "beer-song" + "beer-song", + "custom-set" ], "deprecated": [ diff --git a/custom-set/custom-set_test.rs b/custom-set/custom-set_test.rs new file mode 100644 index 000000000..cfc0b2596 --- /dev/null +++ b/custom-set/custom-set_test.rs @@ -0,0 +1,194 @@ +#![crate_name = "custom-set_test"] +#![crate_type = "lib"] + +use std::collections::{Collection, Set, MutableSet}; + +mod set; + +fn make_set(vec: Vec) -> set::CustomSet { + vec.into_iter().collect() +} + +#[test] +#[ignore] +fn test_empty_set() { + let set = set::CustomSet::<()>::new(); + assert_eq!(set.len(), 0); + assert_eq!(set.is_empty(), true); +} + +#[test] +#[ignore] +fn test_from_iter() { + let set = make_set(vec!(1, 2, 3, 2)); + assert_eq!(set.len(), 3); + assert!(!set.is_empty()); + assert!(set.contains(&1)); + assert!(set.contains(&3)); + assert!(!set.contains(&4)); +} + +#[test] +#[ignore] +fn test_is_disjoint() { + assert!(make_set(vec!()).is_disjoint(&make_set(vec!()))); + assert!(make_set(vec!(1)).is_disjoint(&make_set(vec!()))); + assert!(make_set(vec!()).is_disjoint(&make_set(vec!(1)))); + assert!(make_set(vec!(1, 2)).is_disjoint(&make_set(vec!(3, 4)))); + assert!(!make_set(vec!(1, 2)).is_disjoint(&make_set(vec!(2, 4)))); +} + +#[test] +#[ignore] +fn test_is_subset() { + // Technically improper subset + assert!(make_set(vec!()).is_subset(&make_set(vec!()))); + assert!(!make_set(vec!(1)).is_subset(&make_set(vec!()))); + assert!(make_set(vec!()).is_subset(&make_set(vec!(1)))); + assert!(!make_set(vec!(1, 2)).is_subset(&make_set(vec!(3, 4)))); + assert!(!make_set(vec!(1, 2)).is_subset(&make_set(vec!(2, 4)))); + assert!(make_set(vec!(1, 2)).is_subset(&make_set(vec!(1, 2, 4)))); +} + +#[test] +#[ignore] +fn test_is_superset() { + assert!(make_set(vec!()).is_superset(&make_set(vec!()))); + assert!(make_set(vec!(1)).is_superset(&make_set(vec!()))); + assert!(!make_set(vec!()).is_superset(&make_set(vec!(1)))); + assert!(!make_set(vec!(1, 2)).is_superset(&make_set(vec!(3, 4)))); + assert!(!make_set(vec!(1, 2)).is_superset(&make_set(vec!(2, 4)))); + assert!(!make_set(vec!(1, 2)).is_superset(&make_set(vec!(1, 2, 4)))); + assert!(make_set(vec!(1, 2, 3)).is_superset(&make_set(vec!(1, 2)))); +} + +fn difference(a: Vec, b: Vec) -> Vec { + make_set(a).difference(&make_set(b)).map(|n| n.clone()).collect() +} + +#[test] +#[ignore] +fn test_difference() { + assert_eq!(difference(vec!(), vec!()), vec!()); + assert_eq!(difference(vec!(), vec!(3, 2, 5)), vec!()); + assert_eq!(difference(vec!(1, 2, 3, 4), vec!()), vec!(1, 2, 3, 4)); + assert_eq!(difference(vec!(1, 2, 3, 4), vec!(3, 2, 5)), vec!(1, 4)); +} + +fn intersection(a: Vec, b: Vec) -> Vec { + make_set(a).intersection(&make_set(b)).map(|n| n.clone()).collect() +} + +#[test] +#[ignore] +fn test_intersection() { + assert_eq!(intersection(vec!(), vec!()), vec!()); + assert_eq!(intersection(vec!(), vec!(3, 2, 5)), vec!()); + assert_eq!(intersection(vec!(1, 2, 3, 4), vec!()), vec!()); + assert_eq!(intersection(vec!(1, 2, 3, 4), vec!(3, 2, 5)), vec!(2, 3)); +} + +fn union(a: Vec, b: Vec) -> Vec { + make_set(a).union(&make_set(b)).map(|n| n.clone()).collect() +} + +#[test] +#[ignore] +fn test_union() { + assert_eq!(union(vec!(), vec!()), vec!()); + assert_eq!(union(vec!(), vec!(3, 2, 5)), vec!(2, 3, 5)); + assert_eq!(union(vec!(1, 2, 3, 4), vec!()), vec!(1, 2, 3, 4)); + assert_eq!(union(vec!(1, 2, 3, 4), vec!(3, 2, 5)), vec!(1, 2, 3, 4, 5)); +} + +#[test] +#[ignore] +fn test_insert() { + let mut set = make_set(vec!(1, 2, 3)); + assert!(set.contains(&2)); + assert!(!set.insert(2)); + assert!(set.contains(&2)); + assert!(!set.contains(&4)); + assert!(set.insert(4)); + assert!(set.contains(&4)); +} + +// Equality on this is modulo 3. +#[deriving(Eq, Show)] +struct Modulo3(uint); + +impl PartialEq for Modulo3 { + fn eq(&self, other: &Modulo3) -> bool { + let &Modulo3(ref a) = self; + let &Modulo3(ref b) = other; + a % 3 == b % 3 + } +} + +impl PartialOrd for Modulo3 { + fn partial_cmp(&self, other: &Modulo3) -> Option { + let &Modulo3(ref a) = self; + let &Modulo3(ref b) = other; + (a % 3).partial_cmp(&(b % 3)) + } +} + +impl Ord for Modulo3 { + fn cmp(&self, other: &Modulo3) -> Ordering { + let &Modulo3(ref a) = self; + let &Modulo3(ref b) = other; + (a % 3).cmp(&(b % 3)) + } +} + +#[test] +#[ignore] +fn test_insert_no_double() { + // This test abuses the ord and eq mechanisms a bit to check that a set doesn't replace + // existing elements with new elements, which could lead to interesting bugs if the programmer + // is stupid enough to trigger that behaviour. + let mut set = vec!(Modulo3(1)).into_iter().collect::>(); + assert!(set.contains(&Modulo3(1))); + assert!(set.contains(&Modulo3(4))); + assert!(set.insert(Modulo3(2))); + assert!(set.contains(&Modulo3(5))); + assert!(!set.insert(Modulo3(8))); + let v = set.iter().collect::>(); + let &Modulo3(ref v0) = v[0]; + let &Modulo3(ref v1) = v[1]; + assert_eq!(v0, &1); + assert_eq!(v1, &2); +} + +#[test] +#[ignore] +fn test_remove() { + let mut set = make_set(vec!(1, 2, 3)); + assert!(set.contains(&2)); + assert!(set.remove(&2)); + assert!(!set.contains(&2)); + assert!(!set.remove(&4)); + assert!(!set.contains(&4)); +} + +#[test] +#[ignore] +fn test_clear() { + let mut set = make_set(vec!(1, 2, 3)); + assert!(set.contains(&2)); + assert_eq!(set.len(), 3); + assert!(!set.is_empty()); + set.clear(); + assert!(!set.contains(&2)); + assert_eq!(set.len(), 0); + assert!(set.is_empty()); +} + +#[test] +#[ignore] +fn test_traits() { + let s: set::CustomSet<()> = set::CustomSet::<()>::new(); + let _ = &s as &Collection; + let _ = &s as &Set<()>; + let _ = &s as &MutableSet<()>; +} diff --git a/custom-set/example.rs b/custom-set/example.rs new file mode 100644 index 000000000..766e4cbb2 --- /dev/null +++ b/custom-set/example.rs @@ -0,0 +1,268 @@ +use std::collections::{Collection, Set, MutableSet}; +use std::slice; + +#[deriving(Show)] +pub struct CustomSet(Vec); + +/// Create an empty set. +impl CustomSet { + pub fn new() -> CustomSet { + CustomSet(vec!()) + } + + pub fn iter(&self) -> Items { + let &CustomSet(ref v) = self; + Items(v.iter()) + } +} + +pub struct Items<'a, T: 'a>(slice::Items<'a, T>); + +impl<'a, T> Iterator<&'a T> for Items<'a, T> { + fn next(&mut self) -> Option<&'a T> { + let &Items(ref mut iter) = self; + iter.next() + } +} + +impl CustomSet { + fn find_index(&self, value: &T) -> Option { + let &CustomSet(ref v) = self; + for (idx, x) in v.iter().enumerate() { + if x == value { + return Some(idx) + } + } + return None + } +} + +impl CustomSet { + /// Visit the values representing the difference, in ascending order. + /// + /// The difference between set A and set B is all the items in set A that are not in set B. + pub fn difference<'a>(&'a self, other: &'a CustomSet) -> DifferenceItems { + DifferenceItems(box self + .diff_iter(other) + .filter(|&(_, diff)| diff == OnlyA) + .map(|(v, _)| v)) + } + + /// Visit the values representing the intersection, in ascending order. + /// + /// The intersection of set A and set B is all the items in set A that are also in set B. + pub fn intersection<'a>(&'a self, other: &'a CustomSet) -> IntersectionItems { + IntersectionItems(box self + .diff_iter(other) + .filter(|&(_, diff)| diff == Both) + .map(|(v, _)| v)) + } + + /// Visit the values representing the union, in ascending order. + /// + /// The union of set A and set B is all the items in both sets (with doubles removed). + pub fn union<'a>(&'a self, other: &'a CustomSet) -> UnionItems { + UnionItems(box self.diff_iter(other).map(|(v, _)| v)) + } + + fn diff_iter<'a>(&'a self, other: &'a CustomSet) -> DiffIter { + let &CustomSet(ref self_v) = self; + let &CustomSet(ref other_v) = other; + DiffIter { a: CachingWrapper::wrap(self_v.iter()), + b: CachingWrapper::wrap(other_v.iter()) } + } +} + +// Those boxes are used just to avoid having to specify a complex Map<..., Filter<..., DiffIter>> +// nested iterator type. +pub struct DifferenceItems<'a, T>(Box + 'a>); +pub struct IntersectionItems<'a, T>(Box + 'a>); +pub struct UnionItems<'a, T>(Box + 'a>); + +impl<'a, T> Iterator<&'a T> for DifferenceItems<'a, T> { + fn next(&mut self) -> Option<&'a T> { + let &DifferenceItems(ref mut iter) = self; + iter.next() + } +} + +impl<'a, T> Iterator<&'a T> for IntersectionItems<'a, T> { + fn next(&mut self) -> Option<&'a T> { + let &IntersectionItems(ref mut iter) = self; + iter.next() + } +} + +impl<'a, T> Iterator<&'a T> for UnionItems<'a, T> { + fn next(&mut self) -> Option<&'a T> { + let &UnionItems(ref mut iter) = self; + iter.next() + } +} + +impl Collection for CustomSet { + fn len(&self) -> uint { + let &CustomSet(ref v) = self; + v.len() + } + + fn is_empty(&self) -> bool { + let &CustomSet(ref v) = self; + v.is_empty() + } +} + +impl Set for CustomSet { + fn contains(&self, value: &T) -> bool { + self.find_index(value).is_some() + } + + fn is_disjoint(&self, other: &CustomSet) -> bool { + // Traverse the ordered vectors, the big benefit of this over unordered vectors (which + // would make the add method O(1) is that this method is O(n+m) where it would otherwise be + // O(n*m). Not that for the chosen representation performance is actually anywhere near + // decent, the standard lib sets are much much better. + let mut diff_iter = self.diff_iter(other); + for (_, diff) in diff_iter { + match diff { + Both => return false, + _ => {} + } + } + true + } + + fn is_subset(&self, other: &CustomSet) -> bool { + let mut diff_iter = self.diff_iter(other); + for (_, diff) in diff_iter { + match diff { + OnlyA => return false, + _ => {} + } + } + true + } +} + +impl Mutable for CustomSet { + fn clear(&mut self) { + let &CustomSet(ref mut v) = self; + v.clear(); + } +} + +enum InsertPos { + Begin, + Before(uint), + End, +} + +fn find_insert_pos(v: &Vec, value: &T) -> Option { + for (idx, x) in v.iter().enumerate() { + match value.cmp(x) { + Less if idx == 0 => return Some(Begin), + Less => return Some(Before(idx)), + Equal => return None, + Greater => () + } + } + Some(End) +} + +impl MutableSet for CustomSet { + fn insert(&mut self, value: T) -> bool { + let &CustomSet(ref mut v) = self; + match find_insert_pos(v, &value) { + None => false, + Some(Begin) => { v.insert(0, value); true }, + Some(Before(idx)) => { v.insert(idx-1, value); true }, + Some(End) => { v.push(value); true }, + } + } + + fn remove(&mut self, value: &T) -> bool { + let idx = match self.find_index(value) { + None => return false, + Some(idx) => idx + }; + let &CustomSet(ref mut v) = self; + v.remove(idx); + true + } +} + +impl FromIterator for CustomSet { + fn from_iter>(mut iterator: T) -> CustomSet { + let mut set: CustomSet = CustomSet::new(); + for v in iterator { + set.insert(v); + } + set + } +} + +// A wrapper around an iterator so that a single element can be viewed multiple times. +struct CachingWrapper<'a, T: 'a> { + iter: slice::Items<'a, T>, + cache: Option<&'a T>, +} + +impl<'a, T> CachingWrapper<'a, T> { + fn wrap(iter: slice::Items<'a, T>) -> CachingWrapper<'a, T> { + let mut wrapper = CachingWrapper { iter: iter, cache: None }; + wrapper.advance(); + wrapper + } + fn current(&self) -> Option<&'a T> { + self.cache + } + fn advance(&mut self) { + self.cache = self.iter.next(); + } +} + +#[deriving(PartialEq, Eq)] +enum Diff { + OnlyA, + OnlyB, + Both +} + +// Iterator for the differences between two ordered vectors. +struct DiffIter<'a, T: 'a> { + a: CachingWrapper<'a, T>, + b: CachingWrapper<'a, T>, +} + +impl<'a, T: Ord> Iterator<(&'a T, Diff)> for DiffIter<'a, T> { + fn next(&mut self) -> Option<(&'a T, Diff)> { + match (self.a.current(), self.b.current()) { + (None, None) => None, + (Some(a_val), None) => { + self.a.advance(); + Some((a_val, OnlyA)) + }, + (None, Some(b_val)) => { + self.b.advance(); + Some((b_val, OnlyB)) + }, + (Some(a_val), Some(b_val)) => { + match (*a_val).cmp(b_val) { + Less => { + self.a.advance(); + Some((a_val, OnlyA)) + } + Equal => { + self.a.advance(); + self.b.advance(); + Some((a_val, Both)) + } + Greater => { + self.b.advance(); + Some((b_val, OnlyB)) + } + } + } + } + } +} From ff5317f1e8791e19ab86bcf2d9e3f16a8fd735a3 Mon Sep 17 00:00:00 2001 From: Peter Minten Date: Mon, 13 Oct 2014 16:48:09 +0200 Subject: [PATCH 2/2] Don't force iteration order and don't insult users --- custom-set/custom-set_test.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/custom-set/custom-set_test.rs b/custom-set/custom-set_test.rs index cfc0b2596..51a5f2d1a 100644 --- a/custom-set/custom-set_test.rs +++ b/custom-set/custom-set_test.rs @@ -63,7 +63,9 @@ fn test_is_superset() { } fn difference(a: Vec, b: Vec) -> Vec { - make_set(a).difference(&make_set(b)).map(|n| n.clone()).collect() + let mut v = make_set(a).difference(&make_set(b)).map(|n| n.clone()).collect::>(); + v.sort(); + v } #[test] @@ -76,7 +78,9 @@ fn test_difference() { } fn intersection(a: Vec, b: Vec) -> Vec { - make_set(a).intersection(&make_set(b)).map(|n| n.clone()).collect() + let mut v = make_set(a).intersection(&make_set(b)).map(|n| n.clone()).collect::>(); + v.sort(); + v } #[test] @@ -89,7 +93,9 @@ fn test_intersection() { } fn union(a: Vec, b: Vec) -> Vec { - make_set(a).union(&make_set(b)).map(|n| n.clone()).collect() + let mut v = make_set(a).union(&make_set(b)).map(|n| n.clone()).collect::>(); + v.sort(); + v } #[test] @@ -146,14 +152,15 @@ impl Ord for Modulo3 { fn test_insert_no_double() { // This test abuses the ord and eq mechanisms a bit to check that a set doesn't replace // existing elements with new elements, which could lead to interesting bugs if the programmer - // is stupid enough to trigger that behaviour. + // triggers that behaviour. let mut set = vec!(Modulo3(1)).into_iter().collect::>(); assert!(set.contains(&Modulo3(1))); assert!(set.contains(&Modulo3(4))); assert!(set.insert(Modulo3(2))); assert!(set.contains(&Modulo3(5))); assert!(!set.insert(Modulo3(8))); - let v = set.iter().collect::>(); + let mut v = set.iter().collect::>(); + v.sort(); let &Modulo3(ref v0) = v[0]; let &Modulo3(ref v1) = v[1]; assert_eq!(v0, &1);