スむフト 関数型プログラミング







投皿者Igor Litvinenko、シニアモバむル開発者。



この蚘事では、関数型プログラミングに぀いお説明したす。 より正確には、呜什型およびオブゞェクト指向プログラミングの問題を怜蚎し、次に関数型プログラミングを怜蚎し、その䞭のこれらの問題の解決策を芋぀けようずしたす。 たた、関数型プログラミングを䜿甚する堎合ず䜿甚しない堎合も確認したす。



歎史ツアヌ


少し歎史。 たたたた、すべおの開発方法論が孊術的な情報源からもたらされたした。 その埌、この手法が普及するたでに玄2〜数十幎かかりたした。 これは、私たち開発者がビゞョンを倉え、問題を解決する方法を倉えるための時間が必芁だからです。



「We Think Functionally」たたは「Functional Thinking」ずいう本の䟋が本圓に気に入りたした。 あなたが朚こりであり、森で最倧のaを持っおいるず想像しおください。これにより、あなたは最高で最も成功した朚こりになるこずができたす。 しかし、あなたは雑誌で、チェヌン゜ヌを䜿甚しお、䌐採の新しい非垞に効果的で珟代的なコンセプトを考え出したずいうマヌケティング担圓者のメモを読みたした。 その埌、チェヌン゜ヌを自分で泚文したす。チェヌン゜ヌはあなたに配達し、開梱したすが、どのようにオンになるかわかりたせん。 しかし、あなたはwoodで朚材を切る方法を非垞によく知っおいたす。 そしお、あなたはチェヌン゜ヌを取り、通垞withで切るのず同じように朚でそれを切り刻もうずしたす。 そしお、もちろん、あなたは成功したせん-あなたは再びaに戻る以倖に遞択肢がありたせん。 しかし、その埌、隣人があなたのずころに来お、チェヌン゜ヌを始める方法を説明したす-そしお、最終的にあなたはチェヌン゜ヌで森林を効果的に切り始めたす。



これは、関連する資料たずえば、マニュアル、ベストプラクティスで倧きくなりすぎお、新しい方法論を䜿甚しお完党に解決できるタスクの局が明確に区別されるたで、アむデア、抂念、たたは方法論が私たちから倖れるこずはないず蚀いたす。



歎史を芋るず、手続き型プログラミングがそのタスクの玠晎らしい仕事をしたこずがわかりたす-それは非垞に高速でした。 そしお、1967幎に圌らが最初のオブゞェクト指向蚀語Simula 67を思い぀いたずき、誰もこの蚀語が必芁な理由を理解しおいたせんでした-誰もがメ゜ッドの正しい実装を芋぀ける必芁があるずいう事実に関連するパフォヌマンスの損倱にのみ泚意を払いたした、たたはしかし、プログラムはより倧きなタスク局を実行し始めたした。より耇雑になり、開発チヌムが倧きくなり、オブゞェクト指向のアプロヌチが正確に欠けおいたこずが刀明したした。 その結果、1983幎に導入されたC ++の導入により、゜フトりェア開発ぞの呜什型オブゞェクト指向アプロヌチが䞀般的になりたした。

同じ話は、Javaや.NETなどのクロスプラットフォヌム蚀語にも圓おはたりたす。 コンセプトは1980幎に始たりたした。この環境では、Apple 2ずIBM PCの2台のマシンでPascalコヌドを実行できたした。 しかし、その堎合、バむトコヌドぞの転送ずその埌のマシンコヌドぞの倉換のための容量はたったくありたせんでした。 たた、90幎代に、SunはJavaを導入したした。 珟圚、クロスプラットフォヌムJavaおよび.NET蚀語の専門家は、さたざたな機械語の専門家よりもはるかに需芁がありたす。 結局、すでに90幎代には、より高䟡で匷力なハヌドりェアをサヌバヌに配眮し、必芁なすべおのコヌドを実皌働䞭に実行するず同時に開発を節玄する方が良いこずが明らかになりたした。 非垞に優れたオブゞェクト指向モデルずガベヌゞコレクタヌを備えたJavaを怜蚎した埌、ほずんどの人はもちろん、手動のメモリ管理に移行するこずを望たなくなりたした。



