Initial commit with my view on how to answer the exercices

This commit is contained in:
Zykino 2018-11-24 21:12:16 +01:00
commit 9059b76493
8 changed files with 391 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/target
**/*.rs.bk

4
Cargo.lock generated Normal file
View File

@ -0,0 +1,4 @@
[[package]]
name = "chapter8_exercices"
version = "0.1.0"

7
Cargo.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "chapter8_exercices"
version = "0.1.0"
authors = ["Zykino <Zykino@users.noreply.github.com>"]
[dependencies]
# regex = "1"

10
README.md Normal file
View File

@ -0,0 +1,10 @@
# My take Rust second edition chapter 8 exercices
> Given a list of integers, use a vector and return the mean (the average value), median (when sorted, the value in the middle position), and mode (the value that occurs most often; a hash map will be helpful here) of the list.
>
> Convert strings to pig latin. The first consonant of each word is moved to the end of the word and “ay” is added, so “first” becomes “irst-fay.” Words that start with a vowel have “hay” added to the end instead (“apple” becomes “apple-hay”). Keep in mind the details about UTF-8 encoding!
>
> Using a hash map and vectors, create a text interface to allow a user to add employee names to a department in a company. For example, “Add Sally to Engineering” or “Add Amir to Sales.” Then let the user retrieve a list of all people in a department or all people in the company by department, sorted alphabetically.
Any feedback to make it more idiomatic Rust and/or have better API is welcome.
I sometimes use the `showroom` functions to keep all the code (lib + tests) in the same file.

179
src/company.rs Normal file
View File

@ -0,0 +1,179 @@
// extern crate regex;
//
// use self::regex::Regex;
use std::str::FromStr;
use std::str::SplitWhitespace;
#[derive(Debug, Clone, PartialEq)]
enum Service {
Engineering,
Marketing,
Sales,
}
#[derive(Debug, Clone)]
pub struct Employee {
name: String,
service: Service,
}
// https://www.reddit.com/r/rust/comments/2vqama/parse_string_as_enum_value/cojzafn/
impl FromStr for Service {
type Err = ();
fn from_str(s: &str) -> Result<Service, ()> {
match s {
"engineering" => Ok(Service::Engineering),
"marketing" => Ok(Service::Marketing),
"sales" => Ok(Service::Sales),
_ => Err(()),
}
}
}
pub fn parse_command(employees: &mut Vec<Employee>, cmd: &str) -> Vec<Employee> {
let cmd = String::from(cmd);
let mut iter = cmd.split_whitespace();
let employees = match iter.next() {
Some(a) => match a.to_lowercase().as_str() {
"add" => add_employee(employees, iter),
"remove" => remove_employee(employees, iter),
"list" => list_employees(employees.to_vec(), iter),
_ => {
println!("Valid actions are Add, Remove and List.");
employees.to_vec()
}
},
None => {
println!("You need to write an action");
employees.to_vec()
}
};
employees
// Regex attempt
// let re = Regex::new(r"(?P<action>\w+) (?P<name>\w+) to (?P<service>\w+)").unwrap();
// let caps = match re.captures(cmd) {
// Some(c) => c,
// None => {
// println!("Command not understood");
// return;
// }
// };
// println!(
// "{:?}, {:?}, {:?}",
// &caps["action"], &caps["name"], &caps["service"]
// );
}
fn add_employee(employees: &mut Vec<Employee>, mut iter: SplitWhitespace) -> Vec<Employee> {
let mut new_employee = Employee {
name: String::from(""),
service: Service::Engineering,
};
new_employee.name = if let Some(name) = iter.next() {
String::from(name)
} else {
println!("Enter the employee's name and service.");
return employees.to_vec();
};
if let None = iter.next() {
println!(r#"You need the "to" preposition in order to form an intelligible sentence."#);
return employees.to_vec();
};
new_employee.service = if let Some(service) = iter.next() {
match Service::from_str(&service.to_lowercase()) {
Ok(srv) => srv,
Err(_) => {
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
return employees.to_vec();
}
}
} else {
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
return employees.to_vec();
};
println!("Adding {} to {:?}", new_employee.name, new_employee.service);
employees.push(new_employee);
employees.to_vec()
}
fn remove_employee(employees: &mut Vec<Employee>, mut iter: SplitWhitespace) -> Vec<Employee> {
let mut delete_employee = Employee {
name: String::from(""),
service: Service::Engineering,
};
delete_employee.name = if let Some(name) = iter.next() {
String::from(name)
} else {
println!("Enter the employee's name and service.");
return employees.to_vec();
};
if let None = iter.next() {
println!(r#"You need the "from" preposition in order to form an intelligible sentence."#);
return employees.to_vec();
};
delete_employee.service = if let Some(service) = iter.next() {
match Service::from_str(&service.to_lowercase()) {
Ok(srv) => srv,
Err(_) => {
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
return employees.to_vec();
}
}
} else {
println!("Precise the employee's service: Engineering, Marketing, or Sales.");
return employees.to_vec();
};
let deleted = employees
.iter_mut()
.position(|n| n.name == delete_employee.name && n.service == delete_employee.service);
match deleted {
Some(x) => {
let removed = employees.remove(x);
println!("Fired {:?} !", removed);
}
None => println!("No employee fired."),
};
employees.to_vec()
}
fn list_employees(employees: Vec<Employee>, mut iter: SplitWhitespace) -> Vec<Employee> {
let service = if let Some(service) = iter.next() {
match Service::from_str(&service.to_lowercase()) {
Ok(srv) => Some(srv),
Err(_) => {
if &service.to_lowercase() == "all" {
None
} else {
println!(
"Precise the employee's service: Engineering, Marketing, Sales or all."
);
// return employees;
None
}
}
}
} else {
println!("Precise the employee's service: Engineering, Marketing, Sales or all.");
// return Err("Precise the employee's service: Engineering, Marketing, or Sales.");
None
};
let mut list = employees.to_owned();
if let Some(srv) = service {
println!("Keeping only the employees from the selected service ({:?})", srv);
list.retain(|empl| empl.service == srv);
}
list
}

37
src/main.rs Normal file
View File

@ -0,0 +1,37 @@
mod math_vector;
mod pig_latin;
mod company;
fn main() {
math_vector::showroom();
pig_latin::showroom();
let mut employees: Vec<company::Employee> = vec![];
// Test fail cases
company::parse_command(&mut employees, "");
company::parse_command(&mut employees, "Toto");
company::parse_command(&mut employees, "Add Toto");
company::parse_command(&mut employees, "Add Toto Sales");
company::parse_command(&mut employees, "Add Toto to Streamer");
company::parse_command(&mut employees, "Add Sally toto Engineering");
company::parse_command(&mut employees, "Add Amir to Sales");
company::parse_command(&mut employees, "Add Sarah to Marketing");
company::parse_command(&mut employees, "Add Amir to Engineering");
company::parse_command(&mut employees, "Add Paul to Engineering");
company::parse_command(&mut employees, "Add Paul to Engineering");
println!("{:?}", employees);
company::parse_command(&mut employees, "Remove Amir from Sales");
company::parse_command(&mut employees, "Remove Paul from Engineering");
company::parse_command(&mut employees, "Remove Sarah from Sales");
println!("{:?}", employees);
println!("{:?}", company::parse_command(&mut employees, "List Engineering employees"));
println!("{:?}", company::parse_command(&mut employees, "List Marketing employees"));
println!("{:?}", company::parse_command(&mut employees, "List Sales employees"));
println!("{:?}", company::parse_command(&mut employees, "List all employees"));
}

108
src/math_vector.rs Normal file
View File

@ -0,0 +1,108 @@
use std::collections::HashMap;
pub fn showroom() {
println!("Calculate the Mean, Median and Mode of some vectors");
let v = vec![1, 2, 8, 5, 1, 9, 5, 4, 6, 5, 5];
println!("Vector: {:?}", v);
match mean(&v) {
Some(x) => println!("Mean: {:?}", x),
None => println!("Not possible to process the Mean of this Vec<T>."),
}
match median(&v) {
Some(x) => println!("Median: {:?}", x),
None => println!("Not possible to process the Median of this Vec<T>."),
}
match mode(&v) {
Some(x) => println!("Modes: {:?}", x),
None => println!("Not possible to process the Modes of this Vec<T>."),
}
let v = vec![1, 2, 8, 7, 8, 6, 7, 5, 1, 9, 5, 4, 6, 5, 5, 9, 9, 9, 9];
println!("Vector: {:?}", v);
match mean(&v) {
Some(x) => println!("Mean: {:?}", x),
None => println!("Not possible to process the Mean of this Vec<T>."),
}
match median(&v) {
Some(x) => println!("Median: {:?}", x),
None => println!("Not possible to process the Median of this Vec<T>."),
}
match mode(&v) {
Some(x) => println!("Modes: {:?}", x),
None => println!("Not possible to process the Modes of this Vec<T>."),
}
let v = vec![];
println!("Vector: {:?}", v);
match mean(&v) {
Some(x) => println!("Mean: {:?}", x),
None => println!("Not possible to process the Mean of this Vec<T>."),
}
match median(&v) {
Some(x) => println!("Median: {:?}", x),
None => println!("Not possible to process the Median of this Vec<T>."),
}
match mode(&v) {
Some(x) => println!("Modes: {:?}", x),
None => println!("Not possible to process the Modes of this Vec<T>."),
}
println!();
}
pub fn mean(v: &Vec<i32>) -> Option<f64> {
if v.len() == 0 {
return None
}
let mut sum = 0;
for elem in v {
sum += elem;
}
let sum = sum as f64;
Some((sum / v.len() as f64).into())
}
// https://stackoverflow.com/a/29578077
pub fn median(v: &Vec<i32>) -> Option<f64> {
if v.len() == 0 {
return None
}
let mut v_sorted = vec![0; v.len()];
v_sorted.copy_from_slice(&v);
v_sorted.sort();
if v_sorted.len() % 2 == 0 {
Some((v_sorted[(v_sorted.len() / 2) - 1] + v_sorted[(v_sorted.len() / 2) - 1]) as f64 / 2.)
} else {
Some(v_sorted[v_sorted.len() / 2].into())
}
}
pub fn mode(v: &Vec<i32>) -> Option<i32> {
if v.len() == 0 {
return None
}
let mut map = HashMap::new();
for elem in v {
let count = map.entry(elem).or_insert(0);
*count += 1;
}
let mut mode_key = 0;
let mut mode_val = 0;
for (key, val) in map.iter() {
if val > &mode_val {
mode_key = **key;
mode_val = *val;
}
}
Some(mode_key)
}

44
src/pig_latin.rs Normal file
View File

@ -0,0 +1,44 @@
pub fn showroom() {
println!("Change a world String to \"pig-latin\".");
let consonant = String::from("first");
let vowel = String::from("apple");
let symbol = String::from("!symbolic");
let uppercase = String::from("UPPERCASE");
let accent = String::from("école");
let accent_multiples_char = String::from("église");
let consonant = to_pig_latin(consonant);
println!("And the suffix to add is: {}", consonant);
let vowel = to_pig_latin(vowel);
println!("And the suffix to add is: {}", vowel);
let symbol = to_pig_latin(symbol);
println!("And the suffix to add is: {}", symbol);
let uppercase = to_pig_latin(uppercase);
println!("And the suffix to add is: {}", uppercase);
let accent = to_pig_latin(accent);
println!("And the suffix to add is: {}", accent);
let accent_multiples_char = to_pig_latin(accent_multiples_char);
println!("And the suffix to add is: {}", accent_multiples_char);
println!();
}
pub fn to_pig_latin(mut s: String) -> String {
let vowels = vec!['a', 'e', 'i', 'o', 'u', 'y'];
// FIXME: is there something better than removing the 1st char like this ?
// Since 2 of the 3 branches need to reform the word.
let first_letter = s.remove(0);
if !first_letter.is_alphabetic() {
// Not starting with a letter, ignore and reset the letter
format!("{}{}", first_letter, s)
} else if vowels.contains(&first_letter.to_ascii_lowercase()) {
// Starting with a vowel, reform the word and place add "-hay"
format!("{}{}-hay", first_letter, s)
} else {
// Starting with a consonant, place the consonant at the end of the word followed by "ay"
format!("{}-{}ay", s, first_letter)
}
}