2011-01-02 8 views
9

私の目標は、特定の拡張機能のすべてのファイル名を含むが、拡張子を持たない配列を持つことです。C++ STLの `transform`のようなObjective-Cアルゴリズムはありますか?

述語フィルタとinstructions on how to split a path into filename and extensionを使用しているelegant solution to get all filenames of a specific extensionがありますが、それらを結合するにはループを作成する必要があります(ひどいのではなく、どちらも優雅ではありません)。

Objective-C(述語機構に似ているかもしれません)が配列のすべての要素にいくつかの関数を適用し、結果を2番目の配列に入れる方法がありますか?C++ STLのtransformアルゴリズムのようにですか?私が書きたいのですがどのような

// let's pretend 'anArray' was filled by querying the filesystem and not hardcoded 
NSArray* anArray = [[NSArray alloc] initWithObjects:@"one.ext", @"two.ext", nil]; 

// that's what I liked to write (pseudo code) 
NSArray* transformed = [anArray transform: stringByDeletingPathExtension]; 

// Yuji's answer below proposes this (which may be as close as you can get 
// to my wish with Objective C) 
NSArray* transformed = [anArray my_arrayByApplyingBlock:^(id x){ 
          return [x stringByDeletingPathExtension]; 
         }]; 
+2

は要素をループで、私は本当に自分自身そうすることのぎこちなさを見ていないよ... –

+0

あなたがしたいですかインプレースで行うか、新しいアレイを作成するか?また、ファイルへのパスを削除する必要がありますか?サンプルデータ(初期配列要素を示し、結果をどのようにしたいか)が役立ちます。質問を書く際には、情報を幅広く提供し、簡潔にしておくことが最善の方法です。 – outis

+1

@Nicholas:抽象度が高く、保守性が高いです。 1つの行にあなたの意図を表すことができ、同じループを何度も書く必要がない(またはコピー/ペーストする)と、コードを理解して維持するのがより簡単になります。 1行が十分に進んでいれば(普通のプログラマーにとってはあまりにも賢明です)、私はループすることも好きです。 – pesche

答えて

9

