Reinder de Vriesによって2020年7月9日にSwiftのアプリ開発で作成されました

Swiftでは、forループを使用せずに、配列や辞書などのコレクションをループするには、map()reduce()、およびfilter()を使用します。

map、reduce、filter関数は、関数型プログラミング(FP)の領域から来ています。 これらは、入力として関数を取るため、高階関数と呼ばれます。 たとえば、そのデータを変換するために、配列に関数を適用しています。

SwiftのMap、Reduce、Filter関数は、頭を包むのに難しいことができます。 特に、反復問題を解決するためにfor inループを常にコード化している場合。 このガイドでは、Swiftでmap(_:)reduce(_:_:)、およびfilter(_:)関数を使用する方法を学習します。

始めましょう!

  1. マップ、削減、フィルタの概要
  2. クイックスタート: Swiftの高次関数
  3. Map関数の使用
  4. Reduce関数の使用
  5. Filter関数の使用
  6. Map、Reduce、Filterの組み合わせ
  7. さらに読む

Map、Reduce、Filterの概要

iOSをビルドするときアプリでは、通常、手続き型またはオブジェクト指向プログラミングを使用します。 関数型プログラミングは異なります:それは関数だけを扱います。 変数なし、状態なし、for-loopsなし-単なる関数。

Swiftプログラミング言語は、関数型プログラミングとOOPのような非機能的なアプローチを混合するのに完全に適しています。 関数型コードを厳密に記述する必要はなく、関数型プログラミングの概念を採用することで、より良いコード化方法を学ぶことができます。

map(_:)reduce(_:_:)filter(_:)関数は、入力として関数を受け取り、出力として関数を返すため、高階関数と呼ばれます。 技術的には、Swiftは操作の結果を返します(つまり 純粋な関数型言語は関数のコレクションを返しますが、高階関数を使用する場合は変換された配列)。 Swiftでは、これらの関数の入力はクロージャです。

ここでは、彼らがどのように動作するかです:

  • map()関数は、コレクション内のすべての項目に関数を適用します。 ある値のセットを別の値のセットに”マッピング”または変換することを考えてください。
  • reduce()関数はコレクションを1つの値に変換します。 一連の数値を平均化するように、多くの値を1つに結合すると考えてください。
  • filter()関数は、単にif-ステートメントを渡した値を返し、その条件がtrueになった場合にのみ返します。

あなたが考えている場合:”見て、私のアプリはそれをしないので、関数型プログラミングやデータ処理は必要ありません!”じゃあここで止まらないでください。 最近のアプリプロジェクトでは、私は複数の機会にMap、Reduce、Filterを使用しました:

  • 折れ線グラフに値を表示する前に、しきい値を満たすためにfilter(_:)でコスト/収益の値をフィルタリングする
  • 数千の映画の評価を一つの値に平均化するreduce(_:_:)
  • ハッシュタグを持つ文字列に対していくつかの操作をマッピングし、正規化されたコレクションに変換する。map(_:)

forループでこれらの問題をすべて解決できましたが、map()reduce()filter()を使用すると、より簡潔で読みやすく、パフォーマンスの高いコードが生成されます。

クイックスタート: Swiftの高階関数

このチュートリアルでは、map()reduce()filter()に焦点を当てます。 先に進む前に、Swiftで最も一般的な高次関数の概要を以下に示します:

  • map(_:) シーケンス内のすべての項目をループし、各要素に関数を適用し、変換された結果
  • reduce(_:_:)シーケンス内のすべての項目をループし、それらを一つの値に結合し、結合された結果
  • filter(_:)シーケンス内のすべての項目をループし、指定されたフィルタリング関数
  • flatMap(_:)を満たす項目のみを含む結果のシーケンスを返します。map(_:)と同じです。それは結果のシーケンスを平坦化することを除いて、すなわち ネストされた配列は、ネストされていないか、”平坦化”されています”
  • compactMap(_:) map(_:)と同じですが、結果のシーケンスからnil値を削除してから