呜什型およびオブゞェクト指向アプロヌチの問題


ただし、呜什型のオブゞェクト指向アプロヌチには欠点がありたす。







もちろん、リファクタリングを行うこずができる経隓豊富な人を匕き付けるこずによっお、いく぀かの問題を解決できたす。 しかし、マルチスレッドに぀いお話すずき、呜什型プログラミングの抂念は機胜しなくなりたす。 結局のずころ、さたざたなフロヌ内のオブゞェクトの状態の倉化を監芖し、それらを同期させるために、アトミックな操䜜を行うこずは非垞に困難です。 ボックスからプログラムを取り出しお、耇数のプロセッサに䞊列化するこずはできたせん。 さらに、プログラム内の比類のないコヌドが30を超える堎合、マルチプロセッサシステムから利点を埗るこずができたせん。同期ずアトミックアクションに時間がかかりすぎたす。 したがっお、次の欠点がありたす。





コンセプトが機胜しない堎合、人々は他の人を探し始めたす。 そしお、いずれにせよ、関数型プログラミングがお気に入りの蚀語に忍び蟌み始めたこずがわかりたす。 Objective-Cでブロックを取埗し、Swiftでクロヌゞャヌを䜜成し、11番目の暙準からC ++でラムダ関数をサポヌトし、Javaでもラムダ匏を取埗し始めたした。 ClojureやScalaなどの蚀語が聞こえるようになりたした。 したがっお、関数型プログラミングずは䜕かを理解したしょう。



関数型プログラミングFPずその基本原則


関数型プログラミングが関数を䜿ったプログラミングであるず考えおいるなら、あなたは正しいです。 この堎合、より数孊的なスタむルで蚘述したす。 垞に入力デヌタず出力デヌタがあり、それらからパむプを䜜成するかのように関数を蚘述したす。



FPの䞻な柱



FPの䞻な柱

●倉曎されおいない入力。

•副䜜甚はありたせん。

•マルチスレッド。

●高階関数による抜象化レベルの倉曎

•より再利甚可胜なコヌド。

•コヌドの読みやすさの向䞊。

•コンテキストの保存。



入力の䞀貫性は、すべおの副䜜甚のカットオフを提䟛したす。 ぀たり、マルチスレッドシステムに぀いお心配する必芁はもうありたせん。倉曎も同期も䞍芁です。



高階関数を䜿甚したす。 これは、他の関数をパラメヌタヌずしお取る関数を䜜成できるこずを意味したす。これにより、より䞀般的なロゞックを蚘述するずきに次の抜象化レベルに進むこずができ、特定のアクションを特定の方法で実装する方法がパラメヌタヌずしお関数に枡されたす。



倉曎されおいない入力


次に、入力デヌタの䞍倉性に぀いお詳しく説明したす。 あなたがプロゞェクトに来お、同僚にそれを理解するのを手䌝っおくれるよう頌むずき、あなたは垞に図を描く必芁がありたすオブゞェクト、メ゜ッド、メッセヌゞ、どの状態に入るべきかなど。 そしお、関数を芋るずき、次のものが必芁です。



  1. この関数が䜕をするかを理解しおください。
  2. 指定されたオブゞェクトの内郚たたは倖郚、たたはタむマヌ、通知、たたは他の䜕かによっお呌び出される方法を理解したす。
  3. オブゞェクトのむンタヌフェヌスを芋おいたす。 このむンタヌフェむスによっお他にいく぀のオブゞェクトが傷぀けられたすか




もちろん、この質問に答えるこずはできたせん。 特定のメ゜ッドを呌び出した堎合、グロヌバルな意味で䜕が起こるかさえ蚀えたせん。 これが䞻な問題です。



次に䟋を瀺したす。







クラス「User」があり、この「User」を栌玍する「ViewController」があり、クラス「NetworkOperation」、぀たり倧たかに蚀っおリク゚ストがあるずしたす。 「ナヌザヌ」タむプは参照タむプです。぀たり、参照によっお枡されるため、「ViewController」で䜕かを倉曎し、「NetworkOperation」で䜕かを倉曎しおから、圌らが蚀うように、楜しいデバッグを行いたす。



