Table of Contents
- What is an operator in Swift?
- Types of operators in Swift
- Operator notation
- Operator precedence and associativity in Swift
- Common operators in Swift
- Creating custom operators in Swift
- Defining a custom operator in Swift
- Setting precedence of custom Swift operators
- Specifying the notation of a custom operator in Swift
Operators are one of the basic constructs of any programming language. They are represented as symbols and have various associated properties, and understanding them is crucial to mastering any programming language.
In this article, we’ll look at some of the operators that Swift ships with and also create our own operators.
What is an operator in Swift?
Before we begin looking at the different operators that Swift provides us with and creating custom operators, let’s understand a few basic terms related to them.
Here’s an example:
let a = 5 let b = 10 let sum = a + b
Here, we can see a few variables and constants being defined, along with a few symbols (e.g., =
and +
). Let’s define two terms here:
- Operators: any symbol that is used to perform a logical, computational, or assignment operation.
=
is used to assign and+
is used to add in the above example and are therefore operators - Operands: variables on which operations are performed, e.g., on line 1 we have
a
as an operand, and on line 3, we havea
andb
as two operands on which the+
operator is being applied
Types of operators in Swift
There are three different types of operators, which are defined by the number of operands they work on.
- Unary operators: operators that work on only one operand (e.g.,
=
and!
) - Binary operators: work on two operands, such as
+
,-
,*
, etc. - Ternary operator: works on three operands, e.g.,
?:
Operator notation
Notation defines the position of the operator when used with the operands. There are again three types:
Infix
: when the operators are used in between operands. Binary and ternary operators are alwaysinfixed
Prefix
: when the operators are used before an operand, e.g.,++
,--
, and!
Postfix
: when the operators are used after an operand, e.g.,…
Operator precedence and associativity in Swift
When working with operators in Swift, you should also know the priority order in which the operator is executed. Operator precedence only matters for infix
operators, as they work on multiple operands.
For example, if an expression has multiple operators in it, Swift needs to know which operator needs to be executed first.
var result = 4 + 6 * 2 // 6*2 is done first which yields 12, then we do 4 + 12 = 16
In the above example, *
has more precedence compared to +
, which is why 6*2
is executed before 4+6
.
In case you have two operators with the same precedence being used in an expression, they’ll fall back on their associativity. Associativity defines the direction in which the expression will start resolving in case the precedence of the operators are the same.
Associativity is of two types:
left
: this means that the expression to the left will be resolved firstright
: this means the expression to the right will be resolved first
For example, we have the following statement:
let result = 5 + 10 - 2 //13
Because both +
and -
have the same precedence, Swift falls back to the associativity of the operators. +
and -
have associativity of left
, so we resolve the expression from the left so that +
is evaluated first.
Here’s a table of the precedence order that Swift follows. The table lists the precedence in the order of decreasing priority.
Precedence Name | Operators Included |
---|---|
BitwiseShiftPrecendence |
<< , >> , &<< , &>> |
MultiplicationPrecedence |
* , / , % , & , &* |
AdditionPrecedence |
+ , - , | , ^ , &+ , &- |
RangeFormationPrecedence |
..< , ... |
CastingPrecedence |
is , as , as? , as! |
NilCoalescingPrecedence |
?? |
ComparisonPrecedence |
< , <= , > , >= , == , != , === , !== , ~= , .< , .<= , .> , .>= , .== , .!= |
LogicalConjunctionPrecedence |
&& , .& |
LogicalDisjunctionPrecedence |
|| , .| , .^ |
TernaryPrecedence |
?: |
AssignmentPrecedence |
= , *= , /= , %= , += , -= , <<= , >>= , &= , |= , ^= , &*= , &+= , &-= , &<<= , &>>= , .&= , .|= , .^= |
Now that we know the basics of what Swift operators are and how they are used, let’s look at popular operators that ship with Swift.
Common operators in Swift
The most common operators that Swift ships with fall under the following categories:
1. Assignment operator
Assignment operators are used to assign values to a constant or variable. The symbol is =
.
2. Arithmetic operators
These operators are used to perform basic arithmetic operations, such as +
, -
, *
, /
, and %
.
3. Logical operators
Logical operators are used to combine two conditions to give a single boolean value output, e.g., !
, &&
, and ||
.
4. Comparison operators
These operators are used to compare numbers and give out a true
or false
output: >
, <
, ==
, >=
, <=
, and !=
.
5. Ternary operator
This operator is a shorthand operator for writing if-else conditions. Even though it does the same job, it is not a good idea to consider it a replacement for if-else conditions because it hampers readability.
Therefore, when you have different code blocks to run for different conditions, it’s better to use if-else blocks. For shorter, simpler cases, use the ternary operator. The operator, ?:
, is used like this:
var value = 5 > 2 ? "5 is greater than 2" : "5 is less than 2"
6. Nil coalescing operator
This is a shorthand operator to return default values in case a particular variable is nil. The operator symbol is ??
and commonly used like this:
var identity: String? = nil let nonNillableIdentity: String = identity ?? "Jane Doe" // since `identity` is nil, we return a default value of Jane Doe and store it in the nonNilalbleIdentity variable
7. Range operators
These operators are used to define ranges, e.g., …
and ..<
. They are mainly used with array/strings to extract sub arrays/strings.
var array = [1,2,3,4,5] let a = array[1...3] // [2,3,4] let b = array[1..<3] // [2,3] let c = array[2...] // [3,4,5] let d = array[...3] // [1,2,3,4]
For a list of all the basic operators and their descriptions, read the Swift documentation.
Creating custom operators in Swift
Now that we’ve seen the operators that Swift ships with, let’s create our own operators. Custom operators are generally defined for three purposes:
- To define a completely new operator because you think the symbol’s meaning can express what the operator will do better than a function with a name can
- To define an operator that exists for basic data types like numbers or strings but does not exist for classes or structs that have been defined by you
- You want to override an operator that already exists for your classes and structs, but you need it to behave differently
Let’s look at all three purposes with examples.
Creating a new operator with a new symbol
Let’s say you want to define an operator with the △
symbol that calculates the hypotenuse of a right angle triangle given its two sides.
infix operator △ func △(lhs: Double, rhs: Double) -> Double { return sqrt(lhs*lhs + rhs*rhs) } var hypotenuse = 3△4 // 5
Creating an operator for a custom class or struct
We know that the +
operator is used to add two numbers, but let’s say we wanted to add two instances of our struct called Velocity
, which is a two-dimensional entity:
struct Velocity { var xVelocity: Double var yVelocity: Double }
If we were to run the following command, you’d get a compiler error:
let velA = Velocity(xVelocity: 2, yVelocity: 4) let velB = Velocity(xVelocity: 2, yVelocity: 4) let velC = velA + velB // Binary operator + cannot be applied to two Velocity operands
In this case, it makes sense to define a +
operator that takes care of adding two Velocity
instances:
extension Velocity { static func +(lhs: Velocity, rhs: Velocity) -> Velocity { return Velocity(xVelocity: lhs.xVelocity + rhs.xVelocity, yVelocity: lhs.yVelocity + rhs.yVelocity) } }
Now the earlier statement will execute properly.
Overriding a Swift operator that already exists for a class/struct
Let’s say you have a struct that is used to define an item in a supermarket.
struct Item: Equatable { let id = UUID() let itemName: String let itemType: String let itemPrice: Double }
When we need to compare two items, such that if two items have the same itemName
, itemType
, and itemPrice
, we need to consider them equal regardless of what their id
is.
If you run the following piece of code, you’ll see that the two variables are not equal:
let detergentA = Item(itemName: "Tide", itemType: "Detergent", itemPrice: 56.5) let detergentB = Item(itemName: "Tide", itemType: "Detergent", itemPrice: 56.5) let areDetergentsEqual = detergentA == detergentB // false
The reason for this is that instances detergentA
and detergentB
have different ids
. In this case, we need to override the ==
operator and custom logic to determine equality.
extension Item { static func ==(lhs: Item, rhs: Item) -> Bool { return lhs.itemName == rhs.itemName && lhs.itemPrice == rhs.itemPrice && lhs.itemType == rhs.itemType } }
Now that we know the different scenarios in which we define custom operators, let’s define our custom operator.
Defining a custom operator in Swift
A custom operator is defined just like a function is defined in Swift. There are two types of operators you can define:
- Global operator
- Operator specific to a class/struct
The definition looks like the following:
func <operator symbol>(parameters) { // operator logic }
There are different types of parameters you can define based on whether it is an infix
or a prefix
/postfix
operator.
If it’s an infix
operator, you can have two parameters, and, when it’s a prefix
/postfix
operator, you can have a single parameter.
Global operator
With global operators, we first define the operator with the notation keyword (infix
, prefix
or postfix
) and the operator
keyword, like this:
<notation> operator <operator symbol>
Here’s an example:
infix operator ^ // the operator is an infix operator with the symbol ^
Operator for class/struct
When we want to define an operator for a class or a struct, we need to define the operator function as a static function. The notation parameter is optional in case the operator is an infix
operator (i.e., you have two parameters in the function signature), else you need to specify prefix
or postfix
:
extension <class name> { static <notation> func ^(parameters) { // note that this needs to be static function // operator logic } }
Given that we’ve already looked at defining basic custom operators like +
and ==
, let’s review some examples of how you can define custom compound operators or operators that mutate the parameters themselves.
We’ll do this by defining a custom compound arithmetic operator for the Velocity
struct from earlier:
extension Velocity { static func +=(lhs: inout Velocity, rhs: Velocity) { // note that the first parameter is an inout variable lhs.xVelocity = lhs.xVelocity + rhs.xVelocity lhs.yVelocity = lhs.yVelocity + rhs.yVelocity } }
Now let’s execute the following statement:
var velA = Velocity(xVelocity: 5, yVelocity: 5) var velB = Velocity(xVelocity: 5, yVelocity: 5) velA += velB print(velA) // Velocity(xvelocity: 10.0, yVelocity: 10.0)
Now, we’ll specify the notation of a custom operator.
Specifying the notation of a custom operator in Swift
In order to define the notation of a custom operator, you have three keywords for the corresponding notation:
infix
postfix
prefix
When defining a global operator, you need to first define the operator’s notation, then define its functions. Note that this is not a compulsory step, but it’s necessary if you want to define global operators or hope to assign a precedence to it (which we’ll discuss in the next section).
Let’s look at the hypotenuse operator again, which is being defined as a global operator. You can see that we first define the operator with the notation and the symbol, then implement its logic:
infix operator △ // define the operator with its notation and symbol func △(lhs: Double, rhs: Double) -> Double { // implement the operator logic return sqrt(lhs*lhs + rhs*rhs) } var hypotenuse = 3△4 // 5
When defining an operator for your own class/struct, you can directly use the notation keyword with the method definition. For example, let’s define a negation operator for Velocity
as well, which negates both the xVelocity
and yVelocity
properties. This operator is going to be defined as a prefix
operator:
extension Velocity { // defining a prefix negation operator static prefix func -(operand: Velocity) -> Velocity { return Velocity(xVelocity: -val.xVelocity, yVelocity: -val.yVelocity) } } let vel = Velocity(xVelocity: 4, yVelocity: 4) print(-vel) // xVelocity: -4, yVelocity: -4
Setting precedence of custom Swift operators
You can also define the precedence of your operator using the precedence keywords, as mentioned in the precedence table above. This is usually done in the definition step:
infix operator ~: AdditionPrecedence // now this operator's priority is the same at the operators mentioned in the AdditionPrecedence func ~(lhs: Int, rhs: Int) -> Double { return sqrt(Double(lhs*lhs-rhs*rhs)) }
This way, the compiler knows which operator needs to be executed first. In case no precedence is specified, it defaults to DefaultPrecedence
, which is higher than TernaryPrecedence
.
Conclusion
This brings us to the conclusion of what operators are in Swift and how you can create your own. Although they are pretty simple to use, understanding them is important because they are one of the most foundational constructs of any programming language.
You should be familiar with basic operators, as you will be using them frequently in your code, and, when it comes to custom operators, define them only if the symbol’s original meaning makes sense for you.
The post Creating custom operators in Swift appeared first on LogRocket Blog.
from LogRocket Blog https://ift.tt/RntYlBN
via Read more