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

后端 未结 6 2365
你的背包
你的背包 2020-12-12 12:12
let sortedNumbers = numbers.sort { $0 > $1 }
print(sortedNumbers)

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

相关标签:
6条回答
  • 2020-12-12 12:30

    $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
    }
    
    0 讨论(0)
  • 2020-12-12 12:30

    It represents shorthanded arguments sent into a closure, this example breaks it down:

    Swift 4:

    var add = { (arg1: Int, arg2: Int) -> Int in
        return arg1 + arg2
    }
    add = { (arg1, arg2) -> Int in
        return arg1 + arg2
    }
    add = { arg1, arg2 in
        arg1 + arg2
    }
    add = {
        $0 + $1
    }
    
    let result = add(20, 20) // 40
    
    0 讨论(0)
  • 2020-12-12 12:30

    It is shorthand argument names.

    Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.

    If you use these shorthand argument names within your closure expression, you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type. The in keyword can also be omitted, because the closure expression is made up entirely of its body:

        reversed = names.sort( { $0 > $1 } )
    

    Here, $0 and $1 refer to the closure’s first and second String arguments.

    0 讨论(0)
  • 2020-12-12 12:35

    In Addition with @Bobby's Answer I would like to Add an Example

    var add: (Int,Int,Int)->Int
    add = {
    //So here the $0 is first argument $1 is second argument $3 is third argument
        return $0 + $1 + $2
    //The above statement can also be written as $0 + $1 + $2 i.e is return is optional
    }
    
    let result = add(20, 30, 40) 
    print(result) // Prints 90
    
    0 讨论(0)
  • 2020-12-12 12:43

    The refer to the first and second arguments of sort. Here, sort compares 2 elements and order them. You can look up Swift official documentation for more info:

    Swift automatically provides shorthand argument names to inline closures, which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.

    0 讨论(0)
  • 2020-12-12 12:48

    TL;DR

    Swift 5.3

    $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 Objective-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 uppercasedCompanies = companies.map { (item) -> String in item.uppercased() }
    
    /* RESULT: ["BMW", "KFC", "IBM", "HTC"] */
    

    SAN in HOF map with dot notation

    let uppercasedCompanies = 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] */
    


    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 and $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"] */
    


    Shorthand Argument Name in Completion Handler

    let completionHandler: (Bool) -> Void = {
        if $0 {
            print("It is true, sister...")
        }
    }
    

    Regular syntax, however, is as following:

    let completionHandler: (Bool) -> Void = { sayTheTruth in
        if sayTheTruth {
            print("It is true, sister...")
        }
    }
    


    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] */
    
    0 讨论(0)
提交回复
热议问题