4

別の質問では、ユーザーは次のようなコードを書くことを提案しました。

def list = ['a', 'b', 'c', 'd']
def i = 0; 
assert list.collect { [i++] } == [0, 1, 2, 3]

このようなコードは、他の言語では、collectのコンテンツがそのコンテキストの状態を変更するため(ここではの値を変更するため)、悪い習慣と見なされますi。言い換えれば、閉鎖には副作用があります。

このような高階関数は、クロージャを並行して実行し、新しいリストに再度アセンブルできる必要があります。クロージャでの処理が長く、CPUを集中的に使用する操作である場合は、それらを別々のスレッドで実行する価値があるかもしれません。collectそれを達成するためにを使用するように変更するのは簡単ExecutorCompletionServiceですが、それは上記のコードを壊します。

問題の別の例は、何らかの理由collectで、たとえば逆の順序でコレクションを参照する場合です。この場合、結果はになります[3, 2, 1, 0]。この場合、リストは元に戻されていないことに注意してください。0は、実際には「d」にクロージャを適用した結果です。

興味深いことに、これらの関数は、コレクションのJavaDocのこのコレクションを反復処理する」で文書化されています。これは、反復がシーケンシャルであることを示しています。

groovy仕様は、またはのような高階関数で実行の順序を明示的に定義していますcollecteach?上記のコードは壊れていますか、それとも大丈夫ですか?

4

2 に答える 2

3

上記の理由から、クロージャで明示的な外部変数が信頼されるのは好きではありません。

確かに、私が定義しなければならない変数が少なければ少ないほど、私は幸せです;-)

おそらく並列のことについても、単一のスレッドで処理するには多すぎることが判明した場合は、常にある程度のGParsの愛らしさでラップすることを目的としてコーディングしてください。このため、あなたが言うように、あなたは可能な限り可変性を少なくし、副作用(上記の外部カウンターパターンなど)を完全に回避しようとします

質問自体については、collect関数の例としてソースコードを調べると、Object(イテレータの参照方法CollectionMapは少し異なりますが、同様の方法で実行されます)が繰り返されInvokerHelper.asIterator(self)、次のようになります。結果リストへの各クロージャ呼び出しの結果。

InvokerHelper.asIterator(ここでもソースはここにあります)基本的iterator()に、渡されたオブジェクトのメソッドを呼び出します。

したがって、リストなどの場合、イテレータによって定義された順序でオブジェクトを繰り返し処理します。

したがって、IterableインターフェイスIterableデザインに従って独自のクラスを作成し(ダックタイピングのおかげで実装する必要はありません)、コレクションの反復方法を定義することができます。

Groovyの仕様について聞いてみると、この答えはあなたが望むものではないかもしれませんが、答えはないと思います。Groovyが実際に「完全な」仕様を持ったことは一度もありません(実際、これは一部の人々が嫌うgroovyに関するポイントです)。

于 2011-11-11T12:12:59.997 に答える
1

関数の受け渡しcollectfindAll副作用のない状態を維持することは、複雑さを低く抑えるだけでなく、将来並列実行が必要になった場合に備えてコードをより並列対応にするために、一般的には良い考えだと思います。

ただしeach、関数の副作用がない状態を維持する意味はあまりありません。何も実行されないためです(実際、このメソッドの唯一の目的は、for-eachループとしての動作を置き換えることです)。Groovyのドキュメントには、実行順序を定義する必要がある使用例each(およびそのバリアント、eachWithIndexおよび)がいくつかあります。reverseEach

さて、実用的な観点から、のようなメソッドでいくつかの副作用のある関数を使用しても問題ない場合があると思いますcollect。たとえば、リストを[index, value]ペアで変換するには、atransposeとrangeを使用できます

def list = ['a', 'b', 'c']
def enumerated = [0..<list.size(), list].transpose()
assert enumerated == [[0,'a'], [1,'b'], [2,'c']]

またはinject

def enumerated = list.inject([]) { acc, val -> acc << [acc.size(), val] }

しかし、acollectとcounterもそのトリックを実行し、結果が最も読みやすいと思います。

def n = 0, enumerated = list.collect{ [n++, it] }

collectさて、Groovyがindex-value-param関数を備えた同様のメソッドを提供した場合(Jiraの問題を参照) 、この例は意味がありませんが、実用性が純粋なIMOよりも優れている場合があることを示しています:)

于 2011-11-11T21:08:44.217 に答える