を返すことを除いて、配列、辞書、セット、範囲、シーケンス、および反復可能な他のSwift型でこれらの関 あなたはcompactMap(_:)flatMap(_:)についての詳細を知りたい場合は、このチュートリアルをチェックしてください。

SwiftのArrayやDictionaryなどのいくつかの型には、クロージャを入力として受け入れる関数があります。 クイックピック:

  • contains(where:) コレクションをループし、すべての項目に述語(クロージャ)を適用し、項目が述語を満たす場合はtrueを返し、そうでない場合はtrueを返しますfalse
  • first(where:) コレクションをループし、すべての項目に述語(クロージャ)を適用し、述語
  • firstIndex(where:)を満たす場合に項目を返しますfirst(where:)と同じですが、値

ではなくインデックスを返す点を除いて、これらの関数の詳細については、このチュートリアルで学ぶことができます。 Swiftでwhereがどのように使用されているかも興味深いです。

このチュートリアルでは、Swiftの”map”と”reduce”関数がmap(_:)reduce(_:_:)として記述されているのを見てきました。 これらの関数のアンダースコアとコロンは、関数のシグネチャの一部であり、関数パラメータを示すための特別な形式です。 たとえば、map(_:)関数には名前のないパラメーターが1つあり、reduce(_:_:)関数には2つあります。 これについての詳細は、このチュートリアルで学ぶことができます:Swiftの関数について説明しました。

Ios開発者として雇われる

Swift5でiOS14アプリを構築する方法を学ぶ

私のiOS開発コースにサインアップし、プロのiOS開発者としてのキャリアを開始す

Map関数

を使用すると、map(_:)関数はコレクション内のすべての項目をループし、コレクション内の各要素に操作を適用します。 これは、操作が適用された結果の項目のコレクションを返します。

例を見てみましょう。 私たちは、あなたが華氏に変換したい摂氏の温度の配列を持っています。

次のようにforループを使用できます:

let celsius = var fahrenheit: = for value in celsius { fahrenheit += }print(fahrenheit)// Output: 

コードは正常に動作しますが、冗長すぎます。 計算された変換を保存するには、変更可能な「ヘルパー」変数fahrenheitが必要であり、変換自体には3行のコードが必要です。

ここでは、map(_:)関数で同じことを行う方法です:

摂氏=
華氏=摂氏とします。地図{ $0 * (9/5) + 32 }
プリント(華氏)
警告を非表示にする

あなたも一行ですべてのことを行うことができます:

.map {  * (9/5) + 32 }

ここで何が起こる?

  1. 定数celsiusが定義され、doubleの配列が定義され、いくつかのランダムな摂氏値で初期化されます。
  2. 関数map(_:)celsius配列で呼び出されます。 この関数には、摂氏から華氏に変換するクロージャという引数があります。
  3. 最後に、結果が出力されます:摂氏から華氏に変換された配列。

map(_:)関数は、配列内のすべての項目に関数を適用することによって、ある配列を別の配列に変換します。 クロージャ * (9/5) + 32は、入力値を摂氏で受け取り、値を華氏で返します。 結果のmap(_:)の配列は、これらの変換された値から構築されます。

閉鎖を詳しく見てみましょう。 以前にクロージャを使用したことがある場合は、短いクロージャ構文を認識します。 これは、構文の多くを省くことによって、クロージャをコード化するより短い方法です。

ここではあまり簡潔な代替案があります:

let celsius = let fahrenheit = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})print(fahrenheit)

実際のmap(_:)関数呼び出しとそのクロージャはこれです:

··· = celsius.map({ (value: Double) -> Double in return value * (9/5) + 32})

何が起きてるんだ? map(_:)関数は、配列celsiusで呼び出されます。 1つの引数を取ります:(Double) -> Double型のクロージャです。

