web-dev-qa-db-ja.com

forではなくforEach(_ :)を使用する場合

ArrayDictionary の両方で文書化されているforEach(_:)インスタンスメソッド:

For-inループと同じ順序でsequenceの各要素で指定されたクロージャーを呼び出します。

それにもかかわらず、 シーケンスの概要 から適応:

シーケンスは、一度に1つずつステップ実行できる値のリストです。 シーケンスの要素を反復する最も一般的な方法は、for-inループを使用することです。

forEach(_:)または_for in_による反復シーケンスの意味:

_let closedRange = 1...3

for element in closedRange { print(element) } // 1 2 3

closedRange.forEach { print($0) } // 1 2 3
_

または(配列):

_let array = [1, 2, 3]

for element in array { print(element) } // 1 2 3

array.forEach { print($0) } // 1 2 3
_

同じ出力が得られます。

なぜforEach(_:)も存在するのですか?つまり、_for in_ループの代わりにそれを使用する利点は何ですか?パフォーマンスの観点からは同じでしょうか?

前提として、特に関数型プログラミングを使用する場合は、構文上の砂糖になる可能性があります。

24
Ahmad F

forEachによるパフォーマンス上の利点はありません。実際、 ソースコードを見ると の場合、forEach関数は実際には単にfor-inを実行します。リリースビルドの場合、単にfor-inを使用するだけの場合のこの関数のパフォーマンスオーバーヘッドは重要ではありませんが、デバッグビルドの場合は、パフォーマンスに目に見える影響があります。

forEachの主な利点は、関数型プログラミングを行うときに実現されます。関数呼び出しのチェーンに追加できます。for-in構文を使用した場合に必要な別の変数に以前の結果を保存する必要はありません。したがって、代わりに:

let objects = array.map { ... }
    .filter { ... }

for object in objects {
    ...
}

代わりに、関数型プログラミングパターン内にとどまることができます。

array.map { ... }
    .filter { ... }
    .forEach { ... }

その結果、構文上のノイズが少なく、より簡潔な機能コードが作成されます。

FWIW、 ArrayDictionary 、および Sequence のドキュメントはすべて、forEachによって導入された制限を思い出させます:

  1. breakまたはcontinueステートメントを使用して、bodyクロージャーの現在の呼び出しを終了したり、後続の呼び出しをスキップしたりすることはできません。

  2. returnクロージャーでbodyステートメントを使用すると、現在のbodyの呼び出しからのみ終了し、外部スコープからは終了せず、後続の呼び出しをスキップしません。

53
Rob

私は最近、for inよりも具体的な方法でforEachを使用することが望ましいユースケースに出くわしました。レイヤーからすべてのサブレイヤーを削除するとします。以下のようなステートメントは、[CALayer]をアンラップする必要があるため機能しません

for layer in self.videoContainerView.layer.sublayers!

サブレイヤーがゼロの場合、クラッシュします。これにより、最初にサブレイヤーがあるかどうかを確認する必要があります。ただし、forEachを使用すると、次のようにこれがはるかに簡単になります。

self.videoContainerView.layer.sublayers?.forEach { $0.removeFromSuperlayer() }
11
C6Silver

それらは多かれ少なかれ互換性がありますが、2つの重要な違いがあります。

  1. break/continueは、for .. in
  2. returnforEachはクロージャを終了しますが、反復を停止しません。

その理由は、for .. inは、言語では特別な形式です(これにより、期待どおりに中断して作業を続けることができます)。言語自体を使用して同じ方法で実装することはできません。

ただし、forEachは特別な形式ではなく、関数として記述することで同じように再実装できます。

extension Sequence {
    func myOwnForEach(_ body: (Self.Element) throws -> Void) rethrows {
        let it = Self.makeIterator()
        while let item = it.next() {
            body(item)
        }
    }
}
10
plinth