-
-
Notifications
You must be signed in to change notification settings - Fork 359
Gale-Shapley implementation in Rust #715
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gale-Shapley implementation in Rust #715
Conversation
Hello, thanks for your first PR here! As I'm just a contributor here, and I've only just started learning Rust, I cannot really review your code. That said, it does seem quite clean. I have not tried to build it, but I trust you on that for now before any real review is done. Problem is, there is an open PR (#687) on my part to improve the Python implementation, so you might want to branch off from simply translating the current Python implementation, and maybe write more idiomatic Rust if possible. In any case, thanks again for your interest in the AAA! |
I have already kept in mind the changes that you have made in the Python implementation.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All in all I am not happy with the way you link men to the women. The way of indexing is rather complex and hard to parse mentally.
Also I would like it if you could use more iterator logic. This would make the code better readable and a bit more concise.
#[derive(Eq, PartialEq, PartialOrd)] | ||
struct Person { | ||
name: char, // a single character denoting the person e.g 'A', 'B', 'F' | ||
index: usize, // index in men/women vecotr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is typo here. It should be vector
.
|
||
impl Person { | ||
fn propose_to_next(&mut self, women: &mut Vec<Person>) { | ||
/* propose to next most preferred person */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would use //
for normal comments and ///
for doc strings.
/* propose to next most preferred person */ | ||
|
||
if self.pref_index >= self.preference.len() { | ||
() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use return
here as it is way cleaner than ()
.
@@ -0,0 +1,136 @@ | |||
#[derive(Eq, PartialEq, PartialOrd)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All these are not needed.
|
||
/* Resolve */ | ||
let mut cont = true; | ||
while cont { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use a named break here:
...
'resolve: loop {
...
for man in &men {
if man.partner.is_none() {
break 'resolve;
}
}
}
...
} | ||
} | ||
|
||
println!("\nStable Matching: "); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is only personal preference but i would rather write
print!("\n");
print!("Stable Matching: \n");
than println!("\nStable Matching: ");
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think rust recommends to use just a println!()
invocation without parameters. I think clippy even has a lint for that.
so:
println!();
println!("Stable Matching: ");
|
||
println!("\nStable Matching: "); | ||
for man in &men { | ||
match man.partner { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can do a simple unwrap here as all man must have a partner at this point.
@Liikt I have rewritten the entire implementation. It is much more concise and 'Rusty' now. The initial declaration and display of the vectors takes up a lot of lines though. |
for man in men { | ||
let partner_id = stable_matching.get(&man.id); | ||
match partner_id { | ||
Some(p) => println!("{} + {}", man.id, p), | ||
None => () | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get the feeling that iter
and .filter_map()
might be useful here. (I'm not sure, though.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fanninpm How would we use .filter_map()
here? Won't it just over complicate things? We will have to iterate and then collect into vector and then print the vector. I am not sure though. Is there a better way to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@n1trob4ct3r You can use .for_each()
on the iterator, but again, I doubt its usefulness. Besides, you refer to both man.id
and partner_id
in your solution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@fanninpm Yes that would be a problem too.
Is there anything else? Is it ready for merging?
#[derive(Hash, Eq, PartialEq, Copy, Clone)] | ||
struct PersonId(pub char); | ||
|
||
impl fmt::Display for PersonId { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe you want to just derive debug, instead of manually implementing display, to simplify the entire code a bit. Even if you don't want to remove the Display implementation, deriving debug is never a bad practice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I now see that your code relies on the exact representation quite a bit, so then maybe just add a derive debug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... reviewing the details is good, but the algorithm implemented does not necessarily produce a stable pairing. Even though we discussed it on Discord previously, I think it needs to be here.
What it does is assign each man to its first free preference, in a first come first served way. It does not take into account the preferences of the women, which is why the pairing is not stable in general.
Your previous implementation was demonstrably more correct, so I would approve getting back to that state.
This is, more or less, a translation of the Python implementation.