81005


Swiftの通常のfor文で、以下のようなコードを書きました。

[code]

    import Foundation
    
    let testArray = [0,1,2,3,4,5]
    var newArray = [Int]()
    for test in testArray
    {
        newArray.append(test*2)
        if test == 4
        {
            break
        }
    }
    print(newArray)
    
[/code]

このコードを実行すると、`[0, 2, 4, 6, 8]`がprintされます。

しかし、for文より.mapのほうが処理速度が早いという記事を読んだので  
これを.mapに書き換えたいと考えています。  
ただ、.mapだとbreak文に相当するものがあるのか分かりません。

以下のようなコードを書いたのですが、breakするにはどうしたら良いでしょうか？  
また、.mapの使用方法にもやや自信がないのですが、このような場合、filterなどを使ったほうが良いのでしょうか？  

[code]

    import Foundation
    
    let testArray = [0,1,2,3,4,5]
    var newArray = [Int]()
    _ = testArray.map
    {
        let test = $0
        newArray.append(test*2)
        if test == 4
        {
            //break
        }
    }
    print(newArray)
    
[/code]


CC BY-SA 4.0
2021-08-24T03:36:29.137
81003
2021-08-24T09:47:12.397
36446
question
swift
.mapでbreak処理をするにはどうしたらよいか


> for文より.mapのほうが処理速度が早いという記事を読んだ

場合によりけりです。for-inを使ったコードを`map`に置き換えれば必ず早くなるとは思わない方がいいでしょう。

> 以下のようなコードを書いたのですが、breakするにはどうしたら良いでしょうか？  
>  また、.mapの使用方法にもやや自信がないのですが、このような場合、filterなどを使ったほうが良いのでしょうか？

Swiftの`map`には途中でのbreakを実現する機能はありません。

たとえば出来るだけ`map`の働きを生かして書こうと思うと、こんな感じになるでしょう。

[code]

        let endIndex = testArray.firstIndex(of: stopValue) ?? testArray.endIndex
        let newArray = testArray[0...endIndex]
            .map {$0 * 2}
    
[/code]

(`stopValue`には、あなたのコードで`4`に相当する値が入っている想定。)

但し、最初に書きましたが、手元でmacOSのCommand Line
Toolプロジェクトとして配列の要素数を1億(停止する要素の位置を4千万番目)とかでRelease設定(最適化が`-O`:速度優先になる)で実行時間を比べてみましたが、どちらかが有意に高速と言う結果は得られませんでした。

[code]

    import Foundation
    
    let testArray = Array(0..<100_000_000)
    let stopValue = 40_000_000
    
    do {
        let start = Date()
        let endIndex = testArray.firstIndex(of: stopValue) ?? testArray.endIndex
        let newArray = testArray[0...endIndex]
            .map {$0 * 2}
        let end = Date()
        print(newArray.count, end.timeIntervalSince(start))
        print(newArray.reduce(0, +))
    }
    
    do {
        let start = Date()
        var newArray = [Int]()
        //newArray.reserveCapacity(testArray.count)
        for test in testArray
        {
            newArray.append(test*2)
            if test == stopValue
            {
                break
            }
        }
        let end = Date()
        print(newArray.count, end.timeIntervalSince(start))
        print(newArray.reduce(0, +))
    }
    
[/code]

私の手元での実行結果

[code]

    40000001 0.18691504001617432
    1600000040000000
    40000001 0.2031090259552002
    1600000040000000
    
[/code]

* * *

「for文より.mapのほうが処理速度が早い」といった単純化されすぎてときに適切ではなくなる「標語」を覚えるのではなく、配列に要素を追加していく処理を繰り返す場合、最初に`reserveCapacity`を呼んでおくと高速化できる、と言った要素を個別に覚えていかれた方が良いでしょう。


CC BY-SA 4.0
2021-08-24T05:06:06.803
81005
2021-08-24T05:06:06.803
13972
81003
answer


Sequence.map の実装は以下にあります。

[swift/Sequence.swift at main ·
apple/swift](https://github.com/apple/swift/blob/main/stdlib/public/core/Sequence.swift#L622)

これを元に `mapUntil` メソッドを作成するという方法も考えられます。判定用の `condFunc`(closure) を追加しています。

[code]

    extension Sequence {
      public func mapUntil<T>(
        _ transform: (Element) throws -> T,
        until condFunc: (Element) -> Bool
      ) 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 {
          let element = iterator.next()!
          result.append(try transform(element))
          if condFunc(element) {
            return Array(result)
          }
        }
        // Add remaining elements, if any.
        while let element = iterator.next() {
          result.append(try transform(element))
          if condFunc(element) {
            break
          }
        }
        return Array(result)
      }
    }
    
[/code]

**動作確認**

[code]

    let testArray = [0, 1, 2, 3, 4, 5]
    
    print("testArray:", testArray)
    for i in [4, 2, 0, 6] {
      print(
        String(format: "%d:", i),
        testArray.mapUntil({ $0 * 2 }, until: { $0 == i })
      )
    }
    
    =>
    testArray: [0, 1, 2, 3, 4, 5]
    4: [0, 2, 4, 6, 8]
    2: [0, 2, 4]
    0: [0]
    6: [0, 2, 4, 6, 8, 10]
    
[/code]


CC BY-SA 4.0
2021-08-24T09:47:12.397
81014
2021-08-24T09:47:12.397
47127
81003
answer