Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What does $0 and $1 mean in Swift Closures?

let sortedNumbers = numbers.sort { $0 > $1 } print(sortedNumbers) 

Can anyone explain, what $0 and $1 means in swift?

More Sample

array.forEach {     actions.append($0) } 
like image 885
aashish tamsya Avatar asked Mar 22 '16 00:03

aashish tamsya


People also ask

What is use of $0 in Swift?

$0 is a shortcut to mean "first argument" in a closure.

How do closures work in Swift?

Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. Closures can capture and store references to any constants and variables from the context in which they're defined. This is known as closing over those constants and variables.

What are closures in Swift 4?

Swift 4 facilitates the user to represent Inline closures as shorthand argument names by representing $0, $1, $2 --- $n. Closures argument list is omitted in definition section when we represent shorthand argument names inside closure expressions. Based on the function type the shorthand argument names will be derived.


2 Answers

$0 is the first parameter passed into the closure. $1 is the second parameter, etc. That closure you showed is shorthand for:

let sortedNumbers = numbers.sort { (firstObject, secondObject) in      return firstObject > secondObject } 
like image 126
AdamPro13 Avatar answered Sep 16 '22 15:09

AdamPro13


TL;DR

Swift 5.5

$0 and $1 are Closure’s first and second shorthand arguments (a.k.a. Shorthand Argument Names or SAN for short). The shorthand argument names are automatically provided by Swift. The first argument can be referenced by $0, the second argument can be referenced by $1, the third one by $2, and so on.

As you know, a Closure is a self-contained block of functionality (a function without name) that can be passed around and used in your code. Closure has different names in other programming languages as well as slight differences in meaning – it's Lambda in Python and Kotlin, or Block in C and Obj-C.


Shortening a Closure

let coffee: [String] = ["Cappuccino", "Espresso", "Latte", "Ristretto"] 

1. Normal Function

func backward(_ n1: String, _ n2: String) -> Bool {     return n1 > n2 } var reverseOrder = coffee.sorted(by: backward)   /* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */ 

2. Inline Closure Expression

reverseOrder = coffee.sorted(by: { (n1: String,                                      n2: String) -> Bool in return n1 > n2 } ) 

3. Inferring Type From Context

reverseOrder = coffee.sorted(by: { n1, n2 in return n1 > n2 } ) 

4. Implicit Returns from Single-Expression Closures

reverseOrder = coffee.sorted(by: { n1, n2 in n1 > n2 } ) 

5. Shorthand Argument Names

reverseOrder = coffee.sorted(by: { $0 > $1 } )  /* $0 and $1 are closure’s first and second String arguments. */ 

6. Operator Methods

reverseOrder = coffee.sorted(by: >)  /* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */ 


Higher Order Function map with dot notation

let companies = ["bmw", "kfc", "ibm", "htc"]  let uppercased = companies.map { (item) -> String in item.uppercased() }  /* RESULT: ["BMW", "KFC", "IBM", "HTC"] */ 

Shorthand Argument Name in HOF map

let uppercased = companies.map { $0.uppercased() }  /* RESULT: ["BMW", "KFC", "IBM", "HTC"] */ 


SAN in HOF filter with remainder operator

let numbers: [Int] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  let filteredNumbers = numbers.filter { ($0 % 2) == 0 }  print(filteredNumbers)  /* RESULT: [2, 4, 6, 8, 10] */ 


SAN in Variadic functions

Variadic functions are ones that accept any number of parameters. Shorthand Argument Names are perfect for such cases.

fileprivate func dessert(_ fruits: String...) -> Bool {      return fruits.contains { $0 == "Apple" } }  let contains = dessert("Mango", "Durian", "apple")  print(contains)  /* RESULT:  false */ 


Repeating $0

let cubedNumber = { $0 * $0 * $0 } (25)  print(cubedNumber)  /* RESULT:  25^3 = 15625 */ 


Three Shorthand Argument Names – $0, $1, $2

let math: (Int8, Int8, Int8) -> Int8 = { $0 + $1 - $2 }  func feedClosure() -> (Int8, Int8, Int8) -> Int8 {     return math } feedClosure()(10, 20, 100)  /* RESULT:  (10 + 20 - 100) = -70 */ 


