-
Notifications
You must be signed in to change notification settings - Fork 17.6k
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
proposal: slices: partition slice into two slice by predicate #67326
Comments
Partition split the original slice into two slice. Fixes golang#67326.
Change https://go.dev/cl/585018 mentions this issue: |
It seems could only be two parts. Why don't we use GroupBy instead ? |
how common is this operation? "some scenarios" is not strong justification |
Here is a simple search in github: It may not be an exact match, but it is enough to prove that the usage scenarios of partition are rich enough. Some specific examples:
|
Two main reason:
|
That search result contains a lot of results irrelevant to this proposal |
In addition, I also found that the
|
Potential optimization: allocate a bit vector containing f(i) for every index; then allocate a single array of length n and copy the true elements into the bottom and the false elements into the top; finally, return two cap-limited slices of this array. This would eliminate reallocations in append and reduce the amortized allocation overhead from 12.5% (append's average waste) to 1 bit per element (1.5% for a word-sized element, less for larger elements). [Update: on reflection, a 12.5% allocation improvement doesn't seem worth the extra complexity. Never mind.] |
It element order is not important ([edit] and the input one is allowed to be changed), then no allocation is needed. |
@adonovan This give me an idea to partition in place and return the index of the dividing point. I have seen this practice in rust, called as @go101 And if I understand correctly, what you expressed can also be considered as |
I wrote a For the fun of it, here's a zero-allocation implementation that runs in Edit: Here's a version that does preserve ordering. Edit 2: Version 3, now with overall easier-to-read code but annoyingly repetitive condition checks. Edit 3: I wasn't paying attention to the elements of the |
In fact, I write my version of Below is my implementations in my packgae func appendSeq[E any](s Seq[E], x E) Seq[E] {
return func(yield func(x E) bool {
for e := range s {
if !yield(e) {
return
}
}
yield(x)
})
}
func Partition[E any](s Seq[E], f func(E) bool) (Seq[E], Seq[E]) {
true:=func(yield func(E) bool) {}
false:=func(yield func(E) bool) {}
for x := range s {
if f(x) {
true = appendSeq(true, x)
}else {
false = appendSeq(false, x)
}
}
return true, false
} And the second implementation: Partition, it iterate over
I think we should reverse the version that keep order. Leave the choice to the user. Finally, your comments are quite valuable and I will integrate these versions of implementation into the original proposal |
Will |
Following the above discussion, we will have two variants: func Partition[S ~[]E, E any](s S, f func(E) bool) (S, S)
func PartitionInPlace[S ~[]E, E any](s S, f func(E) bool) (S, S) Is there anything else that needs to be discussed? If not, I will update the PR later to reflect the latest changes. |
Proposal Details
In some scenarios, we need to divide the slice into two sub-slices that match and do not match by predicate, so it is proposed to provide the
Partition
andPartitionInPlace
function.Here are implementations
Partition
: Alloc new slice, and keep order, don't modify the original slice.PartitionInPlace
: No alloc slice, also not keep order, modify original slice.Usecases
Here is a simple search in github:
https://github.com/search?q=%2Ffunc%5Cs*%28%28P%7Cp%29artition%7C%28S%7Cs%29plit%29.*func%5C%28.*%5C%29+bool%5C%29%5Cs*%5C%28%2F+language%3AGo+++NOT+is%3Aarchived+NOT+is%3Afork+&type=code
It may not be an exact match, but it is enough to prove that the usage scenarios of partition are rich enough.
Also includes the following situations, which cannot be quickly retrieved by github search:
Some specific examples:
go/src/net/ipsock.go
Line 114 in 07fc591
The text was updated successfully, but these errors were encountered: