mirror of
https://github.com/apple/swift-foundation.git
synced 2025-05-17 02:22:10 +08:00
287 lines
10 KiB
Swift
287 lines
10 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
/// A comparison algorithm for a given type.
|
|
@preconcurrency
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
public protocol SortComparator<Compared>: Hashable, Sendable {
|
|
/// The type that the `SortComparator` provides a comparison for.
|
|
associatedtype Compared
|
|
|
|
/// The relative ordering of lhs, and rhs.
|
|
///
|
|
/// The result of comparisons should be flipped if the current `order`
|
|
/// is `reverse`.
|
|
///
|
|
/// If `compare(lhs, rhs)` is `.orderedAscending`, then `compare(rhs, lhs)`
|
|
/// must be `.orderedDescending`. If `compare(lhs, rhs)` is
|
|
/// `.orderedDescending`, then `compare(rhs, lhs)` must be
|
|
/// `.orderedAscending`.
|
|
///
|
|
/// - Parameters:
|
|
/// - lhs: A value to compare.
|
|
/// - rhs: A value to compare.
|
|
func compare(_ lhs: Compared, _ rhs: Compared) -> ComparisonResult
|
|
|
|
/// If the `SortComparator`s resulting order is forward or reverse.
|
|
var order: SortOrder { get set }
|
|
}
|
|
|
|
/// The orderings that sorts can be performed with.
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
@frozen
|
|
public enum SortOrder: Hashable, Codable, Sendable {
|
|
/// The ordering where if compare(a, b) == .orderedAscending,
|
|
/// a is placed before b.
|
|
case forward
|
|
/// The ordering where if compare(a, b) == .orderedAscending,
|
|
/// a is placed after b.
|
|
case reverse
|
|
|
|
public init(from decoder: Decoder) throws {
|
|
let container = try decoder.singleValueContainer()
|
|
let isForward = try container.decode(Bool.self)
|
|
if isForward {
|
|
self = .forward
|
|
} else {
|
|
self = .reverse
|
|
}
|
|
}
|
|
|
|
public func encode(to encoder: Encoder) throws {
|
|
var container = encoder.singleValueContainer()
|
|
switch self {
|
|
case .forward: try container.encode(true)
|
|
case .reverse: try container.encode(false)
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
extension ComparisonResult {
|
|
package func withOrder(_ order: SortOrder) -> ComparisonResult {
|
|
if order == .reverse {
|
|
if self == .orderedAscending { return .orderedDescending }
|
|
if self == .orderedDescending { return .orderedAscending }
|
|
}
|
|
return self
|
|
}
|
|
}
|
|
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
package struct AnySortComparator: SortComparator, Sendable {
|
|
var _base: any Hashable & Sendable // internal for testing
|
|
|
|
/// Takes `base` and two values to be compared and compares the two values
|
|
/// using `base`.
|
|
private let _compare: @Sendable (Any, Any, Any) -> ComparisonResult
|
|
|
|
/// Takes `base` inout, and a new `SortOrder` and changes `base`s `order`
|
|
/// to match the new `SortOrder`.
|
|
private let setOrder: @Sendable (inout any Hashable & Sendable, SortOrder) -> any Hashable & Sendable
|
|
|
|
/// Gets the current `order` property of `base`.
|
|
private let getOrder: @Sendable (Any) -> SortOrder
|
|
|
|
package init<Comparator: SortComparator>(_ comparator: Comparator) where Comparator : Sendable {
|
|
self._base = comparator
|
|
self._compare = { (base: Any, lhs: Any, rhs: Any) -> ComparisonResult in
|
|
(base as! Comparator).compare(lhs as! Comparator.Compared, rhs as! Comparator.Compared)
|
|
}
|
|
self.setOrder = { (base: inout any Hashable & Sendable, newOrder: SortOrder) -> AnyHashable in
|
|
var typedBase = base as! Comparator
|
|
typedBase.order = newOrder
|
|
base = typedBase
|
|
return AnyHashable(typedBase)
|
|
}
|
|
self.getOrder = { (base: Any) -> SortOrder in
|
|
(base as! Comparator).order
|
|
}
|
|
}
|
|
|
|
package var order: SortOrder {
|
|
get {
|
|
return getOrder(_base)
|
|
}
|
|
set {
|
|
_base = setOrder(&_base, newValue)
|
|
}
|
|
}
|
|
|
|
package func compare(_ lhs: Any, _ rhs: Any) -> ComparisonResult {
|
|
return _compare(_base, lhs, rhs)
|
|
}
|
|
|
|
package func hash(into hasher: inout Hasher) {
|
|
hasher.combine(_base)
|
|
}
|
|
|
|
package static func == (lhs: Self, rhs: Self) -> Bool {
|
|
func compare<L : Equatable, R : Equatable>(_ l : L, _ r : R) -> Bool {
|
|
guard let rr = r as? L else {
|
|
return false
|
|
}
|
|
return l == rr
|
|
}
|
|
|
|
return compare(lhs._base, rhs._base)
|
|
}
|
|
}
|
|
|
|
/// Compares `Comparable` types using their comparable implementation.
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
public struct ComparableComparator<Compared: Comparable>: SortComparator, Sendable {
|
|
public var order: SortOrder
|
|
|
|
#if FOUNDATION_FRAMEWORK
|
|
@available(FoundationPreview 0.1, *)
|
|
public init(order: SortOrder = .forward) {
|
|
self.order = order
|
|
}
|
|
#else
|
|
// No need for availability on this initializer in the package.
|
|
public init(order: SortOrder = .forward) {
|
|
self.order = order
|
|
}
|
|
#endif
|
|
|
|
private func unorderedCompare(_ lhs: Compared, _ rhs: Compared) -> ComparisonResult {
|
|
if lhs < rhs { return .orderedAscending }
|
|
if lhs > rhs { return .orderedDescending }
|
|
return .orderedSame
|
|
}
|
|
|
|
public func compare(_ lhs: Compared, _ rhs: Compared) -> ComparisonResult {
|
|
return unorderedCompare(lhs, rhs).withOrder(order)
|
|
}
|
|
}
|
|
|
|
/// A comparator which orders optional values using a comparator which
|
|
/// compares the optional's `Wrapped` type. `nil` values are ordered before
|
|
/// non-nil values when `order` is `forward`.
|
|
package struct OptionalComparator<Base: SortComparator>: SortComparator {
|
|
private var base: Base
|
|
|
|
package init(_ base: Base) {
|
|
self.base = base
|
|
}
|
|
|
|
package var order: SortOrder {
|
|
get { base.order }
|
|
set { base.order = newValue }
|
|
}
|
|
|
|
package func compare(_ lhs: Base.Compared?, _ rhs: Base.Compared?) -> ComparisonResult {
|
|
guard let lhs else {
|
|
if rhs == nil { return .orderedSame }
|
|
return .orderedAscending.withOrder(order)
|
|
}
|
|
guard let rhs else { return .orderedDescending.withOrder(order) }
|
|
return base.compare(lhs, rhs)
|
|
}
|
|
}
|
|
|
|
extension OptionalComparator : Sendable where Base : Sendable { }
|
|
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
extension Never: SortComparator {
|
|
public typealias Compared = Never
|
|
|
|
public func compare(_ lhs: Never, _ rhs: Never) -> ComparisonResult {}
|
|
|
|
public var order: SortOrder {
|
|
get {
|
|
switch self {}
|
|
}
|
|
set {
|
|
switch self {}
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
extension Sequence {
|
|
/// Returns the elements of the sequence, sorted using the given comparator
|
|
/// to compare elements.
|
|
///
|
|
/// - Parameters:
|
|
/// - comparator: the comparator to use in ordering elements
|
|
/// - Returns: an array of the elements sorted using `comparator`.
|
|
public func sorted<Comparator: SortComparator>(using comparator: Comparator) -> [Element] where Comparator.Compared == Element {
|
|
return self.sorted {
|
|
comparator.compare($0, $1) == .orderedAscending
|
|
}
|
|
}
|
|
|
|
/// Returns the elements of the sequence, sorted using the given array of
|
|
/// `SortComparator`s to compare elements.
|
|
///
|
|
/// - Parameters:
|
|
/// - comparators: an array of comparators used to compare elements. The
|
|
/// first comparator specifies the primary comparator to be used in
|
|
/// sorting the sequence's elements. Any subsequent comparators are used
|
|
/// to further refine the order of elements with equal values.
|
|
/// - Returns: an array of the elements sorted using `comparators`.
|
|
public func sorted<S: Sequence, Comparator: SortComparator>(using comparators: S) -> [Element] where
|
|
S.Element == Comparator,
|
|
Element == Comparator.Compared
|
|
{
|
|
return self.sorted {
|
|
comparators.compare($0, $1) == .orderedAscending
|
|
}
|
|
}
|
|
}
|
|
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
extension Sequence {
|
|
/// If `lhs` is ordered before `rhs` in the ordering described by the given
|
|
/// sequence of `SortComparator`s
|
|
///
|
|
/// The first element of the sequence of comparators specifies the primary
|
|
/// comparator to be used in sorting the sequence's elements. Any subsequent
|
|
/// comparators are used to further refine the order of elements with equal
|
|
/// values.
|
|
public func compare<Comparator: SortComparator>(_ lhs: Comparator.Compared, _ rhs: Comparator.Compared) -> ComparisonResult where Element == Comparator {
|
|
for comparator in self {
|
|
let result = comparator.compare(lhs, rhs)
|
|
if result != .orderedSame { return result }
|
|
}
|
|
return .orderedSame
|
|
}
|
|
}
|
|
|
|
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, *)
|
|
extension MutableCollection where Self: RandomAccessCollection {
|
|
/// Sorts the collection using the given comparator to compare elements.
|
|
/// - Parameters:
|
|
/// - comparator: the sort comparator used to compare elements.
|
|
public mutating func sort<Comparator: SortComparator>(using comparator: Comparator) where Comparator.Compared == Element {
|
|
self.sort {
|
|
comparator.compare($0, $1) == .orderedAscending
|
|
}
|
|
}
|
|
|
|
/// Sorts the collection using the given array of `SortComparator`s to
|
|
/// compare elements.
|
|
///
|
|
/// - Parameters:
|
|
/// - comparators: an array of comparators used to compare elements. The
|
|
/// first comparator specifies the primary comparator to be used in
|
|
/// sorting the sequence's elements. Any subsequent comparators are used
|
|
/// to further refine the order of elements with equal values.
|
|
public mutating func sort<S: Sequence, Comparator: SortComparator>(using comparators: S) where S.Element == Comparator, Element == Comparator.Compared {
|
|
self.sort {
|
|
comparators.compare($0, $1) == .orderedAscending
|
|
}
|
|
}
|
|
}
|