理解Swift flat map语法

5
在下面的'parseB'函数中,flatMap使用括号而不是花括号的语法。
struct Episode {
    let id: String
    let title: String
}

extension Episode {
    init?(dictionary: [String: AnyObject]) {
        guard let id = dictionary["id"] as? String, title = dictionary["title"] as? String else { return nil }
        self.id = id
        self.title = title
    }
}

func parseA(dictionaries: [[String: AnyObject]]) -> [Episode] {
    return dictionaries.flatMap { dic in Episode.init(dictionary: dic)}
}

func parseB(dictionaries: [[String: AnyObject]]) -> [Episode] {
    return dictionaries.flatMap (Episode.init)
}

有一个扁平化地图的实现,看起来像这样:
public func flatMap<U>(@noescape f: (Wrapped) throws -> U!) rethrows -> U!

阅读这篇文章,我仍然不明白为什么在上面的例子中,Episodes上的init没有括号。

有时候Swift非常擅长推断出东西,让人很难理解正在发生的事情。请问有人能帮助我理解为什么Episodes的init方法没有使用括号吗?

...flatMap (Episode.init)

参考1:https://talk.objc.io/episodes/S01E01-networking 参考2:https://github.com/objcio/S01E01-networking

这些链接提供了关于网络编程的信息和示例代码。

这是因为在 Swift 中,实例方法是柯里化函数。 - Cristik
4个回答

4

让我们从一个简单的代码片段开始

[1,2,3].map(String.init) // ["1", "2", "3"]

这里的map遍历了数组[1,2,3]中的每个元素,并将n-th元素传递给String初始化程序。

这个例子能够成功运行是因为:

  1. 该数组的泛型元素为Int
  2. String具有一个仅接受Int作为参数的初始化方法

为什么要用( ... )而不是{ ... }

在这种情况下,我们使用这种形式的.map(...)而不是.map{ ... },因为我们正在向map传递在别处定义的函数(或者说初始化程序),而不是直接编写函数/闭包。

就像我们写代码时会这样:

func isEven(n:Int) -> Bool {
    return n % 2 == 0
}

[1,2,3].filter(isEven) // [2]

在这里,我们正在传递给过滤器上面定义的函数,因此我们使用()
当然,我们也可以直接使用{...}编写函数。
[1,2,3].filter { (n:Int) -> Bool in
    return n % 2 == 0
}

你的代码

在你的代码中,这个有效

dictionaries.flatMap (Episode.init)

因为Episode有一个接受与dictionaries中元素相同类型的init方法。
事实上,dictionaries的每个元素都具有这种类型[String: AnyObject],而Episodeinit方法也接受相同类型。
init?(dictionary: [String: AnyObject]) { ... }

1
那是因为在Swift中,实例方法是柯里化函数。这意味着Episode.init是一个函数(实际上是一个命名闭包),它接收一个参数 - self,恰好(或不恰好)与flatMap期望的闭包的签名相匹配。
在你的代码中发生的情况是,你传递了一个对flatMap期望的闭包的引用,而不是调用它,因为调用该闭包只会返回一个Episode实例。

1

这是关于编程的内容,Trailing Closures swift 可以在苹果文档中找到https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

flatMap 等待一个类型为 (A)->B 的参数,但它可以是函数或闭包:

let initializer: (Int) -> String = String.init

相同

let closure:(Int) -> String = {return String($0)}

为了简单,我们可以称之为map方法:

[1,2,3].map(initializer) == [1,2,3].map(closure)

但如果我们使用闭包作为最后一个参数,则可以在方法外部编写,就像我们的map方法一样

[1,2,3].map(){return String($0)}

我们不能省略的不仅仅是()

[1,2,3].map{return String($0)}

0
class Employee {
    var name: String = ""

    init(name: String) {
        self.name = name
    }
}

实例方法是Swift中的柯里化函数。 这是一个柯里化函数,它接受一个字符串参数并返回一个员工对象。
let employeeFunc = Employee.init  //  (String) -> Employee

让我们定义自己的数组类型映射函数

extension Array {
    func myMap <U> (f: (Element) -> U) -> [U] {
        var result = [U]()

        for element in self {
            result.append(f(element))
        }
        return result
    }
}


var allEmployee = [Employee]()
let names = ["Mario", "Luigi"]

您可以逐个传递值给柯里化函数。这个柯里化函数只期望一个参数。

let firstEmployee = employeeFunc("Superman")

同样地,我们可以传递多个名称给多个员工以创建实例

allEmployee = names.myMap(f: employeeFunc)

我们也可以这样映射

allEmployee = names.myMap(){ employeeFunc($0)}

让我们打印这个值

for employee in allEmployee {
    print (employee.name)
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接