Skip to content
Snippets Groups Projects
Commit eb9668bd authored by orestis.malaspin's avatar orestis.malaspin Committed by Michaël El Kharroubi
Browse files

Adds part08 illustrates Vec and iterators

parent 300f0df7
No related branches found
No related tags found
1 merge request!13Adds part08 illustrates Vec and iterators
[package]
name = "part08"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
use crate::minimum::Minimum;
/// Larger ints based on a [Vec] of [u8] to repensent arbitrary lengthy numbers.
/// The number has a sign as well.
pub struct CustomInt {
/// The data contains the unsigned integers that are read from right to left
/// The number 1337 is stored as vec![7, 3, 3, 1]. Each number must be in the range [0,9]
/// and no trailing 0s are allowed.
data: Vec<u8>,
/// Contains the sign of the number +/-1;
sign: i8,
}
impl CustomInt {
/// Tries to create a new [CustomInt]. If the number is valid it returns
/// an Ok(CustomInt) an Error otherwise.
///
/// # Examples
///
/// ```
/// use part08::custom_int::CustomInt;
/// let num = CustomInt::try_new(vec![1, 2, 3, 4], 1);
/// assert!(num.is_ok());
/// let num = CustomInt::try_new(vec![1, 2, 3, 4], -1);
/// assert!(num.is_ok());
/// let num = CustomInt::try_new(vec![1, 2, 3, 4], 10);
/// assert!(num.is_err());
/// let num = CustomInt::try_new(vec![1, 2, 3, 4], -10);
/// assert!(num.is_err());
/// ```
///
pub fn try_new(data: Vec<u8>, sign: i8) -> Result<Self, String> {
// EXERCISE:
// We don't check for trailing 0s but maybe this could be an exercise.
// Also we should check numbers are between 0 and 9.
if sign == 1 || sign == -1 {
Ok(CustomInt { data, sign })
} else {
Err(String::from("Invalid sign."))
}
}
}
impl Clone for CustomInt {
fn clone(&self) -> Self {
CustomInt {
data: self.data.clone(),
sign: self.sign,
}
}
}
impl Minimum for CustomInt {
// EXERCISE: Correct this function by using clippy and let it guide you.
// Get inspiration from Display to compute the Minimum
fn min(self, rhs: Self) -> Self {
if self.sign < rhs.sign {
return self;
} else if self.sign > rhs.sign {
return rhs;
}
if self.data.len() < rhs.data.len() {
return self;
} else if self.data.len() > rhs.data.len() {
return rhs;
}
for (l, r) in self.data.iter().rev().zip(rhs.data.iter().rev()) {
let ls = (*l as i8) * self.sign;
let rs = (*r as i8) * self.sign;
if ls < rs {
return self;
} else if ls > rs {
return rhs;
}
}
self
}
}
impl PartialEq for CustomInt {
fn eq(&self, other: &Self) -> bool {
if self.sign == other.sign && self.data.len() == other.data.len() {
self.data
.iter()
.zip(other.data.iter())
.try_fold(true, |_, (l, r)| if l == r { Some(true) } else { None })
.is_some()
} else {
false
}
}
}
impl std::fmt::Display for CustomInt {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// This could be replaced by an `?`
if self.sign == -1 {
if let Err(e) = write!(f, "-") {
return Err(e);
}
}
// This could be replaced by an `?`
let res = self
.data
.iter()
.rev()
.try_fold((), |_, t| write!(f, "{}", t));
res
}
}
// EXERCISE: write tests
// EXERCISE: Modify Minimum trait to take references?
#[cfg(test)]
mod tests {}
use std::io::BufRead;
use crate::custom_int::CustomInt;
/// Reads i32 from the command line and returns a [Vec] containing
/// these numbers. Returns errors when the parsing fails.
pub fn read_command_line() -> Result<Vec<i32>, String> {
let mut v = Vec::new();
let stdin = std::io::stdin();
println!("Enter a list of numbers, one per line. End with Ctrl-D (Linux) or Ctrl-Z (Windows).");
for line in stdin.lock().lines() {
let line = match line {
Ok(l) => l,
Err(_) => {
return Err(String::from("Could not read line"));
}
};
match line.trim().parse::<i32>() {
Ok(num) => v.push(num),
Err(_) => {
return Err(String::from("Could not parse integer"));
}
}
}
Ok(v)
}
// EXERCISE: Rewrite using ?
pub fn read_command_line_custom_int() -> Result<Vec<CustomInt>, String> {
let v = vec![
CustomInt::try_new(vec![1, 3, 6, 9], 1).unwrap(),
CustomInt::try_new(vec![2, 4, 2, 1], -1).unwrap(),
CustomInt::try_new(vec![7, 4, 5, 3], 1).unwrap(),
CustomInt::try_new(vec![4, 1, 1, 1], -1).unwrap(),
];
Ok(v)
}
/// Prints all the elements of the `tab`.
/// Tab is borrowed here
pub fn print_tab(tab: &Vec<i32>) {
for t in tab {
print!("{} ", t);
}
println!();
}
/// Prints all the elements of the `tab`.
/// Tab is borrowed here
pub fn print_tab_CustomInt(tab: &Vec<CustomInt>) {
for i in tab {
println!("{i} ");
}
println!();
}
/*!
part08 illustrates the use of [Vec] and the Error Handling with [Option] and [Result].
It also showcases struct enums.
*/
pub mod custom_int;
pub mod io;
mod minimum;
pub mod something_or_nothing;
#[cfg(test)]
mod tests {
use crate::minimum::Minimum;
use crate::something_or_nothing::{find_min, SomethingOrNothing};
#[test]
fn test_creation() {
let n1: SomethingOrNothing<i32> = SomethingOrNothing::default();
assert!(n1 == SomethingOrNothing::default());
let n2: SomethingOrNothing<i32> = SomethingOrNothing::new(1);
assert!(n2 == SomethingOrNothing::new(1));
}
#[test]
#[should_panic]
fn test_failure_creation() {
let n2: SomethingOrNothing<i32> = SomethingOrNothing::new(1);
assert!(n2 == SomethingOrNothing::default());
assert!(n2 == SomethingOrNothing::new(2));
}
#[test]
fn test_min() {
let a = vec![1, 5, -1, 2, 0, 10, 11, 0, 3];
let min = find_min(&a);
assert!(min == SomethingOrNothing::new(-1));
}
#[test]
fn test_min_empty() {
let a: Vec<i32> = vec![];
let min = find_min(&a);
assert!(min == SomethingOrNothing::default());
}
#[test]
fn test_min_i32() {
let x = 5;
let y = 10;
assert_eq!(Minimum::min(x, y), x);
assert_eq!(Minimum::min(y, x), x);
assert_eq!(Minimum::min(x, x), x);
assert_eq!(Minimum::min(y, y), y);
}
#[test]
fn test_min_something_or_nothing() {
let x = SomethingOrNothing::new(5i32);
let y = SomethingOrNothing::new(10i32);
let z = SomethingOrNothing::default();
assert!(x.min(y) == x);
assert!(y.min(x) == x);
assert!(z.min(y) == y);
assert!(y.min(z) == y);
assert!(z.min(z) == z);
}
}
use part08::io;
use part08::something_or_nothing::find_min;
fn main() -> Result<(), String> {
let tab = match io::read_command_line_custom_int() {
Ok(tab) => tab,
Err(s) => return Err(s),
};
println!("Among the Big Ints in the list:");
io::print_tab_CustomInt(&tab);
// There are alternatives to access fields of tuples
let min = find_min(&tab);
// The first field is not used therefore we can replace it with "_"
min.print();
Ok(())
}
// If we remove Copy, we have a problem with the t in tab
// in the computation of the minimum.
pub trait Minimum: Clone {
fn min(self, rhs: Self) -> Self;
}
impl Minimum for i32 {
fn min(self, rhs: Self) -> Self {
if self < rhs {
self
} else {
rhs
}
}
}
use std::fmt::Display;
use crate::minimum::Minimum;
/// An generic enumerated type that encapsulates and Option<T>.
#[derive(Clone, Copy)]
pub struct SomethingOrNothing<T>(Option<T>);
impl<T: Minimum + Display> SomethingOrNothing<T> {
pub fn new(val: T) -> Self {
SomethingOrNothing(Some(val))
}
/// A static function that prints the content of a SomethingOrNothing.
pub fn print(&self) {
match &self.0 {
None => println!("Nothing."),
Some(val) => println!("Something is: {}", val),
}
}
}
impl<T> Default for SomethingOrNothing<T> {
/// By Default a [SomethingOrNothing] is a nothing.
fn default() -> Self {
SomethingOrNothing(None)
}
}
impl<T: PartialEq + Minimum> PartialEq for SomethingOrNothing<T> {
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(None, None) => true,
(Some(lhs), Some(rhs)) => lhs == rhs,
_ => false,
}
}
}
impl<T: Minimum + Display> Minimum for SomethingOrNothing<T> {
fn min(self, rhs: Self) -> Self {
match (self.0, rhs.0) {
(None, None) => SomethingOrNothing(None),
(Some(lhs), Some(rhs)) => SomethingOrNothing::new(lhs.min(rhs)),
(None, Some(rhs)) => SomethingOrNothing::new(rhs),
(Some(lhs), None) => SomethingOrNothing::new(lhs),
}
}
}
/// Computes the minimum of an Array of a type T which implements the [Minimum] trait.
/// Returns a [Some] containing the the minimum value
/// or [None] if no minimum value was found.
///
/// # Examples
///
/// ```
/// # use part08::something_or_nothing::{SomethingOrNothing, find_min};
/// # fn main() {
/// let tab = vec![10, 32, 12, 43, 52, 53, 83, 2, 9];
/// let min = find_min(&tab);
/// assert!(min == SomethingOrNothing::new(2));
/// # }
/// ```
///
/// ```
/// # use part08::something_or_nothing::{SomethingOrNothing, find_min};
/// # fn main() {
/// let tab: Vec<i32> = vec![];
/// let min = find_min(&tab);
/// assert!(min == SomethingOrNothing::default());
/// # }
/// ```
pub fn find_min<T: Minimum + Display>(tab: &Vec<T>) -> SomethingOrNothing<T> {
// A very elegant fold applied on an iterator
tab.iter().fold(SomethingOrNothing::default(), |res, x| {
res.min(SomethingOrNothing::new(x.clone()))
})
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment