Loading s4prg_rust_04_generic...
find_min_pos<T>(seq: &[T]) -> Option<usize>.
<T>indique que cette fonction dépend d'un type nommé arbitrairement T que nous ne connaissons pas encore ; ce nom sert à symboliser le type des éléments désignés par la slice seq.
rustup doc --std 'std::cmp::PartialOrd'et
rustup doc --std 'where'afin de déterminer comment compléter le prototype de la fonction find_min_pos().
pub fn find_best_pos<T, C>(seq: &[T], compare: C) -> Option<usize>.
FnMut(&T, &T) -> bool.
<ou
>par un appel à compare(). Pour en vérifier le fonctionnement, complétez la fonction chapter_4::section_1() de cette façon :
|a, b| a.abs() < b.abs().
rustup doc --std 'slice'.
FnMut(&T) -> bool, qui s'utilisera comme une fonction indiquant si l'élément désigné en paramètre correspond à celui que nous recherchons.
rustup doc --std 'std::iter::Iterator').
bm.record("v1, linear", 2.0, |t| { for (word, _occurrences) in word_occurrences.iter() { s4::bm_call!( t, linear_pos_v1( &word_occurrences, |(w, _o): &(String, _)| w == word ) ); } });
FnMut(&T) -> std::cmp::Ordering, qui s'utilisera comme une fonction indiquant si l'élément désigné en paramètre est inférieur, équivalent ou supérieur à celui que nous recherchons (voir
rustup doc --std 'std::cmp::Ordering').
a.cmp(&b)(voir
rustup doc --std 'std::cmp::Ord'). Réalisez une fonction binary_pos_v2() très semblable à la précédente, mais qui utilise une approche plus idiomatique (fonctionnelle).
rustup doc --std 'slice').
rustup doc --std 'std::result::Result').
rustup doc --std 'trait') nommé Area proposant comme unique service une fonction area() désignant le type concerné par une référence partagée (non-mutable) et renvoyant un réel f64. Désormais, il est possible d'exprimer la fonction resistance() comme ceci :
rustup doc --std 'trait') Area pour quelques types :
s4::assert_float_eq!()servira à vérifier l'équivalence à un écart près.
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#[allow(unused_imports)] use crate::s4;
pub const SECTIONS: &[fn()] = &[section_1, section_2, section_3];
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// Finds the position of the minimal element in a sequence. /// /// In case the sequence is empty, `None` is returned, otherwise /// the index in the sequence is provided. /// /// # Examples /// ``` /// use s4prg::chapter_4::find_min_pos; /// let int_vec = vec![1_i32, -2, 5, -3, 0, 4]; /// let word_array = ["listen", "to", "her", "lovely", "story"]; /// assert_eq!(find_min_pos(&int_vec[0..0]), None); /// assert_eq!(find_min_pos(&int_vec), Some(3)); /// assert_eq!(find_min_pos(&word_array), Some(2)); /// ``` pub fn find_min_pos<T>(seq: &[T]) -> Option<usize> where T: std::cmp::PartialOrd, { if false { // original specific version let mut min_pos_opt = None; let mut min_elem_ref_opt = None; for (pos, elem_ref) in seq.iter().enumerate() { let update_min = match min_elem_ref_opt { Some(min_elem_ref) => elem_ref < min_elem_ref, None => true, }; if update_min { min_elem_ref_opt = Some(elem_ref); min_pos_opt = Some(pos); } } min_pos_opt } else { // reuse a more general function in order to avoid code duplication find_best_pos(seq, |a, b| a < b) } }
/// Finds the position of the maximal element in a sequence. /// /// In case the sequence is empty, `None` is returned, otherwise /// the index in the sequence is provided. /// /// # Examples /// ``` /// use s4prg::chapter_4::find_max_pos; /// let int_vec = vec![1_i32, -2, 5, -3, 0, 4]; /// let word_array = ["listen", "to", "her", "lovely", "story"]; /// assert_eq!(find_max_pos(&int_vec[0..0]), None); /// assert_eq!(find_max_pos(&int_vec), Some(2)); /// assert_eq!(find_max_pos(&word_array), Some(1)); /// ``` pub fn find_max_pos<T>(seq: &[T]) -> Option<usize> where T: std::cmp::PartialOrd, { if false { // original specific version let mut max_pos_opt = None; let mut max_elem_ref_opt = None; for (pos, elem_ref) in seq.iter().enumerate() { let update_max = match max_elem_ref_opt { Some(min_elem_ref) => elem_ref < min_elem_ref, None => true, }; if update_max { max_elem_ref_opt = Some(elem_ref); max_pos_opt = Some(pos); } } max_pos_opt } else { // reuse a more general function in order to avoid code duplication find_best_pos(seq, |a, b| a > b) } }
/// Finds the position of the best element in a sequence, /// according to a specific comparison criterion. /// /// An element `a` is considered better than an element `b` if /// `compare(&a, &b)` returns `true`. /// In case the sequence is empty, `None` is returned, otherwise /// the index in the sequence is provided. /// /// # Examples /// ``` /// use s4prg::chapter_4::find_best_pos; /// let int_vec = vec![1_i32, -2, 5, -3, 0, 4]; /// let word_array = ["listen", "to", "her", "lovely", "story"]; /// assert_eq!(find_best_pos(&int_vec[0..0], |a, b| a.abs() < b.abs()), None); /// assert_eq!(find_best_pos(&int_vec, |a, b| a.abs() < b.abs()), Some(4)); /// assert_eq!(find_best_pos(&word_array, |a, b| a.len() > b.len()), Some(0)); /// ``` pub fn find_best_pos<T, C>( seq: &[T], mut compare: C, ) -> Option<usize> where C: FnMut(&T, &T) -> bool, { let mut best_pos_opt = None; let mut best_elem_ref_opt = None; for (pos, elem_ref) in seq.iter().enumerate() { let update_best = match best_elem_ref_opt { Some(best_elem_ref) => compare(elem_ref, best_elem_ref), None => true, }; if update_best { best_elem_ref_opt = Some(elem_ref); best_pos_opt = Some(pos); } } best_pos_opt }
fn section_1() { println!("\n~~~~ chapter 4, section 1 ~~~~"); let int_vec = vec![1_i32, -2, 5, -3, 0, 4]; let word_array = ["listen", "to", "her", "lovely", "story"]; println!("integers: {:?}", int_vec); println!("words: {:?}", word_array); // if let Some(pos) = find_min_pos(&int_vec) { println!("min integer at {} ~~> {:?}", pos, int_vec[pos]); } if let Some(pos) = find_min_pos(&word_array) { println!("min word at {} ~~> {:?}", pos, word_array[pos]); } // if let Some(pos) = find_max_pos(&int_vec) { println!("max integer at {} ~~> {:?}", pos, int_vec[pos]); } if let Some(pos) = find_max_pos(&word_array) { println!("max word at {} ~~> {:?}", pos, word_array[pos]); } // if let Some(pos) = find_best_pos(&int_vec, |a, b| a.abs() < b.abs()) { println!( "best integer (by minimal absolute value) at {} ~~> {:?}", pos, int_vec[pos] ); } if let Some(pos) = find_best_pos(&word_array, |a, b| a.len() > b.len()) { println!( "best word (by maximal length) at {} ~~> {:?}", pos, word_array[pos] ); } }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// Finds the position of an element in a sequence for which the detection /// predicate is true. /// /// This function uses a linear search and stops as soon as such an element /// is found. /// In case no such element is found, `None` is returned, otherwise /// the index in the sequence is provided. /// /// # Examples /// ``` /// use s4prg::chapter_4::linear_pos_v1 as linear_pos; /// let int_vec = vec![1_i32, -2, 5, -3, 0, 4]; /// let word_array = ["listen", "to", "her", "lovely", "story"]; /// assert_eq!(linear_pos(&int_vec, |e| *e == 8), None); /// assert_eq!(linear_pos(&int_vec, |e| *e == 5), Some(2)); /// assert_eq!(linear_pos(&word_array, |e| *e == "ugly"), None); /// assert_eq!(linear_pos(&word_array, |e| *e == "lovely"), Some(3)); /// ``` pub fn linear_pos_v1<T, D>( seq: &[T], mut detect: D, ) -> Option<usize> where D: FnMut(&T) -> bool, { for (pos, elem_ref) in seq.iter().enumerate() { if detect(elem_ref) { return Some(pos); } } None }
/// Finds the position of an element in a sequence for which the detection /// predicate is true. /// /// This function uses a linear search and stops as soon as such an element /// is found. /// In case no such element is found, `None` is returned, otherwise /// the index in the sequence is provided. /// This version uses the idiomatic (predefined) standard function. /// /// # Examples /// ``` /// use s4prg::chapter_4::linear_pos_v2 as linear_pos; /// let int_vec = vec![1_i32, -2, 5, -3, 0, 4]; /// let word_array = ["listen", "to", "her", "lovely", "story"]; /// assert_eq!(linear_pos(&int_vec, |e| *e == 8), None); /// assert_eq!(linear_pos(&int_vec, |e| *e == 5), Some(2)); /// assert_eq!(linear_pos(&word_array, |e| *e == "ugly"), None); /// assert_eq!(linear_pos(&word_array, |e| *e == "lovely"), Some(3)); /// ``` pub fn linear_pos_v2<T, D>( seq: &[T], detect: D, ) -> Option<usize> where D: FnMut(&T) -> bool, { seq.iter().position(detect) }
/// Finds the position of an element in a sequence for which the detection /// predicate reports equality. /// /// This function uses a binary search and stops as soon as such an element /// is found. /// The predicate should report `Less` when the visited element is located /// before the searched element, should report `Greater` when the visited /// element is located after the searched element, and `Equal` when the /// element is found. /// The sequence is supposed to be sorted according to the predicate, /// otherwise the result is meaningless. /// In case no such element is found, `None` is returned, otherwise /// the index in the sequence is provided. /// /// # Examples /// ``` /// use s4prg::chapter_4::binary_pos_v1 as binary_pos; /// let int_vec = vec![-3_i32, -2, 0, 1, 4, 5]; /// let word_array = ["her", "listen", "lovely", "story", "to"]; /// assert_eq!(binary_pos(&int_vec, |e| e.cmp(&8)), None); /// assert_eq!(binary_pos(&int_vec, |e| e.cmp(&1)), Some(3)); /// assert_eq!(binary_pos(&word_array, |e| e.cmp(&"ugly")), None); /// assert_eq!(binary_pos(&word_array, |e| e.cmp(&"lovely")), Some(2)); /// ``` pub fn binary_pos_v1<T, D>( seq: &[T], mut detect: D, ) -> Option<usize> where D: FnMut(&T) -> std::cmp::Ordering, { let mut low = 0; let mut high = seq.len(); while low < high { let mid = low + (high - low) / 2; match detect(&seq[mid]) { std::cmp::Ordering::Less => low = mid + 1, std::cmp::Ordering::Equal => return Some(mid), std::cmp::Ordering::Greater => high = mid, } } None }
/// Finds the position of an element in a sequence for which the detection /// predicate reports equality. /// /// This function uses a binary search and stops as soon as such an element /// is found. /// The predicate should report `Less` when the visited element is located /// before the searched element, should report `Greater` when the visited /// element is located after the searched element, and `Equal` when the /// element is found. /// The sequence is supposed to be sorted according to the predicate, /// otherwise the result is meaningless. /// In case no such element is found, `None` is returned, otherwise /// the index in the sequence is provided. /// This version uses the idiomatic (predefined) standard function. /// /// # Examples /// ``` /// use s4prg::chapter_4::binary_pos_v2 as binary_pos; /// let int_vec = vec![-3_i32, -2, 0, 1, 4, 5]; /// let word_array = ["her", "listen", "lovely", "story", "to"]; /// assert_eq!(binary_pos(&int_vec, |e| e.cmp(&8)), None); /// assert_eq!(binary_pos(&int_vec, |e| e.cmp(&1)), Some(3)); /// assert_eq!(binary_pos(&word_array, |e| e.cmp(&"ugly")), None); /// assert_eq!(binary_pos(&word_array, |e| e.cmp(&"lovely")), Some(2)); /// ``` pub fn binary_pos_v2<T, D>( seq: &[T], detect: D, ) -> Option<usize> where D: FnMut(&T) -> std::cmp::Ordering, { seq.binary_search_by(detect).ok() }
fn section_2() { println!("\n~~~~ chapter 4, section 2 ~~~~"); let int_vec = vec![-3_i32, -2, 0, 1, 4, 5]; println!("integers: {:?}", int_vec); for i in [-2, 8, 4] { println!( "linear_pos_v1: {} ~~> {:?}", i, linear_pos_v1(&int_vec, |e| *e == i) ); println!( "linear_pos_v2: {} ~~> {:?}", i, linear_pos_v2(&int_vec, |e| *e == i) ); println!( "binary_pos_v1: {} ~~> {:?}", i, binary_pos_v1(&int_vec, |e| e.cmp(&i)) ); println!( "binary_pos_v2: {} ~~> {:?}", i, binary_pos_v2(&int_vec, |e| e.cmp(&i)) ); } let word_array = ["her", "listen", "lovely", "story", "to"]; println!("words: {:?}", word_array); for w in ["her", "sad", "story"] { println!( "linear_pos_v1: {:?} ~~> {:?}", w, linear_pos_v1(&word_array, |e| *e == w) ); println!( "linear_pos_v2: {:?} ~~> {:?}", w, linear_pos_v2(&word_array, |e| *e == w) ); println!( "binary_pos_v1: {:?} ~~> {:?}", w, binary_pos_v1(&word_array, |e| e.cmp(&w)) ); println!( "binary_pos_v2: {:?} ~~> {:?}", w, binary_pos_v2(&word_array, |e| e.cmp(&w)) ); } // let mut word_occurrences = s4::load_word_occurrences(); for size in [word_occurrences.len(), 10000, 1000, 100, 10] { word_occurrences.truncate(size); let bm = s4::Benchmark::new(format!("linear/binary_pos {}", size)); bm.record("v1, linear", 2.0, |t| { for (word, _occurrences) in word_occurrences.iter() { s4::bm_call!( t, linear_pos_v1( &word_occurrences, |(w, _o): &(String, _)| w == word ) ); } }); bm.record("v2, linear", 2.0, |t| { for (word, _occurrences) in word_occurrences.iter() { s4::bm_call!( t, linear_pos_v2( &word_occurrences, |(w, _o): &(String, _)| w == word ) ); } }); bm.record("v1, binary", 2.0, |t| { for (word, _occurrences) in word_occurrences.iter() { s4::bm_call!( t, binary_pos_v1( &word_occurrences, |(w, _o): &(String, _)| { w.cmp(word) } ) ); } }); bm.record("v2, binary", 2.0, |t| { for (word, _occurrences) in word_occurrences.iter() { s4::bm_call!( t, binary_pos_v2( &word_occurrences, |(w, _o): &(String, _)| { w.cmp(word) } ) ); } }); println!("{}", bm); } }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// Trait for types that can provide their area. /// /// Calculating the resistance of a cable relies on this trait /// in order to materialise the section of the cable. pub trait Area { fn area(&self) -> f64; // m² }
/// Computes the resistance of a cable, given the resistivity of its /// material, its length and its section. /// /// The formula is taken from /// [this document](https://en.wikipedia.org/wiki/Electrical_resistivity_and_conductivity). /// The section could be anything which implements the `Area` trait. /// /// # Examples /// ``` /// use s4prg::chapter_4::{Area, Square, resistance}; /// use s4prg::s4; /// let square = Square { side: 1e-3 }; /// let copper = resistance(1.68e-8, 10.0, &square); /// let tin = resistance(10.9e-8, 10.0, &square); /// let graphite = resistance(500e-8, 10.0, &square); /// s4::assert_float_eq!(copper, 0.168, 1e-6); /// s4::assert_float_eq!(tin, 1.09, 1e-6); /// s4::assert_float_eq!(graphite, 50.0, 1e-6); /// ``` pub fn resistance<S>( resistivity: f64, // Ω·m length: f64, // m section: &S, // area in m² ) -> f64 where S: Area, { resistivity * length / section.area() }
#[derive(Debug, Clone)] pub struct Circle { pub diameter: f64, // m } impl Area for Circle { fn area(&self) -> f64 { std::f64::consts::PI * 0.25 * self.diameter * self.diameter // m² } }
#[derive(Debug, Clone)] pub struct Square { pub side: f64, // m } impl Area for Square { fn area(&self) -> f64 { self.side * self.side // m² } }
impl Area for f64 { fn area(&self) -> f64 { *self // m² } }
#[derive(Debug, Clone)] pub enum Section { Circle(Circle), Square(Square), Value(f64), } impl Area for Section { fn area(&self) -> f64 { match self { Self::Circle(c) => c.area(), Self::Square(s) => s.area(), Self::Value(v) => v.area(), } } }
fn section_3() { println!("\n~~~~ chapter 4, section 3 ~~~~"); /* resistivity values taken from https://en.wikipedia.org/wiki/Electrical_resistivity_and_conductivity */ let materials = [ ("copper", 1.68e-8), // Ω·m ("tin", 10.9e-8), // Ω·m ("graphite", 500e-8), // Ω·m ]; let length = 10.0; // m let circle = Circle { diameter: 1e-3 }; // m println!("{:?}", circle); for (material, resistivity) in materials { println!( "• {}, {} m ~~> {} Ω", material, length, resistance(resistivity, length, &circle) ); } let square = Square { side: 1e-3 }; // m println!("{:?}", square); for (material, resistivity) in materials { println!( "• {}, {} m ~~> {} Ω", material, length, resistance(resistivity, length, &square) ); } let area_value = 1e-6; // m² println!("section {:?}", area_value); for (material, resistivity) in materials { println!( "• {}, {} m ~~> {} Ω", material, length, resistance(resistivity, length, &area_value) ); } println!("...mixed sections..."); let sections = [ Section::Circle(circle), Section::Square(square), Section::Value(area_value), ]; for (material, resistivity) in materials { for section in sections.iter() { println!( "{}, {} m, {:?} ~~> {} Ω", material, length, section, resistance(resistivity, length, section) ); } } }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#[allow(unused_imports)] use s4prg::{chapter_4::*, s4};
#[test] fn chapter_4_section_1() { let mut word_occurrences = s4::load_word_occurrences(); let least_used = word_occurrences[find_best_pos( &word_occurrences, |(_w1, o1), (_w2, o2)| o1 < o2, ) .unwrap()] .clone(); let most_used = word_occurrences[find_best_pos( &word_occurrences, |(_w1, o1), (_w2, o2)| o1 > o2, ) .unwrap()] .clone(); // .sort_unstable_by_key() would shuffle equivalent elements! word_occurrences.sort_by_key(|(_w, o)| *o); assert_eq!(word_occurrences.first().unwrap(), &least_used); assert_eq!(word_occurrences.last().unwrap(), &most_used); }
#[test] fn chapter_4_section_2() { let mut word_occurrences = s4::load_word_occurrences(); word_occurrences.truncate(1000); for (word, _occurrences) in word_occurrences.iter() { let eq_word = |(w, _o): &(String, _)| w == word; let cmp_word = |(w, _o): &(String, _)| w.cmp(word); let l1 = linear_pos_v1(&word_occurrences, eq_word); let l2 = linear_pos_v2(&word_occurrences, eq_word); let b1 = binary_pos_v1(&word_occurrences, cmp_word); let b2 = binary_pos_v2(&word_occurrences, cmp_word); assert_eq!(l1, l2); assert_eq!(l2, b1); assert_eq!(b1, b2); assert_eq!(word_occurrences[b2.unwrap()].0, *word); } }
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~