diff --git a/src/array.cr b/src/array.cr index a01839b0892f..aab8b9eff7cd 100644 --- a/src/array.cr +++ b/src/array.cr @@ -1577,96 +1577,20 @@ class Array(T) dup.shuffle!(random) end - # Returns a new array with all elements sorted based on the return value of - # their comparison method `#<=>` - # - # ``` - # a = [3, 1, 2] - # a.sort # => [1, 2, 3] - # a # => [3, 1, 2] - # ``` - def sort : Array(T) - dup.sort! - end - - # :ditto: - # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort : Array(T) - dup.unstable_sort! - end - - # Returns a new array with all elements sorted based on the comparator in the - # given block. - # - # The block must implement a comparison between two elements *a* and *b*, - # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`. - # The comparison operator `<=>` can be used for this. - # - # ``` - # a = [3, 1, 2] - # b = a.sort { |a, b| b <=> a } - # - # b # => [3, 2, 1] - # a # => [3, 1, 2] - # ``` - def sort(&block : T, T -> U) : Array(T) forall U - {% unless U <= Int32? %} - {% raise "expected block to return Int32 or Nil, not #{U}" %} - {% end %} - - dup.sort! &block - end - - # :ditto: - # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort(&block : T, T -> U) : Array(T) forall U - {% unless U <= Int32? %} - {% raise "expected block to return Int32 or Nil, not #{U}" %} - {% end %} - - dup.unstable_sort!(&block) - end - - # Modifies `self` by sorting all elements based on the return value of their - # comparison method `#<=>` - # - # ``` - # a = [3, 1, 2] - # a.sort! - # a # => [1, 2, 3] - # ``` - def sort! : Array(T) + # :inherit: + def sort! : self to_unsafe_slice.sort! self end - # :ditto: - # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort! : Array(T) + # :inherit: + def unstable_sort! : self to_unsafe_slice.unstable_sort! self end - # Modifies `self` by sorting all elements based on the comparator in the given - # block. - # - # The given block must implement a comparison between two elements - # *a* and *b*, where `a < b` returns `-1`, `a == b` returns `0`, - # and `a > b` returns `1`. - # The comparison operator `<=>` can be used for this. - # - # ``` - # a = [3, 1, 2] - # a.sort! { |a, b| b <=> a } - # a # => [3, 2, 1] - # ``` - def sort!(&block : T, T -> U) : Array(T) forall U + # :inherit: + def sort!(&block : T, T -> U) : self forall U {% unless U <= Int32? %} {% raise "expected block to return Int32 or Nil, not #{U}" %} {% end %} @@ -1675,11 +1599,8 @@ class Array(T) self end - # :ditto: - # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort!(&block : T, T -> U) : Array(T) forall U + # :inherit: + def unstable_sort!(&block : T, T -> U) : self forall U {% unless U <= Int32? %} {% raise "expected block to return Int32 or Nil, not #{U}" %} {% end %} @@ -1688,57 +1609,6 @@ class Array(T) self end - # Returns a new array with all elements sorted. The given block is called for - # each element, then the comparison method #<=> is called on the object - # returned from the block to determine sort order. - # - # ``` - # a = %w(apple pear fig) - # b = a.sort_by { |word| word.size } - # b # => ["fig", "pear", "apple"] - # a # => ["apple", "pear", "fig"] - # ``` - def sort_by(&block : T -> _) : Array(T) - dup.sort_by! { |e| yield(e) } - end - - # :ditto: - # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort_by(&block : T -> _) : Array(T) - dup.unstable_sort_by! { |e| yield(e) } - end - - # Modifies `self` by sorting all elements. The given block is called for - # each element, then the comparison method #<=> is called on the object - # returned from the block to determine sort order. - # - # ``` - # a = %w(apple pear fig) - # a.sort_by! { |word| word.size } - # a # => ["fig", "pear", "apple"] - # ``` - def sort_by!(&block : T -> _) : Array(T) - sorted = map { |e| {e, yield(e)} }.sort! { |x, y| x[1] <=> y[1] } - @size.times do |i| - @buffer[i] = sorted.to_unsafe[i][0] - end - self - end - - # :ditto: - # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort_by!(&block : T -> _) : Array(T) - sorted = map { |e| {e, yield(e)} }.unstable_sort! { |x, y| x[1] <=> y[1] } - @size.times do |i| - @buffer[i] = sorted.to_unsafe[i][0] - end - self - end - def to_a self end diff --git a/src/indexable/mutable.cr b/src/indexable/mutable.cr index c2f200fb0c7a..f641e4061310 100644 --- a/src/indexable/mutable.cr +++ b/src/indexable/mutable.cr @@ -219,4 +219,292 @@ module Indexable::Mutable(T) self end + + # Returns a new instance with all elements sorted based on the return value of + # their comparison method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm. + # + # ``` + # a = [3, 1, 2] + # a.sort # => [1, 2, 3] + # a # => [3, 1, 2] + # ``` + # + # See `#sort!` for details on the sorting mechanism. + # + # Raises `ArgumentError` if the comparison between any two elements returns `nil`. + def sort : self + dup.sort! + end + + # Returns a new instance with all elements sorted based on the return value of + # their comparison method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm. + # + # ``` + # a = [3, 1, 2] + # a.sort # => [1, 2, 3] + # a # => [3, 1, 2] + # ``` + # + # See `#unstable_sort!` for details on the sorting mechanism. + # + # Raises `ArgumentError` if the comparison between any two elements returns `nil`. + def unstable_sort : self + dup.unstable_sort! + end + + # Returns a new instance with all elements sorted based on the comparator in the + # given block, using a stable sort algorithm. + # + # ``` + # a = [3, 1, 2] + # b = a.sort { |a, b| b <=> a } + # + # b # => [3, 2, 1] + # a # => [3, 1, 2] + # ``` + # + # See `#sort!(&block : T, T -> U)` for details on the sorting mechanism. + # + # Raises `ArgumentError` if for any two elements the block returns `nil`. + def sort(&block : T, T -> U) : self forall U + {% unless U <= Int32? %} + {% raise "expected block to return Int32 or Nil, not #{U}" %} + {% end %} + + dup.sort! &block + end + + # Returns a new instance with all elements sorted based on the comparator in the + # given block, using an unstable sort algorithm. + # + # ``` + # a = [3, 1, 2] + # b = a.unstable_sort { |a, b| b <=> a } + # + # b # => [3, 2, 1] + # a # => [3, 1, 2] + # ``` + # + # See `#unstable_sort!(&block : T, T -> U)` for details on the sorting mechanism. + # + # Raises `ArgumentError` if for any two elements the block returns `nil`. + def unstable_sort(&block : T, T -> U) : self forall U + {% unless U <= Int32? %} + {% raise "expected block to return Int32 or Nil, not #{U}" %} + {% end %} + + dup.unstable_sort!(&block) + end + + # Sorts all elements in `self` based on the return value of the comparison + # method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm. + # + # ``` + # a = [3, 1, 2] + # a.sort! + # a # => [1, 2, 3] + # ``` + # + # This sort operation modifies `self`. See `#sort` for a non-modifying option + # that allocates a new instance. + # + # See `Slice#sort!` for details on the implementation. + # + # Raises `ArgumentError` if the comparison between any two elements returns `nil`. + def sort! : self + slice = Slice.new(size) { |i| unsafe_fetch(i) }.sort! + each_index do |i| + unsafe_put(i, slice.unsafe_fetch(i)) + end + self + end + + # Sorts all elements in `self` based on the return value of the comparison + # method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm. + # + # ``` + # a = [3, 1, 2] + # a.unstable_sort! + # a # => [1, 2, 3] + # ``` + # + # This sort operation modifies `self`. See `#unstable_sort` for a non-modifying + # option that allocates a new instance. + # + # See `Slice#unstable_sort!` for details on the implementation. + # + # Raises `ArgumentError` if the comparison between any two elements returns `nil`. + def unstable_sort! : self + slice = Slice.new(size) { |i| unsafe_fetch(i) }.unstable_sort! + each_index do |i| + unsafe_put(i, slice.unsafe_fetch(i)) + end + self + end + + # Sorts all elements in `self` based on the comparator in the given block, using + # a stable sort algorithm. + # + # The block must implement a comparison between two elements *a* and *b*, + # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`. + # The comparison operator `<=>` can be used for this. + # + # ``` + # a = [3, 1, 2] + # # This is a reverse sort (forward sort would be `a <=> b`) + # a.sort! { |a, b| b <=> a } + # a # => [3, 2, 1] + # ``` + # + # This sort operation modifies `self`. See `#sort(&block : T, T -> U)` for a + # non-modifying option that allocates a new instance. + # + # See `Slice#sort!(&block : T, T -> U)` for details on the implementation. + # + # Raises `ArgumentError` if for any two elements the block returns `nil`. + def sort!(&block : T, T -> U) : self forall U + {% unless U <= Int32? %} + {% raise "expected block to return Int32 or Nil, not #{U}" %} + {% end %} + + slice = Slice.new(size) { |i| unsafe_fetch(i) }.sort!(&block) + each_index do |i| + unsafe_put(i, slice.unsafe_fetch(i)) + end + self + end + + # Sorts all elements in `self` based on the comparator in the given block, + # using an unstable sort algorithm. + # + # The block must implement a comparison between two elements *a* and *b*, + # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`. + # The comparison operator `<=>` can be used for this. + # + # ``` + # a = [3, 1, 2] + # # This is a reverse sort (forward sort would be `a <=> b`) + # a.unstable_sort! { |a, b| b <=> a } + # a # => [3, 2, 1] + # ``` + # + # This sort operation modifies `self`. See `#unstable_sort(&block : T, T -> U)` + # for a non-modifying option that allocates a new instance. + # + # See `Slice#unstable_sort!(&block : T, T -> U)` for details on the implementation. + # + # Raises `ArgumentError` if for any two elements the block returns `nil`. + def unstable_sort!(&block : T, T -> U) : self forall U + {% unless U <= Int32? %} + {% raise "expected block to return Int32 or Nil, not #{U}" %} + {% end %} + + slice = Slice.new(size) { |i| unsafe_fetch(i) }.unstable_sort!(&block) + each_index do |i| + unsafe_put(i, slice.unsafe_fetch(i)) + end + self + end + + # Returns a new instance with all elements sorted by the output value of the + # block. The output values are compared via the comparison method `T#<=>` + # (see `Comparable#<=>`), using a stable sort algorithm. + # + # ``` + # a = %w(apple pear fig) + # b = a.sort_by { |word| word.size } + # b # => ["fig", "pear", "apple"] + # a # => ["apple", "pear", "fig"] + # ``` + # + # If stability is expendable, `#unstable_sort_by(&block : T -> _)` provides a + # performance advantage over stable sort. + # + # See `#sort_by!(&block : T -> _)` for details on the sorting mechanism. + # + # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`. + def sort_by(&block : T -> _) : self + dup.sort_by! { |e| yield(e) } + end + + # Returns a new instance with all elements sorted by the output value of the + # block. The output values are compared via the comparison method `#<=>` + # (see `Comparable#<=>`), using an unstable sort algorithm. + # + # ``` + # a = %w(apple pear fig) + # b = a.unstable_sort_by { |word| word.size } + # b # => ["fig", "pear", "apple"] + # a # => ["apple", "pear", "fig"] + # ``` + # + # If stability is necessary, use `#sort_by(&block : T -> _)` instead. + # + # See `#unstable_sort!(&block : T -> _)` for details on the sorting mechanism. + # + # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`. + def unstable_sort_by(&block : T -> _) : self + dup.unstable_sort_by! { |e| yield(e) } + end + + # Sorts all elements in `self` by the output value of the + # block. The output values are compared via the comparison method `#<=>` + # (see `Comparable#<=>`), using a stable sort algorithm. + # + # ``` + # a = %w(apple pear fig) + # a.sort_by! { |word| word.size } + # a # => ["fig", "pear", "apple"] + # ``` + # + # This sort operation modifies `self`. See `#sort_by(&block : T -> _)` for a + # non-modifying option that allocates a new instance. + # + # If stability is expendable, `#unstable_sort_by!(&block : T -> _)` provides a + # performance advantage over stable sort. + # + # See `#sort!(&block : T -> _)` for details on the sorting mechanism. + # + # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`. + def sort_by!(&block : T -> _) : self + slice = Slice.new(size) do |i| + elem = unsafe_fetch(i) + {elem, (yield elem)} + end.sort! { |x, y| x[1] <=> y[1] } + + each_index do |i| + unsafe_put(i, slice.unsafe_fetch(i)[0]) + end + self + end + + # Sorts all elements in `self` by the output value of the + # block. The output values are compared via the comparison method `#<=>` + # (see `Comparable#<=>`), using an unstable sort algorithm. + # + # ``` + # a = %w(apple pear fig) + # a.usntable_sort_by! { |word| word.size } + # a # => ["fig", "pear", "apple"] + # ``` + # + # This sort operation modifies `self`. See `#unstable_sort_by(&block : T -> _)` + # for a non-modifying option that allocates a new instance. + # + # If stability is necessary, use `#sort_by!(&block : T -> _)` instead. + # + # See `#unstable_sort!(&block : T -> _)` for details on the sorting mechanism. + # + # Raises `ArgumentError` if the comparison between any two comparison values returns `nil`. + def unstable_sort_by!(&block : T -> _) : self + slice = Slice.new(size) do |i| + elem = unsafe_fetch(i) + {elem, (yield elem)} + end.unstable_sort! { |x, y| x[1] <=> y[1] } + + each_index do |i| + unsafe_put(i, slice.unsafe_fetch(i)[0]) + end + self + end end diff --git a/src/slice.cr b/src/slice.cr index 88bd690a7f70..616d099ea532 100644 --- a/src/slice.cr +++ b/src/slice.cr @@ -708,98 +708,100 @@ struct Slice(T) @pointer end - # Returns a new slice with all elements sorted based on the return value of - # their comparison method `<=>` + # Sorts all elements in `self` based on the return value of the comparison + # method `T#<=>` (see `Comparable#<=>`), using a stable sort algorithm. # # ``` - # a = Slice[3, 1, 2] - # a.sort # => Slice[1, 2, 3] - # a # => Slice[3, 1, 2] + # slice = Slice[3, 1, 2] + # slice.sort! + # slice # => Slice[1, 2, 3] # ``` - def sort : Slice(T) - dup.sort! - end - - # :ditto: # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort : Slice(T) - dup.unstable_sort! - end - - # Returns a new slice with all elements sorted based on the comparator in the - # given block. + # This sort operation modifies `self`. See `#sort` for a non-modifying option + # that allocates a new instance. # - # The block must implement a comparison between two elements *a* and *b*, - # where `a < b` returns `-1`, `a == b` returns `0`, and `a > b` returns `1`. - # The comparison operator `<=>` can be used for this. + # The sort mechanism is implemented as [*merge sort*](https://en.wikipedia.org/wiki/Merge_sort). + # It is stable, which is typically a good default. # - # ``` - # a = Slice[3, 1, 2] - # b = a.sort { |a, b| b <=> a } + # Stablility means that two elements which compare equal (i.e. `a <=> b == 0`) + # keep their original relation. Stable sort guarantees that `[a, b].sort!` + # always results in `[a, b]` (given they compare equal). With unstable sort, + # the result could also be `[b, a]`. # - # b # => Slice[3, 2, 1] - # a # => Slice[3, 1, 2] - # ``` - def sort(&block : T, T -> U) : Slice(T) forall U - {% unless U <= Int32? %} - {% raise "expected block to return Int32 or Nil, not #{U}" %} - {% end %} - - dup.sort! &block - end - - # :ditto: + # If stability is expendable, `#unstable_sort!` provides a performance + # advantage over stable sort. # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort(&block : T, T -> U) : Slice(T) forall U - {% unless U <= Int32? %} - {% raise "expected block to return Int32 or Nil, not #{U}" %} - {% end %} + # Raises `ArgumentError` if the comparison between any two elements returns `nil`. + def sort! : self + Slice.merge_sort!(self) - dup.unstable_sort!(&block) + self end - # Modifies `self` by sorting all elements based on the return value of their - # comparison method `<=>` + # Sorts all elements in `self` based on the return value of the comparison + # method `T#<=>` (see `Comparable#<=>`), using an unstable sort algorithm.. # # ``` - # a = Slice[3, 1, 2] - # a.sort! - # a # => Slice[1, 2, 3] + # slice = Slice[3, 1, 2] + # slice.unstable_sort! + # slice # => Slice[1, 2, 3] # ``` - def sort! : Slice(T) - Slice.merge_sort!(self) - - self - end - - # :ditto: # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort! : Slice(T) + # This sort operation modifies `self`. See `#unstable_sort` for a non-modifying + # option that allocates a new instance. + # + # The sort mechanism is implemented as [*introsort*](https://en.wikipedia.org/wiki/Introsort). + # It does not guarantee stability between equally comparing elements. + # This offers higher performance but may be unexpected in some situations. + # + # Stablility means that two elements which compare equal (i.e. `a <=> b == 0`) + # keep their original relation. Stable sort guarantees that `[a, b].sort!` + # always results in `[a, b]` (given they compare equal). With unstable sort, + # the result could also be `[b, a]`. + # + # If stability is necessary, use `#sort!` instead. + # + # Raises `ArgumentError` if the comparison between any two elements returns `nil`. + def unstable_sort! : self Slice.intro_sort!(to_unsafe, size) self end - # Modifies `self` by sorting all elements based on the comparator in the given - # block. - # - # The given block must implement a comparison between two elements - # *a* and *b*, where `a < b` returns `-1`, `a == b` returns `0`, - # and `a > b` returns `1`. - # The comparison operator `<=>` can be used for this. + # Sorts all elements in `self` based on the comparator in the given block, using + # a stable sort algorithm. # # ``` - # a = Slice[3, 1, 2] - # a.sort! { |a, b| b <=> a } - # a # => Slice[3, 2, 1] + # slice = Slice[3, 1, 2] + # # This is a reverse sort (forward sort would be `a <=> b`) + # slice.sort! { |a, b| b <=> a } + # slice # => Slice[3, 2, 1] # ``` - def sort!(&block : T, T -> U) : Slice(T) forall U + # + # The block must implement a comparison between two elements *a* and *b*, + # where `a < b` outputs a negative value, `a == b` outputs `0`, and `a > b` + # outputs a positive value. + # The comparison operator (`Comparable#<=>`) can be used for this. + # + # The block's output type must be `<= Int32?`, but returning an actual `nil` + # value is an error. + # + # This sort operation modifies `self`. See `#sort(&block : T, T -> U)` for a + # non-modifying option that allocates a new instance. + # + # The sort mechanism is implemented as [*merge sort*](https://en.wikipedia.org/wiki/Merge_sort). + # It is stable, which is typically a good default. + # + # Stablility means that two elements which compare equal (i.e. `a <=> b == 0`) + # keep their original relation. Stable sort guarantees that `[a, b].sort!` + # always results in `[a, b]` (given they compare equal). With unstable sort, + # the result could also be `[b, a]`. + # + # If stability is expendable, `#unstable_sort!(&block : T, T -> U)` provides a + # performance advantage over stable sort. + # + # Raises `ArgumentError` if for any two elements the block returns `nil`. + def sort!(&block : T, T -> U) : self forall U {% unless U <= Int32? %} {% raise "expected block to return Int32 or Nil, not #{U}" %} {% end %} @@ -809,11 +811,40 @@ struct Slice(T) self end - # :ditto: + # Sorts all elements in `self` based on the comparator in the given block, + # using an unstable sort algorithm. # - # This method does not guarantee stability between equally sorting elements. - # Which results in a performance advantage over stable sort. - def unstable_sort!(&block : T, T -> U) : Slice(T) forall U + # ``` + # slice = Slice[3, 1, 2] + # # This is a reverse sort (forward sort would be `a <=> b`) + # slice.unstable_sort! { |a, b| b <=> a } + # slice # => Slice[3, 2, 1] + # ``` + # + # The block must implement a comparison between two elements *a* and *b*, + # where `a < b` outputs a negative value, `a == b` outputs `0`, and `a > b` + # outputs a positive value. + # The comparison operator (`Comparable#<=>`) can be used for this. + # + # The block's output type must be `<= Int32?`, but returning an actual `nil` + # value is an error. + # + # This sort operation modifies `self`. See `#unstable_sort(&block : T, T -> U)` + # for a non-modifying option that allocates a new instance. + # + # The sort mechanism is implemented as [*introsort*](https://en.wikipedia.org/wiki/Introsort). + # It does not guarantee stability between equally comparing elements. + # This offers higher performance but may be unexpected in some situations. + # + # Stablility means that two elements which compare equal (i.e. `a <=> b == 0`) + # keep their original relation. Stable sort guarantees that `[a, b].sort!` + # always results in `[a, b]` (given they compare equal). With unstable sort, + # the result could also be `[b, a]`. + # + # If stability is necessary, use `#sort!(&block : T, T -> U)` instead. + # + # Raises `ArgumentError` if for any two elements the block returns `nil`. + def unstable_sort!(&block : T, T -> U) : self forall U {% unless U <= Int32? %} {% raise "expected block to return Int32 or Nil, not #{U}" %} {% end %}