Five SANs – $0, $1, $2, $3, $4

let factorial = { $0 * $1 * $2 * $3 * $4 } (1, 2, 3, 4, 5)  print(factorial)  /* RESULT:  5! = 120 */ 


Key Path Expression

In Swift 5.2 you can access parameters of every instance via key path expression:

struct Lighter {     let manufacturer: String     let refillable: Bool }  let zippo = Lighter(manufacturer: "Zippo", refillable: true) let cricket = Lighter(manufacturer: "Cricket", refillable: false)  let lighters: [Lighter] = [zippo, cricket]  let refillableOnes = lighters.map(\.refillable)  print(refillableOnes)  /* RESULT:  [true, false] */ 

Of course, you can alternatively use a familiar syntax:

Regular syntax – $0.property:

let refillableOnes = lighters.map { $0.refillable }  print(refillableOnes)  /* RESULT:  [true, false] */ 


Shorthand Argument Name with a subscript

let arrays: [[String]] = [["Hello", "Hola"], ["world", "mundo"]]  let helloWorld = arrays.compactMap { $0[0] }  print(helloWorld)  /* RESULT:  ["Hello", "world"] */ 

One more example with a subscript:

let dictionaries: [[Int8: Any?]] = [[1: "x"], [2: nil], [3: "z"]]  let values = dictionaries.compactMap { $0[$0.startIndex].value }  print(values)   /* RESULT:  ["x", "z"] */ 

Or look at this example:

let sett: Set<String> = ["One", "", "Three"]  sett.map {     switch $0.isEmpty {         case true:             print("Empty")         case false:             print("Element \($0) isn't empty")     } }  /*   RESULT:   "Element Three isn't empty"  */ /*             "Empty"                      */ /*             "Element One isn't empty"    */ 


Shorthand Argument Name in Completion Handler

let completionHandler: ((Bool) -> Void)? = {     if $0 {         print("It is true, sister...")     } else {         print("False")     } } completionHandler?(true)  /* RESULT:  It is true, sister... */ 

Regular syntax, however, is as following:

let completionHandler: ((Bool) -> Void)? = { sayTheTruth in     if sayTheTruth {         print("It is true, sister...")     } else {         print("False")     } } completionHandler?(false)  /* RESULT:  False */ 


SAN in ForEach structure in SwiftUI

let columns: [GridItem] = Array(repeating: .init(.fixed(70)), count: 5)  var body: some View {     ScrollView {         LazyVGrid(columns: columns) {             ForEach((1...10), id: \.self) {                  Text("\($0)").frame(maxWidth: .infinity)             }         }     } }  /*   RESULT:   1  2  3  4  5   */ /*             6  7  8  9  10  */ 


Operator Method vs SAN

Operator Method:

let records: [Int] = [110, 108, 107, 109, 108]  public func averageSpeed(records: [Int]) throws -> Int {     let average = records.reduce(0, +) / records.count     return average } try averageSpeed(records: records)  /* RESULT:  108 */ 

Shorthand Argument Names $0 and $1:

public func averageSpeed(records: [Int]) throws -> Int {     let average = records.reduce(0) { $0 + $1 } / records.count     return average } try averageSpeed(records: records)  /* RESULT:  108 */ 


Swift vs Kotlin vs Python

Also, let's see how Kotlin's lambda is similar to Swift's closure:

Swift

let element: [String] = ["Argentum","Aurum","Platinum"]  let characterCount = element.map { $0.count }  print(characterCount)  /* RESULT:  [8, 5, 8] */  

Kotlin

Often Kotlin's lambda expression has only one parameter with implicit name: it.

val element = listOf("Argentum","Aurum","Platinum")  val characterCount = element.map { it.length }  println(characterCount)  /* RESULT:  [8, 5, 8] */ 

But in Python there's no equivalent of Shorthand Argument Name.

Python

element = ["Argentum","Aurum","Platinum"]  characterCount = list(map(lambda x: len(x), element))  print(characterCount)  # RESULT:  [8, 5, 8] 
like image 22
Andy Jazz Avatar answered Sep 19 '22 15:09

Andy Jazz