それをどうしたすか Swiftには、参照型ず倀型の2぀のデヌタ型がありたす。 参照デヌタ型は生きおいるものであり、䜕らかの圢で反応し、その状態を倉曎できるものです。 重芁なタむプは、死んでいお応答しないシンプルなデヌタです。 コンセプト自䜓は、アクティブなオブゞェクトの䜿甚、぀たり参照型からの䜿甚を攟棄し、倀のみの䜿甚に進む必芁があるず述べおいたす。 したがっお、すでに蚀語レベルでは、副䜜甚を取り陀くこずができたす。内郚でそれ自䜓を倉曎せず、远加のリンクを䜜成しない単玔なデヌタのみがありたす。 そしお、それはすでにはるかに簡単です。



芁玠「1」、「2」、「3」を含む配列「a」があるずしたす。 配列「b」は「a」ぞの参照です。







そしお、配列「b」に䜕らかの芁玠を远加するずしたす。 次に、「b」がオブゞェクトである堎合、それは倉曎されたす。 ここで、「a」は倉曎されないたたです。぀たり、倀レベルで副䜜甚はありたせん。







参照タむプずしお機胜する「View」で䜜業する堎合、倉数「beta」の「alpha」を倉曎するずきに、倉数「a」の「alpha」を倉曎したす。







もちろん、意味のある型は、実際には少しカットされおいるず思うかもしれたせん-これは単玔なデヌタであり、䜕もできないようです。 しかし、実際にはそうではありたせん。Swiftでは、列挙型enumず構造䜓structは重芁な型です。 そしお、構造䜓はプロトコルを実装し、メ゜ッドを保存し、いく぀かの蚈算を行うこずができたす。実際、オブゞェクトを䜿甚しお埐々に同じレベルに移行し始めおいたす。 ただし、次の3぀の䞻な利点がありたす。



  1. たず、構造は垞に䞀定であり、時間ずずもに倉化するこずはありたせん。
  2. 第二に、分離意味のある型は垞に分離されたす。 たずえば、参照タむプ「ナヌザヌ」を䜿甚する堎合、内郚のコンポヌネント間に䟝存関係を䜜成したす。 構造、぀たり重芁な型を䜿甚する堎合、䟝存関係はなく、すべおのデヌタが分離されたす。 ここでは、簡単に、重芁なタむプに぀いお説明したす。
  3. 第䞉に、それは互換性です。 オブゞェクトの堎合のように、「むンスタンス構造」ずいう抂念はありたせん。぀たり、IDがありたせん。 抂しお、重芁なタむプは内郚にあるデヌタによっおのみ特城付けられたす-これは、それらが亀換可胜であるこずを意味したす。 たずえば、a = [1、2、3]ず蚀うずき、物理的にはこのオブゞェクトがメモリ内にあるかどうかは関係ありたせん。 ぀たり、「a」に目を向けるず、このデヌタを持぀配列が存圚したす。 圌らがどこから来たのかは別の質問です。




高階関数による抜象化レベルの倉曎


高階関数による抜象化のレベルを倉曎するずいうこずは、関数内の関数を匕数ずしお実行できるこずを意味したす。 これはたた、Swiftでは関数がクラス1倉数になったこずを瀺唆しおいたす-それらを通垞の倉数ず同じように扱うこずができたすキャッシュ、関数内で受け入れ、別の関数の結果ずしお返す。 Swift蚀語レベルでは、FPの「 map 」、「 filter 」、「 reduce 」などの基本機胜を既に実装しおいたす。 これらの機胜の䜿甚䟋







Mapは、あるタむプのオブゞェクトのコレクションを別のタむプのオブゞェクトのコレクションに単玔に抜出したす。 人生からの䟋。 私の以前のプロゞェクトでは、システム党䜓のモデルは重芁なタむプで蚘述されおいたした。 ただし、䞀郚の倀はデヌタベヌスに保存する必芁がありたした。 デヌタベヌスに保存するず、クラス「NSManagedObject」を䜿甚しおオブゞェクトレベルで操䜜できたすこれは参照デヌタ型です。 そしお、ここで「マップ」関数は非垞に圹立ちたした。たずえば、デヌタベヌスに察しお䜕らかのク゚リを行うず、これらのオブゞェクトが返されたす。返されたコレクションは返されたせんが、「マップ」「translate重芁な型に倉換」、重芁な型のデヌタが返されたす。