{で始まるクロージャの最初の部分は、このクロージャがDouble型のパラメータvalueを持ち、クロージャがDouble型の値を返すことを示しています。 returnで始まるクロージャ本体は、摂氏から華氏への計算の結果を単純に返します。

上記の拡張コードと短いクロージャ構文を比較すると、次のことがわかります:

  • 関数呼び出しの最後のパラメータがクロージャである場合は、それらを省略できるため、関数括弧()は省略されます。Swiftは1つのDoubleパラメータを入力として使用していると推測でき、Doubleを返すことが期待されるため、() -> in部分は省略できます。 これで、value変数を省略したので、短い手のを使用できます。
  • returnステートメントは、このクロージャが式の結果を返すことが期待されるため、省略することもできます。

上記のコードサンプルではDouble型を使用していますが、これらの型に限定されるものではありません。 結果の型map()関数は、入力した型とは異なる型を持つことができ、map()Dictionaryにも使用できます。

reduce(_:_:)に移りましょう!

Reduce関数を使用する

reduce(_:_:)関数は、コレクション内のすべての項目をループし、それらを1つの値に減らします。 複数の値を1つに結合すると考えてください。

reduce関数は、おそらくmap、reduce、filterの中で理解するのが最も難しいです。 どのようにして値のコレクションから1つの値に行くことができますか?

:

  • 複数の値の合計を作成する、すなわち3 + 4 + 5 = 12
  • 文字列のコレクションを連結する、すなわち = "Zaphod, Trillian, Ford"
  • 値のセットを平均化する、すなわち(7 + 3 + 10) / 3 = 7/3 + 3/3 + 10/3 = 6.667

データ処理では、このような単純な操作が便利になると、多くのシナリオを想像することができます。 前と同様に、forループでこれらの問題を解決できますが、reduce(_:_:)は単に短くて高速です。

:

let values=
let sum=values.reduce(0,+)
print(sum)
警告を隠す

関数reduce(_:_:)は、初期値とクロージャの二つの引数を取ります。 上記のコードでは、+演算子を提供しています。

もちろん、あなた自身の閉鎖を提供することもできます:

値=
を平均=値とします。リデュース(0.0) { $0 + $1 } / ダブル(値。カウント)
プリント(平均))
警告を非表示にする

上記の例では、3つの数値の平均を計算しています。 コード内の値の型はすべてDoubleです。 最初にすべての数字を合計し、それを数字の量で除算します。

reduce(_:_:)関数は二つの方法で異なります:

  1. 初期値、およびクロージャ
  2. reduce(_:_:)に提供されているクロージャも2つのパラメータを持っています; 削減の現在の結果、および削減されようとしている新しい値

ここでは、これをチェックしてください:

let values=
let sum=values.reduce(0){
印刷(“\($0) + \($1) = \($0 + $1)”)
戻る$0 + $1
}
印刷(和)
警告を隠す

上記の例では、クロージャの2つのパラメータであるをはっきりと見ることができます。 コードを実行すると、これが得られる出力です:

0 + 7 = 77 + 3 = 1010 + 10 = 2020

どのように0で始まり、7を追加しているのか見てください。 次のステップでは、現在の削減値である7を取得し、削減の「次の」値である3を追加します。

ここでそれを見ての別の方法です:

0 + 7(0 + 7) + 3((0 + 7) + 3) + 10

これはまた、reduce(_:_:)の初期値が必要な理由を明確にします。

削減は把握するのが難しい場合があります。 値が1つだけ残るまで、+のような操作を一連の値に反復的に適用することを理解することが重要です。 あなたは文字通り値の量を減らします。

filter(_:)に移りましょう!

フィルタ関数

を使用すると、filter関数はコレクション内のすべての項目をループし、include条件を満たす項目のみを含むコレクションを返します。

これは、コレクションにif-statementを適用し、条件を渡す値のみを保持するようなものです。

ここでは、これをチェックアウト:

