big-brain icon indicating copy to clipboard operation
big-brain copied to clipboard

feat: Implement `Highest` `Picker` for picking highest, then first tying, score

Open ndarilek opened this issue 2 years ago • 5 comments

… first tying, score

ndarilek avatar Sep 13 '22 13:09 ndarilek

Awesome, both sound good. I'll see what I can get done in a day or two.

ndarilek avatar Sep 13 '22 16:09 ndarilek

If you have any thoughts on how to reduce the allocation count, please suggest them. I'm a little uncertain about how reduce would help here. I guess you can track the winning value in the accumulator, but that seems less readable. Feels a bit like a premature optimization, but maybe I'm wrong and just haven't hit any specific performance issues in my usage yet.

ndarilek avatar Sep 13 '22 19:09 ndarilek

pickers run very often, so I think this is a worthwhile place to optimize a bit. You can do it with reduce, or you can do it with a variable. The algorithms would just be something like:

let mut highest: Option<(FloatOrd, &Choice)> = None;
for choice in choices {
  if let Some((curr_value, curr_choice)) = highest {
     let val = choice.calculate(scores);
     if val > curr_value {
      highest = Some((val, &choice));
     }
  } else {
    highest = (choice.calculate(scores), &choice);
  }
}
return highest.map(|(_, choice)| choice)

Or something like that. I didn't try it myself. And honestly, I find this more readable than chaining iterators.

zkat avatar Sep 13 '22 19:09 zkat

OK, so you'd prefer something more imperative and less functional? I can do that. Looking at the above, it looks like you may have copy-pasted my code and forgot to edit out the copy-paste? That or you did the same calculation twice, just in different styles. :) Sorry, not trying to be obtuse, going through some apartment remodel/repair drama and my brain is a bit fried right now.

ndarilek avatar Sep 13 '22 19:09 ndarilek

yes, sorry. I copy-pasted it so I could refer to it while I wrote mine.

You can also write yours with reduce, and the logic would ultimately be the same. Initialize the accumulator with None, and go from there. The body of the reduce function would probably look exactly like the iterator body above.

zkat avatar Sep 13 '22 19:09 zkat

I've got a fold version of this which looks like

impl Picker for MaxScore {
    fn pick<'a>(&self, choices: &'a [Choice], scores: &Query<&Score>) -> Option<&'a Choice> {
        let mut max_score = 0f32;
        choices.iter().fold(None, |acc, choice| {
            let score = choice.calculate(scores);

            if score <= max_score {
                return acc;
            }

            max_score = score;
            Some(choice)
        })
    }
}

will-hart avatar Sep 20 '22 22:09 will-hart

@will-hart do you want to just PR that one, since it's already done?

zkat avatar Sep 21 '22 00:09 zkat

Yeah, I'm fine with whichever. Sorry I didn't get to it quicker--dealing with an apartment remodel from hell and it's been on tomorrow's todo list for a few days now. Can still probably get to it tomorrow, but if you've got an equivalent PR then this one can be closed.

Just glad to have this implemented. :)

ndarilek avatar Sep 21 '22 02:09 ndarilek

Ouch that sounds stressful @ndarilek . I don't want to step on your toes but I'm happy to make a PR

will-hart avatar Sep 21 '22 08:09 will-hart