Skip to content
This repository has been archived by the owner on Apr 4, 2024. It is now read-only.

ArrayMap.py #17

Merged
merged 21 commits into from
Feb 29, 2024
Merged

ArrayMap.py #17

merged 21 commits into from
Feb 29, 2024

Conversation

hssahota2
Copy link
Collaborator

No description provided.

Copy link
Member

@nedtwigg nedtwigg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great start!

python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/tests/ArrayMap_test.py Outdated Show resolved Hide resolved
Copy link
Member

@nedtwigg nedtwigg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More good progress!

python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
@hssahota2 hssahota2 marked this pull request as ready for review February 28, 2024 04:20
Copy link
Member

@nedtwigg nedtwigg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work! This looks great. Two problems:

  • ArraySet is missing plusOrThis, which we will need
  • Both classes should have an @classmethod empty which returns a singleton. Anytime we use these classes, we should never call their constructor, we should create an empty object and then add things to it. Otherwise, they will not stay storted. This chat describes a design pattern for achieving this.

python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/selfie_lib/ArrayMap.py Outdated Show resolved Hide resolved
python/selfie-lib/selfie_lib/ArrayMap.py Show resolved Hide resolved
Copy link
Member

@nedtwigg nedtwigg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost there!


def plusOrThis(self, element: K) -> 'ArraySet[K]':
if element not in self.__data:
self.__data.append(element)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't want to mutate the existing structure, and you want to make sure that ArraySet stays sorted.

fun plusOrThis(key: K): ArraySet<K> {
val idxExisting = binarySearch(key)
if (idxExisting >= 0) {
return this
}
val idxInsert = -(idxExisting + 1)
return when (data.size) {
0 -> ArraySet(arrayOf(key))
1 -> {
if (idxInsert == 0)
ArraySet(
arrayOf(
key,
data[0],
))
else ArraySet(arrayOf(data[0], key))
}
else -> {
// TODO: use idxInsert and arrayCopy to do this faster, see ArrayMap#insert
val array = Array(size + 1) { if (it < size) data[it] else key }
if (key is String) {
array.sortWith(STRING_SLASHFIRST as Comparator<Any>)
} else {
(array as Array<K>).sort()
}
ArraySet(array)
}
}

Comment on lines 50 to 60
def plusOrThis(self, element: K) -> 'ArraySet[K]':
new_data = []
added = False
for item in self.__data:
if not added and element < item:
new_data.append(element)
added = True
new_data.append(item)
if not added:
new_data.append(element)
return ArraySet(new_data)
Copy link
Member

@nedtwigg nedtwigg Feb 28, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting approach! Here are the tradeoffs:

  • selfie python
    • you can call the constructor with any data, and it will be sorted O(n log n).
    • you can call plusOrThis and it will do an O(n) search, followed by an O(n log n) sort in the constructor if it wasn't found
  • selfie kotlin
    • you cannot call the constructor, you can only get an empty and plus things from it
    • plusOrThis does an O(log n) search to find out if something has to be added or not. If it does need to be added, the search tells you where it needs to be added, so the constructor doesn't need to do any checks.

Basically, by making the constructor private, we can rely on the data always being sorted, by induction from the empty starting set. That means we never need to do an n log n sort, and can instead always do a log n search.

Your code is simpler and easier to read. There's a good argument to be made that this python is better than the Kotlin. Wait until there's a performance problem, then speed it up.

But if we have allowed the constructor to be public, then it's too late. People might be passing unsorted data in, so we can't make the switch.

It's okay to do the n log n sort instead of the log n search, we can improve performance later, but it's important to hide the constructor.

@nedtwigg
Copy link
Member

Great work! Let me know if you're okay with the changes I made above.

There are a few TODOs left in the code:

  • a missing binary search which could speed things up
  • using a special sort order when the keys happen to be strings

But we don't need to fix them now. Whenever you're satisfied with this PR, click merge and we can move on to the next item in your epic - TypedPath.

@nedtwigg nedtwigg merged commit 757e0fe into diffplug:main Feb 29, 2024
2 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants