Erlangで考えお曞くこずを孊ぶ䟋ずしお2぀の組み合わせ問題を䜿甚

-...その埌、私は圌に銃口を䞎えたす...いいえ、あなたは打ち負かすこずはできたせん

「問題の事実は、あなたが負けるこずができないずいうこずです」ずパニコフスキヌは停善的にため息を぀きたした。 -ベンダヌは蚱可しおいたせん。

I.むルフ、E。ペトロフ。 ゎヌルデンカヌフ 。



冷淡なブラガは透明な容噚に䜏んでいた

その恐怖さえ匷い。 圌女はお腹からは奜きではない-口から盎接

頭に駆け寄り、そこから巊右に投げ始めたした。

メンタルブレヌスず拠点の砎壊。

M.りスペンスキヌ。 私たちがいない堎所 。



おそらく、最初にアヌランの勉匷を始めた人は誰でも、シュラバラガノフの立堎にいるず感じおいるでしょう。 Erlangには、倉数を再割り圓おし、結果を1぀の倉数に蓄積するなど、ほずんどの珟代蚀語の抂念に銎染みがありたせん。 公平には、「グロヌバルな耇数倉曎倉数」のErlangスタむルの動䜜を実装できるこずに泚意しおください。このため、各プロセスにはプログラマ定矩のキヌず倀のペアを栌玍するハッシュディクショナリがありたす。組み蟌み関数putKey、Value、get キヌず他のいく぀かの補助関数ですが、アプリケヌションでそのような蟞曞を䜿甚するのは悪いスタむルず芋なされ、䟋倖的な堎合にのみ掚奚されたす http://www.erlang.org/doc/man/erlang.html\#put-2 。 結果ずしお、ルヌプ内の反埩は、反埩倉数の倀の通垞の発生では実装できたせん。 結果の环積は、再垰、および末尟再垰によるサむクルの線成によっおのみ実行されたす。 もちろん、ルヌプの結果の反埩ず环積は、リストのラむブラリ関数foreach関数、リスト、リストfoldl関数、StartValue、リスト、リストfoldr関数、StartValue、リスト http //www.erlang.org/doc/man/lists.html およびセットの類䌌物 http://www.erlang.org/doc/man/sets.html、http://www.erlang.org /doc/man/ordsets.html、http://www.erlang.org/doc/man/gb_sets.html および配列 http://www.erlang.org/doc/man/array.html 。私たちの目暙は、既成の゜リュヌションを䜿甚せずに、サむクルを蚘述する方法を孊ぶこずです。そのため、ここではそのようなラむブラリの䜿甚を控えたす。



したがっお、Erlangでは、通垞の思考パタヌンを砎り、このプログラミング蚀語のみに特城的な新しいパタヌンに眮き換える必芁がありたす。 もちろん、理想的な治療法は、すべおの「粟神的サポヌトず匷化」を砎るこずができる掗脳です。 しかし、私たちにずっおは、これはおそらく過激すぎる手段であり、逆の方向に進むでしょう。



聖アン゜ニヌ倧王の人生には、圌の匟子の䞀人に぀いおの物語がありたす。 匟子が教䌚に立ち、聖アントニりスが詩salを読んでいるのを聞きたした。 最初の詩線の最初の詩が鳎るずすぐに

, ...





孊生は神殿を去りたした。 それ以来、誰も圌を30幎近く芋おいたせん。そしお、圌が神殿に再び珟れたずき、アン゜ニヌ倧王はなぜ圌をそんなに長く残し、どこに消えたのか尋ねたした。 匟子は答えたした。「父、詩線の蚀葉を聞いお、荒野に匕退しお、これらの蚀葉で蚀われおいるこずを成し遂げようずしたした。 邪悪な考えの評議䌚に行かないでください。」 蚀い換えれば、圌はこれらの蚀葉から実践的な教蚓を孊び、今ではさらに読むようになりたした。 残念ながら、私たちにはそのような時間の䜙裕はなく、私たちの目暙はそれほど高くありたせん。 しかし、基本抂念は採甚できたす。

2぀の暙準的な組み合わせの問題を怜蚎したす。

  1. N個の芁玠の指定されたセットからすべおの可胜な順列を怜玢したす
  2. N個の芁玠の指定されたセットから可胜なすべおの組み合わせを怜玢したす


Erlangプログラミング蚀語を䜿甚しおそれらを解決するためのさたざたなアプロヌチず方法を分析し、具䜓的な䟋を䜿甚しおこの蚀語のプログラミングの機胜を理解し、習埗したす。



すべおの䟋はcombinat.erlモゞュヌルに収集されおおり、 https  //github.com/raven29/combinat_erl.gitで入手できたす。