let values=
let even=values.フィルタ{$0。isMultiple(of:2)}
print(even)
警告を非表示にする

上記の例では、偶数のvaluesから数値をフィルタリングしています。 isMultiple(of:)関数は、2で割ることができる場合はtrueを返し、それ以外の場合はfalseを返します。

map(_:)reduce(_:_:)とは異なり、クロージャfilter(_:)はブール値を返す必要があるため、trueまたはfalseのいずれかになります。 クロージャがtrueを返すと、値は保持され、falseが返されると、値は省略されます。 これがfilter(_:)が入力配列をフィルタリングする方法です。

クロージャを展開した、少し明確な例を次に示します:

let values = let even = values.filter({ (value:Int) -> Bool in return value.isMultiple(of: 2)})print(even) // Output: 

この例では、クロージャは-> Boolで示されるブール値を返します。 これは、1つのパラメータ、コレクション内のアイテムを提供し、isMultiple(of:)の結果を返します。 ニート!

Map、Reduce、Filterの組み合わせ

map()reduce()filter()関数を組み合わせることはできますか? 確かにあなたがすることができます!

生徒のクラスがあるとしましょう。 あなたは、各学生が生まれた年を知っています。 あなたは、2000年以降に生まれたすべての学生の合計年齢を計算したいとします。

ここでは、それを行う方法です:

let now=2020
let years=
let sum=years.フィルター({ $0 >= 2000 }).マップ({now-$0})。reduce(0,+)
print(sum)
警告を非表示にする

上記のコードサンプルでは、連鎖を使用しています。 ある関数の結果を別の関数の入力として使用し、map-reduce-filterを組み合わせます。 map()関数はfilter()関数の結果配列で呼び出され、reduce()関数はmap()関数の結果で呼び出されます。 すごい!

コード自体は単純です:

  1. 定数nowyearsを作成し、それに年の束を割り当てます。
  2. 2000年以下の年を除外します。 >= 2000true
  3. であるものを維持し、now
  4. から年を減算して、すべての年齢を加算して、年齢に変換します。+

より興味深い例を見てみましょう。 FizzBuzzについてのチュートリアルから取得した次のコードをチェックしてください:

>文字列={i in
switch(i%3==0,i)={i in
スイッチ(i%3==0,i)={i in
スイッチ(i%3==0,i% 5 == 0)
{
case(true,true):
“Fizz”を返す
case(false,true):
“Buzz”を返す
case(true,true):
“FizzBuzz”を返す
デフォルト:
戻る”\()”
}
}
result=Array(2…100).マップ(fizzbuzz)。削減(“1”, { $0 + “, ” + $1 })
印刷(結果)
警告を非表示にする

何が起こっているのかを参照してください。 ゲームのルールに基づいて、2から100までの数字を持つ配列をmap(_:)を使用して”Fizz”、”Buzz”、または”FizzBuzz”に変換しています。 最後に、その文字列の配列をreduce(_:_:)を使用して1つの大きな文字列に減らし、すべての値を組み合わせています。 ニート!

さらに読む

このすべてをfor inループでコード化する必要がある場合はどうなりますか? より多くのコードを使用します。 そして、それはmap-reduce-filterのパワーです:それはより簡潔で、しばしば読みやすく、そして—それを認める—かなりクールです!

これらのリソースをチェックしてください:

  • Swift
  • FlatMapとCompactMapで”where”を使用する方法Swift
  • Swift
  • で説明されているSwift
  • のクロージャの究極のガイド方法:Swift
  • で配列内のアイテムを見つけるコードで遊ぶ: Swiftでのバイナリ検索
  • Xcode Playgroundsを使い始める

Ios開発者として雇われる

Swift5でiOS14アプリを構築する方法を学ぶ

私のiOS開発コースにサインアップし、プロのiOS開発者としてのキャリアを開始す

コメントを残す

メールアドレスが公開されることはありません。