Swift高階函數-map、compactMap、filter、reduce

Swift中默認幫我們實現了很多方便的序列操作,一般稱之爲高階函數,在編程中都有很實用的功能。推薦在日程編碼中使用。減少代碼量,提高可讀性。

一、map

返回一個數組,其中包含給定閉包映射到序列元素的結果。

1.1 數組

let list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let mapList = list.map { $0 + 10 }
print(mapList)
---console
[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

Collection協議中源碼

  @inlinable
  public func map<T>(
    _ transform: (Element) throws -> T
  ) rethrows -> [T] {
    // TODO: swift-3-indexing-model - review the following
    let n = self.count
    if n == 0 {
      return []
    }

    var result = ContiguousArray<T>()
    result.reserveCapacity(n)

    var i = self.startIndex

    for _ in 0..<n {
      result.append(try transform(self[i]))
      formIndex(after: &i)
    }

    _expectEnd(of: self, is: i)
    return Array(result)
  }

1.2 字典

map
let dic = ["k1": 1, "k2": 2, "k3": 3, "k4": 4, "k5": 5]
let mapDicList = dic.map { (key, value) in
    return key + "-\(value)"
}
print(mapDicList)
---console
["k2-2", "k3-3", "k1-1", "k5-5", "k4-4"]

Sequence協議中源碼

  @inlinable
  public func map<T>(
    _ transform: (Element) throws -> T
  ) rethrows -> [T] {
    let initialCapacity = underestimatedCount
    var result = ContiguousArray<T>()
    result.reserveCapacity(initialCapacity)

    var iterator = self.makeIterator()

    // Add elements up to the initial capacity without checking for regrowth.
    for _ in 0..<initialCapacity {
      result.append(try transform(iterator.next()!))
    }
    // Add remaining elements, if any.
    while let element = iterator.next() {
      result.append(try transform(element))
    }
    return Array(result)
  }

1.3 mapValues--字典專屬

返回一個新字典,其中包含由給定閉包轉換的該字典的鍵值。

let mapValueDic = dic.mapValues { value in
    value + 10
}
print(mapValueDic)
---console
["k1": 11, "k4": 14, "k2": 12, "k3": 13, "k5": 15]

mapValues源碼

extension _NativeDictionary { // High-level operations
  @inlinable
  internal func mapValues<T>(
    _ transform: (Value) throws -> T
  ) rethrows -> _NativeDictionary<Key, T> {
    let resultStorage = _DictionaryStorage<Key, T>.copy(original: _storage)
    _internalInvariant(resultStorage._seed == _storage._seed)
    let result = _NativeDictionary<Key, T>(resultStorage)
    // Because the current and new buffer have the same scale and seed, we can
    // initialize to the same locations in the new buffer, skipping hash value
    // recalculations.
    for bucket in hashTable {
      let key = self.uncheckedKey(at: bucket)
      let value = self.uncheckedValue(at: bucket)
      try result._insert(at: bucket, key: key, value: transform(value))
    }
    return result
  }
}

---

extension __CocoaDictionary {
  @inlinable
  internal func mapValues<Key: Hashable, Value, T>(
    _ transform: (Value) throws -> T
  ) rethrows -> _NativeDictionary<Key, T> {
    var result = _NativeDictionary<Key, T>(capacity: self.count)
    for (cocoaKey, cocoaValue) in self {
      let key = _forceBridgeFromObjectiveC(cocoaKey, Key.self)
      let value = _forceBridgeFromObjectiveC(cocoaValue, Value.self)
      try result.insertNew(key: key, value: transform(value))
    }
    return result
  }
}

二、compactMap

返回一個數組,其中包含使用此序列的每個元素調用給定轉換閉包的非nil結果。

2.1 數組、字典

let tempList = ["1", "2", "3", "haha", "4"]
let compactMapList: [Int] = tempList.compactMap { el in
    print(el)
    return Int(el)
}
print(compactMapList)
---console
[1, 2, 3, 4]
let tempDic = ["k1": "1", "k2": "2", "k3": "haha", "k4": "4"]
let compactMapDic = tempDic.compactMap { (key, value) in
    return Int(value)
}
print(compactMapDic)
---console
[1, 4, 2]

Sequence源碼

  @inlinable // protocol-only
  public func compactMap<ElementOfResult>(
    _ transform: (Element) throws -> ElementOfResult?
  ) rethrows -> [ElementOfResult] {
    return try _compactMap(transform)
  }

  // The implementation of compactMap accepting a closure with an optional result.
  // Factored out into a separate function in order to be used in multiple
  // overloads.
  @inlinable // protocol-only
  @inline(__always)
  public func _compactMap<ElementOfResult>(
    _ transform: (Element) throws -> ElementOfResult?
  ) rethrows -> [ElementOfResult] {
    var result: [ElementOfResult] = []
    for element in self {
      if let newElement = try transform(element) {
        result.append(newElement)
      }
    }
    return result
  }
}