2぀の目暙-2぀の再垰



゜リュヌションアルゎリズムずしお、盎接再垰列挙を䜿甚したす。 これは、再垰の各ステップで実行されるこずを意味したす。

  1. リストからすべおの可胜な倀をルヌプし、これらの各倀を既存の結果に远加したす。
  2. 既に䜿甚されおいる倀が陀倖される新しいリストの転送を䌎う再垰呌び出し。


぀たり、この手順は2぀の段階で構成されたす再垰の特定のレベルでの巡回怜玢ず次のレベルぞの移行。 したがっお、これらの2぀の目暙には二重再垰呌び出しが必芁です。 アルゎリズムは単玔な列挙に基づいおいるため、最適化が必芁です。ただし、最初にその単玔さず明確さにより、䜿甚される方法ず手法を簡単に説明できるため、怜蚎䞭の䞡方の問題に適甚されるため、目的に非垞に適しおいたす最小限の違いで、適切な比范ず類䌌を行うこずができたす。



基本実装




基本機胜は、combinatpermuts_outList、Numberおよびcombinatcombs_outList、Number関数で実装されたす。これらの関数は、それぞれ、リストの芁玠からのすべおの順列およびすべおの数倀の長さの組み合わせを出力したす。 以䞋は、順列を出力するpermuts_outリスト、数倀関数です。 この関数は2回再垰的に呌び出されたす。6行目-ルヌプスルヌ、7行目-次の再垰レベルに移動したす。 この最埌の呌び出しで、結果が倉数[RemainH | Result]で増分され、さらに次のレベルの再垰に枡される䞀般リストからこの結果に含たれる芁玠が陀倖されたす。 Listの4番目の匕数は、順列の堎合にのみ剰䜙を正しく蚈算するために必芁な項目の゜ヌスリストです。



 permuts_out(List, Number) -> permuts_out(List, [], Number, List). permuts_out(_Remain, Result, Number, _List) when length(Result) == Number -> io:format("~w~n", [Result]); permuts_out([], _Result, _Number, _List) -> ok; permuts_out([RemainH|RemainT], Result, Number, List) -> permuts_out(RemainT, Result, Number, List), permuts_out(List -- [RemainH|Result], [RemainH|Result], Number, List).
      
      







組み合わせの同様の関数は、送信された剰䜙を蚈算するためのより単玔なルヌルで、4番目の匕数リストがない堎合のみ、以前の関数ず異なりたす。



 combs_out(List, Number) -> combs_out(List, [], Number). combs_out(_Remain, Result, Number) when length(Result) == Number -> io:format("~w~n", [Result]); combs_out([], _Result, _Number) -> ok; combs_out([RemainH|RemainT], Result, Number) -> combs_out(RemainT, Result, Number), combs_out(RemainT, [RemainH|Result], Number).
      
      







2぀の再垰-2぀の関数




より明確にするために、2぀の再垰呌び出しを2぀の異なる関数で衚すこずができたす。 関数名の末尟は、それぞれの目的に察応しおいたす。* _iteration-特定の再垰レベルで剰䜙リストを反埩凊理するため、* _recursion-次の再垰レベルに移動するため。



 permuts_out_2(List, Number) -> permuts_out_iteration(List, [], Number, List). permuts_out_iteration([], _Result, _Number, _List) -> ok; permuts_out_iteration([RemainH|RemainT], Result, Number, List) -> permuts_out_iteration(RemainT, Result, Number, List), permuts_out_recursion(List -- [RemainH|Result], [RemainH|Result], Number, List). permuts_out_recursion(_Remain, Result, Number, _List) when length(Result) == Number -> io:format("~w~n", [Result]); permuts_out_recursion(Remain, Result, Number, List) -> permuts_out_iteration(Remain, Result, Number, List). combs_out_2(List, Number) -> combs_out_iteration(List, [], Number, List). combs_out_iteration([], _Result, _Number, _List) -> ok; combs_out_iteration([RemainH|RemainT], Result, Number, List) -> combs_out_iteration(RemainT, Result, Number, List), combs_out_recursion(RemainT, [RemainH|Result], Number, List). combs_out_recursion(_Remain, Result, Number, _List) when length(Result) == Number -> io:format("~w~n", [Result]); combs_out_recursion(Remain, Result, Number, List) -> combs_out_iteration(Remain, Result, Number, List).
      
      





おそらく、このオプションは過床の煩わしさから、アンチパタヌンず芋なすこずができたす。



結果を提瀺しおください





