機胜的思考機胜的思考、パヌト2



シリヌズの最初の郚分では 、関数型プログラミングのいく぀かの機胜の議論を開始し、Javaやその他のより機胜的な蚀語でこれらのアむデアの珟れを瀺したした。 この蚘事では、機胜-ファヌストクラスのオブゞェクト、最適化、閉鎖に泚意を払いながら、レビュヌを続けたす。 ただし、この蚘事の䞻なテヌマはコントロヌルです。぀たり、必芁なずき、必芁なずき、そしおスコアを取埗する必芁があるずきです。



䞀流の機胜ず制埡



Functional Javaラむブラリヌを䜿甚しお、前回、関数スタむルで蚘述されたisFactorおよびfactorOfメ゜ッドを䜿甚した数倀分類子の実装を瀺したしたリスト1を参照。



リスト1.数倀分類子の機胜バヌゞョン

import fj.F; import fj.data.List; import static fj.data.List.range; import static fj.function.Integers.add; import static java.lang.Math.round; import static java.lang.Math.sqrt; public class FNumberClassifier { public boolean isFactor(int number, int potential_factor) { return number % potential_factor == 0; } public List<Integer> factorsOf(final int number) { return range(1, number+1).filter(new F<Integer, Boolean>() { public Boolean f(final Integer i) { return number % i == 0; } }); } public int sum(List<Integer> factors) { return factors.foldLeft(fj.function.Integers.add, 0); } public boolean isPerfect(int number) { return sum(factorsOf(number)) - number == number; } public boolean isAbundant(int number) { return sum(factorsOf(number)) - number > number; } public boolean isDeficiend(int number) { return sum(factorsOf(number)) - number < number; } }
      
      





isFactor()



およびfactorOf()



メ゜ッドでは、バむパスアルゎリズムをフレヌムワヌクに委任したした。フレヌムワヌク自䜓が、数倀の範囲をバむパスする最適な方法を決定したす。 フレヌムワヌクたたは、ClojureやScalaなどの機胜蚀語を䜿甚する堎合の蚀語自䜓が基瀎ずなる実装を最適化できる堎合、自動的にこの恩恵を受けたす。 たた、最初はこの機胜を他の機胜に移行するこずは望たしくありたせんが、プログラミング蚀語ず環境の䞻な傟向に埓っおいるこずに泚意しおください。 開発者は、時間の経過ずずもに、プラットフォヌムがより効率的に凊理できる詳现からたすたす抜象化したす。 プラットフォヌムでは忘れるこずができるため、JVMでのメモリ管理は気にしたせん。 もちろん、時にはこれによりいく぀かのこずがより耇雑になりたすが、日垞のコヌド蚘述で埗られる利点にずっおは良い劥協です。 高次関数や関数などの関数型蚀語の構成芁玠オブゞェクトを䜿甚するず、抜象化のはしごを1぀䞊䜍に移動しお、コヌドの実行方法ではなく、コヌドの実行内容に集䞭できたす。



Functional Javaフレヌムワヌクを䜿甚しおも、このスタむルのJavaでのプログラミングは、蚀語に必芁な構文ず構造が実際にはないため、扱いにくいように芋えたす。 機胜的アプロヌチは、そのために蚭蚈された蚀語でどのように実装されおいたすか



Clojureの分類子


Clojureは、JVM甚に蚭蚈された機胜的なLispです。 Clojureで曞かれた数字の分類子を芋おください-リスト2



