Swift 闭包中的 $0 和 $1 是啥意思?

Posted

技术标签:

【中文标题】Swift 闭包中的 $0 和 $1 是啥意思?【英文标题】:What does $0 and $1 mean in Swift Closures?Swift 闭包中的 $0 和 $1 是什么意思? 【发布时间】:2016-07-08 17:54:10 【问题描述】:
let sortedNumbers = numbers.sort  $0 > $1 
print(sortedNumbers)

谁能解释一下,$0$1 在swift中是什么意思?

更多示例

array.forEach 
    actions.append($0)

【问题讨论】:

【参考方案1】:

$0 是传入闭包的第一个参数。 $1 是第二个参数,等等。您显示的闭包是以下的简写:

let sortedNumbers = numbers.sort  (firstObject, secondObject) in 
    return firstObject > secondObject

【讨论】:

我可以在闭包中使用第一个参数和第二个参数编写我自己的逻辑吗? 是的,这就是关闭的全部目的。 谢谢老兄,你的回答帮助我理解了闭包更好的部分【参考方案2】:

TL;DR

斯威夫特 5.5

$0$1 是 Closure 的第一个和第二个速记参数(又名 Shorthand Argument NamesSAN 简称)。速记参数名称由 Swift 自动提供。第一个参数可以被$0引用,第二个参数可以被$1引用,第三个参数可以被$2引用,等等。

如您所知,Closure 是一个独立的功能块(没有名称的函数),可以在您的代码中传递和使用。闭包在其他编程语言中有不同的名称,含义也略有不同——在 Python 和 Kotlin 中是 Lambda,或 Block 在 C 和 Obj-C 中。

缩短闭包

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

1.普通函数

func backward(_ n1: String, _ n2: String) -> Bool 
    return n1 > n2

var reverseOrder = coffee.sorted(by: backward)


/* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */

2。内联闭包表达式

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

3.从上下文推断类型

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

4.单表达式闭包的隐式返回

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

5.速记参数名称

reverseOrder = coffee.sorted(by:  $0 > $1  )

/* $0 and $1 are closure’s first and second String arguments. */

6.运算符方法

reverseOrder = coffee.sorted(by: >)

/* RESULT: ["Ristretto", "Latte", "Espresso", "Cappuccino"] */

高阶函数map带点符号

let companies = ["bmw", "kfc", "ibm", "htc"]

let uppercased = companies.map  (item) -> String in item.uppercased() 

/* RESULT: ["BMW", "KFC", "IBM", "HTC"] */

HOF 中的简写参数名称 map

let uppercased = companies.map  $0.uppercased() 

/* RESULT: ["BMW", "KFC", "IBM", "HTC"] */

带有余数运算符的 HOF filter 中的 SAN

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

可变参数函数是接受任意数量参数的函数。速记参数名称非常适合这种情况。

fileprivate func dessert(_ fruits: String...) -> Bool 

    return fruits.contains  $0 == "Apple" 


let contains = dessert("Mango", "Durian", "apple")

print(contains)

/* RESULT:  false */

重复$0

let cubedNumber =  $0 * $0 * $0  (25)

print(cubedNumber)

/* RESULT:  25^3 = 15625 */

三个速记参数名称 - $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 */

五个 SAN——$0$1$2$3$4

let factorial =  $0 * $1 * $2 * $3 * $4  (1, 2, 3, 4, 5)

print(factorial)

/* RESULT:  5! = 120 */

关键路径表达式

在 Swift 5.2 中,您可以通过键路径表达式访问每个实例的参数:

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] */

当然,您也可以使用熟悉的语法:

正则语法——$0.property

let refillableOnes = lighters.map  $0.refillable 

print(refillableOnes)

/* RESULT:  [true, false] */

带下标的简写参数名称

let arrays: [[String]] = [["Hello", "Hola"], ["world", "mundo"]]

let helloWorld = arrays.compactMap  $0[0] 

print(helloWorld)

/* RESULT:  ["Hello", "world"] */

另一个带下标的例子:

let dictionaries: [[Int8: Any?]] = [[1: "x"], [2: nil], [3: "z"]]

let values = dictionaries.compactMap  $0[$0.startIndex].value 

print(values) 

/* RESULT:  ["x", "z"] */

或者看看这个例子:

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"    */

完成处理程序中的简写参数名称

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

completionHandler?(true)

/* RESULT:  It is true, sister... */

但是,常规语法如下:

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

completionHandler?(false)

/* RESULT:  False */

SwiftUI 中 ForEach 结构中的 SAN

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  */

运算符方法与 SAN

算子方法:

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 */

简写参数名称 $0 和 $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

另外,让我们看看 Kotlin 的 lambda 与 Swift 的闭包有何相似之处:

斯威夫特

let element: [String] = ["Argentum","Aurum","Platinum"]

let characterCount = element.map  $0.count 

print(characterCount)

/* RESULT:  [8, 5, 8] */ 

科特林

通常 Kotlin 的 lambda 表达式只有一个隐含名称的参数: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]

【讨论】:

您的 Python 示例不必要地使用了 lambda。 list(map(len, ....)) 就足够了。就个人而言,除非在关键性能部分,否则我会使用[len(v) for v in ...],因为它更简洁且更具可读性。 请添加快速文档链接 @AndyFedoroff 官方文档【参考方案3】:

它表示发送到闭包中的简写参数,这个例子将其分解:

斯威夫特 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

【讨论】:

由于加法运算符与闭包具有相同的函数签名,您可以将其进一步分解为:add = (+) 在我的 swift 5 测试中,它应该使用var add:((Int, Int) -&gt; Int) = ... 明确指定案例 3 和 4 的类型。【参考方案4】:

引用排序的第一个和第二个参数。在这里,sort 比较 2 个元素并对它们进行排序。 您可以查找Swift official documentation 了解更多信息:

Swift 自动为内联提供速记参数名称 闭包,可用于引用闭包的值 名称为 $0、$1、$2 等的参数。

【讨论】:

【参考方案5】:

除了@Bobby 的回答,我想添加一个示例

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

【讨论】:

这不能算作一个答案。如果要添加示例,只需编辑@bobby 的答案 哈哈 - 我实际上投了赞成票,但是,我在第一段之后停止阅读闭包定义。我同意,这不是一个答案,它是整个 Swift 文档关于闭包的转录。【参考方案6】:

它是简写的参数名称。

Swift 自动为内联闭包提供速记参数名称,可用于通过名称 $0、$1、$2 等来引用闭包参数的值。

如果您在闭包表达式中使用这些速记参数名称,您可以从其定义中省略闭包的参数列表,并且速记参数名称的数量和类型将从预期的函数类型中推断出来。 in 关键字也可以省略,因为闭包表达式完全由它的主体组成:

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

这里,$0 和 $1 指的是闭包的第一个和第二个字符串参数。

【讨论】:

以上是关于Swift 闭包中的 $0 和 $1 是啥意思?的主要内容,如果未能解决你的问题,请参考以下文章

在 IntelliJ IDEA 中调试 Java 期间生成的变量名称中的美元符号是啥意思?是闭包吗?

Xcode中的swift语言版本构建设置是啥意思?

Swift:viewDidLayoutSubviews 是啥意思?

@objc 动态变量在 Swift 4 中是啥意思

在 Swift 3.0 中的转义闭包中改变自我(结构/枚举)

js闭包是啥?