2009-09-22 12 views
17

を使用して、私はObjective-Cののブロックを使って実験して今日はので、私は賢いこととNSArrayのに私は他の言語で見てきたいくつかの機能的なスタイルのコレクション・メソッドを追加しようと思いました:のObjective-Cブロック

@interface NSArray (FunWithBlocks) 
- (NSArray *)collect:(id (^)(id obj))block; 
- (NSArray *)select:(BOOL (^)(id obj))block; 
- (NSArray *)flattenedArray; 
@end 

collect:メソッドは、配列内の各項目に対して呼び出され、その項目を使用していくつかの操作の結果を返すと予想されるブロックを受け取ります。結果はそれらすべての結果の集合です。ブロックがnilを返す場合は、結果セットに何も追加されません。

select:メソッドは、元のアイテムだけを持つ新しい配列を返します。引数としてブロックに渡されると、ブロックが返されますはい。

最後に、flattenedArrayメソッドは配列の項目を反復処理します。項目が配列の場合、その項目に対して再帰的にflattenedArrayを呼び出し、その結果を結果セットに追加します。項目が配列でない場合は、項目を結果セットに追加します。すべてが終了すると、結果セットが返されます。

インフラストラクチャがあったので、テストケースが必要でした。システムのアプリケーションディレクトリにあるすべてのパッケージファイルを見つけることにしました。これは私が思いついたものです。

NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) collect:^(id path) { return (id)[[[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:nil] collect:^(id file) { return (id)[path stringByAppendingPathComponent:file]; }]; }] flattenedArray] select:^(id fullPath) { return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; }]; 

これはすべて1行であり、恐ろしいことです。私は改行と字下げを追加していくつかのアプローチを試みてそれをきれいにしようとしましたが、実際のアルゴリズムはすべてのノイズで失われているように感じます。私はそれが単なる構文的なものか、それとも問題になっている機能的なスタイルを使った私の相対的な経験であるかはわかりません。

比較のために、私は「昔ながらの方法」それを行うことを決めただけのループを使用します。

NSMutableArray *packagePaths = [NSMutableArray new]; 
for (NSString *searchPath in NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES)) { 
    for (NSString *file in [[NSFileManager defaultManager] contentsOfDirectoryAtPath:searchPath error:nil]) { 
     NSString *packagePath = [searchPath stringByAppendingPathComponent:file]; 
     if ([[NSWorkspace sharedWorkspace] isFilePackageAtPath:packagePath]) { 
      [packagePaths addObject:packagePath]; 
     } 
    } 
} 

IMO、このバージョンを書くのは簡単だったとブーツに、より読みやすいです。

これはどういうわけか悪い例でしたが、ブロックを私に使用する正当な方法のようです。 (私は間違っていますか?)これをきれいにして、ループバージョンよりも明確にするブロックでObjective-Cコードを記述したり構造化する方法について何か不足していますか?

答えて

19

改行を使用して、複数の行にまたがって通話を分割します。

AppleのAPI全体で使用されている標準的なパターンは、メソッドまたは関数が1つのブロック引数しか取らず、その引数が常に最後の引数であることです。

これまでに行ったことがあります。良い。今

、APIを使用したコードを書く、のようなものを実行します。

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES); 
paths = [paths collect: ^(id path) { 
    ... 
}]; 
paths = [paths collect: ^(id path) { 
    ... 
}]; 
paths = [paths select: ^(id path) { 
    ... 
}]; 

すなわち、あなたの収集/選択/フィルタ/フラット/マップ/別々のステップとしてのすべてのステップを実行します。これは、連鎖したメソッド呼び出しよりも速く/遅くはありません。

あなたは完全なインデントとそうそして、ブロックの側面に巣ブロックに必要がある場合:

paths = [paths collect: ^(id path) { 
    ... 
    [someArray select:^(id path) { 
     ... 
    }]; 
}]; 

だけであればネストされたステートメントなどのように。複雑すぎるときは、必要に応じてそれを関数やメソッドにリファクタリングします。

+1

ニースソリューション:マルセルWeiherは、あなたがここに見つけることができるのObjective-CでのHOMに関する広範囲の作業を、行っています。私はおそらくそれらの内容に基づいていくつかの値を命名し、 'paths'と呼ばれるものに終わるでしょう。ちょうど私の2セント。 –

+0

UIViewアニメーションは2ブロックを使用します。 –

+0

彼はパターンがキヤノンではないと言った。複数のブロックを取るメソッドはすべて最終的なパラメータとしてブロックを持ちます。さらに、これらのAPIはiOS 4に登場しました。 – logancautrell

2

私は問題は(Pythonの主張に反する;)空白が問題だと思います。より機能的なスタイルでは、他の関数型言語のスタイルをコピーするのが理にかなっているようです。私はこれがループしたバージョンよりも明確であると言うではないでしょう

NSArray *packagePaths = [[[NSSearchPathForDirectoriesInDomains(NSAllApplicationsDirectory, NSAllDomainsMask, YES) 
          collect:^(id path) { 
             return [[[NSFileManager defaultManager] 
               contentsOfDirectoryAtPath:path 
                    error:nil] 
               collect:^(id file) { 
                 return [path stringByAppendingPathComponent:file]; 
                 } 
              ]; 
            } 
          ] 
          flattenedArray 
          ] 

          select:^(id fullPath) { 
            return [[NSWorkspace sharedWorkspace] isFilePackageAtPath:fullPath]; 
           } 
         ]; 

:あなたの例を記述するために、よりLISP-yの道のようなものである可能性があります。他のツールと同様に、ブロックはツールであり、それらがジョブの適切なツールである場合にのみ使用する必要があります。読みやすさに問題がある場合、私はそれが仕事のための最良のツールではないと言いたいと思います。ブロックは、後で、基本的に不可欠な言語への追加です。関数型言語の簡潔さが本当に必要な場合は、関数型言語を使用してください。