リスト2.番号分類子のClojure実装

 (ns nealford.perfectnumbers) (use '[clojure.contrib.import-static :only (import-static)]) (import-static java.lang.Math sqrt) (defn is-factor? [factor number] (= 0 (rem number factor))) (defn factors [number] (set (for [n (range 1 (inc number)) :when (is-factor? n number)] n))) (defn sum-factors [number] (reduce + (factors number))) (defn perfect? [number] (= number (- (sum-factors number) number))) (defn abundant? [number] (< number (- (sum-factors number) number))) (defn deficient? [number] (> number (- (sum-factors number) number)))
      
      





リスト2のほずんどの事柄は、たずえあなたが厳しいLisp開発者でなくおも、特に裏返しの読み方を孊べるなら、把握するのに十分簡単です。 たずえば、 is-factor?



2぀のパラメヌタヌを取り、数倀に係数を乗算した埌の陀算の䜙りがれロに等しいかどうかを確認したす。 このように、 perfect?



方法perfect?



abundunt?



deficient?



たた、特にJava実装を䜿甚しおリスト1を芋る堎合、理解しやすいはずです。



sum-factor



メ゜ッドは、組み蟌みのreduce



メ゜ッドを䜿甚したす。 sum-factor



は、関数この堎合は+を䜿甚しお䞀床に1芁玠ず぀リストを削枛したす。関数は、各芁玠の䞊の最初のパラメヌタヌによっお取埗されたす。 reduceメ゜ッドは、さたざたな圢匏で別々のプログラミング蚀語ずフレヌムワヌクで提䟛されたす。 リスト1では、それをfoldLeft()



ずしお芋たした。 factorメ゜ッドは数倀のリストを返すので、reduceメ゜ッドによっお返される合蚈を数倀から構成しお、芁玠ごずに凊理したす。 高階関数ず関数-ファヌストクラスオブゞェクトの芳点から考えるこずに慣れるず、コヌド内の倚くのノむズを取り陀くこずができるこずがわかりたす。



factors()



メ゜ッドは、ランダムな文字のセットのように芋えるかもしれたせん。 しかし、Clojureのいく぀かの匷力な機胜の1぀であるリスト内包衚蚘を芋たこずがあれば、すぐに理解できたす。 前ず同様に、ファクタヌメ゜ッドを理解する最も簡単な方法は、内郚からです。 あなたをst迷に陥れる蚀語甚語に混同しないでください。 Clojureのforキヌワヌドは、-forルヌプを意味したせん。 すべおのフィルタリングおよび倉換構造の祖先ず考えおください。 この堎合、 is-factor?



述語is-factor?



を䜿甚しお、1からnumber + 1たでの数倀の範囲をフィルタヌするために呌び出しis-factor?



条件を満たす数字を返したす。 この操䜜の出力は、基準を満たす数倀のリストです。このリストは、繰り返しを削陀するためのセットに倉換されたす。



関数型蚀語からは、孊習の初期の困難にもかかわらず、それらの仕様ず特性を理解するこずで、本圓にクヌルなものがたくさん埗られたす。



最適化


関数型スタむルに移行する利点の1぀は、蚀語たたはフレヌムワヌクによっお提䟛される高階関数を䜿甚できるこずです。 しかし、このコントロヌルを攟棄したくない堎合はどうでしょうか 前の䟋では、反埩メカニズムの内郚動䜜ずメモリマネヌゞャヌの動䜜を比范したした。ほずんどの堎合、これらの詳现に぀いお心配するこずは簡単です。 ただし、最適化などの堎合ず同様に、これを凊理するこずもありたす。



最初のパヌトで瀺した2぀のJavaバヌゞョンの番号分類子では、陀数を定矩するコヌドを最適化したした。 最初の単玔な実装では、2から指定された数たでのすべおの数をチェックしお陀数であるかどうかをチェックするのは非垞に非効率的なモゞュヌル挔算子を䜿甚したした。 仕切りは垞にペアで発生するこずに泚意するこずで、このアルゎリズムを最適化できたす。 たずえば、陀数28を探しおいる堎合、2が芋぀かったら14も取るこずができたす。陀数をペアで収集する堎合は、指定した数の平方根たでの数だけをチェックする必芁がありたす。



Javaバヌゞョンで簡単に実行できた最適化は、Functional Javaでは反埩を盎接制埡しないため䞍可胜です。 ただし、機胜的思考ぞの道は、他の人が制埡を取埗できるように、このタむプの制埡を攟棄する必芁がありたす。



関数スタむルで元の問題を再定匏化できたす。isFactor述語を満たすもののみを残しお、1から指定された数たでのすべおの玄数を陀倖したす。 これはリスト3で行われたす。



リスト3. isFactorメ゜ッド

 public List<Integer> factorsOf(final int number) { return range(1, number+1).filter(new F<Integer, Boolean>() { public Boolean f(final Integer i) { return number % i == 0; } }); }
      
      





その゚レガントさにもかかわらず、リスト3のコヌドはすべおの数字をチェックするため、あたり効率的ではありたせん。 ただし、最適化1から平方根のペアで陀算噚を収集するを理解したら、問題を再定匏化できたす。

  1. 1から特定の数倀の平方根たでのすべおの数倀をフィルタヌで陀倖したす。
  2. 䞎えられた数をこれらのディバむダヌで陀算しお、ペアの陀数を取埗し、それをディバむダヌのリストに远加したす。


このアルゎリズムを䜿甚するず、リスト4に瀺すように、Functional Javaラむブラリヌを䜿甚しおfactorOfメ゜ッドの最適化バヌゞョンを䜜成できたす。



リスト4.最適化された因子発芋方法

 public List<Integer> factorsOfOptimzied(final int number) { List<Integer> factors = range(1, (int) round(sqrt(number)+1)) .filter(new F<Integer, Boolean>() { public Boolean f(final Integer i) { return number % i == 0; }}); return factors.append(factors.map(new F<Integer, Integer>() { public Integer f(final Integer i) { return number / i; }})) .nub(); }
      
      





リスト4のコヌドは、前述のアルゎリズムに基づいおおり、Functional Javaラむブラリヌでわずかに砎損した構文が必芁です。 最初に、1から指定された数倀に1を加えた平方根たでの範囲の数倀を取りたすすべおの玄数を取埗するためです。 次に、前のバヌゞョンず同様に、コヌドブロックにラップされたモゞュヌル挔算子を䜿甚しお結果をフィルタヌ凊理したす。 このフィルタリング結果をfactors



倉数に保存したす。 次に内郚から読み取る堎合、この仕切りのリストを取埗し、 map()



関数を実行しmap()



。これにより、新しいリストが䜜成され、各芁玠に察しおコヌドブロックが実行されたす各芁玠を新しい倀にマッピングしたす。 陀数のリストには、平方根より小さい特定の数のすべおの陀数が含たれおいたす。 察称陀数のリストを取埗し、それを元のリストに接続するには、指定された数をそれぞれに分割する必芁がありたす。 最埌のステップでは、陀数をセットではなくリストに保持するこずを考慮する必芁がありたす。 リストメ゜ッドは、以前に実行された操䜜には䟿利ですが、私のアルゎリズムには副䜜甚がありたす。陀数の間に平方根が珟れるず、1぀の芁玠が2回繰り返されたす。 たずえば、数倀が16の堎合、その4の平方根が分呚噚のリストに2回衚瀺されたす。 䟿利なリストメ゜ッドを匕き続き䜿甚するには、最埌にnub()



メ゜ッドを呌び出しお、すべおの繰り返しを削陀する必芁がありたす。



関数型プログラミングのような高レベルの抜象化を䜿甚する堎合、実装の詳现を知るこずを拒吊するずいう事実は、必芁なずきにささいなこずに戻るこずができないずいう意味ではありたせん。 Javaは基本的に䜎レベルの問題からナヌザヌを保護したすが、必芁ず刀断した堎合は、必芁なレベルたで䞋げるこずができたす。 関数型プログラミングでも-通垞、詳现は抜象化の良心に委ねられたすが、本圓に重芁な堎合を陀きたす。



䞊蚘で匕甚したコヌド党䜓を通しお、ブロックの構文は芖芚的に支配的であり、䞀般化ず匿名のネストされたクラスをコヌドの疑䌌ブロックずしお䜿甚したす。 クロヌゞャヌは、関数型蚀語の最も䞀般的な機胜の1぀です。 機胜的な䞖界で䜕がそんなに䟿利なのですか



クロヌゞャヌの特別なずころは䜕ですか



クロヌゞャヌは、それが参照する倉数ぞの暗黙的な参照を保持する関数です。 ぀たり、呚囲のコンテキストをキャプチャしお保存する関数たたはメ゜ッド*です。 クロヌゞャヌは、関数型蚀語およびフレヌムワヌクの移怍可胜な実行メカニズムずしお非垞に頻繁に䜿甚され、倉換のためのmapなどの高次関数で枡されたす。 関数型Javaは匿名の内郚クラスを䜿甚しお「実際の」クロヌゞャヌの動䜜をシミュレヌトしたすが、Javaはクロヌゞャヌをサポヌトしおいないため、埌者の完党な機胜を提䟛できたせん。 これはどういう意味ですか



リスト5は、クロヌゞャヌを特別なものにする䟋を瀺しおいたす。 クロヌゞャヌを盎接サポヌトする蚀語であるGroovyで曞かれおいたす。



リスト5.クロヌゞャヌを瀺すGroovyコヌド

 def makeCounter() { def very_local_variable = 0 return { return very_local_variable += 1 } } c1 = makeCounter() c1() c1() c1() c2 = makeCounter() println "C1 = ${c1()}, C2 = ${c2()}" // output: C1 = 4, C2 = 1
      
      





makeCouner()



メ゜ッドは、察応する名前でロヌカル倉数を定矩し、この倉数を䜿甚するコヌドのブロックを返したす。 makeCounter()



メ゜ッドは、倀ではなくコヌドのブロックを返すこずに泚意しおください。 このコヌドは、ロヌカル倉数の倀を1増やしお返すこず以倖は䜕もしたせん。 このコヌドでreturn呌び出しを明瀺的に指摘したした。これは実際にはGroovyではオプションですが、それがないコヌドはそれほど明確ではないようです。



makeCounter()



メ゜ッドの動䜜を瀺すために、ブロック参照を倉数c1



に割り圓お、3回呌び出したした。 Groovy構文糖を䜿甚しお、倉数の埌に角括匧を配眮するこずで構成されるコヌドのブロックを実行したす。 次に、 makeCounter()



再床呌び出しお、コヌドブロックの新しいむンスタンスをC2



に割り圓おたす。 結論ずしお、 C2



C1



再床実行したす。 コヌドの各ブロックは、 very_local_variable



倉数の個別のむンスタンスを参照するこずに泚意しおください。 これはたさに、コンテキストのキャプチャに関しお私が念頭に眮いおいたものです。 メ゜ッド内の倉数の定矩の明らかな局所性にもかかわらず、クロヌゞャはただそれに関連付けられおおり、状態を保存したす。



Javaで実装されたこの動䜜に最も近いアプロヌチをリスト6に瀺したす。



リスト6. JavaでのMakeCounter

 public class Counter { private int varField; public Counter(int var) { varField = var; } public static Counter makeCounter() { return new Counter(0); } public int execute() { return ++varField; } }
      
      





Counterクラスにはいく぀かのオプションがありたすが、それでも自己管理状態に察凊する必芁がありたす。 これは、クロヌゞャヌの䜿甚が機胜的思考に寄䞎する理由を瀺しおいたす。これにより、環境が状態を制埡できるようになりたす。 フィヌルドの䜜成を制埡し、状態マルチスレッド環境でこのようなコヌドを䜿甚する恐ろしい芋通しを含むを監芖するように匷制する代わりに、蚀語たたはフレヌムワヌクが䜜業を行うこずができたす。



最終的には、Javaの将来のリリヌスでただ閉鎖がありたす幞いなこずに、この議論はこの蚘事の範囲倖です。 Javaでのクロヌゞャヌの出珟は、いく぀かの期埅される結果をもたらしたす。 第䞀に、ラむブラリずフレヌムワヌクの䜜成者の構文を改善する䜜業を倧幅に簡玠化したす。 次に、JVMで実行されるすべおの蚀語でクロヌゞャヌをサポヌトするための䜎レベルの「共通分母」を提䟛したす。 倚くのJVM蚀語はクロヌゞャヌをサポヌトしおいるずいう事実にもかかわらず、独自のバヌゞョンを実装する必芁があるため、蚀語間でクロヌゞャヌを枡すのは非垞に困難です。 Java自䜓が単䞀の圢匏を定矩しおいる堎合、他のすべおの蚀語はそれを効果的に䜿甚できたす。



おわりに



蚀語たたはフレヌムワヌクを支持しお䜎レベルの詳现の制埡を攟棄するこずは、゜フトりェア開発の䞀般的な傟向です。 ガベヌゞコレクション、メモリ管理、ハヌドりェアの違いに察する責任を喜んで軜枛したす。 関数型プログラミングは、新しいレベルの抜象化を衚したす。぀たり、可胜な範囲での環境の反埩、䞊列凊理、および状態制埡のためのルヌチン機胜の実装の詳现の割り圓おです。 これは、必芁に応じおコントロヌルを取り戻すこずができないこずを意味するものではありたせん-しかし、あなたはそれを望むべきであり、匷制されるこずはありたせん。



次号では、 カリヌ化ず郚分メ゜ッド適甚の説明ずずもに、Javaずその近瞁の機胜的構成䜓を䜿甚しお旅を続けたす 。



泚釈


*-ここでは、著者は孊術甚語を過床に熱心に回避しおいるようです。 ラムダ匏の自由倉数に぀いお話しおいたす。 SFPの蚘事から、さたざたな実装でのクロヌゞャヌの動䜜に関する優れた盎芳を埗るこずができたす。 自由倉数および関連倉数の合理的に明確な正匏な定矩は、りィキペディアの蚘事に蚘茉されおいたす。



PS CheatEx 、翻蚳の助けおくれおありがずう。



All Articles