# reduce() vs. reduce(into:)

{% tabs %}
{% tab title="📗 參考" %}

* [x] Advanced Swift, p.37
  {% endtab %}

{% tab title="🗣 討論" %}

* [Using reduce(into:\_:) to filter adjacent equal elements](https://stackoverflow.com/questions/47613651/using-reduceinto-to-filter-adjacent-equal-elements)
  {% endtab %}

{% tab title="📘 手冊" %}

* Swift ⟩ [Sequence](https://developer.apple.com/documentation/swift/sequence) ⟩
  * [.reduce()](https://developer.apple.com/documentation/swift/sequence/2907677-reduce)
  * [.reduce(into:\_:)](https://developer.apple.com/documentation/swift/sequence/3128812-reduce)
* [SwiftUI](https://developer.apple.com/documentation/swiftui)  ⟩  [Drawing & Animation](https://developer.apple.com/documentation/swiftui/drawing-and-animation)  ⟩ [AnyTransition](https://developer.apple.com/documentation/swiftui/anytransition)
  {% endtab %}

{% tab title="👥 相關" %}

* [seq.reduce(\_:\_:)](/ios/swift/collections/sequence/seq.reduce-_-_.md)
* [seq.reduce(into:\_:)](/ios/swift/collections/sequence/seq.reduce-into-_.md)
  {% endtab %}
  {% endtabs %}

{% hint style="info" %}

* 參數不同：\ <mark style="color:purple;">`reduce(into:_:)`</mark>有 <mark style="color:red;">**inout parameter**</mark>，<mark style="color:purple;">`reduce(_:_:)`</mark>沒有，所以當它將之前累積的結果傳入 <mark style="color:purple;">nextPartialResult</mark> 並需要<mark style="color:red;">**更新**</mark>新的累積結果時，就必須<mark style="color:red;">**複製**</mark> (<mark style="color:orange;">**copy-on-write**</mark>) 之前的結果，所以如果 <mark style="color:purple;">Result</mark> 是一個大型的 <mark style="color:purple;">Array</mark> 或 <mark style="color:purple;">Dictionary</mark> 之類的型別，就會**對程式的效率產生衝擊**。
* 回傳方式不同：

  <mark style="color:purple;">`reduce(_:_:)`</mark> 是將 <mark style="color:purple;">nextPartialResult</mark> <mark style="color:red;">**最後的 return value**</mark> 作為整個方法的**回傳值**。<mark style="color:purple;">`reduce(into:_:)`</mark> 則是用 <mark style="color:purple;">updateAccumulatingResult</mark> 持續對 <mark style="color:purple;">partialResult</mark> 做**更新**，然後以 <mark style="color:purple;">partialResult</mark> <mark style="color:red;">**最後的值**</mark>作為**回傳值**。
  {% endhint %}

{% hint style="danger" %}
所以使用<mark style="color:purple;">`reduce(into:_:)`</mark>時要注意：「一定要透過 <mark style="color:purple;">updateAccumulatingResult</mark> 來更新 <mark style="color:purple;">partialResult</mark>」，否則<mark style="color:red;">**回傳值**</mark>會是**原來的** <mark style="color:purple;">initialResult</mark>:exclamation:
{% endhint %}

```swift
// `reduce(_:_:)`
func reduce<Result>(
    _ initialResult    : Result, 
    // ⭐️ combine function (with return value)
    _ nextPartialResult: (Result, Element) throws -> Result
) rethrows -> Result

// `reduce(into:_:)`
func reduce<Result>(
    into initialResult: Result, 
    _ updateAccumulatingResult: (
        _ partialResult: inout Result,    // ⭐️ inout parameter
        Self.Element    
    ) throws -> ()                        // ⭐️ no return value
) rethrows -> Result
```

{% hint style="warning" %}
.[reduce(into:)](https://developer.apple.com/documentation/swift/array/3126956-reduce) is **preferred** over [reduce()](https://developer.apple.com/documentation/swift/array/2298686-reduce) for **efficiency** when the <mark style="color:red;">**result**</mark> is a **copy-on-write** type, for example an <mark style="color:red;">**Array**</mark> or a <mark style="color:red;">**Dictionary**</mark>. :point\_right: [.reduce(into:\_:)](https://developer.apple.com/documentation/swift/sequence/3128812-reduce)
{% endhint %}

## Example

{% tabs %}
{% tab title="📘 手冊" %}

* View ⟩ [Graphics and Rendering](https://developer.apple.com/documentation/swiftui/view-graphics-and-rendering) ⟩
  * [.transition(\_:)](https://developer.apple.com/documentation/swiftui/view/transition\(_:\))
    {% endtab %}

{% tab title="📗 參考" %}

* [ ] Pavel Zak ⟩  [Mastering transitions in SwiftUI](https://nerdyak.tech/development/2020/10/12/transitions-in-swiftui.html)
  {% endtab %}

{% tab title="👥 相關" %}

* [AnyTransition.combined()](/ios/swiftui/anim/transitions/anytransition/anytransition.combined.md)
  {% endtab %}
  {% endtabs %}

我們用合併 SwiftUI 中的 transitions 來舉例，說明這兩者的用法有哪些不同。在合併 SwiftUI 中的 transitions 時，使用 SwiftUI 原有的語法，顯得太過冗長：

```swift
AnyTransition
    .move(edge: .leading)
    .combined(with: .opacity)
    .combined(with: .scale)
```

若使用 .[reduce()](https://developer.apple.com/documentation/swift/array/2298686-reduce) 或 .[reduce(into:)](https://developer.apple.com/documentation/swift/array/3126956-reduce)，則可以將語法變為更精簡：

```swift
// 無法自行推斷 (infer) AnyTransition 型別的時候：
AnyTransition(.move(edge: .leading), .opacity, .scale)

// 可以自行推斷 AnyTransition 型別的時候：
.combined(.move(edge: .leading), .opacity, .scale)
```

前提是要先加入以下的 extension：

```swift
extension AnyTransition {
    
    // ⭐️ combining transitions
    // AnyTransition([.scale, .opacity, ...]) ........ (1)
    init(_ transitions: [AnyTransition]) {
        // use reduce() or reduce(into:)
    }
    
    // 讓可以使用的語法更豐富，在可以使用 AnyTransition 的地方，直接用：
    // .combined(.move, .scale, ...)
    public static func combined(_ transitions: AnyTransition...) -> AnyTransition {
        .init(transitions)
    }
}

extension AnyTransition: ExpressibleByArrayLiteral {
    // convenience init
    // AnyTransition(.move, .scale, .opacity, ...)
    public init(arrayLiteral elements: AnyTransition...) {
        self.init(elements)    // 使用 (1)
    }
}
```

至於用 .reduce() 還是 .reduce(into:)？它們之間有何不同，請看下面的程式碼：

## .reduce()

```swift
init(_ transitions: [AnyTransition]) {
    let intialValue = AnyTransition.identity
    // ⭐️ 使用 `reduce(_:_)`
    self = transitions.reduce(intialValue) { (result, transition) in
        // ⭐️ 下面的結果會「自動成為新的 `result`」，不需手動改寫 `result`
        //    所以官方稱這個部分的 closure 為：`nextPartialResult`。
        result.combined(with: transition)    // ⭐️ implicit return
    }
}
```

## .reduce(into:)

```swift
init(_ transitions: [AnyTransition]) {
    var intialValue = AnyTransition.identity
    // ⭐️ 使用 `reduce(into:_:)`
    self = transitions.reduce(into: intialValue) { (result, transition) in
        // ⭐️ 記得手動改寫 `result` 變數，否則效果並不會累加。
        //    因為如果我們不寫 `result = ...`，這時 `result` 變數
        //    並不會自動改變，所以官方稱這個部分為 `updateAccumulatingResult`
        //    而不是 `nextPartialResult`。
        result = result.combined(with: transition)    // ⭐️ no return value
    }
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://lochiwei.gitbook.io/ios/swift/collections/sequence/reduce-vs-reduceinto.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
