Skip to content

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

Closed
wants to merge 3 commits into from
Closed

Gale-Shapley implementation in Rust #715

wants to merge 3 commits into from

Conversation

lazyprop
Copy link
Contributor

This is, more or less, a translation of the Python implementation.

@Amaras
Copy link
Member

Amaras commented Jun 12, 2020

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.
The review in that PR might also give you some ideas to improve your code from the direct translation it seems to be.

In any case, thanks again for your interest in the AAA!

@lazyprop
Copy link
Contributor Author

I have already kept in mind the changes that you have made in the Python implementation.

  • No 'getters/setters' are used. There is just an initperson function which I think is necessary because initializing a person requires a lot of repetitive lines.
  • Currently, the number and preferences of people hardcoded because a variable number of people would require randomly generating preferences. The problem is that rand is an external crate in Rust which has to be downloaded from the Internet.
  • I am new to Rust myself. Nevertheless, I have tried my best to write idiomatic Rust.
  • I have updated println!("Pairs: ") to println!("Stable Matching: ") to make the output more descriptive.
  • I have added my name to CONTRIBUTORS.md

Copy link

@Liikt Liikt left a 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
Copy link

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 */
Copy link

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() {
()
Copy link

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)]
Copy link

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 {
Copy link

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: ");
Copy link

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: ");

Copy link
Contributor

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 {
Copy link

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.

@lazyprop lazyprop requested a review from Liikt June 14, 2020 14:56
@lazyprop
Copy link
Contributor Author

@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.

Comment on lines +100 to +106
for man in men {
let partner_id = stable_matching.get(&man.id);
match partner_id {
Some(p) => println!("{} + {}", man.id, p),
None => ()
}
}
Copy link
Contributor

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.)

Copy link
Contributor Author

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?

Copy link
Contributor

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.

Copy link
Contributor Author

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?

@berquist berquist added the Implementation This provides an implementation for an algorithm. (Code and maybe md files are edited.) label Jul 4, 2020
#[derive(Hash, Eq, PartialEq, Copy, Clone)]
struct PersonId(pub char);

impl fmt::Display for PersonId {
Copy link
Contributor

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

Copy link
Contributor

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?

Copy link
Member

@Amaras Amaras left a 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.

@lazyprop lazyprop closed this Oct 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Implementation This provides an implementation for an algorithm. (Code and maybe md files are edited.)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants