Solutions to exercises in The Book

#1

Continuing the discussion from Solutions to the exercises in the official Rust book [Chapter 8]:

Here are my implementations of the three exercises in Chapter 8.

Question 1:

use std::io;
use std::collections::HashMap;

fn main() {
    let mut numbers = Vec::with_capacity(10);

    get_numbers(&mut numbers);

    if numbers.len() > 0 {
        println!("The average is: {}", get_avg(&numbers));

        numbers.sort();
        println!("The median is: {}", get_median(&numbers));

        match get_mode(&numbers) {
            Some(mut v) => if v.len() > 1 {
                v.sort();
                println!("The modes are: {:?}", v);
            } else {
                println!("The mode is: {}", &v[0]);
            },
            None => println!("There is no mode -- each number occurs exactly once!"),
        }
    } else {
        println!("No numbers entered!");
    }
}

fn get_numbers(numbers: &mut Vec<i32>) {
    println!("Enter the numbers and * to exit:");

    loop {
        let mut num = String::new();

        io::stdin().read_line(&mut num)
            .expect("Failed to read line");

        if num.trim() == "*" {
            break;
        }

        let num = match num.trim().parse() {
            Ok(num) => num,
            Err(_) => continue,
        };

        numbers.push(num);
    }
}

fn get_avg(numbers: &Vec<i32>) -> f64 {
    let mut result = 0;

    for number in numbers {
        result += number;
    }

    result as f64 / numbers.len() as f64
}

fn get_median(numbers: &Vec<i32>) -> f64 {
    let even = numbers.len() % 2 == 0;

    let mut middle = numbers.len() / 2;

    if even {
        (numbers[middle-1] + numbers[middle]) as f64 / 2.0
    } else {
        numbers[middle] as f64
    }
}

fn get_mode(numbers: &Vec<i32>) -> Option<Vec<i32>> {
    let mut map = HashMap::new();

    for num in numbers {
        let count = map.entry(num).or_insert(0);
        *count += 1;
    }

    let mut largest_val = 1;

    for (_, value) in &map {
        if *value > largest_val && *value > 1 {
            largest_val = *value;
        }
    }

    let mut modes = Vec::with_capacity(2);
    let mut mode = None;

    if largest_val > 1 {
        for (key, value) in map {
            if value == largest_val {
                modes.push(*key);
            }
        }
        mode = Some(modes);
    }
    mode
}

Question 2:

use std::io;

fn main() {
    println!("Enter a sentence:");

    let mut s = String::new();

    io::stdin().read_line(&mut s)
        .expect("failed to read line");

    let pig_latin = pig_latin(s);

    println!("{}", pig_latin);
}

fn pig_latin(mut s: String) -> String {
    let mut res = String::new();
    s = s.to_lowercase();

    for word in s.split_whitespace() {
        if let Some(c) = word.chars().next() {
            if c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' {
                res.push_str(&format!("{}-hay ", word));
            } else {
                res.push_str(&format!("{}-{}ay ", &word[1..], &c));
            }
        } else {
            println!("No string given");
        }
    }

    res
}

Question 3:

use std::io;
use std::collections::HashMap;

fn main() {
    println!("Type \"Add <name> to <department>\" to add a person");
    println!("Type \"Retrieve <department>\" or \"Retrieve all\"");
    println!("to get a sorted list of names by department");
    println!("Type \"Exit\" to exit");

    let mut depts: HashMap<String, Vec<String>> = HashMap::new();

    loop {
        let command = get_cmd();

        match parse_cmd(command) {
            Cmd::Add(id) => add_name(id, &mut depts),
            Cmd::Ret(dept) => {
                if let "all" = &dept[..] {
                    retrieve_all(&mut depts);
                } else {
                    retrieve_dept(dept, &mut depts);
                }
            },
            Cmd::Exit => break,
            Cmd::Error(e) => println!("Error parsing command: {}", e),
        }
    }
}

fn get_cmd() -> String {
    println!("\nWhat do you want to do?");

    let mut cmd = String::new();

    io::stdin().read_line(&mut cmd)
        .expect("Failed to read line");

    cmd
}

struct CompanyID {
    name: String,
    dept: String,
}

impl CompanyID {
    fn new() -> CompanyID {
        CompanyID { name: String::from(""), dept: String::from(""), }
    }
}

enum Cmd {
    Add(CompanyID),
    Ret(String),
    Exit,
    Error(&'static str),
}

fn parse_cmd(cmd: String) -> Cmd {
    let mut words = cmd.split_whitespace();

    match words.next() {
        Some("Add") => {
            if let Some(x) = words.next() {
                let mut before_to = true;
                let mut id = CompanyID::new();

                id.name.push_str(x);

                loop {
                    if let Some(x) = words.next() {
                        if x == "to" {
                            before_to = false;
                            continue;
                        }

                        if before_to {
                            id.name.push_str(&(" ".to_owned() + x));
                        } else {
                            if id.dept != "" {
                                id.dept.push_str(" ");
                            }
                            id.dept.push_str(x);
                        }
                    } else {
                        break;
                    }
                }

                if id.dept == "" {
                    Cmd::Error("No department given to write to")
                } else {
                    Cmd::Add(id)
                }
            } else {
                Cmd::Error("No name given")
            }
        },
        Some("Retrieve") => {
            if let Some(x) = words.next() {
                if x == "all" {
                    Cmd::Ret(x.to_string())
                } else {
                    Cmd::Ret(cmd[9..].trim().to_string())
                }
            } else {
                Cmd::Error("No department given to read from")
            }
        },
        Some("Exit") => Cmd::Exit,
        _ => Cmd::Error("Invalid input"),
    }
}

fn add_name(id: CompanyID, depts: &mut HashMap<String, Vec<String>>) {
    let dept = &id.dept;

    if depts.contains_key(dept) {
        // unwrap() is safe here, because we know the value exists.
        let names = depts.get_mut(dept).unwrap();
        names.push(id.name);
        names.sort();
    } else {
        depts.insert(id.dept, vec![id.name]);
    }
}

fn retrieve_all(depts: &HashMap<String, Vec<String>>) {
    for key in depts.keys() {
        retrieve_dept(key.to_string(), depts);
    }
}

fn retrieve_dept(dept: String, depts: &HashMap<String, Vec<String>>) {
    if let Some(names) = depts.get(&dept) {
        println!("{}", &dept);
        for name in names {
            println!("  {}", name);
        }
    } else {
        println!("No {} department", dept);
    }
}

I don’t know how idiomatic it is, but it does the job :wink: Also remember that I just read the Book up till chapter 8 and programmed basic C++ and Delphi/Pascal before, so I am not too worried about the fact that I didn’t use iterators. I will discover them as I get more proficient with Rust. That said, any comments are welcome!

1 Like