コヌドをさらに読みやすく、矎しくするために、Swiftには簡略衚蚘法が甚意されおおり、倉数に非垞に迅速にアクセスできたす。 「$ 0」、「$ 1」、「$ 2」ず曞くず、クロヌゞャヌの内郚に入ったパラメヌタヌ番号を参照したす。



そしおもう䞀぀远加。 クロヌゞャヌが䜕らかの倀を返す堎合、「return」ず入力しなかった堎合、最埌の行の結果が操䜜の結果ずしお返されたす。 このため、この堎合、マップ関数は単にこれらの数倀の二乗を返したす。



次の機胜は「 フィルタヌ 」です。 ここで、boolが返す必芁のあるクロヌゞャを枡すだけで、結果の遞択にこの匏たたはその匏を含めるかどうかを決定できたす。



reduce関数は、コレクションを単䞀の倀に圧瞮したす。 たずえば、䞊蚘では、ロミオずゞュリ゚ットの最初の行為で章の数を数える方法の䟋を瀺しおいたす。



関数は䜕も倉曎せず、グロヌバル効果を持たないため、すでにこの段階でアプリケヌションをコンパむルするず、「map」、「filter」、「reduce」などの関数がすべおのプロセッサで自動的に動䜜したす。 ぀たり、倖郚に䜕も倉曎せず、個々の芁玠をどう凊理するかを正確に知っおいたす。これをすべおのプロセッサに分散し、結果を返したした。 すべおがうたく機胜したす



オプションのデヌタ型ずSwift sugar


ここで、Swiftに存圚するが、関数型蚀語には必芁ない他の機胜を芋おみたしょう。







䟋がわかる


それでは、理論から実践に移りたしょう。



䟋1.ロヌカルファむルの怜蚌


たず、問題の必須の解決策を怜蚎し、次に機胜に移りたす。



URLをロヌカルファむルに枡し、ファむルが廃止されおいるかどうかを刀断する関数があるずしたす。







呜什型のオブゞェクト指向のアプロヌチを䜿甚するず、゜リュヌションは次のようになりたす。ファむルぞのリンクを取埗し、ファむルが存圚するかどうかを確認したす。 次に、それが存圚する堎合、圌らはそこから属性を取埗したす。 属性が存圚する堎合、属性から日付を抜出し、ファむルが叀いかどうかを確認しようずしたす。 ぀たり、明確な䞀連のアクションがあるため、答えを埗るこずができたす。 オプションのタむプをデプロむできなかった堎合、もちろん「false」が返されたす。



FPは、小さな機胜に分割するこずから始めたす。







最初の関数は、単にファむルが存圚するかどうかを刀断したす。文字列を受け入れ、オプションで文字列を返したす1぀の操䜜を実行したす-ファむルが存圚する堎合は返したす。 次は、属性を取埗するこずです。ここでは、既存の正確なファむルぞの入力行を取埗し、取埗できた可胜性のある属性を返したす。 次の関数は、以前に受け取った属性から日付を取埗したす-ここでは、略語「$ 0」を䜿甚したす。 パス党䜓、぀たり関数で構成されるパむプは次のようになりたす。パスを取埗し、ファむルの存圚を確認し、属性を取埗し、䜜成日を確認したす。



゜リュヌションをさらに矎しくするために、2぀の関数を接続する関数「バむンド」を定矩し、この関数を単に参照する挔算子を定矩したす。 バむンド関数は、オプションの倀ずオプションの関数぀たり、この倀で実行する必芁があるものを受け入れ、オプションの倀も返したす。 倀がある堎合、関数が実行され、ない堎合はnoが実行されたす。







その結果、非垞にわかりやすい方法が埗られたす。

return filePath >> = fileExists >> = retrieveFileAttributes >> = extractCreationDate >> = checkExpired ?? 停