ココア高次メッセージングと呼ばれる話題だし、ウェブ上の多くの人々によって開発されました。 hereから始めて、グーグルグーグルを試してみてください。

  • [anArray map]は一時オブジェクト(たとえばhom
  • homがメッセージstringByDeletingPathExtension
  • を受け作成します。彼らはあなたが

    NSArray*transformed=[[anArray map] stringByDeletingPathExtension]; 
    

    を行うことができるように、次のような考え方があるNSArrayにカテゴリメソッドを追加します

  • homは、すべての要素にメッセージを再送信します。anArray
  • homは結果を収集し、結果の配列を返します。

あなただけの迅速な変換したい場合、私は、カテゴリメソッドを定義します

@interface NSArray (myTransformingAddition) 
-(NSArray*)my_arrayByApplyingBlock:(id(^)(id))block; 
@end 

@implementation NSArray (myTransformingAddition) 
-(NSArray*)my_arrayByApplyingBlock:(id(^)(id))block{ 
    NSMutableArray*result=[NSMutableArray array]; 
    for(id x in self){ 
      [result addObject:block(x)]; 
    } 
    return result; 
} 
@end 

次にあなたが

NSArray* transformed=[anArray my_arrayByApplyingBlock:^id(id x){return [x stringByDeletingPathExtension];}]; 

ノートにブロックを作成し、構造^ return-type (arguments) { ...}を行うことができます。戻り値の型は省略することができ、clangはそれを推測する上でかなりスマートですが、gccはそれについてかなり厳密で、いつか指定する必要があります。 (この場合には、NSString*を返す[x stringBy...]を持ってreturnの文から推測だ。だから、GCCは、このようにエラーが出たとき、GCCは互換性がないと考える代わりにidNSString*、するブロックの戻り値の型を推測します。)

OS X LeopardまたはiOS 3では、PLBlocksを使用してブロックをサポートできます。私の個人的な主観は、新しいソフトウェアを気にする人は、通常は最新のOSにアップグレードするので、最新のOSをサポートすることは問題ないはずです。古いOSをサポートしても2倍にはならないでしょう。

THAT SAIDは、上記のすべてのことを行う素晴らしいオープンソースフレームワークです。詳しくはhere、特にFunctionalKitのリンクを参照してください。


さらに追加:擬似コード[array transform:stringByDeletingPathExtension]を実際に実現するのは簡単です。

@interface NSArray (myTransformingAddition) 
-(NSArray*)my_transformUsingSelector:(SEL)sel; 
@end 

@implementation NSArray (myTransformingAddition) 
-(NSArray*)my_transformUsingSelector:(SEL)sel;{ 
    NSMutableArray*result=[NSMutableArray array]; 
    for(id x in self){ 
      [result addObject:[x performSelector:sel withObject:nil]]; 
    } 
    return result; 
} 
@end 

次のように次に、あなたがそれを使用することができます。

NSArray*transformed=[array my_transformUsingSelector:@selector(stringByDeletingPathExtension)]; 

を、私はそれはあまり好きではないが。このメソッドを使用するには、配列内のオブジェクトに対してすでにメソッドが定義されている必要があります。たとえば、NSStringにメソッドとして実行したい操作がない場合、この場合はどうしますか?あなたは最初のカテゴリを経由してNSStringに追加する必要があります。

@interface NSString (myHack) 
-(NSString*)my_NiceTransformation; 
@end 

@implementation NSString (myHack) 
-(NSString*)my_NiceTransformation{ 
    ... computes the return value from self ... 
    return something; 
} 
@end 

次にあなたが

NSArray*transformed=[array my_transformUsingSelector:@selector(my_NiceTransformation)]; 

を使用することができます。しかし、あなたが最初に他の場所でメソッドを定義する必要があるため、それは、非常に冗長になりがち。私は

NSArray*transformed=[array my_arrayByApplyingBlock:^id(id x){ 
    ... computes the return value from x ... 
    return something;  
}]; 

に最後に、my_か何かのような接頭辞で始まっていないカテゴリのメソッドを追加したことがないよう、呼び出しサイトで直接操作したいものを提供好みます。たとえば、将来的にAppleはtransformと呼ばれる素晴らしい方法を提供するかもしれません。しかし、すでにカテゴリにtransformというメソッドがあると、未定義の動作につながります。実際には、すでにクラス内にAppleのプライベートメソッドがあることが起こる可能性があります。

+0

私をObjective-Cブロックに紹介してくれてありがとう。今までこのキャレットを見たことがありません...あなたのコードを(質問の編集として追加して)使用しようとすると、次のようなコンパイラエラーが発生します: '互換性のないブロックポインタ型の初期化 'struct NSString *(^)(struct objc_object *) '、' struct objc_object *(^)(struct objc_object *) ''が必要です。私は何が間違っているかを知るのにまだ十分なブロックをマスターしていない。 – pesche

+0

[Apple Documentation on Blocks](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Blocks/Articles/00_Introduction.html)には、ブロックがiOS 4以降で使用できることが記載されています。 iOS 3.1.xでアプリを実行しようとするとどうなりますか? – pesche

+0

ああ、ごめんなさい。編集された答えを参照してください。 – Yuji

11

実際、非常に簡単な方法があります。それは2003年以来ずっとありました。

NSArray *array = [NSArray arrayWithObjects:@"one.ext", @"two.ext", nil]; 

// short solution 
NSArray *transformed = [array valueForKey:@"stringByDeletingPathExtension"]; 

// long solution (more robust against refactoring) 
NSString *key = NSStringFromSelector(@selector(stringByDeletingPathExtension)); 
NSArray *transformed = [array valueForKey:key]; 

は、両方の出力を生成:すべての `transform`が本当にありません考慮

(
    one, 
    two 
) 
関連する問題