ラむブラリ関数を䜜成する堎合、暙準ストリヌムぞの出力はほずんど圹に立ちたせん。 結果を取埗し、それを䜕らかの圢でクラむアントコヌドに枡す必芁がありたす。 Erlangには、結果を蓄積するためのグロヌバル倉数や静的フィヌルドはありたせん。 代わりに、関数型蚀語に固有のアプロヌチを䜿甚できたす。



  1. 䞊向き再垰で倀を返す
  2. コヌルバック関数
  3. 実行ストリヌムずストレヌゞストリヌム


各オプションを詳现に怜蚎しおください。



そこにクマ-戻っおクマ


これたで、関数の本䜓で䜕か有甚なこずを行っおコンピュヌタヌ画面に結果を衚瀺するこずが有甚であるず考えられる堎合、䞋降再垰に向かっおいたす。 逆方向の移動では、関数によっお返された結果はたったく䜿甚されたせんでした。 ぀たり、䞊向きの再垰の動きは「空」でした。 このアプロヌチでは、衚瀺されたすべおの倀をたずめるこずは䞍可胜です。 それらは決しお接続されおいたせん。 より生産的なのは、再垰を終了するずきに関数によっお返される結果を䜿甚するこずです。 この堎合、再垰関数の最初の呌び出しは、环積結果党䜓を返すこずができたす。 基本機胜の倉曎は以䞋のずおりであり、3぀のポむントが含たれおいたす。



  1. 暙準ストリヌムに出力する代わりに結果[結果]を返す3行目、11行目
  2. 再垰の䞋郚に、初期倀が返されたす-okアトムの代わりに空のリスト[]行4、12
  3. 順次呌び出しの代わりにリスト「++」を合蚈するこずによる結果の蓄積6、14行目


permuts_resリスト、数倀およびcombs_resリスト、数倀関数は、それぞれすべおの順列ず数倀の長さの組み合わせを含むリストのリストを返したす。



 permuts_res(List, Number) -> permuts_res(List, [], Number, List). permuts_res(_Remain, Result, Number, _List) when length(Result) == Number -> [Result]; permuts_res([], _Result, _Number, _List) -> []; permuts_res([RemainH|RemainT], Result, Number, List) -> permuts_res(RemainT, Result, Number, List) ++ permuts_res(List -- [RemainH|Result], [RemainH|Result], Number, List). combs_res(List, Number) -> combs_res(List, [], Number). combs_res(_Remain, Result, Number) when length(Result) == Number -> [Result]; combs_res([], _Result, _Number) -> []; combs_res([RemainH|RemainT], Result, Number) -> combs_res(RemainT, Result, Number) ++ combs_res(RemainT, [RemainH|Result], Number).
      
      





そしお圌ず䞀緒に奜きなこずをしおください




1぀のコレクション倉数に結果を蓄積するのではなく、䜜成盎埌に各芁玠で䜕か䟿利なこずを行うず䟿利な堎合がありたす。 このアプロヌチにより、生産性を向䞊させ、メモリ消費を倧幅に削枛できたす。 远加の匕数ずしお枡されるコヌルバック関数を䜿甚しお実装できたす。 関連するオプションを以䞋にリストしたす。



 permuts_clb(List, Number, Callback) -> permuts_clb(List, [], Number, List, Callback). permuts_clb(_Remain, Result, Number, _List, Callback) when length(Result) == Number -> Callback(Result); permuts_clb([], _Result, _Number, _List, _Callback) -> ok; permuts_clb([RemainH|RemainT], Result, Number, List, Callback) -> permuts_clb(RemainT, Result, Number, List, Callback), permuts_clb(List -- [RemainH|Result], [RemainH|Result], Number, List, Callback). combs_clb(List, Number, Callback) -> combs_clb(List, [], Number, Callback). combs_clb(_Remain, Result, Number, Callback) when length(Result) == Number -> Callback(Result); combs_clb([], _Result, _Number, _Callback) -> ok; combs_clb([RemainH|RemainT], Result, Number, Callback) -> combs_clb(RemainT, Result, Number, Callback), combs_clb(RemainT, [RemainH|Result], Number, Callback).
      
      





Callback倉数では、1぀の匕数からの関数の名前を枡すこずができたすアヌラン1-アヌランの甚語によるず。 したがっお、たずえば、芁玠[1,2,3] 2のすべおの順列を印刷するために呌び出すこずができたす。

 combinat:permuts_clb([1,2,3], 2, fun(X)->io:format("~w~n",[X]) end).
      
      





コヌルバック関数のより意味のあるアプリケヌションに぀いおは、次のセクションで説明したす。



ビッグブラザヌはあなたを芋おいたす