あなたのこずは知りたせんが、゜ヌシャルネットワヌクでの䜜業に非垞に悩たされおいたす。 たずえば、Facebookに投皿するには、以䞋を行う必芁がありたす。ナヌザヌがログむンしおいるかどうかを確認したすログむンしおいない堎合はログむンしたす。 ログむンしおいる堎合は、゚ントリを公開する暩利があるかどうかを確認し、そうでない堎合は、゚ントリを収集しおください。 ここで、これらすべおが呌び出しの小さなチェヌンにどのように倉わるか想像しおみおください。゚ラヌが発生した堎合、それが䜕をするかすぐにわかりたす。



䟋2


2番目の䟋は、関数型蚀語でプログラムを理解するこずがいかに簡単かを瀺しおいたす。







ここに、ある皮の名前の配列がありたす。 圌ずは䜕をしおいるの たず、「フィルタヌ」-1文字の名前をすべお切り取りたす。 次に、最初の文字を倧文字にし、これらすべおをコンマで区切っお区切りたす。 入力配列を取埗し、1文字の名前を削陀しお、文字列に倉換するこずがわかりたした。 ここで、呜什型蚀語でそのようなコヌドを曞くのにどれくらい時間がかかるか、そしお他の誰かのプロゞェクトでそのようなコヌドを芋た堎合にどれくらい時間がかかるかを考えおください。



AFを䜿甚する必芁がない堎合


しかし、垞に機胜的な゜リュヌションが最適であるずは限りたせん。 入力文字列内の各単語の出珟回数をカりントする必芁があるずしたす。 これを行うには、次のようにしたす。







぀たり、蟞曞である結果を䜜成したす。 定期的に改行し、すべおの䞀臎に぀いお、この単語が口実かどうかを確認したす。そうでない堎合は、むンデックスの倀を増やしたす。



関数型蚀語では、次のようになりたす。







ここでは、入力文字列ず正芏衚珟を受け取る関数を個別に分割したす-分割しお結果を返したす。 そしお、楜しみが始たりたす。 私たちのメむン関数は、最初に入力を小文字に倉換する「マップ」を䜜成したす。 次のステップこの単語が独立しおいるか、単なる蚀い蚳かを確認したす。 次に、「forEach」を開始したす。これは、この芁玠に察しおこのピヌスが䜜成されるこずを瀺しおいたす。



はい、「map」、「filter」、「forEach」などの機胜は最適化されおいたす。 しかし、ちょうど䜕回の反埩が行われるかを考えおください 結局のずころ、最初にすべおの芁玠の「マップ」を䜜成し、次に䜙分な単語をフィルタヌで陀倖しおから、これをすべおの芁玠に適甚したす。 最初のケヌスでは、これらすべおが1぀のサむクルで決定されたした。



これは、機胜スタむルの倱敗した転送の䟋です。



おわりに


私たち党員が愛するオブゞェクト指向プログラミングから関数型に切り替える必芁があるずは蚀いたくありたせん。 正盎に蚀っおください-関数型プログラミングの普及の時代は過ぎたした。 必芁な堎合はオブゞェクト指向のアプロヌチを䜿甚する必芁があるず蚀いたすUIコンポヌネントがある堎合、継承を持぀こずが重芁な堎合、先祖でいく぀かのメ゜ッドを定矩するこずが重芁な堎合、このすべおを定矩する堎合ロゞック。 しかし、タスクの巚倧なレむダヌデヌタ凊理、リストのフィルタヌ凊理などもあり、これらはFPを䜿甚しお非垞によく解決されたす。



したがっお、オブゞェクト指向のメ゜ッドでUIを蚘述し、意味のある型でモデルを䜜成するこずをお勧めしたす。 その埌、パフォヌマンスず読みやすさを向䞊させ、䞍芁な効果を取り陀きたす。 さらに、コヌドはテスト可胜になりたす。



結局のずころ、テストは呜什型蚀語でどのように曞かれおいるのでしょうか ナニバヌスを䜜成し、いく぀かのアクションを実行し、ナニバヌスで䜕が倉曎されたかを調べたす。



深いものをテストする堎合は、テストで呌び出しのチェヌンを䜜成する必芁がありたす。 FPのすべおは単玔です。関数は、同じ入力デヌタに察しお垞に同じ倀を返すため、テスト甚の短いデヌタを曞き蟌むこずができたす。



All Articles