-
Notifications
You must be signed in to change notification settings - Fork 1
Conversation
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.
Great start!
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.
More good progress!
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.
Excellent work! This looks great. Two problems:
ArraySet
is missingplusOrThis
, 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.
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.
Almost there!
|
||
def plusOrThis(self, element: K) -> 'ArraySet[K]': | ||
if element not in self.__data: | ||
self.__data.append(element) |
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 don't want to mutate the existing structure, and you want to make sure that ArraySet
stays sorted.
selfie-python-wip/jvm/selfie-lib/src/commonMain/kotlin/com/diffplug/selfie/ArrayMap.kt
Lines 257 to 284 in c41e68c
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) | |
} | |
} |
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) |
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.
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.
- you cannot call the constructor, you can only get an
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.
Great work! Let me know if you're okay with the changes I made above. There are a few TODOs left in the code:
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 - |
No description provided.