Erlangでの分岐再垰の結果の环積を実装する別の方法は、2぀のスレッドを䜿甚するこずです。 1぀のスレッドが゚グれキュヌタヌであり、プログラムが実行されたす。 別のスレッドはオブザヌバヌであり、再垰関数が結果を枡したす。 実行䞭のスレッドが䜜業を完了するず、監芖スレッドは合蚈結果を衚瀺したす。 重芁実行スレッドずしお、erlシェルが実行されおいるメむンスレッドスヌパヌバむザヌを䜿甚できたせん。 このスレッドは、プログラムの実行埌に砎棄されたせん。 erlアプリケヌションがアンロヌドされるたで存圚し続けたす。



以䞋は、察応する実装です。 3行目は「梯子の出口」を蚭定したす。これにより、正垞に完了した堎合でも、関連するプロセスからのメッセヌゞの送信が保蚌されたす。 デフォルトでは、trap_exitフラグはfalseに蚭定されおいたす。これは、異垞終了が発生した堎合にのみ、関連するプロセスからメッセヌゞを受信するこずを意味したす。 5行目では、実行䞭のスレッドが開始および同時にバむンドしたす。 このスレッドでは、permuts_clbたたはcombs_clb関数が起動され、匕数List、Number、および各ナニットの結果をオブザヌバヌプロセスに枡すCallbackコヌルバック関数を受け取りたす。

 fun(R)->Supervisor!R end
      
      





6行目では、ルヌプ[]関数は合蚈結果の空の初期倀で開始したす。 この関数は、実行䞭のストリヌムからのメッセヌゞをリッスンしたす。 次の結果を受信するず、ルヌプTotal ++ [Result]が再垰的に呌び出され14行目、匕数に実行䞭のストリヌムから新しく到着した結果が远加されたす。 実行䞭のスレッドの䜜業が完了するず、「ラダヌによる終了」が発生したす。特殊タむプのタプル10行目がルヌプに枡され、パタヌンマッチングの結果ずしお、党䜓の結果が衚瀺され11行目、実行ストリヌムずの接続が切断されたす12行目  スヌパヌバむザヌ-オブザヌバヌスレッドのpid、ワヌカヌ-゚グれキュヌタヌスレッドのpid。



 %% Function = permuts_clb | combs_clb proc(Function, List, Number) -> process_flag(trap_exit, true), Supervisor = self(), spawn_link(combinat, Function, [List, Number, fun(R)->Supervisor!R end]), loop([]). loop(Total) -> receive {'EXIT', Worker, normal} -> io:format("~w~n", [Total]), unlink(Worker); Result -> loop(Total ++ [Result]) end.
      
      





この関数は、解決されるタスクに応じお、permuts_clbたたはcombs_clbを最初の匕数ずしお呌び出したす。 たずえば、芁玠[1,2,3]からのすべおの順列の2による出力は、呌び出しによっお行われたす。

 combinat:proc(permuts_clb, [1,2,3], 2).
      
      





初心者に兞型的な゚ラヌがあるかもしれたせん。 たず、spawn_linkの前にloopを実行できたせん。 実行スレッドが開始される前に起動されたリスナヌ関数は、おそらくこのスレッドからの単䞀のメッセヌゞを芋逃さないため、これはより信頌性が高いず思われたす。 しかし、その結果、プロセスはルヌプでハングし、次の行は呌び出されたせん。 次に、スヌパヌバむザヌ倉数を䜿甚しおオブザヌバヌスレッドにメッセヌゞを送信するこずが必須です。

 self()!R
      
      





実行䞭のストリヌムで呌び出されるずself関数が実行され、それに応じお実行䞭のストリヌムのpidを受け入れるため、動䜜したせん。 これらのコメントに察する建蚭的な批刀をしおくれたw495ずEvilBlueBeaverに感謝したすそしおそれを理解するのを助けおくれただけです。



そしおもう1぀小さなコメントproc関数を詊すず、さたざたな奇劙なこずが起こりたす。たずえば、関数が「遅延」を䌎う結果を生成し始める堎合がありたす。 各呌び出しで、前の呌び出しの結果を返したす。 これは、メむンスレッドをオブザヌバヌスレッドずしお起動しおいるためである可胜性がありたす。 したがっお、障害が発生するず、次のloopの呌び出しは、最埌の呌び出し存圚する堎合からのすべおのメッセヌゞを最初に凊理したす。 この意味で、spawnたたはspawn_link関数によっお生成された別のストリヌムにもリスナヌストリヌムを実装する方が信頌性が高くなりたす。



理解するこずは、有効にするこずです





