Is there a pattern for choosing one of a set of options at random when their selection criteria are equal?
https://softwareengineering.stackexchange.com/questions/410867
-
11-03-2021 - |
Question
I want to choose between several options, and if the criteria I'm using for selection happen to be equal, I want one at random (reasonably so, so it's equal-chance each time, rather than arbitrary). This is easily done with nested ifs, but it gets pretty ugly and ends up duplicating code.
A simplified example case, in case that's not clear.
extern crate rand;
fn main() {
let a = 5;
let b = 5;
if a > b {
println!("Do the thing where a is bigger");
} else if b < a {
println!("Do the thing where b is bigger");
} else {
if rand::random::<bool>() {
// I don't want to duplicate the code here!
println!("Do the thing where a is bigger");
} else {
// Or here!
println!("Do the thing where b is bigger");
}
}
}
Is there a better way to do this which doesn't require duplicating code? In this simplified example I'm only choosing between two possibilities, but it could actually be a longer list. (What about acting based on the highest of a
, b
, c
, d
, where it's possible that there's a two, three, or four-way tie?)
I happen to be learning Rust so if there's any clever language features that'd be cool, but I'm also interested in the general case if possible.
Solution
A first general practice is to reduce duplication using functional decomposition. This means decomposing a complex function into simpler, potentially reusable parts. Your code would then duplicate only the function calls:
fn do_a() {
println!("Do the thing where a is bigger");
}
fn do_b() {
println!("Do the thing where b is bigger");
}
fn main() {
...
if a > b {
do_a();
} else if b < a {
do_b();
} else {
if rand::random::<bool>() {
do_a();
} else {
do_b();
}
}
}
A more effective way is to refactor the code, consolidating the conditional expressions; I don't know Rust, so excuse any language errors, but it would be something like:
fn main() {
let a = 5;
let b = 5;
let agb = if a==b { rand::random::<bool>() } else { a>b };
if agb {
do_a();
} else {
do_b();
}
}