2

From Wikipedia:

Unlike traditional equality operators, which will return true or false depending on whether the arguments are equal or unequal, the spaceship operator will return 1, 0, or −1 depending on the value of the left argument relative to the right argument. If the left argument is greater than the right argument, the operator returns 1. If the left argument is less than the right argument, the operator returns −1. If the two arguments are equal, the operator returns 0.

The spaceship operator is primarily used for comparisons in sorting.

Pang
  • 9,564
  • 146
  • 81
  • 122
Venkat Peri
  • 1,744
  • 3
  • 16
  • 20
  • 8
    A similar functionality in a strongly typed language like Swift, should __not__ be implemented returning an integer. It should be implemented defining an enumeration with three cases. – Analog File Jun 06 '14 at 14:44

3 Answers3

13

Swift does not have a built-in spaceship operator, but creating new operators in swift is trivial. Let's make a new operator that returns an enum instead of an Intmagic numbers are bad and we shouldn't need to use them for the return type in a strongly typed language. While we are at it, let's take advantage of Swift's Unicode support and make the enumeration support a visual indicator of the result using ←, →, & ↔ :

enum Spaceship
{
  case ← // LeftIsGreaterThanRight
  case → // LeftIsLessThanRight
  case ↔ // LeftIsEqualToRight
}

operator infix <=> {}

@infix func <=> <T: Comparable> (left: T, right: T) -> Spaceship {
  if left < right { return SpaceShip.→}
  if left > right { return SpaceShip.←}
  return Spaceship.↔;
}

Now you can use the new operator like this

if someInt <=> someOtherInt == ←
{
  // take off!
}

If you insist on using magic numbers as a comparison result value (which I really, really encourage you not to do in Swift), then here:

@infix func <=><T: Comparable> (left: T, right: T) -> Int {
  if left < right { return -1 }
  if left > right { return  1 }
  return 0
}
Community
  • 1
  • 1
memmons
  • 40,222
  • 21
  • 149
  • 183
  • 3
    IMO, it would be better to use (NS)ComparisonResult which already exists in Foundation and is used in many API's. – kjam Jul 06 '17 at 11:19
  • *Technically*, it would probably be more correct to use the ComparisonResult type, but.. +1 for using unicode characters that match the 3 states so cool!! – Travis Griggs Oct 17 '19 at 22:27
3

No, Swift currently does not include a spaceship operator.

A Custom Spaceship Operator for Swift

A custom operator can be defined to mimic the look and function of spaceship operators in other languages.

The Operator

Since Swift allows the use of <, = and > characters in custom operators, we can define a custom operator to look like this: <=>.

Using the Operator

The operator is a binary infix operator and is used like this:

1 <=> 2   //Returns: -1
2 <=> 1   //Returns: 1
1 <=> 1   //Returns: 0

Defining the Operator

The operator can be defined in Swift like this:

operator infix <=> {}

@infix func <=><T: Comparable> (left: T, right: T) -> Int {
  if left < right { return -1 }
  if left > right { return  1 }
  return 0
}
Bill the Lizard
  • 398,270
  • 210
  • 566
  • 880
Venkat Peri
  • 1,744
  • 3
  • 16
  • 20
  • 6
    As commented on the question, this is a very bad way to implement this in a strongly typed language like Swift. You should use an enumerated type instead of an Int. – Analog File Jun 06 '14 at 14:45
  • 1
    The question was specific about returning integers -1, 0, 1 – Venkat Peri Jun 06 '14 at 22:02
0

As noted by a few ComparisonResult already exists so can be used:

extension Comparable {
    static func <=> (lhs: Self, rhs: Self) -> ComparisonResult {
        if lhs < rhs { return .orderedAscending }
        if lhs > rhs { return .orderedDescending }
        return .orderedSame
    }
}

As mentioned in the question:

The spaceship operator is primarily used for comparisons in sorting.

This is doubly true as it can even be used to implement an elegant stable sort function on top of other Sequence functions:

extension Sequence {
    func sorted(with comparator: (Element, Element) throws -> ComparisonResult) rethrows -> [Element]
    {
        return try enumerated()
            .sorted { (try comparator($0.element, $1.element) || $0.offset <=> $1.offset) == .orderedAscending }
            .map { $0.element }
    }
}

extension ComparisonResult {
    static func || (lhs: Self, rhs: @autoclosure () -> Self) -> ComparisonResult {
        if lhs == .orderedSame {
            return rhs()
        }
        return lhs
    }
}
jjrscott
  • 1,386
  • 12
  • 16