-
-
Notifications
You must be signed in to change notification settings - Fork 366
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
Use smart item selection for building levels #3425
Use smart item selection for building levels #3425
Conversation
…but keep the same sort order
I am curious how it will feel in practice, here I never had trouble with needed and recently used elements being often missing |
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 converted this back to a draft because I was testing and it didn't seem to work the way I expected, so I want to add some logging and see what's going on.
app/src/main/java/de/westnordost/streetcomplete/quests/LastPickedValuesStore.kt
Outdated
Show resolved
Hide resolved
In my area
The common values are grouped in blocks, where there's a bunch of 1/0 and 1/1 together, then a bunch of 2/0 and 2/1. This means when you do encounter two oddballs in the same neighborhood, it's more likely to knock out a common value of the block you're not in, than the previous oddball. I hope to mitigate this by saving more history. In the worst case scenario, two oddballs can force me to open the keyboard 6 timesBold + italics = had to open the keyboard to enter it
This only happens if you run into values in the exact same order that they were knocked out. This doesn't happen often, but it does happen. Shorter runs of "bad luck", like if the oddballs aren't exactly on the corner, are more frequent still annoying. |
Counting the recent values doesn't do much good if you're still only saving 5
Okay, that's fixed. Doesn't help to count 30 into the history if you only store 5 at max :P |
We *already* de-duplicate when retrieving values, in all cases where that's desired, so also de-duplicating when storing values has no impact on functionality— it's essentially a performance improvement, to reduce the number of values we store. At 100 values maximum, I don't think this makes a meaningful difference. By removing the optimization, we now store history for all values, so if we choose to enable "smart sort" for other quests in the future, they'll already have history to pull from. Also, it simplifies the code, just a little.
Huh, I lost track. Didn't I merge this already? |
Ah, I see, good! Well, most of the code in these classes is from you now, so my ability to assess what is the best solution is very limited. If you think it can be made easier, go ahead! |
Implementation is near-totally replaced, but the api remains mostly unchanged, except using sequences instead of linkedlists. I also tried to keep naming conventions, so I hope it should be pretty similar to reason about. 🤷 |
This time: remove the need for the BuildingLevels form to pass in an empty list for defaults
Make "smart" sorting of recently-picked elements work on any sequence. It's more flexible now, and has a simpler type signature.
Left to do:
|
`mostCommonWithin` is the complicated function, that we really care about testing
I removed types from the store locally, but wasn't happy with the result. So I'm continuing to experiment, and I'll let you know if I find something I like. However, in terms of effort-to-review, I'm also feeling like it was a mistake to mix refactoring and feature work in the same PR, even though the refactoring is to fix awkward types created by the feature. I'm considering reverting this PR back to d6aaba4 (when westnordost reviewed it), merging that, and then sending a separate PR with just the refactor. Or, I could go all the way and finish refactoring here, then request another review. Any preference? |
Makes (de)serialization the responsibility of the caller, not the store.
1. With the store being untyped, the compiler does not guarantee that the (de)serialization functions match— or that they both use the same key to store/retrieve the serialized values. 2. Serialization and deserialization are done relatively far apart. Together, these make it hard for a human to notice if there's a problem. Creating a 'Factory' (better name pending) forces the function types to match, to be declared close together, and to be associated with the same key. This should make it easy for humans to notice any issues… while being easier for humans to spot issues *and* more flexible than the previous typed implementation, where changing the (de)serialization method required changes to the store.
Replace test for removed function with a test for a now-public function.
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.
Okay, I am done with this (except for the comment/rename). I can either merge the last-reviewed commit and send the refactoring as a separate PR, or you can re-review it altogether.
app/src/main/java/de/westnordost/streetcomplete/quests/LastPickedValuesStore.kt
Outdated
Show resolved
Hide resolved
I find that this change makes using this store look really complicated. It's just a persisted list of last selected items, man. |
Is it having to create a |
On On chaining: it's easy enough to wrap that in a helper method, but it's actually different in all 3 places where the store is used, so to me it feels like we're just moving the logic further away from the place that cares about it. Also, for what it's worth, I had a similar reaction when looking at the initial code. (by now, of course, I've gotten used to it) |
The chaining is understandable IMO |
I'll swap it to using initializers (injection be damned 😆) and see if that seems better. |
Set everything up via constructor instead of an ugly `Factory` object.
Done. I experimented with adding a companion object with a What we could do if we wanted to make this inject-able is to remove |
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, this looks very good! I have not tested this in action, though; I only reviewed the code.
What we could do if we wanted to make this inject-able is to remove
prefs
from the constructor and pass it to each of the functions instead. (e.g.fun add(prefs: SharedPreferences, value: T)
). Thenfavs
could be initialized normally andprefs
could be injected.
Does this mean that every call of get()
would then need to pass PreferenceManager.getDefaultSharedPreferences(ctx.applicationContext)
as a parameter? Or does ctx.applicationContext
suffice? The latter sounds helpful to me.
app/src/test/java/de/westnordost/streetcomplete/quests/LastPickedValuesStoreTest.kt
Show resolved
Hide resolved
app/src/main/java/de/westnordost/streetcomplete/quests/LastPickedValuesStore.kt
Outdated
Show resolved
Hide resolved
Yes, although it'd be injected and you'd keep a reference. Something like class SomeForm() {
@Inject internal lateinit var prefs: SharedPreferences
private val favs = LastPickedValuesStore<T>(
key = javaClass.simpleName,
serialize = { /* whatever */ },
deserialize = { /* also whatever */ }
)
init {
Injector.applicationComponent.inject(this)
}
fun later() {
favs.get(prefs)
val newValue: T = // whatever
favs.add(prefs, newValue)
}
} …but just now I realized I think we can get the best of both worlds by lazily initializing the @Inject internal lateinit var prefs: SharedPreferences
private val favs by lazy {
LastPickedValuesStore<T>(
prefs,
key = javaClass.simpleName,
serialize = { /* whatever */ },
deserialize = { /* also whatever */ }
)
} |
app/src/main/java/de/westnordost/streetcomplete/quests/LastPickedValuesStore.kt
Outdated
Show resolved
Hide resolved
I tried implementing the injection idea… it turns out that we still need the edit: pushed to smichel17@7e8a3a0 edit2: and here's the with the superclass, which is fewer lines: smichel17@0741fb0 |
@FloEdelmann I think I've addressed everything you mentioned. @westnordost You can ignore everything before this comment. The only important change since you last looked at the code is 2e8f64f. I think it makes using the store as easy as before. edit: There is an open question about whether to keep a favs factory function. Besides that…
|
app/src/main/java/de/westnordost/streetcomplete/quests/LastPickedValuesStore.kt
Outdated
Show resolved
Hide resolved
ef235cd
to
777de5e
Compare
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.
Looks good!
The only thing left to decide is what to do about injection.
Let's leave it like it is now, and postpone the question to #3488 ;)
…but keep the same sort order.
Also, store duplicates in history for all quests, so if we want to switch to the "smart" order later, there will already be data.
Fun story: when I was implementing this, I initially added a version of
get
that implemented the previous de-duplication and maximum size on the other end… then found out we didn't need to use it anywhere, becauseget
is only called from the extension functions defined right there, which all do it themselves already.(as discussed in #3373 (comment))