【问题标题】:Understanding Swift flat map syntax理解 Swift 平面地图语法
【发布时间】:2016-08-17 11:57:12
【问题描述】:

下面函数“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!

读到这里我还是不明白,在上面的例子中,Episode 的 init 是怎么没有括号的。

有时 Swift 非常擅长推断事物,以至于很难理解正在发生的事情。有人可以帮我理解为什么 Episodes 的 init 方法不使用括号吗?

...flatMap (Episode.init)

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

【问题讨论】:

标签: ios swift syntax


【解决方案1】:

让我们从一个简单的代码sn-p开始

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

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

这是因为

  1. 数组有 Int 作为通用元素
  2. String 有一个 init 接受 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 有一个init,它接受dictionaries 中相同类型的元素。

事实上,dictionaries 的每个元素都有 [String: AnyObject] 这个类型,并且 Episode 的 init 接受相同的类型。

init?(dictionary: [String: AnyObject]) { ... }

【讨论】:

    【解决方案2】:

    那是因为在 Swift 中,instance methods are curried functions。这意味着 Episode.init 是一个函数(实际上它是一个命名的闭包),它接收一个参数 - self,巧合(或不)匹配 flatMap 期望的闭包签名。

    在您的代码中发生的情况是,您传递了对 flatMap 期望的闭包的引用,而不是调用它,因为调用闭包只会返回一个 Episode 实例。

    【讨论】:

      【解决方案3】:

      它是 Trailing Closures swift 你可以在苹果文档中找到它 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html

      flatMap 等待类型为(A)-&gt;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)}
      

      【讨论】:

        【解决方案4】:
        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")
        

        同样我们可以将多个名称传递给多个 Employee 以创建实例

        allEmployee = names.myMap(f: employeeFunc)
        

        我们也可以这样映射

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

        让我们打印值

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

        【讨论】:

          猜你喜欢
          • 2014-10-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-07-18
          • 2013-05-18
          相关资源
          最近更新 更多