2.2 compactMapValues--字典專屬

let data = ["a": "1", "b": "three", "c": "///4///"]
let c: [String: Int] = data.compactMapValues { str in Int(str) }
---console
["a": 1]

Dictionary協議中源碼

  @inlinable
  public func compactMapValues<T>(
    _ transform: (Value) throws -> T?
  ) rethrows -> Dictionary<Key, T> {
    let result: _NativeDictionary<Key, T> =
      try self.reduce(into: _NativeDictionary<Key, T>()) { (result, element) in
      if let value = try transform(element.value) {
        result.insertNew(key: element.key, value: value)
      }
    }
    return Dictionary<Key, T>(_native: result)
  }

三、filter

3.1 數組

let list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let retList = list.filter { $0 % 2 == 0 }
print(retList)
---console
[2, 4, 6, 8, 10]

Sequence源碼

  @inlinable
  public __consuming func filter(
    _ isIncluded: (Element) throws -> Bool
  ) rethrows -> [Element] {
    return try _filter(isIncluded)
  }

  @_transparent
  public func _filter(
    _ isIncluded: (Element) throws -> Bool
  ) rethrows -> [Element] {

    var result = ContiguousArray<Element>()

    var iterator = self.makeIterator()

    while let element = iterator.next() {
      if try isIncluded(element) {
        result.append(element)
      }
    }

    return Array(result)
  }

3.2 字典

let dic = ["k1": 1, "k2": 2, "k3": 3, "k4": 4, "k5": 5]
let retList = dic.filter { $0.value > 3 }
print(retList)
---console
["k4": 4, "k5": 5]

Dictionary協議中源碼

  @inlinable
  @available(swift, introduced: 4.0)
  public __consuming func filter(
    _ isIncluded: (Element) throws -> Bool
  ) rethrows -> [Key: Value] {
    // FIXME(performance): Try building a bitset of elements to keep, so that we
    // eliminate rehashings during insertion.
    var result = _NativeDictionary<Key, Value>()
    for element in self {
      if try isIncluded(element) {
        result.insertNew(key: element.key, value: element.value)
      }
    }
    return Dictionary(_native: result)
  }
}

四、reduce

主要是用來對集合中每個元素和疊加器做對應操作

// 函數一
@inlinable public func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Element) throws -> Result) rethrows -> Result
// 函數二
@inlinable public func reduce<Result>(into initialResult: __owned Result, _ updateAccumulatingResult: (inout Result, Element) throws -> ()) rethrows -> Result

2個方法有些類似的,差別在於閉包的定義。

  • 第一個函數閉包,接收ResultElement,返回閉包執行後的Result,後續的操作是將每次閉包執行後的Result當做下一個元素執行閉包的入參,遍歷完所有元素;

  • 第二個函數閉包,接收的也是ResultElement,沒有返回值,並且Result是用inout修飾的,所以傳入閉包的是Result的地址,所以閉包的執行都是基於Result進行操作的。

數組:

// 函數一用法:
let list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let ret = list.reduce(0) { partialResult, el in
    return partialResult + el
}
print(ret)
---console
55

// 函數二用法:
let ret2 = list.reduce(into: 0) { partialResult, el in
    partialResult += el
}
print(ret2)
---console
55

字典:

let dic = ["k1": 1, "k2": 2, "k3": 3, "k4": 4, "k5": 5]
let ret3 = dic.reduce(0) { partialResult, el in
    return partialResult + el.value
}
print(ret3)
---console
15

Sequence協議中源碼:

函數一

  @inlinable
  public func reduce<Result>(
    _ initialResult: Result,
    _ nextPartialResult:
      (_ partialResult: Result, Element) throws -> Result
  ) rethrows -> Result {
    var accumulator = initialResult
    for element in self {
      accumulator = try nextPartialResult(accumulator, element)
    }
    return accumulator
  }

函數二

  @inlinable
  public func reduce<Result>(
    into initialResult: __owned Result,
    _ updateAccumulatingResult:
      (_ partialResult: inout Result, Element) throws -> ()
  ) rethrows -> Result {
    var accumulator = initialResult
    for element in self {
      try updateAccumulatingResult(&accumulator, element)
    }
    return accumulator
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章