-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
sort: make sorting easier, add Slice, SliceStable, SliceIsSorted, reflect.Swapper #16721
Comments
I'd be ok with something like this. It's pragmatic, small, and probably would reduce a lot of sort-related boilerplate. |
I don't have access to the mentioned above dataset, but let me guess: The need-to-be-sorted type declarations and its associated methods declarations constitutes less than 1‰ of code in that corpus. Perhaps far less. If that is true then I don't think this proposal is justified. Also, using reflection in sorting, however small it is, feels to me like encouraging bad practices. |
@cznic, you do have access: https://github.com/blog/2201-making-open-source-data-more-available We'll have to disagree on the metrics at which this is subjectively worthwhile. |
No opinion on this. Only thing I would say is that, sorting a slice is not exactly to be seen as simply sorting an array. Or should we decide that sorting a slice returns a completely new slice ? (and thus allocate ?) Just a data point. (I guess you discussed a similar issue when adding the append function but, just to make sure). |
As the primary force behind golang at our organization, this and no generic max/min function are the two things I find embarrassingly hard to explain. I'm not sure what it's like at Shoreline Blvd but without fail when an engineer joins our team their mouth drops through the floor at their first sort. Subjectively, I would love this in 1.8. |
I'm fine with adding something to the sort package, although of course |
@ianlancetaylor, the benefit of |
Oh, sure, I understand why you suggested it. I'm just commenting that it will still surprise people. |
As much as this is complained about and as many slice types as I have sorted this has never bothered me, so I don't see the point. Boilerplate is never fun, but this pittance is far below the threshold of annoyance. A go generate command to spit out Len and Swap, or even just an editor macro, seems more inline with the Tao of Go here. |
I'm with @jimmyfrasche here. Why can't this be a |
If it's basically as fast as sort.Inferface, and is just an addition to the stdlib, not a language change.... why would anyone ever say no to this? Yes, it's annoying that it has to use Put me down for a +1. |
How would |
I'd prefer usage of a To the |
It would be a private type implementing |
@pbnjay I don't think []interface{} would be very convenient; it's not possible to go from e.g. It's a bit unfortunate that it has to be sort.Slice(mySlice, func(i, j int) bool { return mySlice[i].Less(mySlice[j]) }) But oh well :). I'd still take that over declaring a new type+3 methods just to be able to sort stuff. |
@riannucci yup - that's what i meant by "doesn't intuitively work on all slice types" - maybe in Go 2 we can get a generic slice-interface type! |
@riannucci, in my experience, the sort.Slice(people, func(i, j int) bool { return people[i].Name < people[j].Name }) |
How about Edit: Nevermind, my bad, there will still be reflection. |
@freeformz, you already have to do the reflect a bit anyway, so it doesn't save you anything to pass the length explicitly. |
Following on from @jimmyfrasche's comment (but my follow up may indeed be the subject of another issue/a separate go-nuts thread)
On the This raises the question of whether to distribute core, oft-used programs that are As things stand, every
There are of course some downsides here:
|
@mackross +1 for the missing |
Shouldn’t there also be a stable counterpart? Maybe even with a variadic signature like
so that one could do
to sort |
This only simplifies a trivial unimportant amount of code so I don't see it as more than, at best, a minor annoyance, regardless of frequency. I'm sure Brad's implementation is a negligible hit on performance compared to the current way, but it would have to be used in function bodies to close over the slice like:
so you have to pay the cost of allocating the closure and building the generic sorter on each invocation of If this is going for pure convenience, I'd prefer the version @ianlancetaylor hinted at where the comparator is an interface containing a Maybe I'm just uncomfortable that this is at a weird level between convenient and practical that results in this awkward situation where you have to close over a slice that's being permuted under the covers. All that said, if this goes in, I'll use it—I'm lazy and hypocritical! I don't have any problem with this existing—it's the inclusion in the stdlib that gives me pause. I have absolutely zero reservations about this being a go gettable library: people could even start using it today. The excellent go4.org or even golang.org/x seem like fine places to put this, and if it gets used a lot without issues moving it to the stdlib would be fine with me. |
Taking a key func that's allowed to return one of a list of types ((u)int, []byte, string) makes it look a bit more like sort functions in other languages that take a key func, and would let you later drop in faster sorts specific to the key types without adding APIs. Lots of downsides make that unpalatable (keyFunc'd be an |
Even today with sort.Sort you have to pay the cost of putting a slice into an interface value, since a slice (1 pointer & two ints) is not 0 or 1 pointers (which is all that goes into an interface allocation-free). So it's not significantly different. I'd prefer to keep this issue focused on the proposal and not the implementation, though. |
When I read the title, I thought you were going to propose something like a built-in:
But this is certainly more pragmatic. Saying that 1% of programs touch sort isn't a very good argument against including this. This is scoped to the sort package, so programs that aren't sorting won't care about it. Even more reason to include it. |
@mibk From a clean slate, maybe, but we already have IntsAreSorted, Float64sAreSorted, StringsAreSorted, not AreIntsSorted etc. |
So you're cool with 4 API additions: reflect.Swapper, sort.Slice{,Stable,IsSorted}? |
SGTM |
CL https://golang.org/cl/30088 mentions this issue. |
Swapper returns a func that swaps two elements in a slice. Updates #16721 Change-Id: I7f2287a675c10a05019e02b7d62fb870af31216f Reviewed-on: https://go-review.googlesource.com/30088 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Russ Cox <rsc@golang.org>
@rsc considering that external packages may accept Even in the absence of such concerns, I'd like to argue that the proliferation of parallel functions (SliceStable) to be unfortunate -- I appreciate I feel that this may just boil down to a naming problem -- with a clearer name than MakeInterface, the usage of an interface builder may well become obvious. |
sorry for bringing this up if this was resolved somewhere else, but any particular reason the I think that the fundamental argument of making another point is that getting the length of slice directly would probably be faster than getting it through reflection, though not sure by how much. also, all the previous discussion about not encouraging generic programming through |
@extemporalgenome It's trivial to write a converter from function triple to interface if someone needs that. That's true already today.
If there is evidence that this comes up often in real programs, we can easily add it later. But we'd need to see that evidence first. @suyash I assume that by sort.Search-like syntax you mean code like
as in the comment above (Aug 21). The reason not to prefer this is that this entire proposal is about making sort easier to use. The above is not substantially less code than you have to write today. It does avoid the new type, but it is still full of boilerplate. Compare to:
The new API is meant to be a helper, so it should be as helpful as possible - with as little boilerplate as possible - without restricting the scope of its help. My guess is that >99% of sorting happens on slices, in which case the generality added by the boilerplate serves no useful purpose. If someone has evidence otherwise, I'd be happy to see it. |
CL https://golang.org/cl/30250 mentions this issue. |
@rsc lets take an example that comes up fairly often (for me), lets say you want to sort a set of numbers but you also want to track the original positions of the numbers. So you had a slice like With this addition, you would separately declare a a := []int{...}
type pair struct {
first, second int
}
p := make([]pair, len(a))
for i := 0 ; i < len(a) ; i++ {
p[i] = pair{a[i], i}
}
sort.Slice(p, func (i, j int) bool {
return p[i].first < p[j].first || (p[i].first == p[j].first && p[i].second < p[j].second)
}) while if we had the option to define a swapper, we can just define another slice, initialize that to positions (0, 1, 2...) and in the swap, swap in both slices. something like a := []int{...}
pos := make([]int, len(a))
for i := 0 ; i < len(a) ; i++ {
pos[i] = i
}
sort.Slice(len(a), func(i, j int) {
a[i], a[j] = a[j], a[i]
pos[i], pos[j] = pos[j], pos[i]
}, func(i, j int) bool {
return a[i] < a[j] || (a[i] == a[j] && pos[i] < pos[j])
})
|
@suyash For your example you could use rsc's |
I avoided anywhere in the compiler or things which might be used by the compiler in the future, since they need to build with Go 1.4. I also avoided anywhere where there was no benefit to changing it. I probably missed some. Updates #16721 Change-Id: Ib3c895ff475c6dec2d4322393faaf8cb6a6d4956 Reviewed-on: https://go-review.googlesource.com/30250 TryBot-Result: Gobot Gobot <gobot@golang.org> Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> Reviewed-by: Andrew Gerrand <adg@golang.org>
@suyash What Ian said, but also "text editors can macro-generate your code for you" is a particularly bad rationale for an API decision. Code has to be read and maintained, not just written. Also, if you are OK with text editor macros, one could generate the old, more flexible sort.Interface type+methods for you if you insist. The time it takes to compute the slice length is not going to show up. |
Inspired by comments on golang/go#16721. This will be particularly handy when we get to use a version of go (1.8) which has `sort.Slice`. But in the mean time, it's at least clearer and quicker than hand-rolling Less methods (and potentially safer too!). R=dnj@chromium.org, nodir@chromium.org, vadimsh@chromium.org BUG= Review-Url: https://codereview.chromium.org/2657733002
Initial dumb implementation. - CPU profiling. To see the result, run 07-magic_square, execute `go tool pprof 07-magic_square path/to/cpu.pprof` and input `top[Enter]` in the prompt. - Generic algorithm. The trick is the Float64Slice adaptor; see [1]. Maybe I will use the technique used in the sort library[2][3] which allows simpler API. [1]: http://stackoverflow.com/a/6256127/5266681 [2]: golang/go#16721 [3]: golang/go@22a2bdf
EDIT: the proposal has changed later in this thread. See #16721 (comment) and #16721 (comment)
I propose we make sorting slices a first class concept in Go 1.8.
I think everybody appreciates the cute
sort.Interface
type at this point, but the reality is that:Less
function inline with my code, in context.I think we should add to package sort at least:
And maybe at the same time, or in the future:
The assumption is that the user's
less
function would close over the data to be sorted.For speed, this would not be purely reflect-based (except for one call to to get the slice length). The implementation would generate a custom swapper as needed for the type of the slice elements such that it's compatible with the GC & write barriers. I did a prototype of this which shows that it's as fast as the typical way.
I think there's plenty of evidence in the Github BigQuery dataset that we're making sort way too hard for users.
/cc @griesemer @robpike @ianlancetaylor @adg @broady
The text was updated successfully, but these errors were encountered: