Solutions to the exercises in the official Rust book [Chapter 8]

#1

Hello,

I arrived at Chapter 8 of the official Rust book and at the end there are some exercises. Is there any official (or at least good and well documented) solution out there ? I’ve tried searching the internets with no success, so I’m asking here!

I’d like to compare proper solutions to my crappy beginner solution to learn more.

Thanks in advance.

#2

There aren’t any official answers to the exercises that I know of, you can post them on this forum and request feedback for it, it’s what I did :slight_smile:

#3

That’s correct! Maybe after the book is finished, I can put up solutions :slight_smile:

2 Likes
#4

Ok so here we go. The exercise I did so far :

Given a list of integers, use a vector and return the mean (average), 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.

I’m open to all sorts of comment :

  • whether there’s a better way,
  • whether there’s a faster way,
  • memory/ownership management
  • code safety
  • whether it’s idiomatic or not
  • code style/formatting
  • etc.

Please remember that I haven’t finished the book yet, I’m stil at chapter 8. So I refrained myself from using features explained after like lambdas, function level comment/doc-string and all the things I don’t even know yet.

Also, while I’m “happy” with my solutions for the mean and median (they handle empty vectors and NaN somehow), the code for mode is… beginner-level crap haha.

Thanks in advance for all your comments!

fn exercise1() {
    let int_list: Vec<i32> = vec![1, 2, 3, 3, 4, 5, 5, 5, 6, 7, 51];

    match mean(&int_list) {
        Some(avg) => println!("mean {}", avg),
        None => println!("mean unavailable for {:?}", int_list),
    }
    match median((&int_list)) {
        Some(result) => { println!("median {}", result)},
        None => { println!("median unavailable for {:?}", int_list);}
    }

    println!("mode {}", mode(&int_list));
}

fn mean(int_list: &Vec<i32>) -> Option<f64> {
    let sum: i32 = int_list.iter().sum();
    let maybe_mean = sum as f64 / int_list.len() as f64;

    if maybe_mean.is_nan() {
        None
    } else {
        Some(maybe_mean)
    }
}

fn median(int_list: &Vec<i32>) -> Option<i32> {
    let mut new_list = int_list.clone();
    new_list.sort();

    match (new_list.first(), new_list.last()) {
        (Some(first), Some(last)) => Some((first + last) / 2),
        (_, _) => None,
    }
}

fn mode(int_list: &Vec<i32>) -> i32 {
    let mut occurrences = HashMap::new();

    for v in int_list.iter() {
        let mut count = occurrences.entry(v).or_insert(0);
        *count += 1;
    }

    let mut maximum = occurrences.get(&int_list[0]).expect("No value in occurrences for first value of int_list");
    let mut result = &int_list[0];

    for (key, value) in &occurrences {
        if value > maximum {
            result = *key;
            maximum = value;
        }
    }

    *result
}
``
1 Like
#5

Thanks for sharing this. Your solution to getting the average value looks a lot nicer than mine. However, your “median” function seems to be wrong.

“…return the median( when sorted, the value in the middle position)”

given a sequence of [10,2,45,32,14,38,17,22,93] this, when sorted, leads to:

Value at 0 is : 2
Value at 1 is : 10
Value at 2 is : 14
Value at 3 is : 17
Value at 4 is : 22 <- median
Value at 5 is : 32
Value at 6 is : 38
Value at 7 is : 45
Value at 8 is : 93

your result is 47.
I am too ashamed to post my code- honestly its a disgrace :smile:

#6

The median function takes the first and last value of the sorted vector ((2+93)/2=47.5) and not the middle value.

You might want to run clippy and check out the result and explanations of those warnings.

An other solution to the mode problem is to take the sorted vector as input. You can than count occurrences as they are all grouped.

#7

It shouldn’t, median takes the value from the middle of the ordered collection. If the collection has an even count of elements, the “middle” value is interpolated by calculating the average of the two values closest to the center.

1 Like
#8

Correct, bad wording of mine. What I meant was that Djebbzz his implementation uses the first and last value of the sorted list. And he should use the middle values.

#9

There are a number of issues with the code above:
a) i think its nicer to check if the list holds any elements rathen than running in to a division by 0 and checking for nan (not a number)
b) the median is calculated incorrectly as i pointed out, also median should be returned as a float to be correct in all cases.
c) mode can be undefined, the code does not handle this

Here is my (updated: complete) solution for exercise 8.1. I am thankful for any ideas on how to improve this!

use std::collections::HashMap;

pub fn calc_avg( l: &Vec<i32> ) -> Option<f64> {
    let sum: i32 = l.iter().sum();
    let len: f64 = l.len() as f64;
    if l.len() > 0 {
        return Some(sum as f64 /len);
    }
    else {
        return None
    }
}

pub fn calc_median( x: &Vec<i32> ) -> Option<f64> {
    let mut l = x.clone();
    l.sort();

    let middle_id = l.len()/2;
    match l.get(middle_id) {
        Some(i) => {
            let mut median = *i as f64;
            if l.len() % 2 == 0 {
                // list of even size 
                median += l[middle_id-1] as f64;
                median = median / 2.0;
            }
            return Some(median);
        }
        _ => return None
    }
}

pub fn calc_mode( x: &Vec<i32>  ) -> Option<i32> {
    // add keys to a hashmap and keep counts as values
    let mut m = HashMap::new();
    for i in x {
        let count = m.entry(i).or_insert(0);
        *count += 1;
    }

    let mut highest_count = 0;
    let mut highkey = 0;
    let mut valid = true;

    for (key,value) in m {
        if value > highest_count {
            highest_count = value;
            highkey = *key;
            valid = true;
        }
        else if value == highest_count {
            valid = false;
        }
    }

    if highest_count >= 0 && valid == true {
        Some(highkey)
    } 
    else {
        None
    }    
}

pub fn main() {
    let v: Vec<i32> = vec![4,1,5,3,2,16,5,5,1,1];

    match calc_avg(&v) {
        Some(result) => { println!("average is {}", result)},
        None => { println!("average undefined for {:?}", v);}
    }
    match calc_median(&v) {
        Some(result) => { println!("median is {}", result)},
        None => { println!("median undefined for {:?}", v);}
    }
    match calc_mode( &v ) {
        Some(value) => { println!("mode is {}",value) },
        None => { println!("mode undefined for {:?}", v);}
    }
}
#10

What would be nicer is if it were more interactive, like Codecademy and Khan Academy.

Take a look at how it explains how to do programming

In this link, it is an interactive video, meaning that you can change code inside the video so you can experiment.


In this link I like how they give you an interactive exercise, and I wish to see that in Rust’s documentation as well.

#13

Unfortunately, the technology to make paper interactive has not been perfected yet :wink: (The book is printed by No Starch Press, and first and foremost is written for the purpose of being printed).

If you’re looking for more interactivity, I would suggest checking out other resources such as rustlings or the Rust track in exercism.

4 Likes
#14

Lol XDD I thought you guys were talking about the Rust book online.

Sure I will check em out thanks for hte suggestion :slight_smile:

#15

Thank you everyone!

I gave up learning Rust a long time ago, but I recently re-started, using the “Programming Rust” book instead. So far so good!

I’ll be sure to check again all your suggestions once I finish the book. See you in some far future :wink:

1 Like
Solutions to exercises in The Book