3
\$\begingroup\$

The exercise 1.3 of the book Structure and Interpretation of Computer Programs asks the following:

Exercise 1.3. Define a procedure that takes three numbers as arguments and returns the sum of the squares of the two larger numbers.

My answer is this:

#![feature(core)]
use std::cmp;
fn sum_square_largest(x:f64, y:f64, z:f64) -> f64 {
 match partial_min_three(x, y, z) {
 Some(a) => x * x + y * y + z * z - a * a,
 None => 0.0_f64/0.0_f64
 }
}
fn partial_min_three<T>(v1: T, v2: T, v3: T) -> Option<T> where T: PartialOrd<T> {
 match cmp::partial_min(v2, v3) {
 Some(x) => cmp::partial_min(v1, x),
 None => None
 }
}

Rust is the language I know less, I would really appreciate your advice.

asked Apr 7, 2015 at 17:28
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

I find it always pays to be tidy and conscientious about spacing. Here, I'd add spaces after the : in the argument declaration

// fn sum_square_largest(x:f64, y:f64, z:f64) -> f64 {
fn sum_square_largest(x: f64, y: f64, z: f64) -> f64 {

Next, I'd wrap the where clause onto the next line. where clauses often get large, so I find it best to let them have breathing room, and only put one type per line:

// fn partial_min_three<T>(v1: T, v2: T, v3: T) -> Option<T> where T: PartialOrd<T> {
fn partial_min_three<T>(v1: T, v2: T, v3: T) -> Option<T>
 where T: PartialOrd<T>
{

Checking for Some or None and doing something is a common pattern. In this case, you can use Option::and_then:

// match partial_min(v2, v3) {
// Some(x) => partial_min(v1, x),
// None => None
// }
partial_min(v2, v3).and_then(|x| partial_min(v1, x))

Programming is often about clarity in expressing your intentions, and I doubt that the majority of programmers immediately know what 0 / 0 is for floating point numbers. You should be explicit:

// None => 0.0_f64/0.0_f64
None => std::f64::NAN,

Again, we can use existing patterns to deal with checking for Some / None. This time, we can use Option::map:

// match partial_min_three(x, y, z) {
// Some(a) => x * x + y * y + z * z - a * a,
// None => std::f64::NAN,
// } 
partial_min_three(x, y, z)
 .map(|a| x * x + y * y + z * z - a * a)
 .unwrap_or(std::f64::NAN)

I disagree with your choice of NaN as a magic value. Magic values drive me crazy, and Rust has great choices for avoiding them - Option and Result!

fn sum_square_largest(x: f64, y: f64, z: f64) -> Option<f64> {
 partial_min_three(x, y, z)
 .map(|a| x * x + y * y + z * z - a * a)
}

I'd probably create a tiny function for squaring. It's likely to get inlined, so I'm not worried about performance, just readability:

.map(|a| square(x) + square(y) + square(z) - square(a))

All together (playpen):

fn sum_square_largest(x: f64, y: f64, z: f64) -> Option<f64> {
 partial_min_three(x, y, z)
 .map(|a| square(x) + square(y) + square(z) - square(a))
}
fn partial_min_three<T>(v1: T, v2: T, v3: T) -> Option<T>
 where T: PartialOrd<T>
{
 partial_min(v2, v3).and_then(|x| partial_min(v1, x))
}
fn square(x: f64) -> f64 { x * x }
// Copied from the standard library as it is currently unstable
// and the beta doesn't allow unstable features
use std::cmp::Ordering;
fn partial_min<T: PartialOrd>(v1: T, v2: T) -> Option<T> {
 match v1.partial_cmp(&v2) {
 Some(Ordering::Less) | Some(Ordering::Equal) => Some(v1),
 Some(Ordering::Greater) => Some(v2),
 None => None
 }
}
fn main() {
 let z = sum_square_largest(1.0, 2.0, 3.0);
 println!("{:?}", z);
}
answered Apr 7, 2015 at 22:54
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.