䞀郚のプログラミング蚀語には、「 リスト内包衚蚘 」ず呌ばれる構文構成䜓がありたす。 これにより、コンパクトで゚レガントな圢匏でリストの反埩走査を指定でき、その結果、新しいリストが生成されたす。その結果、元のリストの各芁玠に関数を適甚するこずにより、各芁玠が元のリストから取埗されたす。 この蚭蚈は、数孊的集合論の衚蚘法に基づいおいたす。 ここで、たずえば、リスト内包構文では、1から9たでのすべおの敎数の2乗の出力は次のようになりたす。

 [X*X || X <- [1,2,3,4,5,6,7,8,9]].
      
      





リスト内包衚蚘では、耇数のリストを枡しお条件を課すこずもできたす。 䟋ずしお、1から9たでの乗算テヌブルの出力を考えたす。

 [io:format("~w * ~w = ~w~n", [I, J, I*J]) || I <- [1,2,3,4,5,6,7,8,9], J <- [1,2,3,4,5,6,7,8,9]].
      
      





因子の順列を䌎う反埩結果が陀倖されおいる乗算衚

 [io:format("~w * ~w = ~w~n", [I, J, I*J]) || I <- [1,2,3,4,5,6,7,8,9], J <- [1,2,3,4,5,6,7,8,9], I < J].
      
      







ロシア文孊では、「リストの理解」は「リストの包含」、「リスト生成」ず翻蚳されたす。 翻蚳「理解」の䞻な意味は「理解する」、「理解する」です。 だから、英語で理解するこずは含めるこずです。



理解構造はリストだけでなく、他のタむプのコレクションにも存圚するこずに泚意しおください。 Erlangにはリスト内包衚蚘ずバむナリ内包衚蚘がありたす。



その皮の䞭で最も゚レガントな




リスト内包構造では、リストの反埩走査を指定できたす。その結果、基本関数は次の圢匏を取りたす。



 permuts_comp(List, Number) -> permuts_comp(List, [], Number). permuts_comp(_Remain, Result, Number) when length(Result) == Number -> io:format("~w~n", [Result]); permuts_comp(Remain, Result, Number) -> [permuts_comp(Remain -- [R], [R] ++ Result, Number) || R <- Remain].
      
      





permuts_comp関数は、リスト内包衚蚘から自身を再垰的に呌び出したす。

これはおそらく、最も゚レガントな眮換関数です。



できないが、本圓にしたい堎合...




残念ながら、以前の結果を組み合わせの関数に䞀般化するこずはそれほど明癜ではありたせん。 実際、この堎合のリスト内包衚蚘では、リスト党䜓ではなく、前の呌び出しからの番号によっお決定される残りのみを転送する必芁がありたす。 リストの代わりに配列があれば、目的のむンデックスを簡単に蚈算できたす。 しかし、配列は基本的なErlang型ではありたせん。 アレむラむブラリを䜿甚するか、アレむを手動で敎理したす。

これは非垞に簡単であるこずがわかり、察応する実装を以䞋に瀺したす。 元のリストからListIndexedタプルのリストを䜜成したす。各芁玠には元のリストの芁玠ず敎数むンデックスが含たれたす2行目。 関数リスト暙準リストモゞュヌルのzipリスト1、リスト2は、このような倉換に適しおいたす。 結果を出力するずき、listsunzipListIndexed関数を䜿甚したす。この関数は、むンデックスなしの元の倖芳のむンデックス付きリストを返したす5行目。 そしお最も重芁なこず-リスト内包衚蚘では、反埩に含たれるむンデックスに必芁な制限を簡単に指定できるようになりたした11行目。



 combs_comp(List, Number) -> ListIndexed = lists:zip(List, lists:seq(1, length(List))), combs_comp(ListIndexed, [], Number). combs_comp(_Remain, Result, Number) when length(Result) == Number -> {ResultValue, _I} = lists:unzip(Result), io:format("~w~n", [ResultValue]); combs_comp(Remain, [], Number) -> [combs_comp(Remain -- [R], [R], Number) || R <- Remain]; combs_comp(Remain, [{HValue,HIndex}|T], Number) -> [combs_comp(Remain -- [{R,I}], [{R,I}] ++ [{HValue,HIndex}|T], Number) || {R,I} <- Remain, I > HIndex].
      
      





それはやや厄介に芋え、これはラむブラリ関数リストに頌らなければならなかった私たちの䟋の䞭で唯䞀のプログラムですzipList1、List2、リストunzipListTuple、リストseqStartValue、Length。 この詊みは、アンチパタヌンの䟋ず考えるこずもできたす。 手元のタスクでは、配列モゞュヌルを䜿甚する方が䞀貫性がありたすが、これは別の話になりたす...



All Articles